Share
Embed
[PyCon KR 2017] aiohttp in Production
PyCon Korea 2017 [aiohttp in Production](https://www.pycon.kr/2017/program/133)
Sunghyun Hwang
August 13, 2017
Transcript
135 slides
-
Disclaimer • Requires Python 3.3+ • Assumes general awareness of
• Generators/coroutines • AsyncIO concepts • Async/await syntax • Common programming patterns -
PEP 3156 — Asynchronous IO Support Rebooted: the "asyncio" Module
Guido van Rossum (2012-12-12) -
This module provides infrastructure for writing single-threaded concurrent code using
coroutines, multiplexing I/O access over sockets... -
This module provides infrastructure for writing single-threaded concurrent code using
coroutines, multiplexing I/O access over sockets... -
This module provides infrastructure for writing single-threaded concurrent code using
coroutines, multiplexing I/O access over sockets... -
This module provides infrastructure for writing single-threaded concurrent code using
coroutines, multiplexing I/O access over sockets... -
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) -
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 -
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) 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) -
aiohttp/web.py try: print("======== Running on {} ========\n" "(Press CTRL+C to
quit)".format(', '.join(uris))) loop.run_forever() except (GracefulExit, KeyboardInterrupt): pass -
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 -
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() -
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() -
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() -
Event Loop In computer science, the event loop is a
programming construct that waits for and dispatches events or messages in a program. — Wikipedia -
Event Loop In computer science, the event loop is a
programming construct that waits for and dispatches events or messages in a program. — Wikipedia -
Event Loop In computer science, the event loop is a
programming construct that waits for and dispatches events or messages in a program. — Wikipedia -
Event Loop In computer science, the event loop is a
programming construct that waits for and dispatches events or messages in a program. — Wikipedia -
Event Loop In computer science, the event loop is a
programming construct that waits for and dispatches events or messages in a program. — Wikipedia -
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) -
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 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 -
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 -
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 -
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 -
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 -
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 -
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 -
asyncio/base_events.py def is_running(self): """Returns True if the event loop is
running.""" return (self._thread_id is not None) -
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) ... -
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) ... -
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) ... -
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() -
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) -
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) -
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. """ -
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. """ -
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. """ -
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. """ -
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. """ -
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() -
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() -
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() -
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() -
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 -
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) ) -
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) ) -
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, ...) ) -
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, ...) ) -
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) -
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) -
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) -
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) ) -
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) -
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) -
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) -
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) -
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 -
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 -
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 -
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 -
aiohttp/web_protocol.py class RequestHandler(..., asyncio.Protocol): def data_received(self, data): ... messages, upgraded,
tail = self._request_parser.feed_data(data) -
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) 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) 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) 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) 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) -
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) -
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) -
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 -
$ 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 -
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 -
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 -
2. Design • No global state or context • Separation
between handlers and URLs • HTTP exception by return not raise • Well written code -
2. Design • No global state or context • Separation
between handlers and URLs • HTTP exception by return not raise • Well written code -
2. Design • No global state or context • Separation
between handlers and URLs • HTTP exception by return not raise • Well written code -
2. Design • No global state or context • Separation
between handlers and URLs • HTTP exception by return not raise • Well written code -
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 -
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!') -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
• (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 -
• (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 -
• (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 -
• (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 -
• (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 -
• motor (MongoDB) • aiomysql (MySQL) • asyncpg (PostgreSQL) •
aioredis (Redis) • aiomcache (Memcached) • aiokafka (Apache Kafka) • aiozmq (ZeroMQ) -
In my case, Rainist was • Using RxJava, RxSwift, and
RxJs highly on client side • Implementing CPU bound services with Scala, Akka HTTP, and Monix -
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