Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[PyCon KR 2017] aiohttp in Production

[PyCon KR 2017] aiohttp in Production

PyCon Korea 2017 [aiohttp in Production](https://www.pycon.kr/2017/program/133)

6940536421f1077acc980ec9a78393e1?s=128

Sunghyun Hwang

August 13, 2017
Tweet

Transcript

  1. aiohttp in Production Sunghyun Hwang (2017-08-13)

  2. Disclaimer • Requires Python 3.3+ • Assumes general awareness of

    • Generators/coroutines • AsyncIO concepts • Async/await syntax • Common programming patterns
  3. None
  4. None
  5. aiohttp: Asynchronous HTTP Client/Server

  6. aiohttp: Asynchronous HTTP Client/Server

  7. How aiohttp works

  8. PEP 3156 — Asynchronous IO Support Rebooted: the "asyncio" Module

    Guido van Rossum (2012-12-12)
  9. This module provides infrastructure for writing single-threaded concurrent code using

    coroutines, multiplexing I/O access over sockets...
  10. This module provides infrastructure for writing single-threaded concurrent code using

    coroutines, multiplexing I/O access over sockets...
  11. This module provides infrastructure for writing single-threaded concurrent code using

    coroutines, multiplexing I/O access over sockets...
  12. This module provides infrastructure for writing single-threaded concurrent code using

    coroutines, multiplexing I/O access over sockets...
  13. app = Application() async def hello(request): return Response(text='Hello, Pycon!') app.router.add_get('/',

    hello) run_app(app, host='0.0.0.0', port=8080)
  14. ======== Running on http://0.0.0.0:8080 ======== (Press CTRL+C to quit)

  15. app = Application() async def hello(request): return Response(text='Hello, Pycon!') app.router.add_get('/',

    hello) run_app(app, host='0.0.0.0', port=8080)
  16. class Application(MutableMapping): def __init__(self, *, logger=web_logger, router=None, middlewares=(), handler_args=None, client_max_size=1024**2,

    secure_proxy_ssl_header=None, loop=None, debug=...) aiohttp/web.py
  17. app = Application() async def hello(request): return Response(text='Hello, Pycon!') app.router.add_get('/',

    hello) run_app(app, host='0.0.0.0', port=8080)
  18. app = Application() async def hello(request): return Response(text='Hello, Pycon!') app.router.add_get('/',

    hello) run_app(app, host='0.0.0.0', port=8080) app = Application() async def hello(request): return Response(text='Hello, Pycon!') app.router.add_get('/', hello) run_app(app, host='0.0.0.0', port=8080)
  19. aiohttp/web.py try: print("======== Running on {} ========\n" "(Press CTRL+C to

    quit)".format(', '.join(uris))) loop.run_forever() except (GracefulExit, KeyboardInterrupt): pass
  20. aiohttp/web.py try: print("======== Running on {} ========\n" "(Press CTRL+C to

    quit)".format(', '.join(uris))) loop.run_forever() except (GracefulExit, KeyboardInterrupt): pass try: print("======== Running on {} ========\n" "(Press CTRL+C to quit)".format(', '.join(uris))) loop.run_forever() except (GracefulExit, KeyboardInterrupt): pass
  21. aiohttp/web.py def run_app(app, *, host=None, port=None, ..., loop=None): """Run an

    app locally""" if loop is None: loop = asyncio.get_event_loop()
  22. aiohttp/web.py def run_app(app, *, host=None, port=None, ..., loop=None): """Run an

    app locally""" if loop is None: loop = asyncio.get_event_loop() def run_app(app, *, host=None, port=None, ..., loop=None): """Run an app locally""" if loop is None: loop = asyncio.get_event_loop()
  23. aiohttp/web.py def run_app(app, *, host=None, port=None, ..., loop=None): """Run an

    app locally""" if loop is None: loop = asyncio.get_event_loop() def run_app(app, *, host=None, port=None, ..., loop=None): """Run an app locally""" if loop is None: loop = asyncio.get_event_loop()
  24. Event Loop In computer science, the event loop is a

    programming construct that waits for and dispatches events or messages in a program. — Wikipedia
  25. Event Loop In computer science, the event loop is a

    programming construct that waits for and dispatches events or messages in a program. — Wikipedia
  26. Event Loop In computer science, the event loop is a

    programming construct that waits for and dispatches events or messages in a program. — Wikipedia
  27. Event Loop In computer science, the event loop is a

    programming construct that waits for and dispatches events or messages in a program. — Wikipedia
  28. Event Loop In computer science, the event loop is a

    programming construct that waits for and dispatches events or messages in a program. — Wikipedia
  29. Event Loop while there are still events to process: e

    = get the next event if there is a callback associated with e: call the callback — [An Introduction to libuv](https://nikhilm.github.io/uvbook/basics.html)
  30. class _RunningLoop(threading.local): _loop = None _pid = None _running_loop =

    _RunningLoop() def _get_running_loop(): running_loop = _running_loop._loop if running_loop is not None and _running_loop._pid == os.getpid(): return running_loop asyncio/events.py
  31. class _RunningLoop(threading.local): _loop = None _pid = None _running_loop =

    _RunningLoop() def _get_running_loop(): running_loop = _running_loop._loop if running_loop is not None and _running_loop._pid == os.getpid(): return running_loop asyncio/events.py class _RunningLoop(threading.local): _loop = None _pid = None _running_loop = _RunningLoop() def _get_running_loop(): running_loop = _running_loop._loop if running_loop is not None and _running_loop._pid == os.getpid(): return running_loop
  32. class _RunningLoop(threading.local): _loop = None _pid = None _running_loop =

    _RunningLoop() def _get_running_loop(): running_loop = _running_loop._loop if running_loop is not None and _running_loop._pid == os.getpid(): return running_loop asyncio/events.py class _RunningLoop(threading.local): _loop = None _pid = None _running_loop = _RunningLoop() def _get_running_loop(): running_loop = _running_loop._loop if running_loop is not None and _running_loop._pid == os.getpid(): return running_loop
  33. asyncio/events.py class AbstractEventLoop: """Abstract event loop.""" # Running and stopping

    the event loop. def run_forever(self): """Run the event loop until stop() is called.""" raise NotImplementedError def run_until_complete(self, future): """Run the event loop until a Future is done. Return the Future's result, or raise its exception. """ raise NotImplementedError def is_running(self): """Return whether the event loop is currently running.""" raise NotImplementedError
  34. asyncio/events.py class AbstractEventLoop: """Abstract event loop.""" # Running and stopping

    the event loop. def run_forever(self): """Run the event loop until stop() is called.""" raise NotImplementedError def run_until_complete(self, future): """Run the event loop until a Future is done. Return the Future's result, or raise its exception. """ raise NotImplementedError def is_running(self): """Return whether the event loop is currently running.""" raise NotImplementedError class AbstractEventLoop: """Abstract event loop.""" # Running and stopping the event loop. def run_forever(self): """Run the event loop until stop() is called.""" raise NotImplementedError def run_until_complete(self, future): """Run the event loop until a Future is done. Return the Future's result, or raise its exception. """ raise NotImplementedError def is_running(self): """Return whether the event loop is currently running.""" raise NotImplementedError
  35. asyncio/events.py class AbstractEventLoop: """Abstract event loop.""" # Running and stopping

    the event loop. def run_forever(self): """Run the event loop until stop() is called.""" raise NotImplementedError def run_until_complete(self, future): """Run the event loop until a Future is done. Return the Future's result, or raise its exception. """ raise NotImplementedError def is_running(self): """Return whether the event loop is currently running.""" raise NotImplementedError class AbstractEventLoop: """Abstract event loop.""" # Running and stopping the event loop. def run_forever(self): """Run the event loop until stop() is called.""" raise NotImplementedError def run_until_complete(self, future): """Run the event loop until a Future is done. Return the Future's result, or raise its exception. """ raise NotImplementedError def is_running(self): """Return whether the event loop is currently running.""" raise NotImplementedError
  36. asyncio/events.py class AbstractEventLoop: """Abstract event loop.""" # Running and stopping

    the event loop. def run_forever(self): """Run the event loop until stop() is called.""" raise NotImplementedError def run_until_complete(self, future): """Run the event loop until a Future is done. Return the Future's result, or raise its exception. """ raise NotImplementedError def is_running(self): """Return whether the event loop is currently running.""" raise NotImplementedError class AbstractEventLoop: """Abstract event loop.""" # Running and stopping the event loop. def run_forever(self): """Run the event loop until stop() is called.""" raise NotImplementedError def run_until_complete(self, future): """Run the event loop until a Future is done. Return the Future's result, or raise its exception. """ raise NotImplementedError def is_running(self): """Return whether the event loop is currently running.""" raise NotImplementedError
  37. asyncio/futures.py class Future: _state = _PENDING _result = None _exception

    = None
  38. asyncio/futures.py class Future: _state = _FINISHED _result = None _exception

    = None
  39. asyncio/futures.py class Future: _state = _CANCELLED _result = None _exception

    = None
  40. asyncio/events.py class AbstractEventLoop: """Abstract event loop.""" # Running and stopping

    the event loop. def run_forever(self): """Run the event loop until stop() is called.""" raise NotImplementedError def run_until_complete(self, future): """Run the event loop until a Future is done. Return the Future's result, or raise its exception. """ raise NotImplementedError def is_running(self): """Return whether the event loop is currently running.""" raise NotImplementedError class AbstractEventLoop: """Abstract event loop.""" # Running and stopping the event loop. def run_forever(self): """Run the event loop until stop() is called.""" raise NotImplementedError def run_until_complete(self, future): """Run the event loop until a Future is done. Return the Future's result, or raise its exception. """ raise NotImplementedError def is_running(self): """Return whether the event loop is currently running.""" raise NotImplementedError
  41. asyncio/base_events.py def is_running(self): """Returns True if the event loop is

    running.""" return (self._thread_id is not None)
  42. asyncio/base_events.py def run_until_complete(self, future): ... future.add_done_callback(_run_until_complete_cb) try: self.run_forever() except: if

    new_task and future.done() and not future.cancelled(): future.exception() raise finally: future.remove_done_callback(_run_until_complete_cb) ...
  43. asyncio/base_events.py def run_until_complete(self, future): ... future.add_done_callback(_run_until_complete_cb) try: self.run_forever() except: if

    new_task and future.done() and not future.cancelled(): future.exception() raise finally: future.remove_done_callback(_run_until_complete_cb) ... def run_until_complete(self, future): ... future.add_done_callback(_run_until_complete_cb) try: self.run_forever() except: if new_task and future.done() and not future.cancelled(): future.exception() raise finally: future.remove_done_callback(_run_until_complete_cb) ...
  44. asyncio/base_events.py def run_until_complete(self, future): ... future.add_done_callback(_run_until_complete_cb) try: self.run_forever() except: if

    new_task and future.done() and not future.cancelled(): future.exception() raise finally: future.remove_done_callback(_run_until_complete_cb) ... def run_until_complete(self, future): ... future.add_done_callback(_run_until_complete_cb) try: self.run_forever() except: if new_task and future.done() and not future.cancelled(): future.exception() raise finally: future.remove_done_callback(_run_until_complete_cb) ...
  45. asyncio/base_events.py def _run_until_complete_cb(fut): exc = fut._exception ... fut._loop.stop()

  46. asyncio/base_events.py def _run_until_complete_cb(fut): exc = fut._exception ... fut._loop.stop() def _run_until_complete_cb(fut):

    exc = fut._exception ... fut._loop.stop()
  47. asyncio/base_events.py def run_forever(self): """Run until stop() is called.""" self._check_closed() ...

    self._thread_id = threading.get_ident() ... try: events._set_running_loop(self) while True: self._run_once() if self._stopping: break finally: self._stopping = False self._thread_id = None events._set_running_loop(None)
  48. asyncio/base_events.py def run_forever(self): """Run until stop() is called.""" self._check_closed() ...

    self._thread_id = threading.get_ident() ... try: events._set_running_loop(self) while True: self._run_once() if self._stopping: break finally: self._stopping = False self._thread_id = None events._set_running_loop(None) def run_forever(self): """Run until stop() is called.""" self._check_closed() ... self._thread_id = threading.get_ident() ... try: events._set_running_loop(self) while True: self._run_once() if self._stopping: break finally: self._stopping = False self._thread_id = None events._set_running_loop(None)
  49. asyncio/base_events.py def _run_once(self): """Run one full iteration of the event

    loop. This calls all currently ready callbacks, polls for I/O, schedules the resulting callbacks, and finally schedules 'call_later' callbacks. """
  50. asyncio/base_events.py def _run_once(self): """Run one full iteration of the event

    loop. This calls all currently ready callbacks, polls for I/O, schedules the resulting callbacks, and finally schedules 'call_later' callbacks. """
  51. asyncio/base_events.py def _run_once(self): """Run one full iteration of the event

    loop. This calls all currently ready callbacks, polls for I/O, schedules the resulting callbacks, and finally schedules 'call_later' callbacks. """
  52. asyncio/base_events.py def _run_once(self): """Run one full iteration of the event

    loop. This calls all currently ready callbacks, polls for I/O, schedules the resulting callbacks, and finally schedules 'call_later' callbacks. """
  53. asyncio/base_events.py def _run_once(self): """Run one full iteration of the event

    loop. This calls all currently ready callbacks, polls for I/O, schedules the resulting callbacks, and finally schedules 'call_later' callbacks. """
  54. asyncio/base_events.py def _run_once(self): ... ntodo = len(self._ready) for i in

    range(ntodo): handle = self._ready.popleft() if handle._cancelled: continue if self._debug: try: self._current_handle = handle t0 = self.time() handle._run() dt = self.time() - t0 if dt >= self.slow_callback_duration: logger.warning('Executing %s took %.3f seconds', _format_handle(handle), dt) finally: self._current_handle = None else: handle._run()
  55. asyncio/base_events.py def _run_once(self): ... ntodo = len(self._ready) for i in

    range(ntodo): handle = self._ready.popleft() if handle._cancelled: continue if self._debug: try: self._current_handle = handle t0 = self.time() handle._run() dt = self.time() - t0 if dt >= self.slow_callback_duration: logger.warning('Executing %s took %.3f seconds', _format_handle(handle), dt) finally: self._current_handle = None else: handle._run() def _run_once(self): ... ntodo = len(self._ready) for i in range(ntodo): handle = self._ready.popleft() if handle._cancelled: continue if self._debug: try: self._current_handle = handle t0 = self.time() handle._run() dt = self.time() - t0 if dt >= self.slow_callback_duration: logger.warning('Executing %s took %.3f seconds', _format_handle(handle), dt) finally: self._current_handle = None else: handle._run()
  56. asyncio/base_events.py def _run_once(self): ... ntodo = len(self._ready) for i in

    range(ntodo): handle = self._ready.popleft() if handle._cancelled: continue if self._debug: try: self._current_handle = handle t0 = self.time() handle._run() dt = self.time() - t0 if dt >= self.slow_callback_duration: logger.warning('Executing %s took %.3f seconds', _format_handle(handle), dt) finally: self._current_handle = None else: handle._run() def _run_once(self): ... ntodo = len(self._ready) for i in range(ntodo): handle = self._ready.popleft() if handle._cancelled: continue if self._debug: try: self._current_handle = handle t0 = self.time() handle._run() dt = self.time() - t0 if dt >= self.slow_callback_duration: logger.warning('Executing %s took %.3f seconds', _format_handle(handle), dt) finally: self._current_handle = None else: handle._run()
  57. asyncio/base_events.py def _run_once(self): ... ntodo = len(self._ready) for i in

    range(ntodo): handle = self._ready.popleft() if handle._cancelled: continue if self._debug: try: self._current_handle = handle t0 = self.time() handle._run() dt = self.time() - t0 if dt >= self.slow_callback_duration: logger.warning('Executing %s took %.3f seconds', _format_handle(handle), dt) finally: self._current_handle = None else: handle._run() def _run_once(self): ... ntodo = len(self._ready) for i in range(ntodo): handle = self._ready.popleft() if handle._cancelled: continue if self._debug: try: self._current_handle = handle t0 = self.time() handle._run() dt = self.time() - t0 if dt >= self.slow_callback_duration: logger.warning('Executing %s took %.3f seconds', _format_handle(handle), dt) finally: self._current_handle = None else: handle._run()
  58. aiohttp/web.py try: print("======== Running on {} ========\n" "(Press CTRL+C to

    quit)".format(', '.join(uris))) loop.run_forever() except (GracefulExit, KeyboardInterrupt): pass try: print("======== Running on {} ========\n" "(Press CTRL+C to quit)".format(', '.join(uris))) loop.run_forever() except (GracefulExit, KeyboardInterrupt): pass
  59. aiohttp/web.py server_creations, uris = _make_server_creators( handler, loop=loop, ssl_context=ssl_context, host=host, port=port,

    path=path, sock=sock, backlog=backlog) servers = loop.run_until_complete( asyncio.gather(*server_creations, loop=loop) )
  60. aiohttp/web.py server_creations, uris = _make_server_creators( handler, loop=loop, ssl_context=ssl_context, host=host, port=port,

    path=path, sock=sock, backlog=backlog) servers = loop.run_until_complete( asyncio.gather(*server_creations, loop=loop) ) server_creations, uris = _make_server_creators( handler, loop=loop, ssl_context=ssl_context, host=host, port=port, path=path, sock=sock, backlog=backlog) servers = loop.run_until_complete( asyncio.gather(*server_creations, loop=loop) )
  61. aiohttp/web.py def _make_server_creators(handler, *, loop, ssl_context, host, port, path, sock,

    backlog): host_binding = hosts[0] if len(hosts) == 1 else hosts server_creations.append( loop.create_server(handler, host_binding, port, ...) )
  62. aiohttp/web.py def _make_server_creators(handler, *, loop, ssl_context, host, port, path, sock,

    backlog): host_binding = hosts[0] if len(hosts) == 1 else hosts server_creations.append( loop.create_server(handler, host_binding, port, ...) ) def _make_server_creators(handler, *, loop, ssl_context, host, port, path, sock, backlog): host_binding = hosts[0] if len(hosts) == 1 else hosts server_creations.append( loop.create_server(handler, host_binding, port, ...) )
  63. asyncio/base_events.py @coroutine def create_server(self, protocol_factory, host=None, port=None, *, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE,

    sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None): ... for sock in sockets: sock.listen(backlog) sock.setblocking(False) self._start_serving(protocol_factory, sock, ssl, server, backlog)
  64. asyncio/base_events.py @coroutine def create_server(self, protocol_factory, host=None, port=None, *, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE,

    sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None): ... for sock in sockets: sock.listen(backlog) sock.setblocking(False) self._start_serving(protocol_factory, sock, ssl, server, backlog) @coroutine def create_server(self, protocol_factory, host=None, port=None, *, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None): ... for sock in sockets: sock.listen(backlog) sock.setblocking(False) self._start_serving(protocol_factory, sock, ssl, server, backlog)
  65. asyncio/base_events.py @coroutine def create_server(self, protocol_factory, host=None, port=None, *, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE,

    sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None): ... for sock in sockets: sock.listen(backlog) sock.setblocking(False) self._start_serving(protocol_factory, sock, ssl, server, backlog) @coroutine def create_server(self, protocol_factory, host=None, port=None, *, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None): ... for sock in sockets: sock.listen(backlog) sock.setblocking(False) self._start_serving(protocol_factory, sock, ssl, server, backlog)
  66. aiohttp/web.py server_creations, uris = _make_server_creators( handler, loop=loop, ssl_context=ssl_context, host=host, port=port,

    path=path, sock=sock, backlog=backlog) servers = loop.run_until_complete( asyncio.gather(*server_creations, loop=loop) ) server_creations, uris = _make_server_creators( handler, loop=loop, ssl_context=ssl_context, host=host, port=port, path=path, sock=sock, backlog=backlog) servers = loop.run_until_complete( asyncio.gather(*server_creations, loop=loop) )
  67. aiohttp/web.py handler = app.make_handler( loop=loop, access_log=access_log, **make_handler_kwargs )

  68. aiohttp/web.py def make_handler(self, *, loop=None, secure_proxy_ssl_header=None, **kwargs): self._set_loop(loop) ... return

    Server(self._handle, request_factory=self._make_request, loop=self.loop, **kwargs)
  69. aiohttp/web.py def make_handler(self, *, loop=None, secure_proxy_ssl_header=None, **kwargs): self._set_loop(loop) ... return

    Server(self._handle, request_factory=self._make_request, loop=self.loop, **kwargs) def make_handler(self, *, loop=None, secure_proxy_ssl_header=None, **kwargs): self._set_loop(loop) ... return Server(self._handle, request_factory=self._make_request, loop=self.loop, **kwargs)
  70. aiohttp/web_server.py class Server: ... @asyncio.coroutine def shutdown(self, timeout=None): coros =

    [conn.shutdown(timeout) for conn in self._connections] yield from asyncio.gather(*coros, loop=self._loop) self._connections.clear() self.time_service.close() def __call__(self): return RequestHandler(self, loop=self._loop, **self._kwargs)
  71. aiohttp/web_server.py class Server: ... @asyncio.coroutine def shutdown(self, timeout=None): coros =

    [conn.shutdown(timeout) for conn in self._connections] yield from asyncio.gather(*coros, loop=self._loop) self._connections.clear() self.time_service.close() def __call__(self): return RequestHandler(self, loop=self._loop, **self._kwargs) class Server: ... @asyncio.coroutine def shutdown(self, timeout=None): coros = [conn.shutdown(timeout) for conn in self._connections] yield from asyncio.gather(*coros, loop=self._loop) self._connections.clear() self.time_service.close() def __call__(self): return RequestHandler(self, loop=self._loop, **self._kwargs)
  72. aiohttp/web_protocol.py class RequestHandler(..., asyncio.Protocol):

  73. aiohttp/web_protocol.py class RequestHandler(..., asyncio.Protocol): class RequestHandler(..., asyncio.Protocol):

  74. asyncio/protocols.py class Protocol(BaseProtocol): """Interface for stream protocol. ... State machine

    of calls: start -> CM [-> DR*] [-> ER?] -> CL -> end * CM: connection_made() * DR: data_received() * ER: eof_received() * CL: connection_lost() """ def data_received(self, data): pass def eof_received(self): pass
  75. asyncio/protocols.py class Protocol(BaseProtocol): """Interface for stream protocol. ... State machine

    of calls: start -> CM [-> DR*] [-> ER?] -> CL -> end * CM: connection_made() * DR: data_received() * ER: eof_received() * CL: connection_lost() """ def data_received(self, data): pass def eof_received(self): pass class Protocol(BaseProtocol): """Interface for stream protocol. ... State machine of calls: start -> CM [-> DR*] [-> ER?] -> CL -> end * CM: connection_made() * DR: data_received() * ER: eof_received() * CL: connection_lost() """ def data_received(self, data): pass def eof_received(self): pass
  76. asyncio/protocols.py class Protocol(BaseProtocol): """Interface for stream protocol. ... State machine

    of calls: start -> CM [-> DR*] [-> ER?] -> CL -> end * CM: connection_made() * DR: data_received() * ER: eof_received() * CL: connection_lost() """ def data_received(self, data): pass def eof_received(self): pass class Protocol(BaseProtocol): """Interface for stream protocol. ... State machine of calls: start -> CM [-> DR*] [-> ER?] -> CL -> end * CM: connection_made() * DR: data_received() * ER: eof_received() * CL: connection_lost() """ def data_received(self, data): pass def eof_received(self): pass
  77. asyncio/protocols.py class Protocol(BaseProtocol): """Interface for stream protocol. ... State machine

    of calls: start -> CM [-> DR*] [-> ER?] -> CL -> end * CM: connection_made() * DR: data_received() * ER: eof_received() * CL: connection_lost() """ def data_received(self, data): pass def eof_received(self): pass class Protocol(BaseProtocol): """Interface for stream protocol. ... State machine of calls: start -> CM [-> DR*] [-> ER?] -> CL -> end * CM: connection_made() * DR: data_received() * ER: eof_received() * CL: connection_lost() """ def data_received(self, data): pass def eof_received(self): pass
  78. aiohttp/web_protocol.py class RequestHandler(..., asyncio.Protocol): def data_received(self, data): ... messages, upgraded,

    tail = self._request_parser.feed_data(data)
  79. aiohttp server patterns

  80. app = Application() app['conf'] = configuration app['db'] = db app.on_startup.append(init_db)

    app.on_cleanup.append(close_db) app.router.add_get('/', index_handler) app.router.add_get(r'/resources/{resource_id:\d+}', resource_handler) app.middlewares.append(error_handling_middleware)
  81. app = Application() app['conf'] = configuration app['db'] = db app.on_startup.append(init_db)

    app.on_cleanup.append(close_db) app.router.add_get('/', index_handler) app.router.add_get(r'/resources/{resource_id:\d+}', resource_handler) app.middlewares.append(error_handling_middleware) app = Application() app['conf'] = configuration app['db'] = db app.on_startup.append(init_db) app.on_cleanup.append(close_db) app.router.add_get('/', index_handler) app.router.add_get(r'/resources/{resource_id:\d+}', resource_handler) app.middlewares.append(error_handling_middleware)
  82. app = Application() app['conf'] = configuration app['db'] = db app.on_startup.append(init_db)

    app.on_cleanup.append(close_db) app.router.add_get('/', index_handler) app.router.add_get(r'/resources/{resource_id:\d+}', resource_handler) app.middlewares.append(error_handling_middleware) app = Application() app['conf'] = configuration app['db'] = db app.on_startup.append(init_db) app.on_cleanup.append(close_db) app.router.add_get('/', index_handler) app.router.add_get(r'/resources/{resource_id:\d+}', resource_handler) app.middlewares.append(error_handling_middleware)
  83. app = Application() app['conf'] = configuration app['db'] = db app.on_startup.append(init_db)

    app.on_cleanup.append(close_db) app.router.add_get('/', index_handler) app.router.add_get(r'/resources/{resource_id:\d+}', resource_handler) app.middlewares.append(error_handling_middleware) app = Application() app['conf'] = configuration app['db'] = db app.on_startup.append(init_db) app.on_cleanup.append(close_db) app.router.add_get('/', index_handler) app.router.add_get(r'/resources/{resource_id:\d+}', resource_handler) app.middlewares.append(error_handling_middleware)
  84. app = Application() app['conf'] = configuration app['db'] = db app.on_startup.append(init_db)

    app.on_cleanup.append(close_db) app.router.add_get('/', index_handler) app.router.add_get(r'/resources/{resource_id:\d+}', resource_handler) app.middlewares.append(error_handling_middleware) app = Application() app['conf'] = configuration app['db'] = db app.on_startup.append(init_db) app.on_cleanup.append(close_db) app.router.add_get('/', index_handler) app.router.add_get(r'/resources/{resource_id:\d+}', resource_handler) app.middlewares.append(error_handling_middleware)
  85. My one year with aiohttp

  86. 1. Performance import pymongo from flask import Flask app =

    Flask(__name__) client = pymongo.MongoClient('localhost', 27017) db = client.test @app.route('/') def hello(): return 'Hello, Pycon!' @app.route('/fetch') def fetch(): document = db.greetings.find_one() return document['pycon'] if __name__ == '__main__': app.run(debug=False)
  87. 1. Performance import motor.motor_asyncio from aiohttp.web import Application, Response, run_app

    app = Application() client = motor.motor_asyncio.AsyncIOMotorClient() app['db'] = client.test async def hello(request): return Response(text='Hello, Pycon!') async def fetch(request): db = request.app['db'] document = await db.greetings.find_one() return Response(text=document['pycon']) app.router.add_get('/', hello) app.router.add_get('/fetch', fetch) if __name__ == '__main__': run_app(app)
  88. 1. Performance $ wrk -t12 -c400 -d10s flask Running 10s

    test @ flask 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 75.59ms 11.75ms 135.98ms 78.48% Req/Sec 139.45 59.91 323.00 64.60% 15775 requests in 10.09s, 2.51MB read Socket errors: connect 0, read 870, write 7, timeout 0 Requests/sec: 1562.90 Transfer/sec: 254.89KB $ wrk -t12 -c400 -d10s aiohttp Running 10s test @ aiohttp 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 56.36ms 9.80ms 133.68ms 70.69% Req/Sec 539.28 95.55 0.93k 74.25% 64714 requests in 10.08s, 10.12MB read Socket errors: connect 0, read 257, write 0, timeout 0 Requests/sec: 6423.19 Transfer/sec: 1.00MB $ wrk -t12 -c400 -d10s flask Running 10s test @ flask 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 75.59ms 11.75ms 135.98ms 78.48% Req/Sec 139.45 59.91 323.00 64.60% 15775 requests in 10.09s, 2.51MB read Socket errors: connect 0, read 870, write 7, timeout 0 Requests/sec: 1562.90 Transfer/sec: 254.89KB $ wrk -t12 -c400 -d10s aiohttp Running 10s test @ aiohttp 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 56.36ms 9.80ms 133.68ms 70.69% Req/Sec 539.28 95.55 0.93k 74.25% 64714 requests in 10.08s, 10.12MB read Socket errors: connect 0, read 257, write 0, timeout 0 Requests/sec: 6423.19 Transfer/sec: 1.00MB
  89. $ wrk -t12 -c400 -d10s flask Running 10s test @

    flask 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 75.59ms 11.75ms 135.98ms 78.48% Req/Sec 139.45 59.91 323.00 64.60% 15775 requests in 10.09s, 2.51MB read Socket errors: connect 0, read 870, write 7, timeout 0 Requests/sec: 1562.90 Transfer/sec: 254.89KB $ wrk -t12 -c400 -d10s aiohttp Running 10s test @ aiohttp 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 56.36ms 9.80ms 133.68ms 70.69% Req/Sec 539.28 95.55 0.93k 74.25% 64714 requests in 10.08s, 10.12MB read Socket errors: connect 0, read 257, write 0, timeout 0 Requests/sec: 6423.19 Transfer/sec: 1.00MB $ wrk -t12 -c400 -d10s flask Running 10s test @ flask 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 75.59ms 11.75ms 135.98ms 78.48% Req/Sec 139.45 59.91 323.00 64.60% 15775 requests in 10.09s, 2.51MB read Socket errors: connect 0, read 870, write 7, timeout 0 Requests/sec: 1562.90 Transfer/sec: 254.89KB $ wrk -t12 -c400 -d10s aiohttp Running 10s test @ aiohttp 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 56.36ms 9.80ms 133.68ms 70.69% Req/Sec 539.28 95.55 0.93k 74.25% 64714 requests in 10.08s, 10.12MB read Socket errors: connect 0, read 257, write 0, timeout 0 Requests/sec: 6423.19 Transfer/sec: 1.00MB 1. Performance
  90. 1. Performance (w/ MongoDB) $ wrk -t12 -c400 -d10s flask/fetch

    Running 10s test @ flask/fetch 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 118.74ms 10.27ms 147.84ms 89.06% Req/Sec 89.69 40.24 200.00 63.36% 10785 requests in 10.09s, 1.72MB read Socket errors: connect 0, read 1039, write 6, timeout 0 Requests/sec: 1068.89 Transfer/sec: 174.32KB $ wrk -t12 -c400 -d10s aiohttp/fetch Running 10s test @ aiohttp/fetch 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 178.17ms 31.92ms 321.67ms 66.33% Req/Sec 169.80 79.86 333.00 57.36% 20344 requests in 10.09s, 3.18MB read Socket errors: connect 0, read 253, write 0, timeout 0 Requests/sec: 2015.68 Transfer/sec: 322.82KB $ wrk -t12 -c400 -d10s flask/fetch Running 10s test @ flask/fetch 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 118.74ms 10.27ms 147.84ms 89.06% Req/Sec 89.69 40.24 200.00 63.36% 10785 requests in 10.09s, 1.72MB read Socket errors: connect 0, read 1039, write 6, timeout 0 Requests/sec: 1068.89 Transfer/sec: 174.32KB $ wrk -t12 -c400 -d10s aiohttp/fetch Running 10s test @ aiohttp/fetch 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 178.17ms 31.92ms 321.67ms 66.33% Req/Sec 169.80 79.86 333.00 57.36% 20344 requests in 10.09s, 3.18MB read Socket errors: connect 0, read 253, write 0, timeout 0 Requests/sec: 2015.68 Transfer/sec: 322.82KB
  91. 1. Performance (w/ MongoDB) $ wrk -t12 -c400 -d10s flask/fetch

    Running 10s test @ flask/fetch 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 118.74ms 10.27ms 147.84ms 89.06% Req/Sec 89.69 40.24 200.00 63.36% 10785 requests in 10.09s, 1.72MB read Socket errors: connect 0, read 1039, write 6, timeout 0 Requests/sec: 1068.89 Transfer/sec: 174.32KB $ wrk -t12 -c400 -d10s aiohttp/fetch Running 10s test @ aiohttp/fetch 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 178.17ms 31.92ms 321.67ms 66.33% Req/Sec 169.80 79.86 333.00 57.36% 20344 requests in 10.09s, 3.18MB read Socket errors: connect 0, read 253, write 0, timeout 0 Requests/sec: 2015.68 Transfer/sec: 322.82KB $ wrk -t12 -c400 -d10s flask/fetch Running 10s test @ flask/fetch 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 118.74ms 10.27ms 147.84ms 89.06% Req/Sec 89.69 40.24 200.00 63.36% 10785 requests in 10.09s, 1.72MB read Socket errors: connect 0, read 1039, write 6, timeout 0 Requests/sec: 1068.89 Transfer/sec: 174.32KB $ wrk -t12 -c400 -d10s aiohttp/fetch Running 10s test @ aiohttp/fetch 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 178.17ms 31.92ms 321.67ms 66.33% Req/Sec 169.80 79.86 333.00 57.36% 20344 requests in 10.09s, 3.18MB read Socket errors: connect 0, read 253, write 0, timeout 0 Requests/sec: 2015.68 Transfer/sec: 322.82KB
  92. 2. Design • No global state or context • Separation

    between handlers and URLs • HTTP exception by return not raise • Well written code
  93. 2. Design • No global state or context • Separation

    between handlers and URLs • HTTP exception by return not raise • Well written code
  94. 2. Design • No global state or context • Separation

    between handlers and URLs • HTTP exception by return not raise • Well written code
  95. 2. Design • No global state or context • Separation

    between handlers and URLs • HTTP exception by return not raise • Well written code
  96. 3. Test asyncio based tests are well supported by libraries.

  97. 3. Test (official) $ pip install pytest-aiohttp

  98. 3. Test (official) from aiohttp import web async def hello(request):

    return web.Response(text='Hello, world') async def test_hello(test_client, loop): app = web.Application() app.router.add_get('/', hello) client = await test_client(app) resp = await client.get('/') assert resp.status == 200 text = await resp.text() assert 'Hello, world' in text
  99. 3. Test (preference) $ pip install asynctest

  100. 3. Test (preference) class TestFetch(asynctest.TestCase): use_default_loop = True forbid_get_event_loop =

    True async def test_fetch(self): outcome = await fetch() self.assertEqual(outcome, 'Hello, Pycon!')
  101. 4. Optimization async def update_entity(request): await data_repository.update_category(entity_id) await cache_repository.clear(entity_id)

  102. 4. Optimization async def update_entity(request): await asyncio.gather( data_repository.update_category(entity_id), cache_repository.clear(entity_id), loop=request.app.loop

    )
  103. 4. Optimization $ pip install uvloop

  104. 4. Optimization import asyncio import uvloop asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

  105. 4. Optimization

  106. 4. Optimization $ wrk -t12 -c400 -d10s aiohttp Running 10s

    test @ aiohttp 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 50.67ms 18.08ms 116.25ms 69.37% Req/Sec 601.92 98.27 0.92k 77.78% 72351 requests in 10.07s, 11.32MB read Socket errors: connect 0, read 250, write 0, timeout 0 Requests/sec: 7184.30 Transfer/sec: 1.12MB
  107. 4. Optimization $ wrk -t12 -c400 -d10s aiohttp Running 10s

    test @ aiohttp 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 50.67ms 18.08ms 116.25ms 69.37% Req/Sec 601.92 98.27 0.92k 77.78% 72351 requests in 10.07s, 11.32MB read Socket errors: connect 0, read 250, write 0, timeout 0 Requests/sec: 7184.30 Transfer/sec: 1.12MB 1.1x faster
  108. 4. Optimization CPU Bound(ex. HTTP Request Parser) >> I/O Bound

  109. 4. Optimization $ pip install cchardet

  110. 4. Optimization $ pip install ujson

  111. 4. Optimization async def hello(request): return json_response( data={'greeting': 'Hello, Pycon!'},

    dumps=ujson.dumps )
  112. 4. Optimization $ pip install aiohttp-ultrajson

  113. 4. Optimization async def hello(request): return jsonify({'greeting': 'Hello, Pycon!'})

  114. 5. Deployment $ pip install gunicorn

  115. 5. Deployment gunicorn app:app --bind localhost:8080 --worker-class aiohttp.GunicornWebWorker

  116. 5. Deployment (w/ UVLoop) gunicorn app:app --bind localhost:8080 --worker-class aiohttp.GunicornUVLoopWebWorker

  117. 5. Deployment (production) import multiprocessing # Server socket bind =

    'unix:app.sock' backlog = 2048 # Worker processes workers = multiprocessing.cpu_count() * 2 + 1 worker_class = 'aiohttp.worker.GunicornUVLoopWebWorker' timeout = 30
  118. 5. Deployment (production) import multiprocessing # Server socket bind =

    'unix:app.sock' backlog = 2048 # Worker processes workers = multiprocessing.cpu_count() * 2 + 1 worker_class = 'aiohttp.worker.GunicornUVLoopWebWorker' timeout = 30 import multiprocessing # Server socket bind = 'unix:app.sock' backlog = 2048 # Worker processes workers = multiprocessing.cpu_count() * 2 + 1 worker_class = 'aiohttp.worker.GunicornUVLoopWebWorker' timeout = 30
  119. 5. Deployment (production) import multiprocessing # Server socket bind =

    'unix:app.sock' backlog = 2048 # Worker processes workers = multiprocessing.cpu_count() * 2 + 1 worker_class = 'aiohttp.worker.GunicornUVLoopWebWorker' timeout = 30 import multiprocessing # Server socket bind = 'unix:app.sock' backlog = 2048 # Worker processes workers = multiprocessing.cpu_count() * 2 + 1 worker_class = 'aiohttp.worker.GunicornUVLoopWebWorker' timeout = 30
  120. 5. Deployment (production) import multiprocessing # Server socket bind =

    'unix:app.sock' backlog = 2048 # Worker processes workers = multiprocessing.cpu_count() * 2 + 1 worker_class = 'aiohttp.worker.GunicornUVLoopWebWorker' timeout = 30 import multiprocessing # Server socket bind = 'unix:app.sock' backlog = 2048 # Worker processes workers = multiprocessing.cpu_count() * 2 + 1 worker_class = 'aiohttp.worker.GunicornUVLoopWebWorker' timeout = 30
  121. 5. Deployment (production) gunicorn --config file:/app/gunicorn.py app:app

  122. 6. Fun and Difficulties • (AFAIK) No one uses aiohttp

    • (AFAIK) No one knows aiohttp • DIY is required • aiohttp and aiobotocore contributions • small community but well maintained • Asynchronous programming is usually more difficult
  123. • (AFAIK) No one uses aiohttp • (AFAIK) No one

    knows aiohttp • DIY is required • aiohttp and aiobotocore contributions • small community but well maintained • Asynchronous programming is usually more difficult 6. Fun and Difficulties
  124. • (AFAIK) No one uses aiohttp • (AFAIK) No one

    knows aiohttp • DIY is required • aiohttp and aiobotocore contributions • small community but well maintained • Asynchronous programming is usually more difficult 6. Fun and Difficulties
  125. • (AFAIK) No one uses aiohttp • (AFAIK) No one

    knows aiohttp • DIY is required • aiohttp and aiobotocore contributions • small community but well maintained • Asynchronous programming is usually more difficult 6. Fun and Difficulties
  126. • (AFAIK) No one uses aiohttp • (AFAIK) No one

    knows aiohttp • DIY is required • aiohttp and aiobotocore contributions • small community but well maintained • Asynchronous programming is usually more difficult 6. Fun and Difficulties
  127. • (AFAIK) No one uses aiohttp • (AFAIK) No one

    knows aiohttp • DIY is required • aiohttp and aiobotocore contributions • small community but well maintained • Asynchronous programming is usually more difficult 6. Fun and Difficulties
  128. to use aiohttp, or not to use aiohttp?

  129. aiohttp requires asyncio supported libraries

  130. • motor (MongoDB) • aiomysql (MySQL) • asyncpg (PostgreSQL) •

    aioredis (Redis) • aiomcache (Memcached) • aiokafka (Apache Kafka) • aiozmq (ZeroMQ)
  131. Is your team ready for asynchronous programming?

  132. In my case, Rainist was • Using RxJava, RxSwift, and

    RxJs highly on client side • Implementing CPU bound services with Scala, Akka HTTP, and Monix
  133. from __future__ import asyncio

  134. Rainist: We are hiring! https://rainist.com https://medium.com/rainist-engineering

  135. References 1. Luciano Ramalho, <Fluent Python>, 2015 2. PEP 380

    Syntax for Delegating to a Subgenerator 3. PEP 3156 Asynchronous IO Support Rebooted: the "asyncio" Module 4. https://magic.io/blog/uvloop-blazing-fast-python-networking/ 5. http://aiohttp.readthedocs.io/en/stable/ 6. https://nikhilm.github.io/uvbook/basics.html