I'm still not entirely sure why the problems inherent in single-stream connections (request/response) couldn't be solved simply by removing the artificial RFC-recommended limitation on parallel connections to a server. As the author says, providers have been escaping this limitation for years by simply adding hostname aliases, but has nothing negative to say about it. Modern HTTP servers are highly concurrent; allowi…
Because running multiple TCP connections in parallel plays havoc with TCP congestion control and also plays poorly with the TCP slow-start logic. Every TCP connection begins its receive window again and so it starts small, so fetching many moderately-sized or large resources (think images) will cost you many round trips you didn't need to spend.
Http2 explained
31–40 of 67 posts
Re: Http2 explained
#32Earlier quoted context omitted.
Because running multiple TCP connections in parallel plays havoc with TCP congestion control and also plays poorly with the TCP slow-start logic. Every TCP connection begins its receive window again and so it starts small, so fetching many moderately-sized or large resources (think images) will cost you many round trips you didn't need to spend.
Moreover, it's bad from a QoS standpoint. Opening many TCP streams in parallel is not fair to other users sharing the routers between you and the server. You'd get more than your fair share of bandwidth.
Re: Http2 explained
#33I'm still not entirely sure why the problems inherent in single-stream connections (request/response) couldn't be solved simply by removing the artificial RFC-recommended limitation on parallel connections to a server. As the author says, providers have been escaping this limitation for years by simply adding hostname aliases, but has nothing negative to say about it. Modern HTTP servers are highly concurrent; allowi…
Will Chan of Chromium wrote a good post explaining the congestion issues with opening many TCP connections - https://insouciant.org/tech/network-congestion-and-web-brows...
The question is how to get it right, and the problem is that you can't get it right without knowing the amount of bandwidth available to you in advance. Limiting concurrency limits congestion on slow networks, but it caps you unnecessarily on fast ones. The same is true for SPDY/http2; using a single stream will never give you the same concurrency as multiple streams.
Re: Http2 explained
#34Earlier quoted context omitted.
Because running multiple TCP connections in parallel plays havoc with TCP congestion control and also plays poorly with the TCP slow-start logic. Every TCP connection begins its receive window again and so it starts small, so fetching many moderately-sized or large resources (think images) will cost you many round trips you didn't need to spend.
I don't buy the congestion-control argument. Transit routers have millions of connections going through them at any given point. What's an extra 10x in concurrency to them?
The startup phase of a TCP stream is essentially not governed by congestion control feedback because there hasn't been enough (or maybe any) feedback yet. It is initially controlled by a constant (IW) and then slowly feels its way before dynamically finding the right rate. IW can generally range from 2 to 10 segments. Whether this is too much or too little for any individual circumstance is somewhat immaterial - its generally going to be wrong just because that's the essence of the probing phase - you start with a guess and go from there.
Each stream has a beginning a middle and an end. 1 large stream has 1 beginning 1 (large) middle and 1 end, but N small streams have N beginnings N (smaller) middles, and N ends. The amount of data in the beginning is not a factor of the stream size (other than it being the max), it is rather governed by the latency and bandwidth of the network. So more streams means more data gets carried in the startup phase. If the beginning is known to be a poor performing stage (and for TCP it is) then creating more of them and having them cover more of the data is a bad strategy.
In practice, IW is too small for the median stream - but there is a wide distribution of "right sizes" so its ridiculously hard to get right.. maybe it is IW=10 and the right size is 30 segments; but that's one stream 3x too small- it isn't 20x or 50x too small, so when you open 50 parallel tcp connections you are effectively sending at IW * 50. And that does indeed cause congestion and packet loss.. and its not the kind of "I dropped 1 packet from a run of 25 please use fast-retransmit or SACK to fix it for me" packet loss we like to see.. its more of the "that was a train wreck I need slow timers on the order of hundreds of milliseconds to try again for me" packet loss that brings tears to my eyes. One of the reasons for this goes back to the N beginnings problem - if you lose a SYN or your last data packet the recovery process is inherently much slower, and N streams have N times more SYNS and "last" packets than 1 stream does. Oh, and 50 isn't an exaggeration. HTTP routinely wants to generate a burst of 100 simultaneous requests these days (which is why header compression when doing multiplexing is critical - but that's another post).
So the busier larger flow both induces less loss and is more responsive when it experiences loss. That's a win.
And after all that you still have the priority problem. 50 uncoordinated streams all entering the network at slightly different times with slightly different amounts of data will be extremely chaotic wrt which data gets transferred first. And "first" matters a lot to the web - things like js/css/fonts all block you from using the web, but images might not.. and even within those images some are more important than others (some might not even turn out to be on the screen at first - oh wait, you just scrolled I need to change those priorities). Providing a coordination mechanism for that is one of the real pieces of untapped potential hiding in h2's approach to mux everything together.
There is a downside. If you have a single non-induced loss (i.e. due to some other data source) and it impacts the early part of the 1 single tcp connection then it impacts all the other virtual streams because of tcp's in-order delivery properties. If they were split into N tcp connections then only one of them would be impacted. This is a much discussed property in the networking community, and I've seen it in the wild - but nobody has demonstrated that it is a significant operational problem.
The h2 arrangement is the right thing to do in a straightforward TLS/HTTP1-COMPATIBLE-SEMANTIC/TCP.. making further improvements will involve breaking out of that traditional tcp box a bit (quic is an example, minion is also related, even mosh is related) and is appropriately separated as next-stage work that wasn't part of h2. Its considerably more experimental.
Re: Http2 explained
#35> 8.4.4. “Not being ASCII is a deal-breaker” > Yes, we like being able to see protocols in the clear since it makes debugging and tracing easier. But text based protocols are also more error prone and open up for much more parsing and parsing problems. > If you really can't take a binary protocol, then you couldn't handle TLS and compression in HTTP 1.x either and its been there and used for a very long time. First,…
Why not just have the wire sniffer decode the frames before presenting them. If you're interested in what's going on at the HTTP level, you aren't reading packetwise IP packet dumps, because it's hard to make sense of anything and everything is all mixed together; you're looking at abstracted, higher-level flows, where it's just taken for granted that you have a set of linear TCP streams. HTTP2 is a(n SCTPish) transp…
Certainly possible, but why make our lives harder by implementing HTTP/2 such that it requires a decoder to read in the first place? If the number of bytes sent remains the same, why not make the fields as self-documenting as possible?
> Everything that speaks HTTP2 also speaks HTTP1.1
No, they have fundamentally different wire formats. This statement isn't even true for HTTP/1.1 and HTTP/1.0, which both have the same wire formats and share many fields but have different interpretations for some of them.
Re: Http2 explained
#36Earlier quoted context omitted.
Additionally, a TCP connection is essentially an operating system resource; you need to set aside a port and space for a send and receive buffer. It might seem fine for a client to open hundreds connections, but imagine being a server with thousands of clients all opening hundreds of connections to you. You very quickly run out of resources and either have to close connections or reject new connections.
That hasn't been a practical problem in many years. Most servers have gigabytes of RAM and a 64-bit kernel nowadays.
Not sure about FreeBSD or any other OSes.
Re: Http2 explained
#37Earlier quoted context omitted.
Moreover, it's bad from a QoS standpoint. Opening many TCP streams in parallel is not fair to other users sharing the routers between you and the server. You'd get more than your fair share of bandwidth.
Only if you don't assume that everyone else will also use the same number of streams in parallel. If everyone else's utilization is increased by the same factor, the utilization balance should remain the same.
I think ideally, we'd create a TCP variant where localhost maintains a per-destination receiving window for all flows to that destination, so flows running in parallel or flows started in rapid succession won't have to start their windows at 0 and slowly increase them. Moreover, this way congestion control applies to all packet flows for a (source, destination) pair, instead of to individual flows.
HTTP/2 and HTTP pipelining take a crack at this by running multiple application-level flows (i.e. HTTP requests) through the same receiving window (i.e. the same TCP socket), but they're not the only application-level protocols that could stand to benefit.
Re: Http2 explained
#38Earlier quoted context omitted.
Good luck getting everybody to upgrade their kernel to support your new transport protocol. Realistically, UDP and TCP are what we have. We may wish they were more suited to modern use cases, but realistically we must build on those foundations. If that means violating "layering" for performance, so be it.
I think he/she is not talking about the transport protocol (TCP/UDP), but the higher up layers (5 - 7) cramming functionality from the lower layers. Might be reading it the wrong way.
Re: Http2 explained
#39Earlier quoted context omitted.
Why not just have the wire sniffer decode the frames before presenting them. If you're interested in what's going on at the HTTP level, you aren't reading packetwise IP packet dumps, because it's hard to make sense of anything and everything is all mixed together; you're looking at abstracted, higher-level flows, where it's just taken for granted that you have a set of linear TCP streams. HTTP2 is a(n SCTPish) transp…
> Why not just have the wire sniffer decode the frames before presenting them. Certainly possible, but why make our lives harder by implementing HTTP/2 such that it requires a decoder to read in the first place? If the number of bytes sent remains the same, why not make the fields as self-documenting as possible? > Everything that speaks HTTP2 also speaks HTTP1.1 No, they have fundamentally different wire formats. Th…
Re: Http2 explained
#40Earlier quoted context omitted.
Because running multiple TCP connections in parallel plays havoc with TCP congestion control and also plays poorly with the TCP slow-start logic. Every TCP connection begins its receive window again and so it starts small, so fetching many moderately-sized or large resources (think images) will cost you many round trips you didn't need to spend.
Moreover, it's bad from a QoS standpoint. Opening many TCP streams in parallel is not fair to other users sharing the routers between you and the server. You'd get more than your fair share of bandwidth.