Python, do you even async?
-
Upload
saul-ibarra-corretge -
Category
Technology
-
view
1.295 -
download
0
description
Transcript of Python, do you even async?
Python, do you even async?
Saúl Ibarra Corretgé@saghul
DomCode, August 2014
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>>>
github.com/saghul
Please give feedback!https://joind.in/event/view/2471
The road toasynchronous i/o
import socketimport socket!server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)server.bind(('127.0.0.1', 1234))server.listen(511)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()
except Exception:
• We can only handle one client at a time!• Solutions for handling multiple clients• Threads
• I/O multiplexing
• Check the C10K problem if you haven’t already!• http://www.kegel.com/c10k.html
try: sockets + threadsimport socketimport 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.upper())!server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)server.bind(('127.0.0.1', 1234))server.listen(511)print("Server listening on: {}".format(server.getsockname()))!while True: client, addr = server.accept() thread.start_new_thread(handle_client, (client, addr))
except Exception:
• Threads have overhead• Stack size
• Context switching
• Synchronisation • GIL? • Not for I/O!
I/O multiplexing
• Examine and block for I/O in multiple file descriptors at the same time• Single thread
• A file descriptor is ready if the corresponding I/O operation can be performed without blocking
• File descriptor has to be set to benon-blocking
I/O is hard
• Different paradigms in Unix vs Windows• “are you ready?” vs “call me later”
• Frameworks• Abstract platform differences
• Provide tools for easier development of applications
The included batteries
• select• asyncore / asyncchat
• selectors• asyncio
“batteries don’t fit”http://bit.ly/182HcHT
Frameworks
• Platform abstraction• Protocol implementations• Integration with other event loops: Qt,
GLib, ...• Different API styles• Async file i/o?
import twisted
• Uses select, poll, kqueue, epoll from the select module
• IOCP on Windows• Integration with other event loops• Factory / Protocol / Transport
abstractions• Deferreds
import tornado
• Uses select, poll, kqueue, epoll from the select module
• select() on Windows :-(• Mainly oriented to web development• Synchronous looking API with
coroutines / Futures
import gevent
• Uses libev for platform abstraction
• select() on Windows :-(
• Syncronous API using greenlet
• ME LIKEY! :-)
Solution!
I’m not trying to reinvent the wheel. I’m trying to build a good one. !
Guido van Rossum
import tulipasyncio
import asyncio
• Reference implementation for PEP-3156
• Basic components for doing async i/o• Works on Python >= 3.3• Trollius: backport for Python >= 2.6
Goals
• Modern implementation of asynchronous i/o for Python
• Use yield from (PEP-380)• But don’t force it
• Don’t use anything that requiresPython > 3.3
• Interoperability with other frameworks
Goals
• Unix and Windows support
• IPv4 and IPv6
• TCP, UDP and pipes
• Basic SSL (secure by default)
• Subprocesses
Non goals
• Perfection• Replacing current frameworks• Protocol implementations• Replace httplib, smtplib, ...• Make it work on Python < 3.3
Interoperability
selectors iocp
asyncio
twisted tornado gevent ...
Example: Tornado
asyncio / twisted
tornado
epoll/kqueue
tornado
Architecture
Components
Event loop, policy
Coroutines, Futures, Tasks
Transports, Protocols, Streams
Event loop
Calculatepoll time Poll
Runcallbacks
Event loop & policy
• Chooses the best i/o mechanism for a given platform
• APIs for creating server and client connections (TCP, UDP, …)
• Establish the context for a loop
Working with threads
• loop.call_soon_threadsafe(func, *args)• loop.run_in_executor(exc, func, *args)• loop.set_default_executor(exc)
!
• PEP-3148 executor
Coroutines, Futures & Tasks
Coroutines, Futures & Tasks
• Coroutines• a generator function, can receive values
• decorated with @coroutine
• Future• promise of a result or an error
• Task• Future which runs a coroutine
Coroutines & yield fromimport asyncioimport socket!loop = asyncio.get_event_loop()[email protected] 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)[email protected] 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(511)server.setblocking(False)print("Server listening on: {}".format(server.getsockname()))!loop.run_until_complete(accept_connections(server))
Coroutines & yield from
• Imagine the yield from is not there• Imagine the code is executed
sequentially• Not exactly the formal definition of yield
from (from PEP-380)
Futures
• Similar to Futures from PEP-3148• API (almost) identical• Adapted for asyncio’s single threaded
model
Futures + Coroutines
• yield from works with Futures!• f = Future()• Someone will set the result or exception
• r = yield from f• Waits until done and returns f.result()
• Usually returned by functions
Undoing callbacks (I)
@asyncio.coroutinedef 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)
Undoing callbacks (II)
!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 fut
Tasks
• Unicorns covered in fairy dust• It’s a coroutine wrapped in a Future• Inherits from Future• Works with yield from• r = yield from Task(coro(...))
Tasks vs coroutines
• A coroutine doesn’t “advance” without a scheduling mechanism
• Tasks can advance in their own• The event loop is the scheduler!
Exampleimport 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)[email protected] 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()
Transports & Protocols
Transports & Protocols
• Transport: represents a connection (socket, pipe, ...)
• Protocol: represents an application (HTTP server, IRC client, ...)
• They always go together
• API is based on function calls and callbacks
Streams
• Abstraction on top of transports / protocols
• File-like API using coroutines
Transport -> Protocol
• connection_made(transport)• data_received(data)• eof_received()• connection_lost(exc)
!
• UDP, pipes and subprocesses are slightly different
Protocol -> Transport
• write(data)• writelines(seq)• write_eof()• close()
Enter Trollius!
• Backport of asyncio for Python >= 2.6
• By Victor Stinner (@haypo)
• Slightly different syntax
• yield instead of yield from
• raise Return(x) instead of return x on generators
• pip install trollius
That was all?!
• Go read PEP-3156
• Implement a simple protocol (an IRC client for example)
• Checkout the links on the last slide
• Use asyncio in your next project
Questions?
bettercallsaghul.com@saghul
Links
• https://code.google.com/p/tulip/
• http://legacy.python.org/dev/peps/pep-3156/
• https://code.google.com/p/tulip/wiki/ThirdParty
• http://asyncio.org/
• http://www.slideshare.net/saghul