Libevent provides cross-platform asynchronous callbacks on sockets and file descriptors. Different operating systems have different ways of handling this efficiently, for example linux has kernel support for this operation which can scale to tens of thousands of sockets. It’s all pretty complicated but libevent makes it very simple. Along with a basic api that is used by highly scalable projects like memcached and thrift, it also has asyncronus a dns lookup api and a http server api.
Here’s an example of how simple it is to write a basic http server.
#include <sys/types.h>
#include <sys/time.h>
#include <sys/queue.h>#include <stdlib.h>
#include <err.h>
#include <event.h>
#include <evhttp.h>
void generic_handler(struct evhttp_request *req, void *arg)
{
struct evbuffer *buf;
buf = evbuffer_new();
if (buf == NULL)
err(1, "failed to create response buffer");
evbuffer_add_printf(buf, "Requested: %sn", evhttp_request_uri(req));
evhttp_send_reply(req, HTTP_OK, "OK", buf);
}
int main(int argc, char **argv)
{
struct evhttp *httpd;
event_init();
httpd = evhttp_start("0.0.0.0", 8080);
/* Set a callback for requests to "/specific". */
/* evhttp_set_cb(httpd, "/specific", another_handler, NULL); */
/* Set a callback for all other requests. */
evhttp_set_gencb(httpd, generic_handler, NULL);
event_dispatch();
/* Not reached in this code as it is now. */
evhttp_free(httpd);
return 0;
}
But is it fast? check out the benchmarks (on my laptop):
ab -c 1000 -n 10000 http://localhost/
Apache2: Requests per second: 1274.14 [#/sec] (mean)
libevent: Requests per second: 1584.37 [#/sec] (mean)
Kickass!
jake libevent, web service
It’s the age old problem… I have at least 50 user accounts on different web sites; del.icio.us, gmail, digg, etc… All of them with similar usernames and passwords. How nice would it be to have a single login that I can use to identify myself uniquely to any site or service. Enter the “new” OpenID standard and yet another great idea by the folks at SixApart. I know it sounds like microsoft passport but this is different because you can control every aspect of it, you can even run your own openid server.
With OpenID you’re user name becomes your website or blog url (if you have one). If you don’t have a blog or want one you can sign up with an openid provider. So if you see a comment on your blog from “3.rdrail.net” you can be sure we added it. If you see comments by “3.rdrail.net.somespammer.com”, it should be obvious that we didn’t.
Again, our OpenID is 3.rdrail.net, if you goto our site and look in the head tag you’ll see the following:
<link rel="openid.server"
href="http://www.myopenid.com/server" />
<link rel="openid.delegate"
href="http://tjake.myopenid.com/" />
<meta http-equiv="X-XRDS-Location"
content="http://www.myopenid.com/xrds?username=tjake.myopenid.com" />
This redirects a OpenID enabled service to my OpenID provider.
So if I use a openid enabled service, I enter my domain which proxies to my openid provider. Once I login to my OpenID provider and an openid persona (registration details) are sent along to that service.
This is under my control, I can deny services from accessing my persona, change my OpenID provider at anytime or run my own OpenID service. Thats why it rocks. It’s simple for newbies but experts have complete control over their own service.
You can be sure that our next project will include OpenID integration (We’ll try to add it to junkdepot.com too!)
jake openid, standards, web service
Synchronous processing and asynchronous processing have different strengths and weaknesses. Asynchronous processing is often confusing to system developers, but it scales really well. Synchronus processing (i.e. multi-threaded) is easy to add into a traditional program but is often resource intensive.
A good analogy of Asynch vs Synch programming is writing a SAX XML parser vs a DOM parser… DOM is easy to code but heavyweight. SAX is more complicated to code since but way faster and less resource intensive.
When it comes to web services they need to be able to support many simultaneous clients and perform complicated backend processing.
A great backend component we’ve mentioned before is memcached. This uses asynchronus processing so it can support tens of thousands of connections and is extremely fast because it’s actual work it to store and retrieve data from an in memory hash table.
But sometimes you need to perform processing intensive requests like searching a large index or image processing… to do this using an asynchronous processing model would be tricky and ineffective. At the same time, reading and writing the request over a socket using a synchronous processing model (one thread per request) would be a waste (imagine 200 56k clients connecting to you at the same time, that means 200 threads). The best solution is to perform the network IO using asynchronous processing and request processing using multiple threads (synchronus processing).
The ACE toolkit defined this design pattern years ago and I’ve used it quite effectively in the past however ACE is a very heavyweight only c++ library and its learning curve is pretty steep. This is why we are using thrift instead, since its interoperable with many languages and contains a lightweight c++ toolkit.
We are building some pretty cool web services using Thrift we recently worked with facebook to implement Half-Synch/Half-Asynch support to its c++ toolkit. As a result we can now support thousands of long lived connections while processing requests in a large thread pool.. Best of both worlds!
jake c++, coding, facebook, programming, scaling, thrift, web service