Hola Habr! Les presento la traducción del artículo "Introducción a ASGI: surgimiento de un ecosistema web asíncrono de Python" por Florimond Manca.

"Tortugas cerca del estanque", Ricard Baraham en unsplash.com
Python no se limita a Data Science, el desarrollo web de Python ha regresado con un nuevo giro asíncrono en el desarrollo del lenguaje.
Muchos eventos importantes están teniendo lugar en el ecosistema de desarrollo web de Python. Uno de los principales impulsores de estos cambios es ASGI - Asynchronous Standard Gateway Interface.
Ya mencioné ASGI varias veces en mi blog, en particular, cuando anuncié Bocadillo ( marco web Python asíncrono de código abierto - aprox. Per . ) Y tartiflette-starlette ( biblioteca para construir API GraphQL sobre HTTP a través de ASGI - aprox. . ), pero nunca escribí una introducción detallada sobre él. Ahora lo haré.
Este artículo está dirigido a personas interesadas en las últimas tendencias en el desarrollo web de Python. Quiero invitarlos a una excursión desde la cual aprenderán qué es ASGI y qué significa para el desarrollo web moderno en el mundo de Python.
Antes de comenzar, me gustaría decirles que recientemente creé Amazing-asgi , una gran lista para rastrear el ecosistema ASGI en constante expansión.
Todo comenzó con async / wait
A diferencia de JavaScript o Go, en el momento de su aparición, Python no proporcionaba la capacidad de ejecutar código asincrónicamente. Durante mucho tiempo, la ejecución de código paralelo en Python se pudo realizar solo con la ayuda de un procesamiento multiproceso o multiprocesador, o utilizando bibliotecas de red especializadas como eventlet, gevent o Twisted. (En 2008, Twisted tenía una API para las rutinas asincrónicas, por ejemplo, en forma de inlineCallbacks
y deferredGenerator
)
Todo ha cambiado en Python 3.4+. En Python 3.4, asyncio se incluyó en la biblioteca estándar, lo que resultó en soporte para la multitarea cooperativa basada en generadores y el yield from
sintaxis.
Más adelante en Python 3.5 , se agregó la sintaxis async/await
. Gracias a esto, aparecieron las corutinas nativas, independientes de la implementación subyacente, lo que condujo a la fiebre del oro en torno a la concurrencia en Python.
¡La carrera loca ha comenzado! Desde el lanzamiento de la versión 3.5, la comunidad literalmente ha sincronizado todo lo que hay a su alrededor. Si está interesado, muchos de los proyectos resultantes se enumeran en aio-libs y awesome-asyncio .
Como puede suponer, esto también significa que los servidores web y las aplicaciones Python se están moviendo hacia la asincronía. De hecho, ¡todos los chicos geniales lo hacen! ( Incluso Django ) ( Habr: Django 3.0 será asíncrono , ya lanzado el 12/02/2019 - aproximadamente por ) .
Revisión de ASGI
Entonces, ¿cómo encaja ASGI en todo esto?
El ASGI de nivel superior se puede ver como un enlace que permite que los servidores y las aplicaciones asincrónicas de Python interactúen entre sí. Repite muchas de las ideas arquitectónicas de WSGI , y a menudo se le presenta como su sucesor con la asincronía incorporada.
Así es como se puede representar en el diagrama:

En un nivel muy alto, ASGI es una interfaz para la comunicación entre aplicaciones y servidores. Pero, de hecho, todo es un poco más complicado.
Para comprender cómo funciona realmente ASGI, echemos un vistazo a la especificación ASGI .
ASGI consta de dos componentes diferentes:
- Servidor de protocolo: escucha en sockets y los convierte en conexiones y mensajes de eventos dentro de cada conexión.
- Una aplicación ( aplicación ), que vive dentro del servidor de protocolo, su instancia se crea una vez para cada conexión y procesa los mensajes de eventos a medida que ocurren.
Por lo tanto, de acuerdo con la especificación, lo que ASGI realmente indica es el formato del mensaje y cómo estos mensajes deben transferirse entre la aplicación y el servidor de protocolo que lo ejecuta.
Ahora podemos hacer una versión más detallada del diagrama:

