Taking Channels Async

Taking Channels Async

A talk I gave at DjangoCon Europe 2018.

077e9a0cb34fa3eba2699240c9509717?s=128

Andrew Godwin

May 24, 2018
Tweet

Transcript

  1. TAKING Andrew Godwin @andrewgodwin CHANNELS ASYNC

  2. Hi, I’m Andrew Godwin • Django core team member •

    Senior Software Engineer at • Does network programming for "fun"
  3. 2015: "django-onair"

  4. 2015: Channels 0.1

  5. 2017: Channels 1.0

  6. Python 2.7 compatible Twisted for webserver Synchronous Django code

  7. Webserver (Twisted) Django (Sync worker process) Clients Channel Layer (Redis

    or other) Webserver (Twisted) Django (Sync worker process)
  8. Too many moving pieces No asyncio support Easy to shoot

    yourself in the foot
  9. I was wrong.

  10. 2018: Channels 2.0

  11. Asyncio-native Python 3.5 and up Supports async coroutines and sync

    threads
  12. Webserver (Twisted) Django (Sync worker process) Clients Webserver (Twisted) Django

    (Sync worker process) Channel Layer (Redis or other)
  13. 75% rewrite

  14. Django had to be partially async

  15. WSGI/ASGI Handler (request translator) URL Routing (path to view mapping)

    Django Middleware (auth, sessions, etc.) Views (logic and presentation) ORM (database kerfuffling)
  16. WSGI/ASGI Handler (request translator) URL Routing (path to view mapping)

    Django Middleware (auth, sessions, etc.) Views (logic and presentation) ORM (database kerfuffling) ASGI Routing (path/protocol/etc. mapping) ASGI Middleware (auth, sessions, etc.) Consumers (logic and presentation) synchronous
  17. Async-native most of the way

  18. But how?

  19. sync_to_async async_to_sync

  20. sync_to_async async_to_sync

  21. Synchronous code has to run in threads ThreadPoolExecutor does most

    of the hard work
  22. loop = asyncio.get_event_loop() future = loop.run_in_executor( self.threadpool, func, ) return

    await future
  23. Calling the ORM Rendering templates Handing off to Django views

  24. sync_to_async async_to_sync

  25. Async code runs on the event loop We need to

    go find it! Or make our own.
  26. # Make a future for the return information call_result =

    Future() # Use call_soon_threadsafe to schedule a synchronous callback on the # main event loop's thread if not (self.main_event_loop and self.main_event_loop.is_running()): # Make our own event loop and run inside that. loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) try: loop.run_until_complete(self.main_wrap(args, kwargs, call_result)) finally: try: if hasattr(loop, "shutdown_asyncgens"): loop.run_until_complete(loop.shutdown_asyncgens()) finally: loop.close() asyncio.set_event_loop(self.main_event_loop) else: self.main_event_loop.call_soon_threadsafe( self.main_event_loop.create_task, self.main_wrap( args, kwargs, call_result, ), ) # Wait for results from the future. return call_result.result()
  27. Make a Future Jump to the main thread and add

    the coroutine Tie the coroutine's end to triggering the Future Block the thread on the Future
  28. Lets us provide async-native APIs And still let synchronous code

    use them
  29. Both async and sync code are useful Channels lets you

    write your code as both
  30. Async interfaces are separate to sync You can't provide both

    through one API
  31. bit.ly/py-async-simp https://www.aeracode.org/2018/02/19/python-async-simplified/

  32. ASGI Handler (ASGI-to-request translator) URL Routing (path to view mapping)

    Django Middleware (auth, sessions, etc.) Views (logic and presentation) ORM (database kerfuffling) ASGI Routing (path/protocol/etc. mapping) ASGI Middleware (auth, sessions, etc.) Consumers (logic and presentation) synchronous
  33. How to make WSGI async?

  34. ASGI: It's like WSGI but with an A in it

    Also asyncio and stuff like that
  35. class Application: def __init__(self, scope): ... async def __call__(self, receive,

    send): event = await receive() ... await send(data)
  36. "Turtles all the way down" Routing is an ASGI app.

    Middleware is too.
  37. What does this mean for Django?

  38. How much can we make async? Can we keep sync

    compatability?
  39. What does an async ORM look like? Is it even

    a sensible endeavour?
  40. Do we need to replace WSGI? Or does nobody want

    long-polling or websockets?
  41. Multiple servers (daphne, uvicorn) Multiple frameworks (Django, …?)

  42. Do we want to have everyone writing async? What's the

    balance? How do we be flexible enough?
  43. What is Django?

  44. Thanks. Andrew Godwin @andrewgodwin aeracode.org