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
© Copyright 2024