Hay muchos más detalles interesantes en el protocolo. Por ejemplo, puede echar un vistazo a las especificaciones HTTP y WebSocket .
Además, aunque la especificación se centra principalmente en la interacción entre el servidor y la aplicación, ASGI logra cubrir muchos más aspectos.
Llegamos a esto en un minuto, pero primero ...
Conceptos básicos de ASGI
Ahora que hemos visto cómo ASGI se adapta al ecosistema web de Python, echemos un vistazo más de cerca a cómo se traduce esto en código.
ASGI se basa en un modelo simple: cuando un cliente se conecta a un servidor, se crea una instancia de aplicación. Luego, los datos entrantes se transfieren a la aplicación y todos los datos que devuelve se envían de vuelta.
Pasar datos a la aplicación aquí en realidad significa llamar a la aplicación como si fuera una función, es decir algo que toma algo de entrada y devuelve salida.
De hecho, todo lo que representa una aplicación ASGI es invocable (llamado objeto). Los parámetros de este objeto llamado, de nuevo, están determinados por la especificación ASGI :
async def app(scope, receive, send): ...
La firma de esta función es exactamente lo que significa "I" en "ASGI": la interfaz que la aplicación debe implementar para que el servidor la llame.
Veamos los parámetros de la función:
scope
es un diccionario que contiene información sobre una solicitud entrante. Su contenido es diferente para las conexiones HTTP y WebSocket .receive
es una función asincrónica para recibir mensajes sobre eventos ASGI.send
es una función asincrónica para enviar mensajes sobre eventos ASGI.
De hecho, estos parámetros le permiten recibir ( receive()
) y transmitir ( send()
) datos a través del canal de comunicación que admite el servidor de protocolo, así como comprender en qué contexto (o scope
) se creó este canal.
No sé sobre usted, pero realmente me gusta la apariencia general y la estructura de esta interfaz. En cualquier caso, ahora veamos un código de muestra.
Muestra el código!
Para tener una idea práctica de cómo se ve ASGI, creé un proyecto mínimo en ASGI desnudo que demuestra una aplicación HTTP servida por uvicorn (un servidor ASGI popular):
async def app(scope, receive, send): assert scope["type"] == "http" await send({ "type": "http.response.start", "status": 200, "headers": [ [b"content-type", b"text/plain"], ] }) await send({ "type": "http.response.body", "body": b"Hello, world!", })
Código fuente: https://glitch.com/edit/#!/asgi-hello-world
Aquí usamos send()
para enviar una respuesta HTTP al cliente: primero enviamos los encabezados y luego el cuerpo de la respuesta.
Admito que debido a todos estos diccionarios y datos binarios en bruto, el ASGI simple no es muy conveniente para el trabajo.
Afortunadamente, hay opciones de nivel superior, y ahí es cuando empiezo a hablar de Starlette .
Starlette es un proyecto verdaderamente fantástico y, en mi opinión, una parte fundamental del ecosistema ASGI.
Brevemente, proporciona un conjunto de componentes de alto nivel, como solicitudes y respuestas, que puede usar para abstraerse de algunos de los detalles de ASGI. Aquí, eche un vistazo al "hola mundo" en Starlette:
Starlette tiene todo lo que espera de un marco web real: enrutamiento, middleware, etc. Pero decidí mostrar esta versión simplificada para insinuar el poder real de ASGI, que es ...
Tortugas hasta el final
Un concepto interesante y que cambia las reglas para ASGI es Turtles All the Way , una expresión originalmente inventada (¿creo?) Por Andrew Godwin, quien creó Django Migrations y actualmente está trabajando en Django para soportar la asincronía .
Pero, ¿qué significa esto exactamente?
Dado que ASGI es una abstracción que nos permite decir en qué contexto nos encontramos y recibir y enviar datos en cualquier momento, es decir, la idea de que ASGI puede usarse no solo entre servidores y aplicaciones, sino también en cualquier lugar de la pila.
Por ejemplo, el objeto Starlette Response
es la propia aplicación ASGI. De hecho, podemos acortar el código en la aplicación de ejemplo anterior a esto:
¡Qué ridículo se ve eso!
Pero espera, eso no es todo.
La consecuencia más profunda de las "tortugas hasta el final" es que podemos crear todo tipo de aplicaciones, middleware, bibliotecas y otros proyectos y asegurarnos de que sean compatibles siempre que todos implementen la interfaz de aplicación ASGI.
(Además, desde mi experiencia personal en la construcción de Bocadillo , aceptar la interfaz ASGI muy a menudo (si no siempre) conduce a un código mucho más limpio)
Por ejemplo, podemos crear un middleware ASGI (es decir, una aplicación que envuelva otra aplicación) para mostrar el tiempo que tardó en completarse la solicitud:
Para usarlo, simplemente envolvemos la aplicación con él ...
... y mágicamente solo funcionará.
$ uvicorn app:app INFO: Started server process [59405] INFO: Waiting for application startup. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ... INFO: ('127.0.0.1', 62718) - "GET / HTTP/1.1" 200 Took 1.00 seconds
Es TimingMiddleware
que en TimingMiddleware
pueda envolver cualquier aplicación ASGI. La aplicación interna en este ejemplo es súper simple, pero puede ser un proyecto completo y real (imagine cientos de API y puntos finales WebSocket); no importa mientras la interfaz sea compatible con ASGI.
(Existe una versión de este middleware más preparada para uso industrial: timing-asgi ).
¿Por qué debería molestarse?
Si bien creo que la compatibilidad es un argumento muy sólido, existen muchas más ventajas al usar componentes basados en ASGI para construir aplicaciones web Python.
- Velocidad: la naturaleza asincrónica de las aplicaciones y servidores ASGI los hace realmente rápidos (al menos para Python): estamos hablando de solicitudes de 60k-70k por segundo (asumiendo que Flask y Django solo alcanzan 10-20k en una situación similar).
- Características: Los servidores y plataformas ASGI le dan acceso a funciones esencialmente paralelas (WebSocket, Eventos enviados por el servidor, HTTP / 2) que no pueden implementarse utilizando código síncrono y WSGI.
- Estabilidad: ASGI como especificación ha existido durante 3 años, y la versión 3.0 se considera muy estable. Como resultado, las partes principales del ecosistema se están estabilizando.
Desde el punto de vista de las bibliotecas y herramientas, no creo que podamos decir que hemos alcanzado el nivel requerido. Pero gracias a una comunidad muy activa, tengo grandes esperanzas de que el ecosistema ASGI alcance muy pronto la paridad de funciones con el ecosistema síncrono / WSGI tradicional.
¿Dónde puedo encontrar componentes compatibles con ASGI?
De hecho, cada vez más personas están construyendo y mejorando proyectos basados en ASGI. Obviamente, estos son servidores y marcos web, pero también hay aplicaciones de middleware y orientadas a productos como Datasette .
A continuación hay algunos ejemplos de componentes no basados en la web que me interesan:
Es sorprendente observar que el ecosistema se está desarrollando con éxito, sin embargo, personalmente fue difícil para mí mantenerme al día con los cambios.
Es por eso que creé awesome-asgi . Espero que esto ayude a todos a mantenerse al día con todas las cosas increíbles que suceden en el mundo ASGI. (Y al ver que casi alcanzó las 100 estrellas en pocos días, tengo la sensación de que realmente era necesario reunir información sobre los recursos ASGI en un solo lugar).
Conclusiones
Aunque esto puede parecer detalles de implementación, estoy seguro de que ASGI ha sentado las bases para una nueva era en el desarrollo web de Python.
Si desea obtener más información sobre ASGI, consulte las diversas publicaciones (artículos y discursos) que figuran en awesome-asgi
. Si quieres tocarlo, prueba cualquiera de los siguientes proyectos:
Estos proyectos fueron creados y están respaldados por Encode, principalmente Tom Christie. Hay discusiones abiertas sobre la creación de un equipo de soporte de Encode , por lo que si estaba buscando una oportunidad para participar en el desarrollo de código abierto, ¡entonces tiene la oportunidad!
¡Diviértete viajando al mundo ASGI!