document using gevent for async (#5900)
This commit is contained in:
commit
407eb76b27
9 changed files with 168 additions and 142 deletions
|
|
@ -23,12 +23,6 @@ method in views that inherit from the :class:`flask.views.View` class, as
|
||||||
well as all the HTTP method handlers in views that inherit from the
|
well as all the HTTP method handlers in views that inherit from the
|
||||||
:class:`flask.views.MethodView` class.
|
:class:`flask.views.MethodView` class.
|
||||||
|
|
||||||
.. admonition:: Using ``async`` with greenlet
|
|
||||||
|
|
||||||
When using gevent or eventlet to serve an application or patch the
|
|
||||||
runtime, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
|
|
||||||
required.
|
|
||||||
|
|
||||||
|
|
||||||
Performance
|
Performance
|
||||||
-----------
|
-----------
|
||||||
|
|
@ -78,15 +72,15 @@ Flask based on the `ASGI`_ standard instead of WSGI. This allows it to
|
||||||
handle many concurrent requests, long running requests, and websockets
|
handle many concurrent requests, long running requests, and websockets
|
||||||
without requiring multiple worker processes or threads.
|
without requiring multiple worker processes or threads.
|
||||||
|
|
||||||
It has also already been possible to run Flask with Gevent or Eventlet
|
It has also already been possible to :doc:`run Flask with Gevent </gevent>` to
|
||||||
to get many of the benefits of async request handling. These libraries
|
get many of the benefits of async request handling. Gevent patches low-level
|
||||||
patch low-level Python functions to accomplish this, whereas ``async``/
|
Python functions to accomplish this, whereas ``async``/``await`` and ASGI use
|
||||||
``await`` and ASGI use standard, modern Python capabilities. Deciding
|
standard, modern Python capabilities. Deciding whether you should use gevent
|
||||||
whether you should use Flask, Quart, or something else is ultimately up
|
with Flask, or Quart, or something else is ultimately up to understanding the
|
||||||
to understanding the specific needs of your project.
|
specific needs of your project.
|
||||||
|
|
||||||
.. _Quart: https://github.com/pallets/quart
|
.. _Quart: https://quart.palletsprojects.com
|
||||||
.. _ASGI: https://asgi.readthedocs.io/en/latest/
|
.. _ASGI: https://asgi.readthedocs.io
|
||||||
|
|
||||||
|
|
||||||
Extensions
|
Extensions
|
||||||
|
|
@ -120,6 +114,6 @@ implemented async support, or make a feature request or PR to them.
|
||||||
Other event loops
|
Other event loops
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
At the moment Flask only supports :mod:`asyncio`. It's possible to
|
At the moment Flask only supports :mod:`asyncio`. It's possible to override
|
||||||
override :meth:`flask.Flask.ensure_sync` to change how async functions
|
:meth:`flask.Flask.ensure_sync` to change how async functions are wrapped to use
|
||||||
are wrapped to use a different library.
|
a different library. See :ref:`gevent-asyncio` for an example.
|
||||||
|
|
|
||||||
|
|
@ -1,80 +1,8 @@
|
||||||
|
:orphan:
|
||||||
|
|
||||||
eventlet
|
eventlet
|
||||||
========
|
========
|
||||||
|
|
||||||
Prefer using :doc:`gunicorn` with eventlet workers rather than using
|
`Eventlet is no longer maintained.`__ Use :doc:`/deploying/gevent` instead.
|
||||||
`eventlet`_ directly. Gunicorn provides a much more configurable and
|
|
||||||
production-tested server.
|
|
||||||
|
|
||||||
`eventlet`_ allows writing asynchronous, coroutine-based code that looks
|
__ https://eventlet.readthedocs.io
|
||||||
like standard synchronous Python. It uses `greenlet`_ to enable task
|
|
||||||
switching without writing ``async/await`` or using ``asyncio``.
|
|
||||||
|
|
||||||
:doc:`gevent` is another library that does the same thing. Certain
|
|
||||||
dependencies you have, or other considerations, may affect which of the
|
|
||||||
two you choose to use.
|
|
||||||
|
|
||||||
eventlet provides a WSGI server that can handle many connections at once
|
|
||||||
instead of one per worker process. You must actually use eventlet in
|
|
||||||
your own code to see any benefit to using the server.
|
|
||||||
|
|
||||||
.. _eventlet: https://eventlet.net/
|
|
||||||
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
|
|
||||||
|
|
||||||
|
|
||||||
Installing
|
|
||||||
----------
|
|
||||||
|
|
||||||
When using eventlet, greenlet>=1.0 is required, otherwise context locals
|
|
||||||
such as ``request`` will not work as expected. When using PyPy,
|
|
||||||
PyPy>=7.3.7 is required.
|
|
||||||
|
|
||||||
Create a virtualenv, install your application, then install
|
|
||||||
``eventlet``.
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
$ cd hello-app
|
|
||||||
$ python -m venv .venv
|
|
||||||
$ . .venv/bin/activate
|
|
||||||
$ pip install . # install your application
|
|
||||||
$ pip install eventlet
|
|
||||||
|
|
||||||
|
|
||||||
Running
|
|
||||||
-------
|
|
||||||
|
|
||||||
To use eventlet to serve your application, write a script that imports
|
|
||||||
its ``wsgi.server``, as well as your app or app factory.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
:caption: ``wsgi.py``
|
|
||||||
|
|
||||||
import eventlet
|
|
||||||
from eventlet import wsgi
|
|
||||||
from hello import create_app
|
|
||||||
|
|
||||||
app = create_app()
|
|
||||||
wsgi.server(eventlet.listen(("127.0.0.1", 8000)), app)
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
$ python wsgi.py
|
|
||||||
(x) wsgi starting up on http://127.0.0.1:8000
|
|
||||||
|
|
||||||
|
|
||||||
Binding Externally
|
|
||||||
------------------
|
|
||||||
|
|
||||||
eventlet should not be run as root because it would cause your
|
|
||||||
application code to run as root, which is not secure. However, this
|
|
||||||
means it will not be possible to bind to port 80 or 443. Instead, a
|
|
||||||
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
|
|
||||||
in front of eventlet.
|
|
||||||
|
|
||||||
You can bind to all external IPs on a non-privileged port by using
|
|
||||||
``0.0.0.0`` in the server arguments shown in the previous section.
|
|
||||||
Don't do this when using a reverse proxy setup, otherwise it will be
|
|
||||||
possible to bypass the proxy.
|
|
||||||
|
|
||||||
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
|
|
||||||
IP address in your browser.
|
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,12 @@ configurable and production-tested servers.
|
||||||
|
|
||||||
`gevent`_ allows writing asynchronous, coroutine-based code that looks
|
`gevent`_ allows writing asynchronous, coroutine-based code that looks
|
||||||
like standard synchronous Python. It uses `greenlet`_ to enable task
|
like standard synchronous Python. It uses `greenlet`_ to enable task
|
||||||
switching without writing ``async/await`` or using ``asyncio``.
|
switching without writing ``async/await`` or using ``asyncio``. This is
|
||||||
|
not the same as Python's ``async/await``, or the ASGI server spec.
|
||||||
:doc:`eventlet` is another library that does the same thing. Certain
|
|
||||||
dependencies you have, or other considerations, may affect which of the
|
|
||||||
two you choose to use.
|
|
||||||
|
|
||||||
gevent provides a WSGI server that can handle many connections at once
|
gevent provides a WSGI server that can handle many connections at once
|
||||||
instead of one per worker process. You must actually use gevent in your
|
instead of one per worker process. See :doc:`/gevent` for more
|
||||||
own code to see any benefit to using the server.
|
information about enabling it in your application.
|
||||||
|
|
||||||
.. _gevent: https://www.gevent.org/
|
.. _gevent: https://www.gevent.org/
|
||||||
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
|
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
|
||||||
|
|
@ -24,8 +21,7 @@ own code to see any benefit to using the server.
|
||||||
Installing
|
Installing
|
||||||
----------
|
----------
|
||||||
|
|
||||||
When using gevent, greenlet>=1.0 is required, otherwise context locals
|
When using gevent, greenlet>=1.0 is required. When using PyPy,
|
||||||
such as ``request`` will not work as expected. When using PyPy,
|
|
||||||
PyPy>=7.3.7 is required.
|
PyPy>=7.3.7 is required.
|
||||||
|
|
||||||
Create a virtualenv, install your application, then install ``gevent``.
|
Create a virtualenv, install your application, then install ``gevent``.
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ multiple worker implementations for performance tuning.
|
||||||
* It does not support Windows (but does run on WSL).
|
* It does not support Windows (but does run on WSL).
|
||||||
* It is easy to install as it does not require additional dependencies
|
* It is easy to install as it does not require additional dependencies
|
||||||
or compilation.
|
or compilation.
|
||||||
* It has built-in async worker support using gevent or eventlet.
|
* It has built-in async worker support using gevent.
|
||||||
|
|
||||||
This page outlines the basics of running Gunicorn. Be sure to read its
|
This page outlines the basics of running Gunicorn. Be sure to read its
|
||||||
`documentation`_ and use ``gunicorn --help`` to understand what features
|
`documentation`_ and use ``gunicorn --help`` to understand what features
|
||||||
|
|
@ -93,20 +93,19 @@ otherwise it will be possible to bypass the proxy.
|
||||||
IP address in your browser.
|
IP address in your browser.
|
||||||
|
|
||||||
|
|
||||||
Async with gevent or eventlet
|
Async with gevent
|
||||||
-----------------------------
|
-----------------
|
||||||
|
|
||||||
The default sync worker is appropriate for many use cases. If you need
|
The default sync worker is appropriate for most use cases. If you need numerous,
|
||||||
asynchronous support, Gunicorn provides workers using either `gevent`_
|
long running, concurrent connections, Gunicorn provides an asynchronous worker
|
||||||
or `eventlet`_. This is not the same as Python's ``async/await``, or the
|
using `gevent`_. This is not the same as Python's ``async/await``, or the ASGI
|
||||||
ASGI server spec. You must actually use gevent/eventlet in your own code
|
server spec. See :doc:`/gevent` for more information about enabling it in your
|
||||||
to see any benefit to using the workers.
|
application.
|
||||||
|
|
||||||
When using either gevent or eventlet, greenlet>=1.0 is required,
|
.. _gevent: https://www.gevent.org/
|
||||||
otherwise context locals such as ``request`` will not work as expected.
|
|
||||||
When using PyPy, PyPy>=7.3.7 is required.
|
|
||||||
|
|
||||||
To use gevent:
|
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
|
||||||
|
required.
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: text
|
||||||
|
|
||||||
|
|
@ -115,16 +114,3 @@ To use gevent:
|
||||||
Listening at: http://127.0.0.1:8000 (x)
|
Listening at: http://127.0.0.1:8000 (x)
|
||||||
Using worker: gevent
|
Using worker: gevent
|
||||||
Booting worker with pid: x
|
Booting worker with pid: x
|
||||||
|
|
||||||
To use eventlet:
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
$ gunicorn -k eventlet 'hello:create_app()'
|
|
||||||
Starting gunicorn 20.1.0
|
|
||||||
Listening at: http://127.0.0.1:8000 (x)
|
|
||||||
Using worker: eventlet
|
|
||||||
Booting worker with pid: x
|
|
||||||
|
|
||||||
.. _gevent: https://www.gevent.org/
|
|
||||||
.. _eventlet: https://eventlet.net/
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ discusses platforms that can manage this for you.
|
||||||
mod_wsgi
|
mod_wsgi
|
||||||
uwsgi
|
uwsgi
|
||||||
gevent
|
gevent
|
||||||
eventlet
|
|
||||||
asgi
|
asgi
|
||||||
|
|
||||||
WSGI servers have HTTP servers built-in. However, a dedicated HTTP
|
WSGI servers have HTTP servers built-in. However, a dedicated HTTP
|
||||||
|
|
|
||||||
|
|
@ -119,15 +119,16 @@ IP address in your browser.
|
||||||
Async with gevent
|
Async with gevent
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
The default sync worker is appropriate for many use cases. If you need
|
The default sync worker is appropriate for most use cases. If you need numerous,
|
||||||
asynchronous support, uWSGI provides a `gevent`_ worker. This is not the
|
long running, concurrent connections, uWSGI provides an asynchronous worker
|
||||||
same as Python's ``async/await``, or the ASGI server spec. You must
|
using `gevent`_. This is not the same as Python's ``async/await``, or the ASGI
|
||||||
actually use gevent in your own code to see any benefit to using the
|
server spec. See :doc:`/gevent` for more information about enabling it in your
|
||||||
worker.
|
application.
|
||||||
|
|
||||||
When using gevent, greenlet>=1.0 is required, otherwise context locals
|
.. _gevent: https://www.gevent.org/
|
||||||
such as ``request`` will not work as expected. When using PyPy,
|
|
||||||
PyPy>=7.3.7 is required.
|
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
|
||||||
|
required.
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: text
|
||||||
|
|
||||||
|
|
@ -140,6 +141,3 @@ PyPy>=7.3.7 is required.
|
||||||
spawned uWSGI worker 1 (pid: x, cores: 100)
|
spawned uWSGI worker 1 (pid: x, cores: 100)
|
||||||
spawned uWSGI http 1 (pid: x)
|
spawned uWSGI http 1 (pid: x)
|
||||||
*** running gevent loop engine [addr:x] ***
|
*** running gevent loop engine [addr:x] ***
|
||||||
|
|
||||||
|
|
||||||
.. _gevent: https://www.gevent.org/
|
|
||||||
|
|
|
||||||
125
docs/gevent.rst
Normal file
125
docs/gevent.rst
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
Async with Gevent
|
||||||
|
=================
|
||||||
|
|
||||||
|
`Gevent`_ patches Python's standard library to run within special async workers
|
||||||
|
called `greenlets`_. Gevent has existed since long before Python's native
|
||||||
|
asyncio was available, and Flask has always worked with it.
|
||||||
|
|
||||||
|
.. _gevent: https://www.gevent.org
|
||||||
|
.. _greenlets: https://greenlet.readthedocs.io
|
||||||
|
|
||||||
|
Gevent is a reliable way to handle numerous, long lived, concurrent connections,
|
||||||
|
and to achieve similar capabilities to ASGI and asyncio. This works without
|
||||||
|
needing to write ``async def`` or ``await`` anywhere, but relies on gevent and
|
||||||
|
greenlet's low level manipulation of the Python interpreter.
|
||||||
|
|
||||||
|
Deciding whether you should use gevent with Flask, or `Quart`_, or something
|
||||||
|
else, is ultimately up to understanding the specific needs of your project.
|
||||||
|
|
||||||
|
.. _quart: https://quart.palletsprojects.com
|
||||||
|
|
||||||
|
|
||||||
|
Enabling gevent
|
||||||
|
---------------
|
||||||
|
|
||||||
|
You need to apply gevent's patching as early as possible in your code. This
|
||||||
|
enables gevent's underlying event loop and converts many Python internals to run
|
||||||
|
inside it. Add the following at the top of your project's module or top
|
||||||
|
``__init__.py``:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import gevent.monkey
|
||||||
|
gevent.monkey.patch_all()
|
||||||
|
|
||||||
|
When deploying in production, use :doc:`/deploying/gunicorn` or
|
||||||
|
:doc:`/deploying/uwsgi` with a gevent worker, as described on those pages.
|
||||||
|
|
||||||
|
To run concurrent tasks within your own code, such as views, use
|
||||||
|
|gevent.spawn|_:
|
||||||
|
|
||||||
|
.. |gevent.spawn| replace:: ``gevent.spawn()``
|
||||||
|
.. _gevent.spawn: https://www.gevent.org/api/gevent.html#gevent.spawn
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@app.post("/send")
|
||||||
|
def send_email():
|
||||||
|
gevent.spawn(email.send, to="example@example.example", text="example")
|
||||||
|
return "Email is being sent."
|
||||||
|
|
||||||
|
If you need to access :data:`request` or other Flask context globals within the
|
||||||
|
spawned function, decorate the function with :func:`.stream_with_context` or
|
||||||
|
:func:`.copy_current_request_context`. Prefer passing the exact data you need
|
||||||
|
when spawning the function, rather than using the decorators.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7
|
||||||
|
is required.
|
||||||
|
|
||||||
|
|
||||||
|
.. _gevent-asyncio:
|
||||||
|
|
||||||
|
Combining with ``async``/``await``
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
Gevent's patching does not interact well with Flask's built-in asyncio support.
|
||||||
|
If you want to use Gevent and asyncio in the same app, you'll need to override
|
||||||
|
:meth:`flask.Flask.async_to_sync` to run async functions inside gevent.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import gevent.monkey
|
||||||
|
gevent.monkey.patch_all()
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from flask import Flask, request
|
||||||
|
|
||||||
|
loop = asyncio.EventLoop()
|
||||||
|
gevent.spawn(loop.run_forever)
|
||||||
|
|
||||||
|
class GeventFlask(Flask):
|
||||||
|
def async_to_sync(self, func):
|
||||||
|
def run(*args, **kwargs):
|
||||||
|
coro = func(*args, **kwargs)
|
||||||
|
future = asyncio.run_coroutine_threadsafe(coro, loop)
|
||||||
|
return future.result()
|
||||||
|
|
||||||
|
return run
|
||||||
|
|
||||||
|
app = GeventFlask(__name__)
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
async def greet():
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
return f"Hello, {request.args.get("name", "World")}!"
|
||||||
|
|
||||||
|
This starts an asyncio event loop in a gevent worker. Async functions are
|
||||||
|
scheduled on that event loop. This may still have limitations, and may need to
|
||||||
|
be modified further when using other asyncio implementations.
|
||||||
|
|
||||||
|
|
||||||
|
libuv
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
`libuv`_ is another event loop implementation that `gevent supports`_. There's
|
||||||
|
also a project called `uvloop`_ that enables libuv in asyncio. If you want to
|
||||||
|
use libuv, use gevent's support, not uvloop. It may be possible to further
|
||||||
|
modify the ``async_to_sync`` code from the previous section to work with uvloop,
|
||||||
|
but that's not currently known.
|
||||||
|
|
||||||
|
.. _libuv: https://libuv.org/
|
||||||
|
.. _gevent supports: https://www.gevent.org/loop_impls.html
|
||||||
|
.. _uvloop: https://uvloop.readthedocs.io/
|
||||||
|
|
||||||
|
To enable gevent's libuv support, add the following at the *very* top of your
|
||||||
|
code, before ``gevent.monkey.patch_all()``:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import gevent
|
||||||
|
gevent.config.loop = "libuv"
|
||||||
|
|
||||||
|
import gevent.monkey
|
||||||
|
gevent.monkey.patch_all()
|
||||||
|
|
@ -61,6 +61,7 @@ community-maintained extensions to add even more functionality.
|
||||||
patterns/index
|
patterns/index
|
||||||
web-security
|
web-security
|
||||||
deploying/index
|
deploying/index
|
||||||
|
gevent
|
||||||
async-await
|
async-await
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,9 +51,8 @@ use them if you install them.
|
||||||
greenlet
|
greenlet
|
||||||
~~~~~~~~
|
~~~~~~~~
|
||||||
|
|
||||||
You may choose to use gevent or eventlet with your application. In this
|
You may choose to use :doc:`/gevent` with your application. In this case,
|
||||||
case, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
|
greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is required.
|
||||||
required.
|
|
||||||
|
|
||||||
These are not minimum supported versions, they only indicate the first
|
These are not minimum supported versions, they only indicate the first
|
||||||
versions that added necessary features. You should use the latest
|
versions that added necessary features. You should use the latest
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue