Granian – a Rust HTTP server for Python applications
github.comMy first thought was "this better be WSGI!!!" and sure enough it is. If you're doing Python web development and you have no idea what WSGI or ASGI are, you really ought to learn more about them. ASGI is the Async equivalent / answer to WSGI. It is one of the main ways to deploy any web application in Python. Every major web framework supports it OOTB and heck, even one of the fastest web frameworks for Python (FastAPI) uses it exclusively.
Thanks to WSGI you can have projects like this that focus on scaling out your Python web all to the fullest extent possible.
This. There was recently a trendy thread on HN about how great CGI was. WSGI is basically one of the spiritual successors to CGI: it's a simple way for a web server and a Python application to interact.
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.
The thread: "The beauty of CGI and simple design (rubenerd.com)" | 320 points by susam 9 days ago | 215 comments | https://news.ycombinator.com/item?id=34286622
One of the nice things about CGI was that by starting a new process per request it provided isolation between requests. Python's startup overhead obviously makes CGI untenable, but I find the new Web Assembly runtimes interesting because they are able to provide extremely fast startup by reseting state: https://bytecodealliance.org/articles/wasmtime-10-performanc...
PHP was built around this idea for decades %)
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.
Forking can be quite slow (milliseconds), though there are techniques to speed it up: https://www.cs.purdue.edu/homes/pfonseca/papers/eurosys21-od...
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.
boiler up
Do python mains not know what FastCGI or mod_python are?
I have a loos story I can tell that links mod_python to WSGI (a bit).
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.
Thank you so much for your work! It really shaped a generation of web development, giving a shot to small players and startups.
PS That is why I love HN. You get to casually see creators of great projects.
WSGI and mod_wsgi were designed as an improvement over mod_python which was pretty much an Apache only thing.
As for FastCGI, although it is language agnostic it didn't really get traction outside of PHP.
WSGI (same for ASGI) is a level above FastCGI (or CGI or whatever) in the stack; it exists so each Python web framework does not need implement ways to talk to CGI, FastCGI or whatever. Instead the framework implements a Python function, and the user can choose a WSGI server to wrap this Python function into whatever is needed to talk to any kind of language-agnostic gateways, including CGI, FastCGI, or even mod_python.
In practice most deployments I have seen use a WSGI server that directly speaks HTTP so that CGI, FastCGI, or mod_python are not even necessary.
layer below and dead, respectively.
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).
mod_python is pretty much dead at this point. Ubuntu 22.04 ships a port of it to Python 3, but it is buggy. I ended up switching my code from mod_python to WSGI instead of debugging "apache segfaulting in mod_python" issues, on some ~20 year old mod_python code over the last year.
Pro tip: WSGI is pronounced like "whiskey". :-)
Is it?? I’ve been slaughtering that word for almost 15 years. Whether true or not, that’s what I’m calling it from now on.
Ian Bicking is the one who told me, so I feel like I can speak with some authority. :-)
Speaking on behalf of the basic python main community, we just do whatever `requests` does.
That's a HTTP client library.
I've thought a few times of trying to write a server in Rust that runs ASGI applications. The main oddity I found is that I'd end up with two asynchronous runtimes: one for the Rust webserver, and another for the python application itself. Somehow, this never sounded right, so I never gave it a shot.
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.
Granian maintainer here.
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.
Thanks for your reply! Indeed, this should still be more efficient than a pure-python approach.
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.
> if you're doing Python web development and you have no idea what WSGI or ASGI are, you really ought to learn more about them
Unpacking the acronyms might help
> If you're doing Python web development and you have no idea what WSGI or ASGI are
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?
> how is it possible to do Python web development without first learning what WSGI is?
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.
I think it depends how deep into the fundamentals you want to go. If you're building say a shopping cart extension for Django, your time is better spent building within the framework than learning the nitty gritty of WSGI. It's a cost-time trade off and sometimes learning the fundamentals is not worth it when you could be spending more time on working on your actual problem domain.
I would not be surprised how many Django developers setup WSGI and still have no idea how it works or why it exists, they just pull up a Google article on best practices for deploying Django and follow it to the T.
I assume the situation would be that you are just following a FastAPI or Django or Flask tutorial and putting the pieces together, running gunicorn without really even thinking about WSGI.
There were other solutions 10+ years ago, but they're all dead. I guess the comment you're replying to was nervous because when people do python things in another language they sometimes don't follow python standards.
Spot on! Sometimes even in Python people reinvent the wheel because they either had no idea something existed already, or they didn't like it for whatever reason. It also means writing your web framework differently depending on how their unique solution works, it does look like they did roll some enhancements over ASGI (they extended the spec) but the fact it still supports regular Python standards is good enough in my eyes.
This comment is long past editing deadline but since most replies seem to have misinterpreted me, I must have worded it very badly, so will clarify:
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"
> how is it possible to do Python web development without first learning what WSG
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.
That is fine, you don’t have to understand the underlying libraries to write useful code.
Over the years, I have built some internal apps by nesting aiohttp in a python process. Usually this starts with me having an existing process and finding I want to add a web interface to it, for example, as a dashboard. What would be the advantage of putting a commodity web server between the application server and the user, rather than having the user’s browser directly talk to aiohttp?
It's in the end an implementation detail. You can set up a WSGI server and deploy an app without really knowing or understanding what WSGI actually is, even if the letters appeared somewhere deep in a config file.
Looks super interesting. One of the things that I wanted to improve on are Gunicorn's latency and here:
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?
Granian maintainer here.
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.
No comparison with uwsgi in the link.
It's unlikely for new projects to compare against uwsgi because "The project is in maintenance mode".
So you mean feature complete and it only needs updates to fix bugs or security issues? Since it still is maintained (which is what maintenance mode means or not) why would you not include it in a comparison?
I would guess that people see "maintenance mode" and think that means it's pretty much EoL and will no longer receive security patches in the near future.
That is certainly the mental model I have for something in maintenance mode.
To me the whole point of saying "maintenance mode" is to tell people to only expect things like necessary security patches and critical bug fixes instead of new features. If those things aren't happening, then there isn't any maintenance going on.
I am going to still use it until something else catches up with it. uWSGI is fast and feature rich. As long as the maintainers address security issues as they pop up, it is still a good choice, even in maintenance mode.
Yep. The only thing I am missing from uWSGI right now is support for cgroups v2 so that I can limit CPU usage on a per-worker basis without fighting systemd.
There is a similar project https://github.com/sansyrox/robyn
A framework for Python, the core of which is written in Rust
yes, it was shared yesterday on HN ;) also this is more low-leven than Robyn, so I think it could be used to build other frameworks like Robyn
Collaborating with Python/Web is a great way Rust to market itself.
also for tool, see Ruff: https://github.com/charliermarsh/ruff (I'm a big fan!)
Ruff very suddenly popped out of nowhere and re-implemented SO MANY development tools into one single codebase, all while making it stupid fast? I just came across it earlier today and it almost sounds too good to be true.
One of the best thing related to Python in recent years. Completely resolved the speed problem during linting that often keeps me away from using the language.
+1 for ruff! the creator seems like a cool dude too
Interesting project. Would it be able to run a fastapi project just like uvicorn in production?
Yup: `granian --interface asgi your_module:your_fastapi_app`
Can anyone give a quick synopsis of why this RSGI is needed? Can't ASGI code be called from a Rust socket termination, a la Gunicorn?
Is not needed, is just a different async protocol implementation. Granian supports both interfaces, is a matter of how you want to write your application.
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.
Very cool. Sounds like a perfect fit for the Starlite framework, which is using Rust for route handling.
It looks like a wrapper arround hyper?
Any plans on implementing H3?
for those wishing to use http3 with a Python web framework, the ASGI hypercorn[1] currently supports it.
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
That’s so cool, I have been looking around for something similar but in Ruby. Couldn’t find anything, just a talk about the possibility of implementing it.
True. The "bad" thing about hypercorn is its poor performance, at least for now :/
From github
> Have a single, correct HTTP implementation, supporting versions 1, 2 (and eventually 3)
Granian maintainer here. As other people pointed out, the project relies on the Hyper Rust project, which at the moment doesn't support HTTP/3. Once the project will add support for this, will be implemented in Granian as well.
Looking at the code it relies on Hyper which doesn't support H3 yet
Serious question: why would I want to use this? Is the answer as simple as “because I want to develop in Python”?.
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?
Granian is more of a web server for python that just happens to be implemented in Rust. Python developers using Granian wouldn't need to know or write any Rust, just like they don't need to know or write any C in order to use python modules that just happen to have C components to make them faster. The module containing substantial Rust code is merely an implementation detail.
"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!
Theoretically, an asgi server sits in front of your fastapi or starlette. This asgi servers are usually written in python (maybe calling some c code underneath). You can "probably" gain some performance by writing the asgi server in rust. I don't know how much better is this.
You can check benchmarks https://github.com/emmett-framework/granian/tree/v0.2.2/benc... ;)
Python comes with an HTTP server built-in!
This is a different scope aimed at hosting a production service.
The Python included http Server is super useful but for completely different use cases.
The standard lib equivalent would rather be wsgiref.
Yeah. AFAIK that one is not intended for prod.