¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
  1. Libros y videos
  2. Python 3
  3. Programación asíncrona: alternativas
Extrait - Python 3 Tratamiento de datos y técnicas de programación (2ª edición)
Extractos del libro
Python 3 Tratamiento de datos y técnicas de programación (2ª edición) Volver a la página de compra del libro

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 

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 y 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. Se 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:

  • get_hostbyname

  • recibe como argumento...

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 que se van a proporcionar. Estos protocolos se construyen sobre los transportes, tales como el transporte TCP o UDP.

2. Cliente/servidor TCP

Aquí tenemos un ejemplo de servidor TCP básico: devuelve un eco (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 por sobrecargar, para crear un protocolo propio 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 se ha leído el capítulo Programación de red o 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 sabemos, asyncio está disponible solamente para Python 3.4 o posterior. 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 ya lo hemos presentado en el capítulo Programación asíncrona: avanzada. 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 permite de forma más básica, crear subrutinas y tareas y sincronizarlas.

A continuación, se muestra un ejemplo de protocolo UDP (que se parece mucho a lo que hemos visto 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íncrono 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.

He aquí un 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) 

Vamos a compararlo con un ejemplo similar de 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 def test(self, request):  ...