gRPC 一条连接能跑多快?HTTP/2 的流控窗口怎么限制吞吐?动态窗口是什么?多个流共享一条连接时又怎样?这篇用抓包代理人为加延迟,量窗口、RTT 和吞吐的关系。
客户端设置 吞吐 MiB/s 窗口/RTT 的理论值 默认(动态窗口,BDP 估计) 642.6 — 静态窗口 64 KiB 336.5 — 静态窗口 256 KiB 797.4 — 静态窗口 1 MiB 957.7 — 静态窗口 4 MiB 1339.0 —
发送方每发满一个窗口就要等对方放出新窗口,所以吞吐上限是窗口除以 RTT。RTT 5 ms、窗口 64 KiB 的上限是 12.5 MiB/s,实测 9.2;RTT 20 ms 降到 2.5。
客户端设置 吞吐 MiB/s 窗口/RTT 的理论值 默认(动态窗口,BDP 估计) 144.6 — 静态窗口 64 KiB 9.2 12.5 静态窗口 256 KiB 33.4 50.0 静态窗口 1 MiB 124.4 200.0 静态窗口 4 MiB 435.8 800.0
回环上 64 KiB 和 4 MiB 窗口只差 4 倍,RTT 20 ms 时差约 48 倍。跨机房、跨地域的流式传输,默认的小窗口会成为瓶颈。
客户端设置 吞吐 MiB/s 窗口/RTT 的理论值 默认(动态窗口,BDP 估计) 23.4 — 静态窗口 64 KiB 2.5 3.1 静态窗口 256 KiB 9.0 12.5 静态窗口 1 MiB 31.9 50.0 静态窗口 4 MiB 119.5 200.0
grpc-go 默认用 BDP 估计器:通过 PING 测量,逐步放大窗口(WINDOW_UPDATE 增量从 16384 慢慢变大)。代价是传输开始时窗口小,短传输来不及放大。显式设 WithInitialWindowSize 则关闭它(第 8 篇)。
吞吐 22.7 MiB/s;连接级 WINDOW_UPDATE 共 56 个,前 10 个的增量(字节):[16384 16384 16384 16383 65535 16384 32768 32767 32768 98304];客户端发出的 PING 16 个 默认(动态窗口,BDP 估计) 客户端 SETTINGS:"" 静态窗口 64 KiB 客户端 SETTINGS:"INITIAL_WINDOW_SIZE=65536" 静态窗口 1 MiB 客户端 SETTINGS:"INITIAL_WINDOW_SIZE=1048576"
HTTP/2 每个流一个窗口、整条连接一个窗口。多个流共享连接时,连接窗口过小会卡住所有流:流窗口 1 MiB、连接窗口 64 KiB 时 4 个流总共只有 9.1 MiB/s。
连接窗口 65536 字节:4 × 4 MiB 共 16 MiB,用时 1765.9 ms,总吞吐 9.1 MiB/s 连接窗口 4194304 字节:4 × 4 MiB 共 16 MiB,用时 39.7 ms,总吞吐 402.7 MiB/s
小消息只要一个往返,不受窗口影响:RTT 5 ms 下两种窗口都是约 5.8 ms/次。
默认(动态窗口,BDP 估计) 200 次 Unary 平均 5.87 ms/次 静态窗口 64 KiB 200 次 Unary 平均 5.83 ms/次
一般小消息 RPC 保持默认;大量数据的流式传输、跨高延迟链路,考虑调大窗口并留意连接级窗口(见第 36 篇压测);改了就失去动态调整。