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

Friendly and Modern AsyncIO

Friendly and Modern AsyncIO

AsyncIO is a powerful mechanism for building high-performance services, but it has long been thought of as “too complicated”. But it’s easier than you might think, and with Python 3.7, it’s easier than ever to get started using just the standard library! We'll cover the basics of AsyncIO, take a look at the latest features in 3.7, build friendly and expressive APIs, analyze best practices for third-party libraries, and discuss integrating async code with synchronous libraries and codebases.

9c98621c87f4f1333e0dcbb5282fa65b?s=128

John Reese

May 02, 2019
Tweet

Transcript

  1. None
  2. Friendly and Modern AsyncIO John Reese Production Engineer, Python Foundation

    @n7cmdr
 github.com / jreese
  3. 1. What is AsyncIO 2. Why is it useful 3.

    High level concepts 4. Standard library basics 5. Building friendly, asynchronous APIs Topics
  4. github.com/jreese/pycon

  5. github.com/jreese/pycon $ git clone https://github.com/jreese/pycon $ cd pycon/friendly-asyncio $ python3.7

    -m venv venv $ source venv/bin/activate $ pip install -Ur requirements.txt
  6. What is AsyncIO

  7. • Cooperative multitasking • Built around futures/promises • Single threaded

    • Optimized for concurrent I/O What is AsyncIO
  8. • Cooperative multitasking • Built around futures/promises • Single threaded

    • Optimized for concurrent I/O What is AsyncIO
  9. • Parallel execution • Multithreaded • Performance/speed improvement • Optimized

    for computation What AsyncIO is not
  10. • Parallel execution • Multithreaded • Performance/speed improvement • Optimized

    for computation What AsyncIO is not
  11. • Context switching • Thread safety • Control flow •

    “How many?” Why not threads?
  12. • Context switching • Thread safety • Control flow •

    “How many?” Why not threads?
  13. $ python3.7 1-threads.py 1299 runs of fib in 1.0s: 769.83

    usec per run
  14. None
  15. 156 runs of fibs in 1.0s: 6.42 msec per run

    151 runs of fibp in 1.0s: 6.65 msec per run
  16. 156 runs of fibs in 1.0s: 6.44 msec per run

    387 runs of fibp in 1.0s: 2.59 msec per run
  17. But what about I/O?

  18. • Simple TCP server • Accepts an upper bound k

    • “Thinks about it” • Returns a random integer [0,k] Randint-as-a-service $ python3.7 server.py listening on 8080...
  19. $ echo 20 | nc ::1 8080 6 $ echo

    20 | nc ::1 8080 17 $ echo 20 | nc ::1 8080 11
  20. $ python3.7 2-thread-client.py got 19

  21. 10 runs of get_random in 1.0s: 0.104 sec per run

  22. 1 runs of many_random in 3.4s: 3.419 sec per run

  23. 2 runs of many_random in 1.1s: 0.558 sec per run

    1 runs of many_random in 1.4s: 1.351 sec per run 1 runs of many_random in 1.2s: 1.241 sec per run 0 0.001 0.002 0.003 0.004 32 64 128 256 512 1024
  24. $ python3.7 3-asyncio-client.py 10 runs of get_random in 1.0s: 0.105

    sec per run
  25. 7 runs of async_random in 1.0s: 0.147 sec per run

  26. 6 runs of async_random in 1.1s: 0.191 sec per run

    4 runs of async_random in 1.1s: 0.280 sec per run 3 runs of async_random in 1.4s: 0.467 sec per run 0 0.001 0.002 0.003 0.004 32 64 128 256 512 1024 128 256 512 1024 AsyncIO Threads
  27. How does AsyncIO work?

  28. • Object that is not ready, or done • “done”

    means success or exception • Add callbacks, or periodically check state • Result yields final value, or raise exception Back to the futures
  29. • Object that is not ready, or done • “done”

    means success or exception • Add callbacks, or periodically check state • Result yields final value, or raise exception Back to the futures
  30. • Like futures, without polling or callbacks • Object with

    __await__ method • Must be “awaited” via await keyword • Yields value when completed Awaitables
  31. • Asynchronous function • Dedicated async def syntax • Returns

    a coroutine object when called • Only executes when awaited Coroutines
  32. • Event loop controls execution of pending tasks • Tasks

    wrap and await futures automatically • Tasks are awaitable, yield their wrapped futures Event loops and tasks
  33. • Executes current task until done or blocked • Next

    task picked from queue • Tasks can “starve” other tasks Event loops and tasks
  34. $ python3.7 4-asyncio-basics.py got coroutine: <coroutine object foo at 0x1017b64c8>

    running foo got 1 from coroutine
  35. $ python3.7 4-asyncio-basics.py got coroutine: <coroutine object foo at 0x1017b64c8>

    running foo got 1 from coroutine
  36. coro: <coroutine object sleep at 0x1049e84c8> task: <Task pending coro=<sleep()

    running at ...> task: <Task finished coro=<sleep() done, …> result=None>
  37. coro: <coroutine object sleep at 0x1049e84c8> task: <Task pending coro=<sleep()

    running at ...> task: <Task finished coro=<sleep() done, …> result=None>
  38. coro: <coroutine object sleep at 0x1049e84c8> task: <Task pending coro=<sleep()

    running at ...> task: <Task finished coro=<sleep() done, …> result=None>
  39. gather two: [0, 11] gather ten: [1, 8, 13, 4,

    19, 2, 5, 4, 20, 20]
  40. gather two: [0, 11] gather ten: [1, 8, 13, 4,

    19, 2, 5, 4, 20, 20]
  41. $ python3.7 5-starvation.py getting food full now, took 0.1s

  42. getting food nap time nap over full now, took 2.0s

  43. getting food nap time nap over full now, took 2.0s

  44. getting food nap time full now, took 0.1s nap over

  45. None
  46. None
  47. 0: counting to 100000 0: ... 99999 ... 100000 1:

    counting to 100000 1: ... 99999 ... 100000 2: counting to 100000 2: ... 99999 ... 100000 3: counting to 100000 3: ... 99999 ... 100000
  48. None
  49. None
  50. 0: counting to 100000 1: counting to 100000 2: counting

    to 100000 3: counting to 100000 0: ... 99999 ... 100000 1: ... 99999 ... 100000 2: ... 99999 ... 100000 3: ... 99999 ... 100000
  51. Making AsyncIO useful

  52. None
  53. None
  54. None
  55. None
  56. None
  57. None
  58. None
  59. $ curl http://0.0.0.0:8080/randint/25 15 $ curl http://0.0.0.0:8080/randint/2500 2318 $ python

    6-aiohttp-server.py ======== Running on http://0.0.0.0:8080 ======== (Press CTRL+C to quit)
  60. $ python3.7 7-aiohttp-client.py got (200, 14)

  61. $ python3.7 7-aiohttp-client.py got (200, 14)

  62. None
  63. None
  64. None
  65. None
  66. None
  67. $ python3.7 8-aiosqlite.py sender> Bob recipient> Alice message> I need

    your help. inserted row id 1
  68. row #1 from Bob to Alice: I need your help.

    row #2 from Janet to Nina: What time is the developer sync? row #3 from Nina to Janet: I think it's after the design review.
  69. Designing Friendly APIs

  70. • Focus on common use cases • Make interfaces feel

    natural • Use the most obvious syntax features • Support multiple styles if possible Obvious and natural
  71. • Common, asynchronous actions • Initializing async objects • Preparing

    or fetching resources Awaitables
  72. • Implement the __await__ special method Awaitables

  73. None
  74. $ python3.7 9-awaitables.py 7

  75. $ python3.7 9-awaitables.py 23 5

  76. • For use in async for loops • Each iteration

    is an async coroutine • Useful for iteration before all data is ready • Iterate large, async datasets without blocking Async Iterables
  77. • Similar to normal iterables • Use __aiter__ to create

    an iterator • Use __anext__ to return the next value • Raise StopAsyncIteration when exhausted Async Iterables
  78. None
  79. None
  80. None
  81. 19 15 1 10 20

  82. [34, 9, 28]

  83. 0 20 19

  84. • Ephemeral actions, connections, transactions • Async coroutines at enter

    and exit • Ensure clean up on exit or exception Async Context Managers
  85. • Same pattern as regular context managers • __aenter__ called

    when entering • __aexit__ called at exit or exception Async Context Managers
  86. None
  87. $ python3.7 10-contexts.py row #4 from Jack to Jill: I'm

    out of water. row #5 from Jack to Terry: Do you know where my pail is?
  88. None
  89. Mix Paradigms

  90. Phew...

  91. Read the [3.7] docs

  92. Experimentation is key

  93. Consider your workload

  94. Don't be afraid to benchmark

  95. Don't cross the streams

  96. Don't cross the streams

  97. Don't cross the streams

  98. ai o uvloop ai ess aioredis aiobot am aiofiles aiosqlite

    aiopg s aiounittest aioslack trio uvloop omysql aiohttp aiomultiprocess aioredis lack trio uvloop aioitertools aiostream aiofiles a ltiprocess aioredis aiobotocore aiodns aiounittest aiosl aiostream aiofiles aiosqlite aiopg aiomysql aiohttp aiomultipro e aiodns aiounittest aioslack trio uvloop aioitertools aiostream aiof iopg aiomysql aiohttp aiomultiprocess aioredis aiobotocore aiodns ack trio uvloop aioitertools aiostream aiofiles aiosqlite aiopg s aioredis aiobotocore aiodns aiounittest aioslack tr s aiosqlite aiopg aiomysql aiohttp aiomultipr slack trio uvloop aioitertools aiostrea iprocess aioredis aiobotocore stream aiofiles aiosqli aiounittest aio http aio a Don't reinvent the wheel
  99. ai o uvloop ai ess aioredis aiobot am aiofiles aiosqlite

    aiopg s aiounittest aioslack trio uvloop omysql aiohttp aiomultiprocess aioredis lack trio uvloop aioitertools aiostream aiofiles a ltiprocess aioredis aiobotocore aiodns aiounittest aiosl aiostream aiofiles aiosqlite aiopg aiomysql aiohttp aiomultipro e aiodns aiounittest aioslack trio uvloop aioitertools aiostream aiof iopg aiomysql aiohttp aiomultiprocess aioredis aiobotocore aiodns ack trio uvloop aioitertools aiostream aiofiles aiosqlite aiopg s aioredis aiobotocore aiodns aiounittest aioslack tr s aiosqlite aiopg aiomysql aiohttp aiomultipr slack trio uvloop aioitertools aiostrea iprocess aioredis aiobotocore stream aiofiles aiosqli aiounittest aio http aio a
  100. ai o uvloop ai ess aioredis aiobot am aiofiles aiosqlite

    aiopg s aiounittest aioslack trio uvloop omysql aiohttp aiomultiprocess aioredis lack trio uvloop aioitertools aiostream aiofiles a ltiprocess aioredis aiobotocore aiodns aiounittest aiosl aiostream aiofiles aiosqlite aiopg aiomysql aiohttp aiomultipro e aiodns aiounittest aioslack trio uvloop aioitertools aiostream aiof iopg aiomysql aiohttp aiomultiprocess aioredis aiobotocore aiodns ack trio uvloop aioitertools aiostream aiofiles aiosqlite aiopg s aioredis aiobotocore aiodns aiounittest aioslack tr s aiosqlite aiopg aiomysql aiohttp aiomultipr slack trio uvloop aioitertools aiostrea iprocess aioredis aiobotocore stream aiofiles aiosqli aiounittest aio http aio a
  101. ai o uvloop ai ess aioredis aiobot am aiofiles aiosqlite

    aiopg s aiounittest aioslack trio uvloop omysql aiohttp aiomultiprocess aioredis lack trio uvloop aioitertools aiostream aiofiles a ltiprocess aioredis aiobotocore aiodns aiounittest aiosl aiostream aiofiles aiosqlite aiopg aiomysql aiohttp aiomultipro e aiodns aiounittest aioslack trio uvloop aioitertools aiostream aiof iopg aiomysql aiohttp aiomultiprocess aioredis aiobotocore aiodns ack trio uvloop aioitertools aiostream aiofiles aiosqlite aiopg s aioredis aiobotocore aiodns aiounittest aioslack tr s aiosqlite aiopg aiomysql aiohttp aiomultipr slack trio uvloop aioitertools aiostrea iprocess aioredis aiobotocore stream aiofiles aiosqli aiounittest aio http aio a
  102. github.com/jreese/pycon

  103. github.com/jreese/pycon John Reese Production Engineer, Python Foundation @n7cmdr
 github.com /

    jreese