I've been doing some initial work on a Gunicorn worker process that
interfaces with an ASGI consumer callable, rather than a WSGI callable.
https://github.com/tomchristie/asgiworker
In the standard channels setup, the application is run behind a message
bus...
Protocol Server -> Channels <- Worker Process -> ASGI Consumer
In the Gunicorn worker implementation above, we're instead calling the
consumer interface directly...
Protocol Server -> ASGI Consumer
There's a few things that're promising here...
1. The ASGI consumer interface is suitable for async application
frameworks, where WSGI necessarily can't be.
In WSGI the response gets returned when the callable returns, you can't
queue an asynchronous task to perform some work later.
With an ASGI consumer, the messaging interface style means that you can
push tasks onto the event loop and return immediately.
In short, you can use async...await under ASGI.
2. The uvloop and httptools implementations are seriously speedy.
For comparative purposes, plaintext hello world servers against a few
different implementations on my MacBook Air
wrk -d20s -t10 -c200 http://127.0.0.1:8080/
Throughput Latency (stddev)
Go 44,000 req/s 6ms 92%
uvloop+httptools, ASGI 33,000 req/s 6ms 67%
meinheld, WSGI 16,000 req/s 12ms 91%
Node 9,000 req/s 22ms 91%
As application developers those baselines aren't typically a priority, but
if we want Python web frameworks to be able to nail the same kind of
services that node and go currently excel in, then having both async
support, and a low framework overhead *is* important.
It's not immediately clear to me if any of this is interesting to Django
land directly or not. The synchronous nature of the framework means that
having the separation of async application servers, and synchronous workers
behind a channel layer makes a lot of sense. Tho you could perfectly well
run a regular HTTP Django application on top of this implementation
(replacing wsgi.py with an asgi.py that uses ASGIHandler) and be no worse
off for it. (Sure you're running blocking operations while running in the
context of an event loop, but that's no worse than running blocking
operations in a standard WSGI configuration)
However it is valuable if you want to be able to write HTTP frameworks that
support async...await, or if you want to support websockets and don't
require the kinds of broadcast functionality that adding a channel layer
provides for.
---
At the moment I'm working against the ASGI consumer interface as it's
currently specified. There's a few things that I'm interested in next:
1. If there'd be any sense in mandating that the ASGI callable *may* be a
coroutine. (requiring an asyncio worker or server implementation)
2. If there'd be any sense in including `.loop` as either a mandatory or as
an optional attribute on a channel layer that supports the syncio extension.
3. Andrew's mentioned that he's been considering an alternative that maps
more simply onto WSGI, I'd really like to see what he's thinking there.
4. Response streaming isn't a problem - you can send multiple message back
to the channel, and run that off the event loop. However, I've not quite
got my head around how you handle streaming request bodies, or around how
you'd invert the interface so that from the application perspective there's
something like an interface available for `chunk = await body.read()`.
5. One other avenue of interest here might be if it's worth considering
bringing ASGIHandler out of channels and into Django core, so that we can
expose either an asgi consumer callable or a wsgi callable interface to
Django, with `runworker` being only one of a number of possible ASGI
deployments.
Plenty to unpack here, feedback on any aspects most welcome!
Cheers,
T :)
--
You received this message because you are subscribed to the Google Groups
"Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-developers/db158350-60a9-4950-b11c-83f2f7a9221c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.