The Gevent loop engine

Gevent is an amazing non-blocking Python network library built on top of libev and greenlet. Even though uWSGI supports Greenlet as suspend-resume/greenthread/coroutine library, it requires a lot of effort and code modifications to work with gevent. The gevent plugin requires gevent 1.0.0 and uWSGI asynchronous/non-blocking modes (updated to uWSGI 1.9) mode.

Notes

  • The SignalFramework is fully working with Gevent mode. Each handler will be executed in a dedicated greenlet. Look at tests/ugevent.py for an example.
  • uWSGI multithread mode (threads option) will not work with Gevent. Running Python threads in your apps is supported.
  • Mixing uWSGI’s Async API with gevent’s is EXPLICITLY FORBIDDEN.

Building the plugin (uWSGI >= 1.4)

The gevent plugin is compiled in by default when the default profile is used. Doing the following will install the python plugin as well as the gevent one:

pip install uwsgi

Building the plugin (uWSGI < 1.4)

A ‘gevent’ build profile can be found in the buildconf directory.

python uwsgiconfig --build gevent
# or...
UWSGI_PROFILE=gevent make
# or...
UWSGI_PROFILE=gevent pip install git+git://github.com/unbit/uwsgi.git
# or...
python uwsgiconfig --plugin plugins/gevent # external plugin

Running uWSGI in gevent mode

uwsgi --gevent 100 --socket :3031 --module myapp

or for a modular build:

uwsgi --plugins python,gevent --gevent 100 --socket :3031 --module myapp

the argument of –gevent is the number of async cores to spawn

A crazy example

The following example shows how to sleep in a request, how to make asynchronous network requests and how to continue doing logic after a request has been closed.

import gevent
import gevent.socket

def bg_task():
    for i in range(1,10):
        print "background task", i
        gevent.sleep(2)

def long_task():
    for i in range(1,10):
        print i
        gevent.sleep()

def application(e, sr):
    sr('200 OK', [('Content-Type','text/html')])
    t = gevent.spawn(long_task)
    t.join()
    yield "sleeping for 3 seconds...<br/>"
    gevent.sleep(3)
    yield "done<br>"
    yield "getting some ips...<br/>"
    urls = ['www.google.com', 'www.example.com', 'www.python.org', 'projects.unbit.it']
    jobs = [gevent.spawn(gevent.socket.gethostbyname, url) for url in urls]
    gevent.joinall(jobs, timeout=2)

    for j in jobs:
        yield "ip = %s<br/>" % j.value

    gevent.spawn(bg_task) # this task will go on after request end

Monkey patching

uWSGI uses native gevent api, so it does not need monkey patching. That said, your code may need it, so remember to call gevent.monkey.patch_all() at the start of your app. As of uWSGI 1.9, the convenience option --gevent-monkey-patch will do that for you.

A common example is using psycopg2_gevent with django. Django will make a connection to postgres for each thread (storing it in thread locals).

As the uWSGI gevent plugin runs on a single thread this approach will lead to a deadlock in psycopg. Enabling monkey patch will allow you to map thread locals to greenlets (though you could avoid full monkey patching and only call gevent.monkey.patch_thread()) and solves the issue:

import gevent.monkey
gevent.monkey.patch_thread()
import gevent_psycopg2
gevent_psycopg2.monkey_patch()

or (to monkey patch everything)

import gevent.monkey
gevent.monkey.patch_all()
import gevent_psycopg2
gevent_psycopg2.monkey_patch()

Notes on clients and frontends

  • If you’re testing a WSGI application that generates a stream of data, you should know that curl by default buffers data until a newline. So make sure you either disable curl’s buffering with the -N flag or have regular newlines in your output.
  • If you are using Nginx in front of uWSGI and wish to stream data from your app, you’ll probably want to disable Nginx’s buffering.
uwsgi_buffering off;