What Is Async, How Does It Work,
A. Jesse Jiryu Davis
@jessejiryudavis
MongoDB
When Should I Use It?&
Food in NYC
• Subs
• Pizza
• Omakase
Subs
Counter
⚇!client
⚇!sandwich!maker
pool☺
CPU-bound web app
Client ServerClients
• Throughput bound by computation• No async
Pizza
5
Counter
⚇!client
⚇!pizza cook
☺
oven🍕
⚇⚇⚇
Normal web app
Client Server
• Throughput bound by memory • Async
Backend Database,
OAuth service, etc.
Clients
Omakase
7
Counter
⚇!waiter
kitchen
⚇⚇⚇!clients
Websocket application
Client Serversockets
EventssocketsClients
• Number of clients bound by memory • Async
What’s async for?
Minimizes resources per connection.
C10K
kegel.com/c10k.html
Why is async hard to code?BackendClient Server
request
response
store state
request
response
time
Why is async hard to code?BackendClient Server
websocket
event
store state
register for events
event
time
Ways to store state:
Coding difficulty
Threads
Callbacks
Mem
ory
per
co
nn
ectio
n
Ways to store state:
• Threads • Callbacks !
... and: !
• Coroutines • Greenlets !
and so on....
So, what is async?
• Single-threaded • I/O concurrency • Non-blocking sockets • epoll / kqueue • Event loop
asyncio
• AKA “Tulip” • Python 3.4 standard library • Implements PEP 3156 • Standard event loop • Coroutines
17
Layers
18
Application
Protocol
Transport
Event Loop
Selectors
asyncio{autobahn websockets
example.py
from autobahn import (WebSocketServerProtocol,! WebSocketServerFactory)!
example.py
clients = set()!!class ChatProtocol(WebSocketServerProtocol):! def onConnect(self):! clients.add(self)!! def onMessage(self, msg):! for c in clients:! if c is not self:! c.sendMessage(msg)!! def onClose(self):! clients.remove(self)!
example.py
How is this called?
Let’s look at this
example.py
factory = WebSocketServerFactory(! "ws://localhost:8888")!!factory.protocol = ChatProtocol!!loop = asyncio.get_event_loop()!asyncio.Task(! loop.create_server(factory, '127.0.0.1', 8888))!!loop.run_forever()!
class BaseEventLoop(events.AbstractEventLoop):! def create_server(! self, protocol_factory, host, port):!! sock = socket.socket(...)! sock.bind(...)! sock.listen()! sock.setblocking(False)!! fd = sock.fileno()! self._selector.register(! fd,! selectors.EVENT_READ,! (self._accept_connection, None))!Magic
Let’s look at this
asyncio
reader, writer
asyncio
class BaseEventLoop(events.AbstractEventLoop):! def _accept_connection(! self, protocol_factory, sock):! conn, addr = sock.accept()! conn.setblocking(False)!! protocol = protocol_factory()! _SelectorSocketTransport(! self, conn, protocol)!
class _SelectorSocketTransport(_SelectorTransport):! def __init__(self, loop, sock, protocol):! super().__init__(loop, sock, protocol)! self._protocol.connection_made(self)!
ChatProtocol
This was our goal
asyncio
class BaseEventLoop(events.AbstractEventLoop):! def _accept_connection(! self, protocol_factory, sock):! conn, addr = sock.accept()! conn.setblocking(False)!! protocol = protocol_factory()! _SelectorSocketTransport(! self, conn, protocol)!
But how exactly is this called?
Let’s look at this
example.py
factory = WebSocketServerFactory(! "ws://localhost:8888")!!factory.protocol = ChatProtocol!!loop = asyncio.get_event_loop()!asyncio.Task(! loop.create_server(factory, '127.0.0.1', 8888))!!loop.run_forever()!
asyncio
magicclass BaseEventLoop(events.AbstractEventLoop):! def run_forever(self):! while True:! event_list = self._selector.select()!! for fd, mask, data in event_list:! reader, writer = data!! if reader and mask & EVENT_READ:! self._ready.append(reader)!! if writer and mask & EVENT_WRITE:! self._ready.append(writer)!! ntodo = len(self._ready)! for i in range(ntodo):! callback = self._ready.popleft()! callback()!
accept_connection
Application asyncio’s event loop
start_server()
register(fd,! accept_connection)
accept_connection()
run_forever()
onConnect()
Review
• asyncio uses non-blocking sockets. !
• Event loop tracks sockets, and the callbacks waiting for them. !
• selectors: wait for network events. !
• Event loop runs callbacks.
Should I Use It?
Yes: • Slow backend • Websockets • Many connections
29
No: • CPU-bound • No async driver • No async expertise
🍕
A. Jesse Jiryu Davis
@jessejiryudavis
MongoDB