Programación asíncrona: alternativas
Gevent
1. Introducción
La librería Gevent permite crear en Python subrutinas para gestionar de manera eficaz los problemas de red. Se basa en el uso de greenlet y un bucle orientado a eventos con buen rendimiento (libev o libuv). La instalación se hace de la siguiente manera:
$ pip install gevent
Por lo tanto, la librería contiene un objeto socket que trabaja de manera concurrente y algunas funciones útiles, como se muestra en este ejemplo adaptado de la documentación oficial:
>>> 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']
En este ejemplo, vamos a pedir la IP asociada a un nombre de host y vamos a crear varias tareas para cada host, para ponerlas de forma concurrente.
La función joinall permite esperar a que todas las tareas hayan terminado. La última línea permite recuperar el resultado de cada tarea.
2. DNS
Gevent incluye las herramientas necesarias para la resolución de nombre de dominio, como muestra el ejemplo anterior. Se trata de una reescritura iso-funcional parcial de las funcionalidades presentes en el módulo python socket. Por lo tanto, puede consultar la documentación oficial para saber cómo se deben utilizar estas funciones y lo que devuelven.
A continuación se muestra la lista de estas funciones:...
Twisted
1. Introducción
Twisted es una herramienta que permite crear un servidor internet y no solamente un servidor web. En otras palabras, no solo administra el protocolo HTTP, sino que puede hacer WebDav, FTP o implementar sus propios protocolos.
Para instalarla:
$ pip install twisted
El objeto central es un reactor y este último va a hacer funcionar los protocolos, que son descripciones de los datos esperados y las respuestas a proporcionar. Estos protocolos se construyen sobre los transportes, tales como el transporte TCP o UDP.
2. Cliente/servidor TCP
A continuación se muestra un ejemplo de servidor TCP básico: devuelve un echo del dato que recibe. Vamos a empezar importando lo que necesitamos:
from twisted.internet import reactor
from twisted.internet.protocol import Protocol, ServerFactory
A continuación se muestra un ejemplo sencillo de protocolo que permite devolver el dato recibido:
class Echo(Protocol):
def dataReceived(self, data):
"As soon as any data is received, echo it back."
self.transport.write(data)
Es suficiente con conocer las diferentes funciones a sobrecargar, para crear su propio protocolo y hacer lo que queremos.
Para que funcione este protocolo, hay que montar el reactor:
def main():
"""This runs the protocol on port 8000"""
factory = ServerFactory()
factory.protocol = Echo
reactor.listenTCP(8000,factory)
reactor.run()
Este código le debería resultar muy familiar, si ya ha leído el capítulo sobre la programación de red o incluso, el de la programación web.
Después hay que ejecutarlo:
if __name__ == '__main__':
main()
Para la parte cliente, se escribe también un protocolo:
class EchoClient(protocol.Protocol):
def connectionMade(self):
self.transport.write(b"hello, world!")
def dataReceived(self, data):
print("Server said:", data)
...
Trollius
Como sabe, asyncio está disponible solamente para Python 3.4 o superior. sin embargo, existe una alternativa para Python 2.7, cuya rama siempre está activa y lo estará incluso algunos años.
Como sucede con asyncio, trollius ofrece un bucle orientado a eventos, así como los transportes y protocolos entre los que se puede encontrar TCP, UDP o SSL como hemos presentado en el capítulo anterior. Sin embargo, atención, estos protocolos se basan en los de twisted, más que en los de asyncio. Es uno de los motivos por el que hemos introducido twisted justo antes.
También, este módulo le permitirá más básicamente, crear subrutinas y tareas y sincronizarlas.
A continuación se muestra un ejemplo de protocolo UDP (que se parece mucho a lo que hemos podido ver anteriormente):
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,
addr=addr))
...
HTTP asíncrona con aiohttp
1. Lado servidor
Ya se ha presentado bottlepy y mencionado la alternativa flask. Se trata de microframeworks web, que permiten responder a necesidades sencillas.
Hay una alternativa que sintácticamente es muy cercana y que presenta la ventaja de utilizar la programación asíncrona.
A continuación se muestra el ejemplo de introducción 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)
Compárelo con un ejemplo similar con bottlepy:
from bottle import route, run, template
@route('/')
def index():
return "Hello World!"
run(host="localhost", port=8080)
o Flask:
from flask import Flask
app = Flask("test App")
@app.route("/")
def hello():
return "Hello World!"
Con aiohttp, los métodos se pueden escribir usando la programación asíncrona:
from aiohttp.web import json_response
async...