Skip to content

Commit 407eb76

Browse files
authored
document using gevent for async (#5900)
2 parents 4f79d5b + ac5664d commit 407eb76

File tree

9 files changed

+168
-142
lines changed

9 files changed

+168
-142
lines changed

docs/async-await.rst

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,6 @@ method in views that inherit from the :class:`flask.views.View` class, as
2323
well as all the HTTP method handlers in views that inherit from the
2424
:class:`flask.views.MethodView` class.
2525

26-
.. admonition:: Using ``async`` with greenlet
27-
28-
When using gevent or eventlet to serve an application or patch the
29-
runtime, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
30-
required.
31-
3226

3327
Performance
3428
-----------
@@ -78,15 +72,15 @@ Flask based on the `ASGI`_ standard instead of WSGI. This allows it to
7872
handle many concurrent requests, long running requests, and websockets
7973
without requiring multiple worker processes or threads.
8074

81-
It has also already been possible to run Flask with Gevent or Eventlet
82-
to get many of the benefits of async request handling. These libraries
83-
patch low-level Python functions to accomplish this, whereas ``async``/
84-
``await`` and ASGI use standard, modern Python capabilities. Deciding
85-
whether you should use Flask, Quart, or something else is ultimately up
86-
to understanding the specific needs of your project.
75+
It has also already been possible to :doc:`run Flask with Gevent </gevent>` to
76+
get many of the benefits of async request handling. Gevent patches low-level
77+
Python functions to accomplish this, whereas ``async``/``await`` and ASGI use
78+
standard, modern Python capabilities. Deciding whether you should use gevent
79+
with Flask, or Quart, or something else is ultimately up to understanding the
80+
specific needs of your project.
8781

88-
.. _Quart: https://github.com/pallets/quart
89-
.. _ASGI: https://asgi.readthedocs.io/en/latest/
82+
.. _Quart: https://quart.palletsprojects.com
83+
.. _ASGI: https://asgi.readthedocs.io
9084

9185

9286
Extensions
@@ -120,6 +114,6 @@ implemented async support, or make a feature request or PR to them.
120114
Other event loops
121115
-----------------
122116

123-
At the moment Flask only supports :mod:`asyncio`. It's possible to
124-
override :meth:`flask.Flask.ensure_sync` to change how async functions
125-
are wrapped to use a different library.
117+
At the moment Flask only supports :mod:`asyncio`. It's possible to override
118+
:meth:`flask.Flask.ensure_sync` to change how async functions are wrapped to use
119+
a different library. See :ref:`gevent-asyncio` for an example.

docs/deploying/eventlet.rst

Lines changed: 4 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,8 @@
1+
:orphan:
2+
13
eventlet
24
========
35

4-
Prefer using :doc:`gunicorn` with eventlet workers rather than using
5-
`eventlet`_ directly. Gunicorn provides a much more configurable and
6-
production-tested server.
7-
8-
`eventlet`_ allows writing asynchronous, coroutine-based code that looks
9-
like standard synchronous Python. It uses `greenlet`_ to enable task
10-
switching without writing ``async/await`` or using ``asyncio``.
11-
12-
:doc:`gevent` is another library that does the same thing. Certain
13-
dependencies you have, or other considerations, may affect which of the
14-
two you choose to use.
15-
16-
eventlet provides a WSGI server that can handle many connections at once
17-
instead of one per worker process. You must actually use eventlet in
18-
your own code to see any benefit to using the server.
19-
20-
.. _eventlet: https://eventlet.net/
21-
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
22-
23-
24-
Installing
25-
----------
26-
27-
When using eventlet, greenlet>=1.0 is required, otherwise context locals
28-
such as ``request`` will not work as expected. When using PyPy,
29-
PyPy>=7.3.7 is required.
30-
31-
Create a virtualenv, install your application, then install
32-
``eventlet``.
33-
34-
.. code-block:: text
35-
36-
$ cd hello-app
37-
$ python -m venv .venv
38-
$ . .venv/bin/activate
39-
$ pip install . # install your application
40-
$ pip install eventlet
41-
42-
43-
Running
44-
-------
45-
46-
To use eventlet to serve your application, write a script that imports
47-
its ``wsgi.server``, as well as your app or app factory.
48-
49-
.. code-block:: python
50-
:caption: ``wsgi.py``
51-
52-
import eventlet
53-
from eventlet import wsgi
54-
from hello import create_app
55-
56-
app = create_app()
57-
wsgi.server(eventlet.listen(("127.0.0.1", 8000)), app)
58-
59-
.. code-block:: text
60-
61-
$ python wsgi.py
62-
(x) wsgi starting up on http://127.0.0.1:8000
63-
64-
65-
Binding Externally
66-
------------------
67-
68-
eventlet should not be run as root because it would cause your
69-
application code to run as root, which is not secure. However, this
70-
means it will not be possible to bind to port 80 or 443. Instead, a
71-
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
72-
in front of eventlet.
73-
74-
You can bind to all external IPs on a non-privileged port by using
75-
``0.0.0.0`` in the server arguments shown in the previous section.
76-
Don't do this when using a reverse proxy setup, otherwise it will be
77-
possible to bypass the proxy.
6+
`Eventlet is no longer maintained.`__ Use :doc:`/deploying/gevent` instead.
787

79-
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
80-
IP address in your browser.
8+
__ https://eventlet.readthedocs.io

docs/deploying/gevent.rst

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,12 @@ configurable and production-tested servers.
77

88
`gevent`_ allows writing asynchronous, coroutine-based code that looks
99
like standard synchronous Python. It uses `greenlet`_ to enable task
10-
switching without writing ``async/await`` or using ``asyncio``.
11-
12-
:doc:`eventlet` is another library that does the same thing. Certain
13-
dependencies you have, or other considerations, may affect which of the
14-
two you choose to use.
10+
switching without writing ``async/await`` or using ``asyncio``. This is
11+
not the same as Python's ``async/await``, or the ASGI server spec.
1512

1613
gevent provides a WSGI server that can handle many connections at once
17-
instead of one per worker process. You must actually use gevent in your
18-
own code to see any benefit to using the server.
14+
instead of one per worker process. See :doc:`/gevent` for more
15+
information about enabling it in your application.
1916

2017
.. _gevent: https://www.gevent.org/
2118
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
@@ -24,8 +21,7 @@ own code to see any benefit to using the server.
2421
Installing
2522
----------
2623

27-
When using gevent, greenlet>=1.0 is required, otherwise context locals
28-
such as ``request`` will not work as expected. When using PyPy,
24+
When using gevent, greenlet>=1.0 is required. When using PyPy,
2925
PyPy>=7.3.7 is required.
3026

3127
Create a virtualenv, install your application, then install ``gevent``.

docs/deploying/gunicorn.rst

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ multiple worker implementations for performance tuning.
88
* It does not support Windows (but does run on WSL).
99
* It is easy to install as it does not require additional dependencies
1010
or compilation.
11-
* It has built-in async worker support using gevent or eventlet.
11+
* It has built-in async worker support using gevent.
1212

1313
This page outlines the basics of running Gunicorn. Be sure to read its
1414
`documentation`_ and use ``gunicorn --help`` to understand what features
@@ -93,20 +93,19 @@ otherwise it will be possible to bypass the proxy.
9393
IP address in your browser.
9494

9595

96-
Async with gevent or eventlet
97-
-----------------------------
96+
Async with gevent
97+
-----------------
9898

99-
The default sync worker is appropriate for many use cases. If you need
100-
asynchronous support, Gunicorn provides workers using either `gevent`_
101-
or `eventlet`_. This is not the same as Python's ``async/await``, or the
102-
ASGI server spec. You must actually use gevent/eventlet in your own code
103-
to see any benefit to using the workers.
99+
The default sync worker is appropriate for most use cases. If you need numerous,
100+
long running, concurrent connections, Gunicorn provides an asynchronous worker
101+
using `gevent`_. This is not the same as Python's ``async/await``, or the ASGI
102+
server spec. See :doc:`/gevent` for more information about enabling it in your
103+
application.
104104

105-
When using either gevent or eventlet, greenlet>=1.0 is required,
106-
otherwise context locals such as ``request`` will not work as expected.
107-
When using PyPy, PyPy>=7.3.7 is required.
105+
.. _gevent: https://www.gevent.org/
108106

109-
To use gevent:
107+
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
108+
required.
110109

111110
.. code-block:: text
112111
@@ -115,16 +114,3 @@ To use gevent:
115114
Listening at: http://127.0.0.1:8000 (x)
116115
Using worker: gevent
117116
Booting worker with pid: x
118-
119-
To use eventlet:
120-
121-
.. code-block:: text
122-
123-
$ gunicorn -k eventlet 'hello:create_app()'
124-
Starting gunicorn 20.1.0
125-
Listening at: http://127.0.0.1:8000 (x)
126-
Using worker: eventlet
127-
Booting worker with pid: x
128-
129-
.. _gevent: https://www.gevent.org/
130-
.. _eventlet: https://eventlet.net/

docs/deploying/index.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ discusses platforms that can manage this for you.
3636
mod_wsgi
3737
uwsgi
3838
gevent
39-
eventlet
4039
asgi
4140

4241
WSGI servers have HTTP servers built-in. However, a dedicated HTTP

docs/deploying/uwsgi.rst

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,16 @@ IP address in your browser.
119119
Async with gevent
120120
-----------------
121121

122-
The default sync worker is appropriate for many use cases. If you need
123-
asynchronous support, uWSGI provides a `gevent`_ worker. This is not the
124-
same as Python's ``async/await``, or the ASGI server spec. You must
125-
actually use gevent in your own code to see any benefit to using the
126-
worker.
122+
The default sync worker is appropriate for most use cases. If you need numerous,
123+
long running, concurrent connections, uWSGI provides an asynchronous worker
124+
using `gevent`_. This is not the same as Python's ``async/await``, or the ASGI
125+
server spec. See :doc:`/gevent` for more information about enabling it in your
126+
application.
127127

128-
When using gevent, greenlet>=1.0 is required, otherwise context locals
129-
such as ``request`` will not work as expected. When using PyPy,
130-
PyPy>=7.3.7 is required.
128+
.. _gevent: https://www.gevent.org/
129+
130+
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
131+
required.
131132

132133
.. code-block:: text
133134
@@ -140,6 +141,3 @@ PyPy>=7.3.7 is required.
140141
spawned uWSGI worker 1 (pid: x, cores: 100)
141142
spawned uWSGI http 1 (pid: x)
142143
*** running gevent loop engine [addr:x] ***
143-
144-
145-
.. _gevent: https://www.gevent.org/

docs/gevent.rst

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
Async with Gevent
2+
=================
3+
4+
`Gevent`_ patches Python's standard library to run within special async workers
5+
called `greenlets`_. Gevent has existed since long before Python's native
6+
asyncio was available, and Flask has always worked with it.
7+
8+
.. _gevent: https://www.gevent.org
9+
.. _greenlets: https://greenlet.readthedocs.io
10+
11+
Gevent is a reliable way to handle numerous, long lived, concurrent connections,
12+
and to achieve similar capabilities to ASGI and asyncio. This works without
13+
needing to write ``async def`` or ``await`` anywhere, but relies on gevent and
14+
greenlet's low level manipulation of the Python interpreter.
15+
16+
Deciding whether you should use gevent with Flask, or `Quart`_, or something
17+
else, is ultimately up to understanding the specific needs of your project.
18+
19+
.. _quart: https://quart.palletsprojects.com
20+
21+
22+
Enabling gevent
23+
---------------
24+
25+
You need to apply gevent's patching as early as possible in your code. This
26+
enables gevent's underlying event loop and converts many Python internals to run
27+
inside it. Add the following at the top of your project's module or top
28+
``__init__.py``:
29+
30+
.. code-block:: python
31+
32+
import gevent.monkey
33+
gevent.monkey.patch_all()
34+
35+
When deploying in production, use :doc:`/deploying/gunicorn` or
36+
:doc:`/deploying/uwsgi` with a gevent worker, as described on those pages.
37+
38+
To run concurrent tasks within your own code, such as views, use
39+
|gevent.spawn|_:
40+
41+
.. |gevent.spawn| replace:: ``gevent.spawn()``
42+
.. _gevent.spawn: https://www.gevent.org/api/gevent.html#gevent.spawn
43+
44+
.. code-block:: python
45+
46+
@app.post("/send")
47+
def send_email():
48+
gevent.spawn(email.send, to="[email protected]", text="example")
49+
return "Email is being sent."
50+
51+
If you need to access :data:`request` or other Flask context globals within the
52+
spawned function, decorate the function with :func:`.stream_with_context` or
53+
:func:`.copy_current_request_context`. Prefer passing the exact data you need
54+
when spawning the function, rather than using the decorators.
55+
56+
.. note::
57+
58+
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7
59+
is required.
60+
61+
62+
.. _gevent-asyncio:
63+
64+
Combining with ``async``/``await``
65+
----------------------------------
66+
67+
Gevent's patching does not interact well with Flask's built-in asyncio support.
68+
If you want to use Gevent and asyncio in the same app, you'll need to override
69+
:meth:`flask.Flask.async_to_sync` to run async functions inside gevent.
70+
71+
.. code-block:: python
72+
73+
import gevent.monkey
74+
gevent.monkey.patch_all()
75+
76+
import asyncio
77+
from flask import Flask, request
78+
79+
loop = asyncio.EventLoop()
80+
gevent.spawn(loop.run_forever)
81+
82+
class GeventFlask(Flask):
83+
def async_to_sync(self, func):
84+
def run(*args, **kwargs):
85+
coro = func(*args, **kwargs)
86+
future = asyncio.run_coroutine_threadsafe(coro, loop)
87+
return future.result()
88+
89+
return run
90+
91+
app = GeventFlask(__name__)
92+
93+
@app.get("/")
94+
async def greet():
95+
await asyncio.sleep(1)
96+
return f"Hello, {request.args.get("name", "World")}!"
97+
98+
This starts an asyncio event loop in a gevent worker. Async functions are
99+
scheduled on that event loop. This may still have limitations, and may need to
100+
be modified further when using other asyncio implementations.
101+
102+
103+
libuv
104+
~~~~~
105+
106+
`libuv`_ is another event loop implementation that `gevent supports`_. There's
107+
also a project called `uvloop`_ that enables libuv in asyncio. If you want to
108+
use libuv, use gevent's support, not uvloop. It may be possible to further
109+
modify the ``async_to_sync`` code from the previous section to work with uvloop,
110+
but that's not currently known.
111+
112+
.. _libuv: https://libuv.org/
113+
.. _gevent supports: https://www.gevent.org/loop_impls.html
114+
.. _uvloop: https://uvloop.readthedocs.io/
115+
116+
To enable gevent's libuv support, add the following at the *very* top of your
117+
code, before ``gevent.monkey.patch_all()``:
118+
119+
.. code-block:: python
120+
121+
import gevent
122+
gevent.config.loop = "libuv"
123+
124+
import gevent.monkey
125+
gevent.monkey.patch_all()

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ community-maintained extensions to add even more functionality.
6161
patterns/index
6262
web-security
6363
deploying/index
64+
gevent
6465
async-await
6566

6667

0 commit comments

Comments
 (0)