top | item 12713469

Sanic – Python 3.5+ web server that's written to go fast

227 points| shakna | 9 years ago |github.com | reply

69 comments

order
[+] saynsedit|9 years ago|reply
I've actually written a highly optimized asyncio-based Python web server in the past. I meticulously optimized every component, using all the standard CPython optimization techniques (heavy use of stdlib, minimizing method lookups).

In the end, my implementation was nowhere near nginx. I even ran it under PyPy and it fared no better. Then I realized the oxymoronic nature of writing an optimized web server in Python.

[+] dr_zoidberg|9 years ago|reply
If you wanted to run in PyPy, you should've written your code to be PyPy-friendly from the beginning. Standard CPython optimizations don't usually fare well in PyPy, and you can end up with even slower speed if you follow those. Still, you didn't mention profiling the hotspots of your server, nor using Cython to optimize them.

I don't see how writing an optimzed web server in Python becomes oxymoronic -- it can still be the fastest performing python server, and have a valid use case for those that want to work in Python.

[+] e12e|9 years ago|reply
What does "not even close" mean in this context? I find it a bit interesting that the most obvious conclusion from the benchmarks in the story, and the linked one - is that uvloop and go are high-performance and consistent low-latency, high performance python is on par with nodejs - but in general the real jump up is towards go and c++(?).

It's great to see an order of magnitude leap on the python side - but on the face of it I'm not sure the leap really is enough to enable a different class of services in python? Perhaps I'm being too pessimistic - I know I'd be happy to be able to "ignore" node, and only consider eg: Python for most things and go for some things. Just to limit my tech stack.

But where does nginx with lua fit - is it another order of magnitude above go for dynamic content?

[+] paulddraper|9 years ago|reply
True, but so what?

If you wanted to create a static files server, Python is just a bad choice.

Python is a good choice if you need custom, complex logic or access to non-file data stores. And if you need that, Nginx isn't a choice (yes you can use modules -- eventually you recreate the same problem).

So yes, don't try to replace Ngnix or HAProxy or PostgreSQL or such with Python. But the gaps not covered are good to be written in Python. And it's nice if they're fast.

[+] Walkman|9 years ago|reply
> In the end, my implementation was nowhere near nginx.

How could it be?

[+] aosaigh|9 years ago|reply
As a matter of interest, what makes nginx so fast?
[+] tschellenbach|9 years ago|reply
Really excited about these efforts. Python is my main programming language, but for high volume endpoints it's sometimes necessary to switch to other languages. In an ideal world I'd just use Python for everything. It will take a while before that's possible though. These performance gains don't mean much if your DB connection is still blocking.

How does your benchmark compare against GO, Elixir and Node?

[+] mangeletti|9 years ago|reply
Extrapolating from Techempower and from the OP's benchmarks, I got:

    137% as many req/sec as Node
    83% as many req/sec as Go
I'm it sure where this goes when you factor in async IO, but my guess is that Node gains a little and Go loses a lot.

When you add asyncpg to the mix my money is on Sanic winning by a long shot, in a benchmark that includes e.g., 3 queries and returning the results as JSON.

[+] mhuffman|9 years ago|reply
I have found node and tornado to have practically identical performance. I have not had a lot of experience with GO and Elixir for web servers.

nginx (openresty, really) has beat both of them them in all "normal" (ie. not limited by DB access, etc) usage conditions that I have tested.

[+] SwellJoe|9 years ago|reply
It's interesting how so many of these microframework+app server type things spread across many languages have converged on a very, very, similar set of conventions and practices. The example code looks strikingly like every other recent JavaScript/Perl/Python/Ruby microframework+server that I've tinkered with lately (Express, Koa, and Hapi in JS, Mojolicious in Perl, and Flask in Python). Aside from the obvious language differences, the concepts are all so similar.

I wonder if this is a convergence on a local maximum for writing web apps, or if it's just a situation where the most popular ones did it this way, so everyone does it this way. Not that it matters too much, in the general case. The route and app setup plumbing is a small part of most applications, so it's rarely going to be ruinous if you spend an extra hour or two getting it working right. But, since I've been evaluating a bunch of different ways to build web app backends lately, I've just noticed how similar they all are, no matter what language you choose. Most of the ones I've been looking at are asynchronous, so this also has that similarity to most of the others.

While one could argue that it's obvious that they would look the same...but, web development didn't always look this way. The first 10-15 years of web application development I did looked very different, in fact (CGI or mod_perl or PHP, which has some quite different conventions).

[+] Jgrubb|9 years ago|reply
I've had this blog post in my head for a couple years now that some day I'll get around to writing. It's titled "Toward a Common API", and it's basically about this - the convergence of best practices, naming conventions, patterns, etc across languages and frameworks that (IMHO) is a wonderful thing in that it reduces ramp up time when coming to something new.

It came to me after playing with Rails for several years and then taking a look at Laravel, which is clearly Rails inspired in many ways but made some different decisions in the naming of a few things that are present in both frameworks.

[+] mangeletti|9 years ago|reply
> I wonder if this is a convergence on a local maximum...

It's more like a global minimum.

With many of these micro-frameworks, you're observing what is near the minimum API required for mapping a web request to a single function.

[+] minitech|9 years ago|reply
The README suggests this one is written to be Flask-like, and Koa is based on Express/Connect.
[+] yakcyll|9 years ago|reply
Why it is app.run() and not app.go_fast() is beyond me.
[+] btmiller|9 years ago|reply
Good point. With Sanic you gotta .go_fast()
[+] spamizbad|9 years ago|reply
This is a great project. I am going to be watching this!

One question: Why default to ujson? I understand that if you're always dealing with small, simple json objects it's quite fast and safe. But with larger json payloads, its performance and compatibility start to break down. I haven't touched it in the last 8 months, but I had to switch to Python's built-in json module (which is quite fast in 3.5) for compatibility when serializing and deserializing large json objects.

[+] agf|9 years ago|reply
Do you have a good writeup of this problem? I've seen only scattered, anecdotal info about it.
[+] rcarmo|9 years ago|reply
Curious to see how this fares against something like uWSGI with aiohttp support (haven't tried either, but uWSGI brings so much more to the table than speed that I'd like to be able to make an informed choice).
[+] asdfologist|9 years ago|reply
I noticed that the past several Python-related threads on HN haven't contained the usual cynical comments against adopting Python 3. Are we finally at a point where Python 3 has been accepted as the gold standard?
[+] oliwarner|9 years ago|reply
Yes. I think the async syntax in 3.5 was the tipping point for the stragglers. It finally looked like a serious upgrade, not just a bigger number with a load of things to fix.

I know there are people who are stuck on old versions, but the community has recognised how much damage this dispute was doing to Python's image. They know they'll have official Cpython support to 2020, so it's a non-issue until then.

Oh and Ubuntu 16.04 defaulting to 3.5 helped too.

[+] dom0|9 years ago|reply
I've mostly heard this from people who don't use/know Python in the last ~2 years. Imho this has been a trope for some time:

    PHP is such a bad language hue hue
    Java. AbstractCommentPostingRequestSerializerInterface hue hue
    Python. You know Python 3 right? hue hue hue
    command.exe windows suxxx hue hue
    ... countless others with varying stupidity
[+] tobltobs|9 years ago|reply
By my gut feeling this happened this summer. I haven't found any project which wasn't available in Python 3 in the last three months and therefore stumbled upon some which don't support 2 anymore. Also newer answers on SO now usually start with the version for 3 and might have an appendix "if you are still on ...".
[+] shakna|9 years ago|reply
Async handlers, Flask-style API, and fast.

Disclaimer: I have nothing to do with this project.

[+] rgacote|9 years ago|reply
Looks like a nice step up from the raw aiohttp I've been writing. Would be interesting to see this wrapped with a forking module. That would provide an nginx-style deployment--select the number of actual threads to run and then run async within each thread.
[+] sixhobbits|9 years ago|reply
The comparisons are nice, but it would have been nice to see a comparison with Flask + Nginx/Apache2 for example.
[+] __s|9 years ago|reply
Curious how much this benefits from optimizations coming in Python 3.6. Is the benchmark essentially spending all the time in uvloop Cython code?
[+] floatboth|9 years ago|reply
I love the name!! :D

The hard dependency on uvloop looks weird. It's just a pluggable loop for asyncio, anyone can switch to it in two lines of code, no reason to depend on something that's a native extension.

Also why not just improve aiohttp's performance…

[+] nitely|9 years ago|reply
> Also why not just improve aiohttp's performance…

Have you read aiohttp's code? there have been some discussions about improving its performance, there is basically no immediate bottleneck, the whole thing just performs poorly. I hate to say it but sometimes it's just better to start from scratch.

[+] leemalmac|9 years ago|reply
This is awesome, Python is my number one, Node.js already has much larger ecosystem for async i/o. If we need something faster for heavy computations Go is a good choice.

But maybe in near future Python will grow significantly, who knows.

[+] okso|9 years ago|reply
No support for websockets ?
[+] tschellenbach|9 years ago|reply
I think it's fine to use an external library such as Faye for websockets. That comes with the benefit of having many client libraries available. (JS, iOS, Android)
[+] infocollector|9 years ago|reply
Can we just use the webserver of Sanic with Flask on linux?
[+] lbolla|9 years ago|reply
What's up with those latency numbers? Seem huge to me.
[+] sitkack|9 years ago|reply
Who measures average latency? Nearly meaningless.
[+] yahyaheee|9 years ago|reply
This looks awesome, exactly what python needs right now
[+] magicbuzz|9 years ago|reply
I honestly don't understand why you would do this when nginx with Luajit is so fast. I think Python is great but nginx is so robust, widely used and Luajit is so impressively quick.
[+] petre|9 years ago|reply
Probably because even though Lua is super a nice language, writing code in a config file is not that much fun? App servers spitting out HTML written in the language of your choice are so much more flexible than developing on a single platform/stack such as Apache + mod_php|mod_perl, or OpenResty. Of course nothing stops you to proxy them through Nginx and serve the static content directly with Nginx in production.