Re: why not multiple, short-lived HTTP/2 connections?

On Tue, Jun 24, 2014 at 01:50:33PM -0400, bizzbyster@gmail.com wrote:
> I've raised this issue before on the list but it's been a while and reading
> Mark's ops guide doc (https://github.com/http2/http2-spec/wiki/Ops) I'm
> reminded that requiring the use of a single connection for HTTP/2 ("Clients
> SHOULD NOT open more than one HTTP/2 connection") still makes no sense to me.
> Due to multiplexing, HTTP/2 will naturally use FEWER connections than HTTP/1,
> which is a good thing, but requiring a single connection has the following
> drawbacks:
> 
> Servers must keep open idle connections, making load balancing more complex
> and creating DOS vulnerability.

They're not forced to, it's just like with persistent connections in 1.1,
they're free to close them when they don't want to, which will cause the
client to reopen a new one when needed.

> Servers must turn off tcp_slow_start_after_idle in order for browsers to get
> good performance, again creating DOS vulnerability.

It's not that black and white. If you fetch multiple objects over a single
connection, you have less chances of reaching an idle state. However if you
open many connections, each of them starts with a slow start. So if you do
nothing (do not turn off tcp_slow_start_after_idle), you should at least get
the level of performance you have with multiple connections, with the
possibility that sometimes you don't go idle and gain even more.

> The number of simultaneous GET requests I'm able to upload in the first round
> trip is limited to the compressed amount that can fit in a single initcwnd.
> Yes compression helps with this but if I use multiple connections I will get
> the benefit of compression for the requests on the same connection, in
> addition to having multiple initcwnds!

Yes, but many people are against this (the buffer bloat problem), but see below.

> The amount of data I'm able to download in the first round trip is limited to
> the amount that can fit in a single initcwnd.

Which is the exact purpose of initcwnd : send what is almost sure to be
deliverable without overflowing every router in the path. I think that's
the main motive for this.

> Head of line blocking is exacerbated by putting all objects on a single
> connection.

I absolutely agree, and this will be emphasized over lossy networks such
as WiFi. This is one point which might cause some future updates or new
recommendations (or at least practises).

> Multiple short-lived HTTP/2 connections gives us all the performance benefits
> of multiplexing without any of the operational or performance drawbacks.

Not exactly. For each side's TCP stack, multiple connections in parallel
will cause an increased usage of TCP buffers. Of course you also have the
extra cost of accepting/closing a connection and the network overhead of
the opening and closing handshakes. The smallest HTTP/1.1 exchange you
can do today over a short-lived connection is 5 packets using a piggy-back
request, a delayed ack on the server and merging the FIN with the response :

     client      SYN        server
         --------------------->
               SYN-ACK
         <---------------------
               Request
         --------------------->
             Response + FIN
         <---------------------
                 RST
         --------------------->

The smallest you can do using a persistent connection is 3 packets :

     client     Request     server
         --------------------->
               Response 
         <---------------------
                 ACK
         --------------------->

The difference is the volume of a SYN and a SYN-ACK (both of
which carry options, count around 50-60 bytes each on average).
So from a network point of view, short-lived connections have an
extra cost : +66% packets on small objects, +120 bytes.

> As a
> proxy and a browser implementor, I plan to use multiple HTTP/2 connections
> when talking to HTTP/2 servers because it seems like the right thing to do
> from a performance, security, and operational perspective.

To be honnest, I'm pretty sure that in a first time I'll have to do the
same in haproxy because of the massive complexity increase introduced
by HTTP/2. It will require a general architecture change and mixing that
with stream multiplexing at the same time is suicidal. But in the long
term, I hope that we'll get it to work reliably with muxed connections
on both sides. The issue is that all of the burden to make the protocol
efficient for both clients and servers will rely on the shoulders of
intermediary implementers like us, and that's clearly not an easy task :-(

> I know it's very late to ask this but can we remove the "SHOULD NOT"
> statement from the spec? Or, maybe soften it a little for those of us who
> cannot understand why it's there?

Note that a "SHOULD NOT" is not a "MUST NOT", it basically means "MUST NOT
unless you have no other choice". At the beginning I'll clearly be in that
situation.

What I suspect in fact is that when HTTP/2 starts to take off, we'll have
far more HTTP/1 traffic than HTTP/2, and the concurrency of HTTP/1 streams
vs HTTP/2 streams will make HTTP/2 very inefficient over congested networks,
incitating browser editors to start to open multiple connections again,
just to compete fairly with the 1.1 traffic around. I hope that the future
will prove me wrong however...

Regards,
Willy

Received on Tuesday, 24 June 2014 18:16:19 UTC