前端用 HTTP/2、后端仍是 HTTP/1.1,并发和队头阻塞各自落在哪一段?流数上限怎么影响总耗时?TCP 丢包时单条连接的 HTTP/2 比多条连接的 HTTP/1.1 差在哪?这篇用 nginx 1.31.6、curl、h2 库和 dummynet 丢包量出来。
client 方式 | 60 个请求总耗时 | 状态码 | 上游同时在途的请求峰值 | nginx 向上游建立的连接数
-------------------------+-----------+-------------+-------------+----------------
(a) HTTP/1.1,1 条连接串行 | 6.46 s | {'200': 60} | 1 | 1
(b) HTTP/1.1,6 条并行连接 | 1.10 s | {'200': 60} | 6 | 6
(c) HTTP/2,1 条连接 60 个并行流 | 0.14 s | {'200': 60} | 60 | 60 HTTP/2 在前端复用一条连接,但上游是 HTTP/1.1 时每个并行的流占一条上游连接。
http2_max_concurrent_streams | client 看到的上限 | 300 个请求总耗时 | 状态码 | 上游同时在途峰值 | nginx 向上游建立的连接数
-----------------------------+--------------+------------+--------------+----------+----------------
32 | 32 | 1.10 s | {'200': 300} | 32 | 32
128 | 128 | 0.36 s | {'200': 300} | 128 | 162
512 | 512 | 0.18 s | {'200': 300} | 296 | 300
HTTP/2 的并发流数受服务端公布的上限约束:超过的流要等已有的流结束才能开始,所以总耗时大约是 ⌈300 ÷ 上限⌉ 个 0.1 s;上游是 HTTP/1.1,每个并行的流占一条上游连接,上游的峰值并发等于同时在途的流数(受上限约束)超过上限的流要排队,总耗时大约是 ⌈请求数 ÷ 上限⌉ 个单次耗时。
丢包率 | client 方式 | 100 个请求总耗时(中位数) | 3 轮范围 | 请求完成时间 p50 | p99 ----+------------------+-----------------+---------------+------------+------- 0% | HTTP/1.1,1 条连接串行 | 1.44 s | 1.32 ~ 1.47 s | 0.01 s | 0.02 s 0% | HTTP/1.1,6 条并行连接 | 0.28 s | 0.28 ~ 0.28 s | 0.02 s | 0.02 s 0% | HTTP/2,1 条连接 | 0.08 s | 0.08 ~ 0.10 s | 0.05 s | 0.05 s 3% | HTTP/1.1,1 条连接串行 | 2.44 s | 2.42 ~ 3.09 s | 0.02 s | 0.13 s 3% | HTTP/1.1,6 条并行连接 | 0.59 s | 0.52 ~ 0.60 s | 0.02 s | 0.15 s 3% | HTTP/2,1 条连接 | 0.32 s | 0.32 ~ 0.46 s | 0.06 s | 0.30 s 丢包后总耗时相对无丢包时的倍数:HTTP/1.1,1 条连接串行 1.7 倍;HTTP/1.1,6 条并行连接 2.1 倍;HTTP/2,1 条连接 4.0 倍。HTTP/2 的所有流共用一条 TCP 连接,一个包丢了,这条连接上所有流的后续数据都要等重传(TCP 层的队头阻塞);HTTP/1.1 的多条连接各自独立。看倍数而不是绝对值:这个实验里 HTTP/2 在有丢包时的绝对耗时仍然最短,但它相对无丢包时变慢得更多
一条 TCP 连接上的所有流共享重传;绝对耗时仍短,但相对退化更多。