[openstack-dev] Asynchrounous programming: replace eventlet with asyncio
victor.stinner at enovance.com
Tue Feb 4 13:38:39 UTC 2014
I would like to replace eventlet with asyncio in OpenStack for the asynchronous programming. The new asyncio module has a better design and is less "magical". It is now part of python 3.4 arguably becoming the de-facto standard for asynchronous programming in Python world.
The asyncio module is a new module of Python 3.4 written by Guido van Rossum as an abstraction of existing event loop (Twisted, Tornado, eventlet, etc.). In fact, it's more than an abstraction: it has its own event loop and can be used alone. For the background, read the PEP:
"Asynchronous IO Support Rebooted: the "asyncio" Module"
For more information on asyncio, see its documentation:
The asyncio module is also known as "Tulip" which is third-party project written for Python 3.3. Tulip was written first and then it was integrated in Python 3.4.
The main difference between eventlet and asyncio is that context switching between two concurrent tasks is explicit. When a task "blocks" (ex: wait for an event), it should use the "yield from" syntax which will switch to the next task: it's similar to "greenlet.switch" in eventlet. So it becomes obvious which parts of the code do switch and which don't. With eventlet, you have to read the source code of a function before calling it to check if it may call greenlet.switch() or not, and so debugging is more difficult. Or worse, the function may switch in a new version of a module, you won't notice the change.
The asyncio module handles various kind of events: sockets, pipes, subprocesses, UNIX signals, etc. All these things are handled in a single event loop. You can uses an "exector" to run a blocking task in a pool of thread. There is a "low-level" API using transports and protocols, similar to Twisted transports and protocols. But there is also a "high-level" API using streams, which gives a syntax close to eventlet (except that you have to add "yield from"). See an example to send an HTTP request and print received HTTP headers, the "yield from reader.readline()" instruction "blocks" until it gets a full line:
The problem is that the asyncio module was written for Python 3.3, whereas OpenStack is not fully Python 3 compatible (yet). To easy the transition I have ported asyncio on Python 2, it's the new Trollis project which supports Python 2.6-3.4:
The Trollius API is the same than asyncio, the main difference is the syntax in coroutines: "yield from task" must be written "yield task", and "return value" must be written "raise Return(value)".
The first step to move from eventlet to asyncio is a new executor using Trollius in Olso Messaging:
The transition from eventlet to asyncio can be done step by step. Thanks to the greenio project, asyncio can reuse the greenlet event loop (and so run in the main thread). So asyncio and eventlet become "compatible". While asyncio can also run its own event loop in a separated thread.
If eventlet is completely replaced with asyncio in a project, greenio can be dropped, and asyncio event loop can be run its own event loop in the main thread.
When OpenStack will be compatible with Python 3.3, it will be possible to use the builtin asyncio module of Python 3.4 directly instead of Trollus. Since "yield from" is incompatible with Python 2, some parts of the code may need to have two versions (one for Python 2, one for Python 3) if we want to use the "Python 3 flavor" of asyncio... or Python 2 support might be simplify dropped.
More information about the OpenStack-dev