How to Troubleshoot Apps for the Modern Connected Worker
Syncing up with Python’s asyncio for (micro) service development, Joir-dan Gumbs
1. Syncing up with asyncio for (micro)service development
Joir-dan Gumbs - Senior Software Engineer
IBM San Francisco
twitter: @jagumbs
blog: tasteandtrace.co
7. async keyword is an explicit indicator of asynchronous construct
async def - Indicates coroutine (asynchronous function)
async with - Indicates asynchronous context manager
async for - Indicates asynchronous iteration
async + await + event loop = asyncio
8. await keyword is an explicit asynchronous “checkpoint”
Signal to swap to different coroutine
Will resume from checkpoint when task completed and has regained context
async + await + event loop = asyncio
9. event loop is the main execution device for asyncio
handles registration, execution, and cancelling of coroutines
expensive synchronous calls will block the event loop until completion
can delegate expensive (IO/CPU) calls to various Executors
async + await + event loop = asyncio
10. hello asyncio 1 - simple coroutine
import asyncio
import random
async def work_it(i, random_sleep):
await asyncio.sleep(random_sleep)
print("Task {0} completed after random sleep of {1} seconds".format(i, random_sleep))
if __name__ == "__main__":
print("Run basic async function.")
loop = asyncio.get_event_loop()
loop.run_until_complete(work_it(1, random.randrange(1, 5)))
print("Finished!")
11. hello sanic 1 - hello_sanic.py
import asyncio
import uvloop
from sanic import Sanic
from sanic.response import text
app = Sanic(“hello_sanic”)
@app.route(“/async”)
async def async_hello(request):
return text(“Asynchronous Hello Sanic!”)
@app.route(“/sync”)
def sync_hello(request):
return text(“Synchronous Hello Sanic!”)
if __name__ == "__main__":
# Use uvloop cause its fast!
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
app.run(host="0.0.0.0", port=9090)
12. coroutines return a Future object
using await unwraps Future for value(s), if available
we can await multiple Future objects using asyncio.gather
callbacks are optional with asyncio coroutines
created using <Future>.add_done_callback, if you want them
async + await + event loop = asyncio
13. hello asyncio 2 - coroutine collection
import asyncio
import random
async def work_it(i, random_sleep):
await asyncio.sleep(random_sleep)
print("Task {0} completed after random sleep of {1} seconds".format(i, random_sleep))
if __name__ == "__main__":
print("Run basic async function with grouping.")
loop = asyncio.get_event_loop()
futures = [work_it(i, random.randrange(1, 5)) for i in range(0, 10)]
group_future = asyncio.gather(*futures)
loop.run_until_complete(group_future)
print("Finished!")
14. hello sanic 2 - outbound.py
import asyncio
import aiohttp
import uvloop
from sanic import Sanic
from sanic.response import json
app = Sanic(“hello_sanic”)
@app.route(“/”)
async def hello(request):
site = “http://www.ibm.com"
response = await aiohttp.request(“GET”, site)
return json({“website”: site, “size”: len(await response.text())})
if __name__ == "__main__":
# Use uvloop cause its fast!
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
app.run(host="0.0.0.0", port=9090)
15. executors help us use synchronous functions asynchronously
<event_loop>.run_in_executor(executor, fn, *args) is our friend!
use functools.partial if you require **kwargs
works with concurrent.futures.[ThreadPoolExecutor, ProcessPoolExecutor]
defaults to ThreadPoolExecutor when None
asynci-oh no! - executors to the rescue
16. asynci-oh no! - a sync in my async
import asyncio
import requests
async def run(loop):
print("Accessing ibm.com”)
# requests is a synchronous http framework. . .
result = requests.get(“http://www.ibm.com”) # I’m a blocker!!
print("Status Code", result.status_code)
print("Body Length", len(result.text))
if __name__ == "__main__":
print("Run basic async function with executor.")
loop = asyncio.get_event_loop()
loop.run_until_complete(run(loop))
print("Finished!")
17. hello asyncio 3 - async a sync
import asyncio
import requests
async def run(loop):
print("Accessing ibm.com”)
# There are better ways of doing this though . . .
result = await loop.run_in_executor(None, requests.get, "http://www.ibm.com")
print("Status Code", result.status_code)
print("Body Length", len(result.text))
if __name__ == "__main__":
print("Run basic async function with executor.")
loop = asyncio.get_event_loop()
loop.run_until_complete(run(loop))
print("Finished!")
18. hello asyncio 3 - async a sync
import asyncio
import requests
async def run(loop):
print("Accessing ibm.com”)
# There are better ways of doing this though . . .
result = await loop.run_in_executor(None, requests.get, "http://www.ibm.com")
print("Status Code", result.status_code)
print("Body Length", len(result.text))
if __name__ == "__main__":
print("Run basic async function with executor.")
loop = asyncio.get_event_loop()
loop.run_until_complete(run(loop))
print("Finished!")
19. hello asyncio 3 - async a sync (aiohttp edition)
import aiohttp
import asyncio
async def run():
print("Accessing ibm.com")
result = await aiohttp.request("GET", "http://www.ibm.com")
print("Status Code", result.status)
print("Body Length", len(await result.text()))
if __name__ == "__main__":
print("Run basic async function with aiohttp.")
loop = asyncio.get_event_loop()
loop.run_until_complete(run())
print("Finished!")
20. hello asyncio 6 - amethyst
Go to code
https://github.com/SakuraSound/amethyst
21. Similar to standard python context manager
__aenter__ and __aexit__ vs. __enter__ and __exit__
can wait on I/O while creating/destroying “session”
async with - establishing “sessions”