& What Is Async, How Does It Work, When Should I Use It?

What Is Async,
How Does It Work,
&
When Should I Use It?
A. Jesse Jiryu Davis
@jessejiryudavis
MongoDB
Food in NYC
• Subs
• Pizza
• Omakase
Subs
⚇!
☺
Counter
client
!
sandwich!
maker
pool
CPU-bound web app
Client
Clients
Server
• Throughput bound by computation
• No async
Pizza
⚇⚇⚇
⚇!
🍕
oven
☺
pizza
cook
Counter
client
!
5
Normal web app
Client
Clients
Server
• Throughput bound by memory
• Async
Backend
Database,
OAuth service,
etc.
Omakase
!
waiter
clients
Counter
⚇⚇⚇!
chef
7
Websocket application
Client
Clients
sockets
Server
sockets
Events
• 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?
Client
request
Backend
Server
request
time
store state
response
response
Why is async hard to code?
Client
websocket
Backend
Server
time
store state
event
event
Memory per connection
Ways to store state:
Threads
Callbacks
Coding difficulty
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
17
Application
asyncio’s event loop
start_server()
register(fd,!
accept_connection)
run_forever()
epoll / kqueue
accept_connection()
onConnect()
clients = set()!
!
class ChatProtocol(WebSocketServerProtocol):!
def onConnect(self):!
clients.add(self)!
!
How is this called?
def onMessage(self, msg):!
for c in clients:!
if c is not self:!
c.sendMessage(msg)!
!
def onClose(self):!
clients.remove(self)!
example.py
!
!
!
!
!
loop = asyncio.get_event_loop()!
loop.create_server()!
loop.run_forever()!
Let’s look at this
example.py
class EventLoop:!
def create_server():!
!
sock = socket.socket(...)!
sock.bind(...)!
sock.listen()!
sock.setblocking(False)!
!
Let’s
look
at
this
fd = sock.fileno()!
self._selector.register(!
fd,!
selectors.EVENT_READ,!
(self._accept_connection, None))!
Magic
reader, writer
asyncio
class EventLoop:!
def _accept_connection():!
conn, addr = sock.accept()!
conn.setblocking(False)!
!
_SelectorSocketTransport(!
self, conn, protocol)!
ChatProtocol
class _SelectorSocketTransport:!
def __init__(self, loop, sock, protocol):!
!
self._protocol.connection_made(self)!
This was our goal
asyncio
class EventLoop:!
def _accept_connection():!
conn, addr = sock.accept()!
conn.setblocking(False)!
!
_SelectorSocketTransport(!
self, conn, protocol)!
But how exactly is this called?
asyncio
!
!
!
!
!
loop = asyncio.get_event_loop()!
loop.create_server()!
loop.run_forever()!
Let’s look at this
example.py
class EventLoop:!
def run_forever(self):!
magic
while True:!
event_list = self._selector.select()!
!
!
!
accept_connection
!
for data in event_list:!
fd, mask, reader, writer = data!
⚇!
client
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()!
asyncio
Application
asyncio’s event loop
start_server()
register(fd,!
accept_connection)
run_forever()
select()
accept_connection()
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
No:
• CPU-bound
• No async driver
• No async expertise
28
A. Jesse Jiryu Davis
@jessejiryudavis
MongoDB