Programmation asynchrone : alternatives
Gevent
1. Introduction
La bibliothèque Gevent permet de créer en Python des coroutines pour gérer efficacement des problématiques réseau. Elle est basée sur l’utilisation de greenlet et une boucle événementielle performante (libev ou libuv). L’installation se fait ainsi :
$ pip install gevent
La bibliothèque contient donc un objet socket qui travaille de manière concurrente et quelques fonctions utiles, comme le montre cet exemple adapté de la documentation officielle :
>>> from gevent import socket, spawn, joinall
>>> urls = ["eni.fr", "www.inspyration.fr", "www.python.org"]
>>> jobs = [spawn(socket.gethostbyname, url) for url in urls]
>>> joinall(jobs, timeout=10)
>>> [job.value for job in jobs]
['185.42.28.200', '213.246.53.26', '151.101.40.223']
Dans cet exemple, on va demander l’IP associée à un nom d’hôte et on va créer plusieurs tâches pour chaque hôte, pour les mettre en concurrence.
La fonction joinall permet d’attendre que toutes les tâches soient terminées. La dernière ligne permet de récupérer le résultat de chaque tâche.
2. DNS
Gevent inclut les outils nécessaires pour la résolution de nom de domaine, comme l’exemple précédent en est une illustration. Il s’agit d’une réécriture iso-fonctionnelle partielle des fonctionnalités présentes dans le module python socket. Vous pourrez donc consulter la documentation officielle pour savoir comment ces fonctions doivent être utilisées et...
Twisted
1. Introduction
Twisted est un outil permettant de créer un serveur internet et non pas seulement un serveur web. En d’autres mots, il ne gère pas que le protocole HTTP, mais il peut faire du WebDav, du FTP ou implémenter vos propres protocoles.
Pour l’installer :
$ pip install twisted
L’objet central est un réacteur et ce dernier va faire tourner des protocoles, lesquels sont des descriptions des données attendues et des réponses à fournir. Ces protocoles sont construits au-dessus de transports, tels que le transport TCP ou UDP.
2. Client/serveur TCP
Voici un exemple de serveur TCP Basique : il renvoie tel un écho la donnée qu’il reçoit. On va commencer par importer ce dont nous aurons besoin :
from twisted.internet import reactor
from twisted.internet.protocol import Protocol, ServerFactory
Voici un exemple simple de protocole permettant de renvoyer la donnée reçue :
class Echo(Protocol):
def dataReceived(self, data):
"As soon as any data is received, echo it back."
self.transport.write(data)
Il suffit alors de connaître les différentes fonctions à surcharger pour créer son propre protocole et lui faire faire ce que l’on souhaite.
Pour faire tourner ce protocole, il faut monter le réacteur :
def main():
"""This runs the protocol on port 8000"""
factory = ServerFactory()
factory.protocol = Echo
reactor.listenTCP(8000,factory)
reactor.run()
Ce code devrait vous sembler assez familier si vous avez déjà lu les chapitres Programmation réseau et Programmation web.
Il faut ensuite l’exécuter :
if __name__ == '__main__':
main()
Pour la partie cliente, on écrit également un protocole :
class EchoClient(protocol.Protocol):
def connectionMade(self):
self.transport.write(b"hello, world!")
def dataReceived(self, data): ...
Trollius
Comme vous le savez, asyncio est disponible seulement pour Python 3.4 ou supérieur. Une alternative existe cependant pour Python 2.7 dont la branche est toujours active et le restera encore quelques années.
Tout comme asyncio, trollius offre une boucle événementielle ainsi que des transports et protocoles parmi lesquels on peut trouver TCP, UDP ou SSL comme nous vous l’avons présenté dans le chapitre Programmation asynchrone : avancée. Cependant, attention, ces protocoles sont basés sur ceux de twisted plus que sur ceux d’asyncio. C’est une des raisons pour laquelle nous avons introduit twisted juste au-dessus.
Ce module vous permettra aussi, plus basiquement, de créer des coroutines et des tâches et de les synchroniser.
Voici un exemple de protocole UDP (très ressemblant à ce que vous avez pu voir précédemment) :
class ServerUdpEchoProtocol:
def connection_made(self, transport):
print('{transport} started'.format(transport=transport))
self.transport = transport
def datagram_received(self, data, addr):
print('Data {data} received from {addr}'.format(data=data...
HTTP asynchrone avec aiohttp
1. Côté serveur
Nous vous avons déjà présenté bottlepy et évoqué l’alternative flask. Il s’agit de microframeworks web permettant de répondre à des besoins simples.
Il existe une alternative qui, syntaxiquement, est extrêmement proche et qui présente l’avantage d’utiliser la programmation asynchrone.
Voici l’exemple d’introduction de aiohttp :
from aiohttp.web import RouteTableDef, Response , Application, run_app
routes = RouteTableDef()
@routes.get('/')
async def hello(request):
return Response(text="Hello World!")
app = Application()
app.add_routes(routes)
run_app(app)
À comparer à un exemple similaire avec bottlepy :
from bottle import route, run, template
@route('/')
def index():
return "Hello World!"
run(host="localhost", port=8080)
ou Flask :
from flask import Flask
app = Flask("test App")
@app.route("/")
def hello():
return "Hello World!"
Avec aiohttp, les méthodes peuvent s’écrire en utilisant la programmation asynchrone :
from aiohttp.web import json_response
async def test(self...