Hacker News new | past | comments | ask | show | jobs | submit login
Gevent: the Good, the Bad, the Ugly (mixpanel.com)
44 points by btmorex on Oct 29, 2010 | hide | past | favorite | 27 comments

Using a non-blocking server with Python does not really make sense since most libraries in Python are blocking by nature and gevent only solves the most simple conversions by monkey patching (and like the author notes this is very error prone).

If you want to use non-blocking in Python then using something like Twisted is an option since Twisted implements most things in an non-blocking way, but Twisted hasn't really gained that much traction in the many years of its existence.

So yes if you do a hello world benchmark then you get a 4x speed improvement, but if you do any library calls (such as doing a MySQL query) you will get decreased perfomance since the MySQL Python library is blocking (and most other libraries are blocking as well)...

Using a non-blocking server with Python does make sense if you use non-blocking code for the things that take a long time. Here's an example: a server takes 400ms to handle a request. Of that time, 300ms is spent waiting for an HTTP response to an external service. The other 100ms is spent accessing the local database and other blocking operations. If the server does not block waiting for the HTTP response, then an instance of the server can handle 10 requests a second (1000ms / (400ms - 300ms)). If the server does block, then an instance of the server can handle 2.5 requests per second (1000ms / 400ms). An instance of the non-blocking server has 4x the throughput of the blocking server.

My argument is that most things that potentially take a lot of time are blocking by default in Python, which makes it a bad fit for making non-blocking servers... I am not arguing that blocking servers have better perfomance than non-blocking servers...

I am arguing that a small number of things take a long time in a web server. Can you give some examples of things that potentially take a long time in a web server that are not already covered by Tornado (http client, long polling)?

Database drivers (Redis, MySQL, Tokyo Tyrant etc.) and a bad query/overheated database server can easily take over 1 sec execution, which would basically stall your servers each time such a query is run. File handling and system calls are blocking in Python. E.g. if your system call is expensive - say you are processing an image via imagemagick, very common for most web applications, then it will block your server.

Blocking for short periods of time is fine if you run more than one instance of the python application per core.

If database database access is taking more than a second, then I recommend fixing that problem first.

The advantage here is the usage of Libevent which makes it only listen when a eventlet sends back a message. It's essentially using the same tech (event wise) as NGINX.

Because it's using event's it has not blocking IO. But is limited when the tasks are CPU bound

That's simply not true. Yes you have to be smart and know which Python libraries can block (typically those that do their network calls in C, or those that do disk I/O). In practice however this is not a difficult task. After writing several major projects with gevent, all of which make several thousands of network requests per minute, I have to say that it's a pleasure to use. Not magic, but very useful. I have written non-trivial apps with Twisted, and dabbled with Tornado. Gevent avoids all of the "inside out" coding in an elegant way.

gevent runs most of the protocol implementations written on top of standard socket and ssl modules. For example, pure Python mysql-connector works fine with gevent.

There's also a mysql driver for gevent implemented as an extension: http://github.com/mthurlin/gevent-MySQL

I wouldn't recommend mysql-connector in production...

A while back we avoided gevent because of this but used eventlet instead because they had a native MySQL patched driver.

Performance on the pure connector was just too bad.

I mentioned mysql-connector as an example of a complex enough library that runs with gevent via monkey patching (in reply to "gevent only solves the most simple conversions by monkey patching", which is wrong).

Have you tried gevent-MySQL? It's written in Cython and as far as I know is fast.

There's also eventlet, which gevent was (essentially) forked from - it has the advantage of having both libevent backends (like gevent, and aren't threadsafe due to libevent) and threadsafe backends too (not that I have much call to use threads while using a greenthread-based framework).

Sure performance is worse but still better since threads yield while blocking on IO to other threads. You're doing as much work as possible and bottled only by CPU in this case.

The current project I'm working on involves managing and launching a lot of http requests to different web service api's, and gevent really is the best thing since sliced bread.

Things like rate limiting (gevent can monkey patch time.sleep), error handling, and scaling to a large number of concurrent connections (see gevent.pool.Pool) have been added to my app with surprisingly little increase in code complexity. The fact that you write your code as if it was synchronous makes it so much easier to maintain. I don't have to find my way through callback spaghetti. I also find asynchronous much easier to grok than threading (and it's also more lightweight)

This is very interesting, but I don't think comparing it to paste is valid. Paste is more meant as a development server, is it not?

No, not at all. Well, people do use it for development, but it's pretty much the recommended deployment option for several Python web frameworks, including Pylons.

Not true. Production deployments in Pylons are primarily done with mod_wsgi behind apache or in some cases uwsgi behind nginx.

Paste is generally not recommended outside of development purposes.


"There are lots of ways of deploying an application, one of which is to use the paster serve command which takes the configuration file that has already been used to setup the application and serves it on a local HTTP server for production use."

I've spent a good year developing with Pylons and this question of recommended production setup comes up about once every couple months in the official mailing list. Paste is considered by far the worst option in terms of performance, which is why most devs go with Apache/mod_wsgi. Nobody recommends Paste. Some (in my case) deployed with nginx/uwsgi.

The official docs recommend Paste. We've had apps in production running on Paste since 2007. At the time, neither mod_wsgi or uwsgi were around. Certainly it may not be the best way to run in production, but it's not for development only.

I wonder how it might compare to Tornado (http://www.tornadoweb.org/)

Here's a blog post from several months ago: http://nichol.as/benchmark-of-python-web-servers

Gevent uses the http server built into libevent, which is written in C, so it's no surprise that it is fast.

I love Tornado personally, but I wanted to write synchronous code.

Eventlet (the project that Gevent forked from) allows you to write hubs while gevent only supports the libevent hub. So I made http://github.com/vishnevskiy/greentornado to allow Eventlet to run on Tornado's IOLoop. Been working for a while in production with no issues.

Would have made it for gevent though if I could since after reading the code of gevent and Eventlet I found that gevent's code base was much cleaner.

The creators of Tornado recommend writing synchronous code for everything except "long" operations for which you have no control. Examples of these include long polling and requests to third party services. Requests to your local database are not long operations. If these requests are long operations, then you have a latency problem with your application.

The Tornado people recommend running more instances of the application than CPUs to handle the short blocking requests to local databases and what not.

Although it depends on what your app does, my guess is that typical applications written with this philosophy are mostly synchronous code.

With eventlet and gevent you can write asynchronous that looks synchronous. Which is the point of greentornado.

That being said we still use synchronous calls to our databases.

^ This is really good stuff. Writing synchronous code for async programs really helps with maintainability.

how is this better than (the mature, well engineered) twisted?

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact