The end result is that HTTP/2 is an improvement for most common workloads, but not all; especially in app-type scenarios with lots of mobile users with suboptimal connections and comparatively few requests (e.g. because you already do batching rather than sending zillions of requests), HTTP/2 can regress typical performance.
WebSockets over HTTP/2 is now specified in RFC 8441; not sure what the implementation status of that is. That solves one of the main problems.
My understanding is that HTTP/3 (with UDP-based QUIC instead of TCP) then resolves all remaining known systemic regressions between HTTP/1.1 and HTTP/2. So yeah, HTTP/1.1 to HTTP/3 should be pretty close to “free speed”.
But even then, it changes performance and load characteristics, and requires the appropriate software support, and that means that many users will need to be very careful about the upgrade, so that they don’t break things. So it’s not quite free after all.
Now, if you want to take advantage of HTTP/2 features like server push that's another story.
Unfortunately neither of those currently support HTTP2.
For serving Python, I think your best bet right now is uWSGI behind NGINX.
It's easy to complain about almost anything - it tends to be a lot harder to make a proposal for its replacement.
but stateful connection management comes with a cost, especially on tcp.
TCP has stateful connections, but both HTTP versions are being sent over TCP anyway so in that sense the transport was always stateful.
Http/2 is multiplexed, unlike http/0.9-1.1, and while that has some overhead, it being a binary protocol probably makes up for it.
Keep-alive isn't any better. In Apache bad nginx, keep-alive and http/2 parallel requests are handled at a separate thread and hardly adds any noticeable load.
No, http2 is not better. We actually did the tests. We're not quite Google-scale, but having to handle tens of thousands of requests per second put us in the 'high load' camp.
Pipelining might amplify it, but it is always there, especially with unreliable mobile connections.
On pipelined requests it's not too bad, you're not supposed to pipeline requests that aren't safe to retry. But pipelining ends up being somewhat rare in practice. Reusing an inactive connection is actually pretty risky, the server may be able to shut it down, your network may have silently dropped the connection already (some NAT timeouts are really short, I've seen cases in real mobile networks where the timeout was under a minute!).
I'm not thrilled with multiplexing in http/2, but the sensible stream closure would be really nice to have. If you see a goaway, you know it it saw your request or not, so you can resend it with a clear concensce.
Without keepalive, you create a new connection and pay the latency costs of doing so. With keepalive, there's a chance you try to reuse an old connection, and it fails, which requires round trips to learn about, and you still have the latency of creating a new connection. So more total latency in that case.
It seems it would improve the average case but make the worst case slightly worse. If your keepalive timeout is short, maybe it would come up often enough to matter.
The problem is many servers don't send this header when closing idle connections. nginx is a notorious example. But well behaving servers should be sending that header if they intend to close the connection after a request.
However, when the server holds the connection open for some amount of time and then decides to close it, it's not permitted for the server to send a response header, because there's no request to respond to. I would love to be wrong, but I don't think I am, because this scenario is mentioned in the RFC, "For example, a client might have started to send a new request at the same time that the server has decided to close the "idle" connection. From the server's point of view, the connection is being closed while it was idle, but from the client's point of view, a request is in progress." [1]
An example chain of events is:
t0 client opens connection (syn)
t1 server accepts connection (syn+ack)
t2 client sends first request
t3 server sends response and keeps connection open
...
t63 client sends second request
t63 (simultaneously within a margin of the one way trip time), server closes connection because it's been idle for 60 seconds
t64 client receives FIN
t64 server receives data on closed socket and sends RST
t65 client receives RST
http/2 improves this greatly because in this example, a compliant server will send goaway with last-stream-id 1 prior to closing the connection, and the client will know the second request was not processed and should be retried. It still suffers a latency penalty because it has to start a new connection, and it already wasted somewhere between a one way trip and a round trip.
[1] https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8....
The Keep-Alive header [1] is optional, but has parameters timeout, indicating the idle timeout, and max, indicating the number of allowed requests. Max is useful for pipelining, to avoid sending requests that won't be processed; timeout is very helpful for avoiding sending requests when the server is about to close the socket.
[1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Ke... The Connection response header is specificed to have two optional parameters, timeout, and max. timeout
https://www.chromium.org/developers/design-documents/network...
shows they removed it in Chrome due to issues, and weirdly this Firefox ticket:
https://bugzilla.mozilla.org/show_bug.cgi?id=264354
(last updated 5 years ago), seems to show they're not going to enable keep alive for HTTP 1.1, but Firefox is most definitely utilising it.
Firefox most definitely is utilising it fully, and obeys the Keep-Alive params specified, which I can't see any of the other browsers doing.
The "Keep-Alive" header was something tacked onto http/1.0 and doesn't really mean anything these days.
"Sending a 'Connection: keep-alive' will notify Node.js that the connection to the server should be persisted until the next request."
The article seems to confirm this behavior.
So clients have to account for non RFC compliant servers.
https://stackoverflow.com/questions/25372318/error-domain-ns...
MDN's documentation on this header references https://tools.ietf.org/id/draft-thomson-hybi-http-timeout-01... for the parameters, but this is an experimental draft that expired in 2012.
Which is to say, I can't really fault Safari for not respecting keep-alive parameters that never made it out of the experimental draft phase.
I've had apache refuse new requests because old connections were holding slots.
The client would in the middle of sending a new request, but the server would have already decided to close the connection and the request would fail.
I believe this is a common problem, can and yet the spec has nothing to address this obvious race condition.
Right?
But the fact that the underlying HTTP connection is kept-alive by default doesn't necessary mean that the client is going to actually re-use that connection for multiple HTTP requests. And, in fact, in Node.js the connection is not reused by default.
https://fastmail.blog/2011/06/28/http-keep-alive-connection-...
Request count is really not a big deal with HTTP/2 multiplexing.
By forcing base64, you're eliminating all the caching and using much more CPU power to parse that back into a binary image. You're also making the page load slower as the initial payload is bigger and image data has to be handled in line rather than asynchronously.
Do any static site generators rewrite image links as data URIs?
I'm using Hugo and this is at least no default behaviour, not sure though if there is a switch for that.
The SSL connection init costs are real, although SSL session re-use can help there even without keep-alive.
I was burned hard by this in Azure. It seems that the default expiry time is around 4 minutes for the TCP load balancers. You can bump it to 30 min, but if I recall the default interval on Linux is 2 hours. Any long-standing idle TCP connections would get into a state where both sides believed they were connected, but the packets would get dropped to the floor. When the LB timed out, it didn't emit any FIN or RST packets, so neither side knew it had been torn down.
Fun debugging on that one. During the day there was enough activity to keep the connections alive, but at night they'd break. The overall behaviour was that the service worked great all day, but the first few actions out-of-business-hours would fail due to application-layer timeouts, and then everything would work great again until it had sat idle for a while.
(i.e. To handle the case of "HTTP-Request", "huge delay", "final response". Rather than a streaming/chunking reply that is very long/slow.)