Python WSGI ou ASGI

Contrairement à JavaScript ou Go, Python n’est pas un langage dont l’exécution asynchrone a été intégrée dès le départ. Pendant longtemps, l’exécution simultanée en Python ne pouvait être réalisée qu’en utilisant le multithreading ou le multiprocessing, ou en ayant recours à des bibliothèques spécialisées telles que eventlet, gevent ou Twisted.

Mais tout a changé quand asyncio a été ajouté à la bibliothèque standard pour prendre en charge du multitâche coopératif. Plus tard, la syntaxe async/await a été ajoutée dans Python 3.5. Grâce à cela, nous avions des coroutines natives indépendantes de l’implémentation sous-jacente, ce qui a ouvert la ruée vers l’asynchrone en Python.

ASGI (Asynchronous Server Gateway Interface) est une sorte de successeur de WSGI ( Web Server Gateway Interface), destiné à fournir une interface normalisée entre les serveurs Web compatibles async , les frameworks et les applications Python.
Là où WSGI fournissait une norme pour les applications Python synchrones, ASGI en fournit une pour les applications asynchrones et synchrones, avec une implémentation apportant une compatibilité descendante avec WSGI et de nombreux serveurs et frameworks d’application.

ASGI n’est pas nouveau dans l’environnement Python, mais la sortie de Django 3.0 en décembre 2019, a marqué le support de ASGI dans un framework très connu pour les applications web. L’interface asynchrone permet à Python d’aller taquiner les modèles asynchrones de Go et Node en terme de performance.

WSGI n’est pas mauvais, mais il ne possède qu’un seul chemin pour effectuer une requête, ce qui ne permet pas d’avoir des connexions de longue durée comme le long-poll HTTP ou le WebSocket.

Le domaine du ASGI est couvert par des acteurs qui ne sont pas forcément ceux qu’on a l’habitude de croiser dans les développements WSGI traditionnels. Prenons le cas des frameworks, hormis Django depuis la version 3.0, Flask ne supporte pas ASGI, et ce sont plutôt Starlette, Quart, Sanic, Vibora et FastAPI qui dominent jusqu’à présent ce domaine.

Côté serveur, ce sont aussi des acteurs différents qui dominent cette partie de Python. On parlera plutôt de :

  • Daphne , première implémentation de serveur ASGI, développée à l’origine pour alimenter Django Channels
  • Hypercorn, initialement intégré au framework Web Quart, avant d’être séparé en un serveur ASGI autonome
  • Uvicorn, serveur ASGI ultra-rapide, construit sur uvloop et httptools

Des benchmarks indépendants ont montrés que les applications FastAPI s’appuyant sur le serveur Uvicorn en font le framework le plus rapide derrière Starlette et Uvicorn sur lesquels il s’appuie. Des tests internes à Vibora ont démontré la rapidité de ce framework par rapport à certains acteurs synchrones mais aussi à Sanic, principale source d’inspiration de ce framework.

Il est certainement encore trop tôt pour déclarer une émergence flagrante et chacun pourra se faire son propre classement des outils méritant une attention particulière, mais on peut noter que certains couples fonctionnent bien, comme FastAPI et Uvicorn.

Il ne faut pas confondre le traitement asynchrone et le traitement parallèle. Ces deux approches ne répondent pas aux mêmes problèmes. On fait appel au parallélisme lorsque des tâches peuvent s’exécuter en même temps sans qu’elles s’attendent les unes les autres, alors qu’on fait appel à l’asynchronisme lorsque certaines tâches peuvent se faire en même temps mais celles qui ont fini en attendent d’autres pour que l’ensemble des actions puissent continuer afin d’aboutir au résultat.

L’avantage avec FastAPI, c’est qu’on peut tirer aussi bien parti du parallélisme que de l’asynchronisme. Syntaxiquement, voici à quoi ressemble FastAPI :

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def read_root():
    return {"Hello": "Gilbert"}


@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}
(fastapi) gmoisio@MBP-de-Gilbert ~/Projects/fastapi $% uvicorn main:app --reload   
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [14497]
INFO:     Started server process [14500]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
Résultat requête http curl
Résultat des requêtes http

Petit avantage complémentaire : la documentation se construit toute seule et une interface permet de tester graphiquement les requêtes…

Python FastAPI docs
http://127.0.0.1:8000/docs
Python FastAPI redoc
http://127.0.0.1:8000/redoc