0

I understand that HTTP/2 multiplexing solves head-of-line blocking problem in HTTP/1.1. However, head-of-line block still exists in TCP protocol. Even if requests are sent at the same time in application layer, the messages still need to be sent one by one in one TCP connection.
My question is why don't apps using HTTP/2 establish multiple TCP connections, so that HTTP/2 multiplexing won't be restricted in transmission layer(TCP protocol)?
I understand that using HTTP/2 over UDP(QUIC) could avoid this problem. In this post, I'm discussing HTTP/2 over TCP.
If application did establish multiple TCP connections(In case I don't know this implementation already exists). How do the requests split among all TCP connection?

  • *"However, head-of-line block still exists in TCP protocol."* Can you expand on what you mean by that? Receipt of packet 1 doesn't have to be acknowledged before packet 2 is sent, that's why the packets have sequence numbers, so they can be assembled in order at the receiving end. – T.J. Crowder May 16 '21 at 07:39
  • Let's say package1 is lossed in a TCP connection. The server side will be blocking waiting for the missing package1. On the client side, package1 needs to be re-send, and package2 needs to wait for the re-transmission of package1. So why not using multiple TCP connections like HTTP/1.1 did? Or is this restricted by NIC bandwidth, establishing multiple TCP conections using HTTP2 won't help much?(I'm just guessing) – Yichuan Wang May 16 '21 at 08:10
  • *"On the client side, package1 needs to be re-send, and package2 needs to wait for the re-transmission of package1."* I don't think that's true. Again, that's what sequence numbers are for. But I'm not deep into networking details. I do think that if establishing multiple connections would be (or is) useful, it would be (or is being) done. – T.J. Crowder May 16 '21 at 08:23
  • How many wires and routers do you have connecting you to the internet? – symcbean May 16 '21 at 23:36

2 Answers2

0

Yes this is a known issue with HTTP/2 and what HTTP/3 aims to solve by having completely independent streams within the same connection. To do this they can’t use TCP and have to basically build a multiplexed version of TCP (called QUIC) from scratch.

Hooman Beheshti from Fastly did a great talk on this and showed that with a consistent 2% data loss you were better off using HTTP/1.1.

So what should we do? Not use HTTP/2 at all and stick with HTTP/1.1 for now? Or, as you suggest use HTTP/2 with multiple connections.

Well, first of all you need to realise that on most connections data loss, or at least consistent data loss is actually relatively rare. And a consistent 2% data-loss as per that talk is basically a terrible connection! Data loss is much more intermittent and bursty (e.g. as you go out of mobile reception, and then come back in).

Studies before HTTP/2 was released showed that, in most cases HTTP/2 is better than HTTP/1.1 - or at least the opportunities for a well implemented HTTP/2 set up are there to be faster.

Yes, HTTP/2 was overhyped in hindsight and often the gains over a well optimised HTTP/1.1 site were relatively small. And yes, it can also be worse in some cases (particularly if the server doesn't support prioritisation properly). But this was known before it was released and still, in general HTTP/2 is better and recommended.

There are cases, like you give, where it can perform worse. If if your particular application expects heavy loss (I dunno, say you're doing a mobile app often used in mines deep underground and the connection comes and goes) then maybe don't use HTTP/2 and use a better protocol for this situation (including maybe HTTP/1.1). But for general web browsing HTTP/2 is probably better for most cases.

So why don't we use HTTP/2 with multiple connections?

Well the main benefit of HTTP/2 is due to making better use of that single connection. HTTP connection set up (and in particular HTTPS) is expensive - you need a TCP handshake, a TLS handshake and then to make the request. Even after that you have to go through TCP slow-start to get the connection up to full speed. And if the connection is not fully utilised, then it will slow down again and you need to repeat that slow-start method all over again.

Using a single connection means you only pay that set-up price once, and means you are much less likely to have the connection under-utilised and so fall back to slower connection speeds and have to go through a TCP slow-start again. Additionally you can cross-prioritise requests on a single connection. If you have some critical CSS and some lower priority images or async JavaScript also in flight, the server can prioritise the CSS and use the bandwidth for that and hold back the Images and async JavaScript (though again not all servers implemented HTTP/2 prioritisation well). With independent connections that prioritisation across different requests is just not possible and so you end up using your bandwidth inefficiently.

However some have suggested that, on a lossy connection, maybe we should switch back to two or even more connections even under HTTP/2 (see 22-minute point of that above presentation).

HTTP/2 was also a step along the way to HTTP/3 (though that was initially going to be called HTTP/2 over QUIC). The move to HTTP/2 was a big step and big change to how one of the core protocols of the web works. From a developer point of view the mindset switch to realise that you didn't need to bundle as much (though removing bundling completely was too far) under HTTP/2 was what came with HTTP/2 and doesn't change under HTTP/3. HTTP/2 was signed off in 2015 and it's taken 6 years for it's successor, HTTP/3, to come though and solve this issue and it's a much more complicated protocol, with many less benefits initially for that complexity. It will be much more difficult to run HTTP/3 for your servers and optimise for it in the best way.

So holding off for the full solution as will hopefully be delivered with HTTP/3, instead of going ahead with HTTP/2, would have been the wrong call. And using multiple HTTP/2 connections also has it's down sides.

Hope that answers your question.

Barry Pollard
  • 30,554
  • 4
  • 60
  • 77
0

You are right, this is an issue with HTTP/2 and TCP. The thing is, TCP (as per the most up-to-date RFCs) can hardly support multiplexing. This is where QUIC comes to the rescue and is one of the reasons why it is getting so much attention. To do multiplexing with TCP by opening multiple TCP connections for each HTTP resource of interest is probably a valid solution but there are a few issues with it.

  1. There is a big overhead to establishing multiple TCP connections (they all need to establish connection to the server e.g., SYN/SYN-ACK/ACK).
  2. Also, each such connection will start with the initial CWND value (typically 10) and so transfer might end up being slower.
  3. This, I think is the big one: You will likely need all resources before being able to load the web request successfully (meaningfully). For example, consider opening a web page that has some HTML, CSS, and a JS file. You could open 3 separate connections and request all of these individually, but chances are that you do need all three files before any meaningful interaction with the page can be made.
  4. If 3. does not apply for some reason, you will have to take care at the application layer with the fact that resources might arrive in different order.

However, there is one exception to that and I have observed different behaviour on it depending on the browser that you use. This one exception is if the website has a favicon. When making an initial request to the server, you likely need all web-page related material, but you do not really care if the small tab preview icon (the favicon) is received and you would not want any loss on that to stop you from interacting with the page, if this is the only thing that is missing.

I have seen my Firefox opening a new connection to download favicons sometimes, whereas chrome seems to download that and the page material on the same connection. In reality, I suppose it is just an implementation choice since this is the only fringe case I can think of where it can be useful, but again the favicon is so small that it is unlikely to make an impact.

Misho Janev
  • 263
  • 2
  • 11
  • Let's say if a web page contains hundreds of png files. In order to load all of them so that I could interact with the web. Instead of requesting all of them in one TCP connection, wouldn't making requests in severals connections more efficient? e.g. R equest png 0 - 30 in TCP conn1, 31 - 60 in TCP conn2...etc. Are the communication among different TCP connections complex to implement? – Yichuan Wang May 17 '21 at 06:18
  • As I said, depends on the use-case. What you describe might be a more efficient case for this particular instance. Although, you will again have to take into account that png 0-30 might arrive **after** png 31-60 And at that point you'd have to decide whether to partially load the web page and asynchronously load it later as items arrive or do a full-refresh. The scenario you describe is highly hypothetical and possibly assumes that the PNGs have not been compressed, but according to studies the average page size is ~2MB. And browsers optimize for the general case, not the edge-cases. – Misho Janev May 17 '21 at 10:50