1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
.. _migration-guide:
Migrating off of Eventlet
=========================
There are two main use cases for Eventlet:
1. As a required networking framework, much like one would use ``asyncio``,
``trio``, or older frameworks like ``Twisted`` and ``tornado``.
2. As an optional, pluggable backend that allows swapping out blocking APIs
for an event loop, transparently, without changing any code.
This is how Celery and Gunicorn use eventlet.
Pretending to look like a blocking API while actually using an event loop
underneath requires exact emulation of an ever-changing and ever-increasing
API footprint, which is fundamentally unsustainable for a volunteer-driven
open source project.
This is why Eventlet is discouraging new users.
**Most of this document will focus on the first use case: Eventlet as the sole
networking framework.**
For this use case, we recommend migrating to Python's ``asyncio``, and we are
providing infrastructure that will make this much easier, and allow for
*gradual* migration.
For the second use case, we believe this is a fundamentally unsustainable
approach and encourage the upstream frameworks to come up with different
solutions.
Step 1. Switch to the ``asyncio`` Hub
-------------------------------------
Eventlet has different pluggable networking event loops.
By switching the event loop to use ``asyncio``, you enable running ``asyncio``
and Eventlet code in the same thread in the same process.
To do so, set the ``EVENTLET_HUB`` environment variable to ``asyncio`` before
starting your Eventlet program.
For example, if you start your program with a shell script, you can do
``export EVENTLET_HUB=asyncio``.
Alternatively, you can explicitly specify the ``asyncio`` hub at startup,
before monkey patching or any other setup work::
import eventlet.hubs
eventlet.hubs.use_hub("eventlet.hubs.asyncio")
Step 2. Migrate code to ``asyncio``
-----------------------------------
Now that you're running Eventlet on top of ``asyncio``, you can use some new
APIs to call from Eventlet code into ``asyncio``, and vice-versa.
To call ``asyncio`` code from Eventlet code, you can wrap a coroutine (or
anything you can ``await``) into an Eventlet ``GreenThread``.
For example, if you want to make a HTTP request from Eventlet, you can use
the ``asyncio``-based ``aiohttp`` library::
import aiohttp
from eventlet.asyncio import spawn_for_awaitable
async def request():
async with aiohttp.ClientSession() as session:
url = "https://example.com"
async with session.get(url) as response:
html = await response.text()
return html
# This makes a coroutine; typically you'd ``await`` it:
coro = request()
# You can wrap this coroutine with an Eventlet GreenThread, similar to
# ``evenlet.spawn()``:
gthread = spawn_for_awaitable(request())
# And then get its result, the body of https://example.com:
result = gthread.wait()
In the other direction, any ``eventlet.greenthread.GreenThread`` can be
``await``-ed in ``async`` functions.
In other words ``async`` functions can call into Eventlet code::
def blocking_eventlet_api():
eventlet.sleep(1)
# do some other pseudo-blocking work
# ...
return 12
async def my_async_func():
gthread = eventlet.spawn(blocking_eventlet_api)
# In normal Eventlet code we'd call gthread.wait(), but since this is an
# async function we'll want to await instead:
result = await gthread
# result is now 12
# ...
Cancellation of ``asyncio.Future`` and killing of ``eventlet.GreenThread``
should propagate between the two.
Using these two APIs, with more to come, you can gradually migrate portions of
your application or library to ``asyncio``.
Calls to blocking APIs like ``urlopen()`` or ``requests.get()`` can get
replaced with calls to ``aiohttp``, for example.
Depending on your Eventlet usage, during your migration, you may have to
deprecate CLI options that are related to Eventlet, we invite the reader
to take a look to :ref:`manage-your-deprecations`.
For a more comprehensive migration guide, please visit the
Eventlet migration guide available here:
`https://removal.eventlet.org/ <https://removal.eventlet.org/>`_.
This guide provides:
* **Detailed migration steps** to transition from Eventlet to modern alternatives.
* **Multiple alternatives** to Eventlet, including ``asyncio`` for asynchronous
programming and Python's native ``threading`` module for multithreading.
* **Advanced migration paradigms** to help you refactor your code incrementally
and minimize disruptions during the transition.
For example, the section `Preparing for Migration
<https://removal.eventlet.org/guide/preparing-for-migration/>`_
introduces strategies to prepare your codebase for migration.
It covers topics such as identifying blocking APIs, isolating Eventlet-specific
code, and gradually replacing it with ``asyncio`` or other alternatives.
The `awesome-asyncio <https://github.com/timofurrer/awesome-asyncio>`_ GitHub
repository proposes a curated list of awesome Python asyncio frameworks,
libraries, software, and resources. Do not hesitate to take a look at it.
You may find candidates compatible with asyncio that can allow you to replace
some of your actual underlying libraries.
Step 3. Drop Eventlet altogether
--------------------------------
Eventually you won't be relying on Eventlet at all: all your code will be
``asyncio``-based.
At this point you can drop Eventlet and switch to running the ``asyncio``
loop directly.
Known limitations and work in progress
--------------------------------------
In general, ``async`` functions and Eventlet green threads are two separate
universes that just happen to be able to call each other.
In ``async`` functions:
* Eventlet thread locals probably won't work correctly.
* ``evenlet.greenthread.getcurrent()`` won't give the result you expect.
* ``eventlet`` locks and queues won't work if used directly.
* Eventlet multiple readers are not supported, and so using
``eventtlet.debug.hub_prevent_multiple_readers`` neither.
In Eventlet greenlets:
* ``asyncio`` locks won't work if used directly.
We expect to add more migration and integration APIs over time as we learn
more about what works, common idioms, and requirements for migration.
You can track progress in the
`GitHub issue <https://github.com/eventlet/eventlet/issues/868>`_, and file
new issues if you have problems.
Alternatives
------------
If you really want to continue with Eventlet's pretend-to-be-blocking
approach, you can use `gevent <https://www.gevent.org/>`_.
But keep in mind that the same technical issues that make Eventlet maintenance
unsustainable over the long term also apply to Gevent.
|