PEP-3156
Async I/O en Python
Saúl Ibarra Corretgé
@saghul

PyConES 2013
Sunday, November 24, 13
repr(self)
>>> from Amsterdam import saghul
>>> saghul.work()
VoIP, SIP, XMPP, chat, Real Time Communications
>>> saghul.o...
import open_source

• Core Team de libuv
• Tulip / asyncio
• Muchos experimentos con event
loops y más cosas

•

Sunday, N...
import socket
import socket
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
server.bind(('127.0.0.1...
except Exception

• Solo podemos gestionar una conexión
• Soluciones para soportar múltiples
clientes

• Threads
• Proceso...
import thread
import socket
import thread
def handle_client(client, addr):
print("Client connected: {}".format(addr))
whil...
except Exception

• Los threads añaden overhead
•
•

Tamaño del stack
Cambios de contexto

• Sincronización entre threads
...
Multiplexores de I/O

• Examinar y bloquearse a la espera de
que un fd esté listo

• ¡Aun así la operación puede bloquear!...
Sunday, November 24, 13
Multiplexores de I/O
1. Poner un fd en modo no bloqueante
2. Añadir el fd al multiplexor
3. Esperar un rato
4. Realizar la...
def set_nonblocking
import fcntl
def set_nonblocking(fdobj):
# unix only
try:
setblocking = fdobj.setblocking
except Attri...
import select

• El módulo “select” implementa los
distintos multiplexores de i/o

•
•
•
•

select
poll
kqueue
epoll

• En...
sys.platform == ‘win32’

• Soporta select()!
• 64 fds por thread
• >= Vista soporta WSAPoll!
• Está roto: bit.ly/Rg06jX
• ...
import IOCP

• Empezar la operación de i/o en modo
overlapped

• Recibe un callback cuando ha
terminado

• Completion Port...
windows.rules = True

Sunday, November 24, 13
Sunday, November 24, 13
Frameworks

• Los frameworks nos abstraen de las
diferentes plataformas

• Protocolos
• Integracion con otros event loops:...
import twisted

• Usa select, poll, kqueue, epoll del
módulo select

• IOCP en Windows
• Integración con otros loops como ...
import tornado

• Usa select, poll, kqueue, epoll del
modulo select

• select() en Windows :-(
• Principalmente orientado ...
import gevent

• Usa libevent en la 0.x y libev en la 1.x
• select() en Windows :-(
• API síncrona con greenlet

Sunday, N...
import asyncore

• raise RuntimeError(“NOT GOOD ENOUGH”)
• “asyncore: included batteries don’t fit”
bit.ly/182HcHT

Sunday,...
Solución

Sunday, November 24, 13
I’m not trying to reinvent the
wheel. I’m trying to build a
good one.
Guido van Rossum

Sunday, November 24, 13
asyncio

import tulip
Sunday, November 24, 13
import asyncio

• Implementación de referencia del
PEP-3156

• Componentes base para i/o asíncrona
• Funciona en Python >=...
Objetivos

• Implementación moderna de i/o asíncrona
para Python

• Utilizar yield from (PEP-380)
•

Pero no obligar a nad...
Objetivos

• Soporte para Unix y Windows
• IPv4 e IPv6
• TCP, UDP y pipes
• SSL básico (seguro por defecto)
• Subprocesos
...
No Objetivos

• Perfección
• Reemplazar los frameworks actuales
• Implementaciones de protocolos
concretos

• Reemplazar h...
¿Interoperabilidad?

twisted

tornado

gevent

...

asyncio
selectors

Sunday, November 24, 13

iocp
¿Interoperabilidad?

twisted

tornado

gevent

asyncio
rose / pyuv

Sunday, November 24, 13

...
Arquitectura
Sunday, November 24, 13
Componentes
Event loop, policy

Coroutines, Futures, Tasks

Transports, Protocols

Sunday, November 24, 13
Event loop
Sunday, November 24, 13
Event loop y policy

• Selecciona el mejor selector para cada
plataforma

• APIs para crear servidores y
conexiones (TCP, ...
Callbacks

• loop.call_soon(func, *args)
• loop.call_later(delay, func, *args)
• loop.call_at(when, func, *args)
• loop.ti...
Callbacks para I/O

• loop.add_reader(fd, func, *args)
• loop.add_writer(fd, func, *args)
• loop.remove_reader(fd)
• loop....
Señales Unix

• loop.add_signal_handler(sig, func, *args)
• loop.remove_signal_handler(sig)

Sunday, November 24, 13
Interfaz con Threads

• loop.call_soon_threadsafe(func, *args)
• loop.run_in_executor(exc, func, *args)
• loop.set_default...
Inicio y parada

• loop.run_forever()
• loop.stop()
• loop.run_until_complete(f)

Sunday, November 24, 13
Acceso a la instancia

• get_event_loop()
• set_event_loop(loop)
• new_event_loop()

Sunday, November 24, 13
Policy (default)

• Un event loop por thread
• Solo se crea un loop automágicamente
para el main thread

Sunday, November ...
Policy

• Configura qué hacen get/set/new
_event_loop

• Es el único objeto global
• ¡Se puede cambiar! (ejemplo: rose)

Su...
Coroutines, Futures y
Tasks
Sunday, November 24, 13
Coroutines, Futures y Tasks

• Coroutines
•
•

una función generator, puede recibir valores

•

promesa de resultado o err...
Coroutines y yield from
import asyncio
import socket
loop = asyncio.get_event_loop()
@asyncio.coroutine
def handle_client(...
Coroutines y yield from

• Imagina que el yield from no existe
• Imagina que el código es secuencial
• No te bases en la d...
Futures

• Parecidos a los Futures de PEP-3148
•

concurrent.futures.Future

•
•
•
•
•

f.set_result(); r = f.result()

• ...
Futures + Coroutines

• yield from funciona con los Futures!
•
•

f = Future()

•

Alguien pondrá el resultado o la excepc...
Deshaciendo callbacks
@asyncio.coroutine
def sync_looking_function(*args):
fut = asyncio.Future()
def cb(result, error):
i...
Tasks

• Unicornios con polvo de hada
• Es una coroutine metida en un Future
• WAT
• Hereda de Future
• Funciona con yield...
Tasks vs coroutines

• Una coroutine no “avanza” sin un
mechanismo de scheduling

• Los Tasks pueden avanzar solos, sin
ne...
Otro ejemplo
import asyncio
loop = asyncio.get_event_loop()
clients = {} # task -> (reader, writer)
def _accept_client(cli...
Transports y Protocols
Sunday, November 24, 13
Transports y Protocols

• Transport: representa una conexión
(socket, pipe, ...)

• Protocol: representa una aplicación
(s...
Clientes y servidores

• loop.create_connection(...)
•

crea un Transport y un Protocol

•

crea un Transport y un Protoco...
Clientes y servidores

• loop.open_connection(...)
•

wrapper sobre create_connection, devuelve
(stream_reader, stream_wri...
Interfaz Transport -> Protocol

• connection_made(transport)
• data_received(data)
• eof_received()
• connection_lost(exc)...
Interfaz Protocol -> Transport

• write(data)
• writelines(seq)
• write_eof()
• close()
• abort()
Sunday, November 24, 13
MOAR PROTOCOL!

• ¡Esta tarde a las 17:30!
• “Tulip or not Tulip” - Iñaki Galarza

Sunday, November 24, 13
¿Y ahora qué?

• Ven a la charla de Iñaki
• Lee el PEP-3156
• Implementa algún protocolo sencillo
(cliente IRC por ejemplo...
¿Preguntas?

@saghul
bettercallsaghul.com
Sunday, November 24, 13
Referencias

• code.google.com/p/tulip/
• groups.google.com/forum/#!forum/
python-tulip

• PEP-3156
• http://www.youtube.c...
Upcoming SlideShare
Loading in …5
×

PEP-3156: Async I/O en Python

4,261 views

Published on

Slides from my talk about Async I/O in Python which I gave at PyConES.

Published in: Technology
0 Comments
7 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,261
On SlideShare
0
From Embeds
0
Number of Embeds
1,215
Actions
Shares
0
Downloads
17
Comments
0
Likes
7
Embeds 0
No embeds

No notes for slide

PEP-3156: Async I/O en Python

  1. 1. PEP-3156 Async I/O en Python Saúl Ibarra Corretgé @saghul PyConES 2013 Sunday, November 24, 13
  2. 2. repr(self) >>> from Amsterdam import saghul >>> saghul.work() VoIP, SIP, XMPP, chat, Real Time Communications >>> saghul.other() networking, event loops, sockets, MOAR PYTHON >>> saghul.languages() Python, C >>> saghul.message() Estoy encantado de participar en PyConES! >>> Sunday, November 24, 13
  3. 3. import open_source • Core Team de libuv • Tulip / asyncio • Muchos experimentos con event loops y más cosas • Sunday, November 24, 13 github.com/saghul
  4. 4. import socket import socket server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) server.bind(('127.0.0.1', 1234)) server.listen(128) print("Server listening on: {}".format(server.getsockname())) client, addr = server.accept() print("Client connected: {}".format(addr)) while True: data = client.recv(4096) if not data: print("Client has disconnected") break client.send(data) server.close() Sunday, November 24, 13
  5. 5. except Exception • Solo podemos gestionar una conexión • Soluciones para soportar múltiples clientes • Threads • Procesos • Multiplexores de I/O Sunday, November 24, 13
  6. 6. import thread import socket import thread def handle_client(client, addr): print("Client connected: {}".format(addr)) while True: data = client.recv(4096) if not data: print("Client has disconnected") break client.send(data) server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) server.bind(('127.0.0.1', 1234)) server.listen(128) print("Server listening on: {}".format(server.getsockname())) while True: client, addr = server.accept() thread.start_new_thread(handle_client, (client, addr)) Sunday, November 24, 13
  7. 7. except Exception • Los threads añaden overhead • • Tamaño del stack Cambios de contexto • Sincronización entre threads • El GIL - NO es un problema Sunday, November 24, 13
  8. 8. Multiplexores de I/O • Examinar y bloquearse a la espera de que un fd esté listo • ¡Aun así la operación puede bloquear! • El fd tiene que ser puesto en modo no bloqueante • ¿Y Windows? Sunday, November 24, 13
  9. 9. Sunday, November 24, 13
  10. 10. Multiplexores de I/O 1. Poner un fd en modo no bloqueante 2. Añadir el fd al multiplexor 3. Esperar un rato 4. Realizar la operación bloqueante sobre el fd si está listo Sunday, November 24, 13
  11. 11. def set_nonblocking import fcntl def set_nonblocking(fdobj): # unix only try: setblocking = fdobj.setblocking except AttributeError: try: fd = fdobj.fileno() except AttributeError: fd = fdobj flags = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) else: setblocking(False) Sunday, November 24, 13
  12. 12. import select • El módulo “select” implementa los distintos multiplexores de i/o • • • • select poll kqueue epoll • En Python 3.4, módulo “selectors” Sunday, November 24, 13
  13. 13. sys.platform == ‘win32’ • Soporta select()! • 64 fds por thread • >= Vista soporta WSAPoll! • Está roto: bit.ly/Rg06jX • IOCP es lo que mola Sunday, November 24, 13
  14. 14. import IOCP • Empezar la operación de i/o en modo overlapped • Recibe un callback cuando ha terminado • Completion Ports • Abstracción totalmente distinta a Unix Sunday, November 24, 13
  15. 15. windows.rules = True Sunday, November 24, 13
  16. 16. Sunday, November 24, 13
  17. 17. Frameworks • Los frameworks nos abstraen de las diferentes plataformas • Protocolos • Integracion con otros event loops: Qt, GLib, ... • Distintas APIs Sunday, November 24, 13
  18. 18. import twisted • Usa select, poll, kqueue, epoll del módulo select • IOCP en Windows • Integración con otros loops como Qt • Protocolos, transportes, ... • Deferred Sunday, November 24, 13
  19. 19. import tornado • Usa select, poll, kqueue, epoll del modulo select • select() en Windows :-( • Principalmente orientado a desarrollo web • API Sunday, November 24, 13 síncrona con coroutines
  20. 20. import gevent • Usa libevent en la 0.x y libev en la 1.x • select() en Windows :-( • API síncrona con greenlet Sunday, November 24, 13
  21. 21. import asyncore • raise RuntimeError(“NOT GOOD ENOUGH”) • “asyncore: included batteries don’t fit” bit.ly/182HcHT Sunday, November 24, 13
  22. 22. Solución Sunday, November 24, 13
  23. 23. I’m not trying to reinvent the wheel. I’m trying to build a good one. Guido van Rossum Sunday, November 24, 13
  24. 24. asyncio import tulip Sunday, November 24, 13
  25. 25. import asyncio • Implementación de referencia del PEP-3156 • Componentes base para i/o asíncrona • Funciona en Python >= 3.3 Sunday, November 24, 13
  26. 26. Objetivos • Implementación moderna de i/o asíncrona para Python • Utilizar yield from (PEP-380) • Pero no obligar a nadie • No utilizar nada que requiera Python > 3.3 • Interoprabilidad con otros frameworks Sunday, November 24, 13
  27. 27. Objetivos • Soporte para Unix y Windows • IPv4 e IPv6 • TCP, UDP y pipes • SSL básico (seguro por defecto) • Subprocesos Sunday, November 24, 13
  28. 28. No Objetivos • Perfección • Reemplazar los frameworks actuales • Implementaciones de protocolos concretos • Reemplazar httplib, smtplib, ... • Funcionar en Python < 3.3 Sunday, November 24, 13
  29. 29. ¿Interoperabilidad? twisted tornado gevent ... asyncio selectors Sunday, November 24, 13 iocp
  30. 30. ¿Interoperabilidad? twisted tornado gevent asyncio rose / pyuv Sunday, November 24, 13 ...
  31. 31. Arquitectura Sunday, November 24, 13
  32. 32. Componentes Event loop, policy Coroutines, Futures, Tasks Transports, Protocols Sunday, November 24, 13
  33. 33. Event loop Sunday, November 24, 13
  34. 34. Event loop y policy • Selecciona el mejor selector para cada plataforma • APIs para crear servidores y conexiones (TCP, UDP, ...) Sunday, November 24, 13
  35. 35. Callbacks • loop.call_soon(func, *args) • loop.call_later(delay, func, *args) • loop.call_at(when, func, *args) • loop.time() Sunday, November 24, 13
  36. 36. Callbacks para I/O • loop.add_reader(fd, func, *args) • loop.add_writer(fd, func, *args) • loop.remove_reader(fd) • loop.remove_writer(fd) Sunday, November 24, 13
  37. 37. Señales Unix • loop.add_signal_handler(sig, func, *args) • loop.remove_signal_handler(sig) Sunday, November 24, 13
  38. 38. Interfaz con Threads • loop.call_soon_threadsafe(func, *args) • loop.run_in_executor(exc, func, *args) • loop.set_default_executor(exc) Sunday, November 24, 13
  39. 39. Inicio y parada • loop.run_forever() • loop.stop() • loop.run_until_complete(f) Sunday, November 24, 13
  40. 40. Acceso a la instancia • get_event_loop() • set_event_loop(loop) • new_event_loop() Sunday, November 24, 13
  41. 41. Policy (default) • Un event loop por thread • Solo se crea un loop automágicamente para el main thread Sunday, November 24, 13
  42. 42. Policy • Configura qué hacen get/set/new _event_loop • Es el único objeto global • ¡Se puede cambiar! (ejemplo: rose) Sunday, November 24, 13
  43. 43. Coroutines, Futures y Tasks Sunday, November 24, 13
  44. 44. Coroutines, Futures y Tasks • Coroutines • • una función generator, puede recibir valores • promesa de resultado o error • Future que ejecuta una coroutine decorada con @coroutine • Future • Task Sunday, November 24, 13
  45. 45. Coroutines y yield from import asyncio import socket loop = asyncio.get_event_loop() @asyncio.coroutine def handle_client(client, addr): print("Client connected: {}".format(addr)) while True: data = yield from loop.sock_recv(client, 4096) if not data: print("Client has disconnected") break client.send(data) @asyncio.coroutine def accept_connections(server_socket): while True: client, addr = yield from loop.sock_accept(server_socket) asyncio.async(handle_client(client, addr)) server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) server.bind(('127.0.0.1', 1234)) server.listen(128) server.setblocking(False) print("Server listening on: {}".format(server.getsockname())) loop.run_until_complete(accept_connections(server)) Sunday, November 24, 13
  46. 46. Coroutines y yield from • Imagina que el yield from no existe • Imagina que el código es secuencial • No te bases en la definición formal de yield from (PEP-380) Sunday, November 24, 13
  47. 47. Futures • Parecidos a los Futures de PEP-3148 • concurrent.futures.Future • • • • • f.set_result(); r = f.result() • API (casi) idéntico: Sunday, November 24, 13 f.set_exception(e); e = f.exception() f.done() f.add_done_callback(x); f.remove_done_callback(x) f.cancel(); f.cancelled()
  48. 48. Futures + Coroutines • yield from funciona con los Futures! • • f = Future() • Alguien pondrá el resultado o la excepción luego r = yield from f • Espera a que esté listo y devuelve f.result() • Normalmente devueltos por funciones Sunday, November 24, 13
  49. 49. Deshaciendo callbacks @asyncio.coroutine def sync_looking_function(*args): fut = asyncio.Future() def cb(result, error): if error is not None: fut.set_result(result) else: fut.set_exception(Exception(error)) async_function(cb, *args) return (yield from fut) Sunday, November 24, 13
  50. 50. Tasks • Unicornios con polvo de hada • Es una coroutine metida en un Future • WAT • Hereda de Future • Funciona con yield from • Sunday, November 24, 13 r = yield from Task(coro(...))
  51. 51. Tasks vs coroutines • Una coroutine no “avanza” sin un mechanismo de scheduling • Los Tasks pueden avanzar solos, sin necesidad de esperar • El event loop es el scheduler! • Sunday, November 24, 13 Magia!
  52. 52. Otro ejemplo import asyncio loop = asyncio.get_event_loop() clients = {} # task -> (reader, writer) def _accept_client(client_reader, client_writer): task = asyncio.Task(_handle_client(client_reader, client_writer)) clients[task] = (client_reader, client_writer) def client_done(task): del clients[task] task.add_done_callback(client_done) @asyncio.coroutine def _handle_client(client_reader, client_writer): while True: data = (yield from client_reader.readline()) client_writer.write(data) f = asyncio.start_server(_accept_client, '127.0.0.1', 12345) server = loop.run_until_complete(f) loop.run_forever() Sunday, November 24, 13
  53. 53. Transports y Protocols Sunday, November 24, 13
  54. 54. Transports y Protocols • Transport: representa una conexión (socket, pipe, ...) • Protocol: representa una aplicación (servidor HTTP, cliente IRC, ...) • Siempre van juntos • API basada en llamadas a función y callbacks Sunday, November 24, 13
  55. 55. Clientes y servidores • loop.create_connection(...) • crea un Transport y un Protocol • crea un Transport y un Protocol por cada conexión aceptada • devuelve un objeto Server • loop.create_server(...) Sunday, November 24, 13
  56. 56. Clientes y servidores • loop.open_connection(...) • wrapper sobre create_connection, devuelve (stream_reader, stream_writer) • loop.start_server(...) • Sunday, November 24, 13 wrapper sobre create_server, ejecuta un callback con (stream_reader, stream_writer) por cada conexión aceptada
  57. 57. Interfaz Transport -> Protocol • connection_made(transport) • data_received(data) • eof_received() • connection_lost(exc) Sunday, November 24, 13
  58. 58. Interfaz Protocol -> Transport • write(data) • writelines(seq) • write_eof() • close() • abort() Sunday, November 24, 13
  59. 59. MOAR PROTOCOL! • ¡Esta tarde a las 17:30! • “Tulip or not Tulip” - Iñaki Galarza Sunday, November 24, 13
  60. 60. ¿Y ahora qué? • Ven a la charla de Iñaki • Lee el PEP-3156 • Implementa algún protocolo sencillo (cliente IRC por ejemplo) • ¡Usa asyncio en tu próximo proyecto! Sunday, November 24, 13
  61. 61. ¿Preguntas? @saghul bettercallsaghul.com Sunday, November 24, 13
  62. 62. Referencias • code.google.com/p/tulip/ • groups.google.com/forum/#!forum/ python-tulip • PEP-3156 • http://www.youtube.com/watch? v=1coLC-MUCJc • https://www.dropbox.com/s/ essjj4qmmtrhys4/SFMeetup2013.pdf Sunday, November 24, 13

×