Granian – a Rust HTTP server for Python applications
github.com
github.com
Thanks to WSGI you can have projects like this that focus on scaling out your Python web all to the fullest extent possible.
It addresses one of the major shortcomings of CGI by not requiring to launch a new process for every request. 20 years later it is still going strong and is still by far the main way Python web apps are served.
Theoretically one could produce ready-to-run, guaranteed clean Python request handlers very quickly. Run all the startup and imports stuff in a Python process, then freeze it. Fork a copy of it on a new request (with COW it's definitely fast), and terminate it normally after that. This would require some care, e.g. to not open sockets before the fork, so that handlers won't share it.
I bet it has been tried, but I'm too lazy to actually search for that.
I believe wasmtime can be faster as it doesn’t need to create as many new OS resources and just resets some memory maps.
Only really meaningful for simple request handlers taking a few milliseconds.
One of the initial inspirations for creating the Django web framework back a the Lawrence Journal-World newspaper in ~2003 was that we wanted to start using Python on the web, but were concerned that mod_python didn't have quite enough existing users.
What if it turned out not to work for us?
So we designed a simple abstraction layer - a request and response object - as an insurance policy. If mod_python turned out not to work for us we could switch to something else without having to rewrite all of our code.
At around about the same time the Python Web SIG formed - https://simonwillison.net/2003/Oct/18/pythonWebSIG/ - and we joined it with the aim of helping define a standard similar to what we had been building for our own, private newspaper CMS.
WSGI was the eventual result of that SIG - I didn't participate in the working group nearly as actively as I had planned to, so I can't say that I had much if any impact on the result.
But hopefully this illustrates that yes, the people involved were very aware of mod_python back in 2003 when this work kicked off.
PS That is why I love HN. You get to casually see creators of great projects.
As for FastCGI, although it is language agnostic it didn't really get traction outside of PHP.
The point of WSGI is for the server and application to speak the same python-level protocol. You can have a wsgi adapter on top of fcgi, though it’s really a waste of resources if you can skip fcgi entirely (which you usually can).
Pro tip: WSGI is pronounced like "whiskey". :-)
Granian sounds interesting. I'm not sure how you deal with the "two async runtimes" problem tho. Essentially, if you do non-blocking IO, then your processes start to become CPU-bound. So granian can eat up CPU on high load, and leave little CPU for the asgi application (or vice-versa). Somehow that doesn't sound right, but I also can't think of how you'd solve that.
While it might sound "odd", actually having two runtimes running in two different threads is not so strange. This is actually what you end up when spawning multiple Python processes even with uvicorn: each process will have the asyncio event-loop running in the main thread.
In Granian the Python runtime and the Rust ones communicate with each other in a non-blocking way, so – theoretically speaking – there's no additional CPU-bound load compared to a pure Python solution.
Also, theoretically speaking, the only condition I can imagine where the two runtimes fight too much with each other is in case where you have a single core system with so poor performance to not handle 2 threads concurrently, which is probably so rare that we don't need to think about it..
Probably I should add some resource usage tests aside to benchmarks, but – again, theoretically speaking – if the Rust code is more efficient than the Python one, the final CPU usage should be lower, or more efficient.
My main confusion around this is: how do I scale threads? Say I have to CPU cores. Do you run one Granian thread and one python thread? I Granian under-uses CPU, Python can't use the excess. But if I run one Granian thread and two python threads, I risk Python leaving Granian with less CPU than it needs.
In practice it should not be hard to find a balance... but somehow I'm still bothered by the fact that the design has no obvious solution for this.
Unpacking the acronyms might help
ASGI may be new to some, but... how is it possible to do Python web development without first learning what WSGI is? I haven't done much Python dev but WSGI seems to be the standard way - what's the less optimal alternative that people might end up with?
You'll be surprised to find out that there are many software developers who are "fast learners", but actually are shallow learners. Or to put it more kindly, they are not-so-curious learners, i.e. they only learn just enough things they need to accomplish the task at the moment. It's not necessarily a bad trait; but sometimes if you don't learn the fundamentals, it will cost you more (time, effort) along the way.
Basic tutorials for Python newbies use wsgi. Copying & pasting from SO without understanding the code you're copying will inadvertently net you a wsgi setup. FastAPI requires wsgi.
I wasn't stating that everyone deploying Python has a depth of knowledge of wsgi - I was enquiring as to whether there's some other alternative system people pervasively use for Python web apps, as I assumed everyone was on wsgi (whether they're aware of it or not).
TL;DR: by "learning what wsgi is" I just meant "deploying it" not "understanding it in depth"
A vast majority of people who call themselves "python devs" got there by copy-pasting ML scripts from stackoverflow (and now chatGPT) into jupyter notebooks.
https://github.com/emmett-framework/granian/tree/master/benc... shows way better latency for Granian.
Kudos.
just, when would you consider this good enough for production?
We have a couple of services in production at the company I work with, served with granian, they handles ~5k RPM traffic each (not so much) with no particular issues.
You can consider the ASGI support quite robust for the current version, WSGI was introduced in 0.2.0, so it might still suffer from bugs. RSGI is quite new as a protocol, so next versions might introduce breaking changes (but the RSGI specification has a version, so you can stick with a specific revision), at least until version 1.0 will be ready.
That is certainly the mental model I have for something in maintenance mode.
A framework for Python, the core of which is written in Rust
As you can see from benchmakrs (https://github.com/emmett-framework/granian/tree/master/benc...), the main advantage today of RSGI vs ASGI is the performance.
made a Django example last week with a sample client based on the examples from aioquic[2]: https://github.com/djstein/django-http3-example
this example also includes the first pass at async Django REST Framework using adrf[3] based on these GitHub issues:
- https://github.com/encode/django-rest-framework/pull/8617
- https://github.com/encode/django-rest-framework/issues/8496
sources
[1]: https://github.com/pgjones/hypercorn
> Have a single, correct HTTP implementation, supporting versions 1, 2 (and eventually 3)
Because if you’re going to do it in Rust, why not just go all the way with something like Axum?
That being said, first time I hear about WSGI/ASGI, so I did some reading. From an educational perspective I’m intrigued and even will check the codebases at some point to see how it works.
But from a production POV I do not get why I would want to use it. What’s so great about it - besides letting you develop a reasonably fast server in Python, surely it is more then that?
Sounds to me like you've figured out the reason for this project just fine!
The Python included http Server is super useful but for completely different use cases.