第49题:HTTP/2 如何缓解 HTTP/1.1 阻塞;长连接对 Nginx 有什么影响?
1. 来源边界
原表直接给出的核心结论是:
- HTTP/2 在单连接上支持多 Stream 多路复用;
- 使用 Binary Framing;
- 使用 Header Compression;
- 能缓解 HTTP/1.1 的应用层 Head-of-Line Blocking;
- TCP 丢包仍然会影响同一个 HTTP/2 TCP 连接上的多个 Stream;
- 大量长连接会占用 Nginx Connection、文件描述符和内存;
- 需要关注
worker_connections; - 需要调 Keep-Alive / Idle Timeout;
- 需要关注 Upstream Connection Pool;
- 需要监控活跃连接、事件循环延迟和连接复用情况。
2. 核心回答
HTTP/1.1 的主要问题在于:同一个 TCP 连接上,请求和响应缺少 HTTP/2 那样的独立 Stream 标识。
HTTP/1.1 虽然支持 Pipelining:
text
Request A
Request B
Request C
可以连续发送,但服务端仍必须按照:
text
Response A
Response B
Response C
的顺序返回。
如果:
text
A = 慢请求
那么:
text
B / C
即使已经处理完成,也可能受到前一个响应影响。
这属于:
Application-layer Head-of-Line Blocking。
HTTP/2 将每个请求/响应映射成独立 Stream:
text
TCP Connection
├── Stream 1
├── Stream 3
├── Stream 5
└── Stream 7
不同 Stream 的 Frame 可以交错发送:
text
S1 DATA
S3 DATA
S1 DATA
S5 DATA
S3 DATA
因此某一个应用层请求慢下来时,其他 Stream 仍然可以继续传输。
但 HTTP/2 底层仍运行在:
text
TCP
之上。
一旦 TCP 某个 Segment 丢失,TCP 需要按照可靠、有序字节流语义等待丢失数据重传,因此同一连接上的多个 HTTP/2 Stream 都可能受到影响。
所以可以总结成:
text
HTTP/2:
缓解 HTTP 应用层 HOL
TCP:
仍存在 Transport-layer HOL
3. HTTP/1.1 为什么会有 Head-of-Line Blocking
HTTP/1.1 的一个核心限制是:
同一个连接里的 Response 与 Request 主要依赖顺序关联。
例如:
text
Client:
GET /slow
GET /small-a
GET /small-b
假设服务器处理时间:
text
/slow → 5 s
/small-a → 10 ms
/small-b → 10 ms
即使:
text
small-a
small-b
已经计算完成,Pipelining 下 Response 仍需要保持请求顺序。
所以:
text
slow
↓
small-a
↓
small-b
产生队头阻塞。
4. HTTP/1.1 为什么常用多个 TCP 连接
为了绕开单连接上的阻塞,HTTP/1.x Client 通常会:
text
TCP Connection 1 → Request A
TCP Connection 2 → Request B
TCP Connection 3 → Request C
...
这样能够增加并发。
代价是:
- 更多 TCP Handshake;
- 更多 TLS Handshake;
- 更多 Socket;
- 更多 FD;
- 更多 Server Connection State;
- 多条连接分别进行拥塞控制。
因此:
Connections↑ Connections\uparrow Connections↑
虽然提高并发,但 Server 和 Network Resource 也会上升。
5. HTTP/2 怎么解决这一层问题
HTTP/2 引入:
Stream。
每个请求/响应属于一个:
stream_id stream\_id stream_id
例如:
text
Stream 1:
GET /slow
Stream 3:
GET /a
Stream 5:
GET /b
它们共享同一个:
text
TCP Connection
但逻辑上相互独立。
所以可以:
text
Stream 1 HEADERS
Stream 3 HEADERS
Stream 5 HEADERS
Stream 3 DATA
Stream 5 DATA
Stream 1 DATA
...
这就是:
Multiplexing。
6. Multiplexing 为什么能够降低阻塞
HTTP/1.1 Pipelining 更接近:
text
Request A
Request B
Request C
↓
Response A
Response B
Response C
HTTP/2 则可以:
text
A1
B1
C1
B2
A2
C2
其中:
text
A1/A2
属于 Stream A。
text
B1/B2
属于 Stream B。
所以:
Slow(StreamA) Slow(Stream_A) Slow(StreamA)
不会在 HTTP 应用层直接要求:
StreamB Stream_B StreamB
等待 A 的完整 Response 发送结束。
RFC 9113 直接将这一能力描述为:
text
interleaving messages on the same connection
7. Binary Framing 是什么
HTTP/2 的基本协议单位是:
Frame。
常见 Frame 包括:
text
HEADERS
DATA
SETTINGS
WINDOW_UPDATE
RST_STREAM
GOAWAY
一个 HTTP Message 会被拆成多个 Frame。
例如:
text
HTTP Response
↓
HEADERS Frame
DATA Frame
DATA Frame
每个 Frame 带有:
text
Stream Identifier
因此接收端可以判断:
这个 Frame 属于哪个请求。
这为 Multiplexing 提供了基础。
8. 为什么 Binary Framing 也有价值
HTTP/1.x 的协议组织高度依赖文本行、Header 边界等规则。
HTTP/2 使用二进制 Frame 后:
- Frame 类型明确;
- Length 明确;
- Stream ID 明确;
- 协议解析更加结构化;
- 多 Stream Frame 可以统一调度。
Binary Framing 本身不能消除 TCP HOL。
它主要提供更适合 Multiplexing 的传输结构。
9. HTTP/2 Header Compression 是什么
HTTP 请求经常携带大量重复 Header:
text
Host
Cookie
User-Agent
Accept
Authorization
...
如果一个页面产生:
N N N
个请求,这些 Header 会重复传输。
HTTP/2 使用:
HPACK
压缩 Header Field。
可以通过:
- 静态表;
- 动态表;
- 索引表示;
- Huffman Coding;
降低重复 Header 的传输量。
因此:
HeaderBytes↓ HeaderBytes\downarrow HeaderBytes↓
尤其对大量小请求比较有价值。
10. HTTP/2 是否彻底解决了 Head-of-Line Blocking
没有彻底消除。
需要区分两个层次。
10.1 HTTP 应用层
HTTP/2 Stream Multiplexing 可以缓解:
text
HTTP/1.1 Response Ordering
导致的 HOL。
10.2 TCP 传输层
HTTP/2 仍建立在一条可靠、有序 TCP Byte Stream 上。
假设 TCP 数据:
text
Packet 1
Packet 2 ← lost
Packet 3
Packet 4
接收端即使已经收到:
text
Packet 3
Packet 4
TCP 仍需要等待:
text
Packet 2 retransmission
以后才能向上层连续交付后续字节。
11. TCP 丢包为什么会影响多个 HTTP/2 Stream
假设:
text
Packet 1 → Stream A
Packet 2 → Stream B ← Lost
Packet 3 → Stream C
Packet 4 → Stream A
从 HTTP/2 视角:
text
A / B / C
是不同 Stream。
从 TCP 视角:
text
全部是一条有序 Byte Stream。
因此 Packet 2 丢失后,Packet 3 和 Packet 4 对上层的交付会受到 TCP 有序语义影响。
所以:
text
一个 TCP Connection
+
很多 HTTP/2 Streams
意味着网络丢包可能同时影响很多 Stream。
12. HTTP/3 为什么经常被拿来追问
如果面试官继续追问:
那怎么解决 TCP HOL?
可以回答:
HTTP/3 使用:
text
QUIC
运行于 UDP 之上,并在 QUIC 内部实现多个独立 Stream。
因此:
text
Stream A
的数据丢失时,通常不会因为 TCP 的全连接字节流顺序要求阻塞:
text
Stream B
Stream C
已经完整收到的数据。
所以:
text
HTTP/2:
解决 HTTP 层 HOL
HTTP/3 / QUIC:
进一步缓解跨 Stream 的传输层 HOL
这个回答作为扩展即可。
13. HTTP/2 是不是连接越少越好
也不能绝对理解。
HTTP/2 使用较少 TCP Connection 可以:
- 减少 Handshake;
- 减少 Socket;
- 提高连接复用;
- 减少多连接间拥塞竞争。
但单 TCP Connection 也意味着:
text
Network Loss
的 Failure Domain 更集中。
RFC 9113 也明确说明:
HTTP/2 的实际性能还依赖:
- Flow Control;
- Prioritization;
- Stream Scheduling;
- TCP 状态;
- Network Condition。
所以:
HTTP/2 HTTP/2 HTTP/2
并不能保证在所有网络场景中都一定比 HTTP/1.1 快。
14. HTTP/2 还有 Flow Control
HTTP/2 同时具有:
text
Per-Stream Flow Control
和:
text
Connection-level Flow Control
主要通过:
text
WINDOW_UPDATE
实现。
接收端告诉发送端:
当前还允许发送多少 DATA。
如果 Flow Control Window 配置或实现不合理,也可能造成 Stream 进度受限。
因此性能问题不能全部归因于:
text
HOL
还需要检查:
- Flow Control;
- Backend Latency;
- Stream Scheduling;
- Packet Loss;
- Congestion。
15. 接下来为什么会问 Nginx 长连接
HTTP/2 的一个重要特点是:
text
一条连接持续时间更长
+
同连接承载更多请求
而实际生产环境中 Nginx 经常位于:
text
Client
↓
Nginx
↓
Upstream
因此需要考虑:
text
Client Long Connection
和:
text
Upstream Persistent Connection
两种资源。
16. 长连接会不会一个连接占一个 Nginx 线程
Nginx 采用:
Event-driven + Non-blocking I/O
模型。
一个 Worker 可以处理大量 Socket Event。
因此空闲长连接不会对应:
text
1 Connection = 1 Thread
这种资源模型。
但每一个连接仍然会持续占用资源,包括:
- Nginx Connection Structure;
- File Descriptor;
- Kernel Socket;
- Socket Buffer;
- 一定的 Worker Memory;
- TLS Connection State;
- Timer;
- HTTP/2 State。
所以:
text
Event Driven
降低了线程成本。
它不会把每连接成本降到零。
17. Nginx 的 worker_connections 是什么
Nginx 官方定义:
text
worker_connections
表示:
每个 Worker 能同时打开的最大连接数量。
例如:
nginx
worker_processes 4;
events {
worker_connections 4096;
}
粗略来看理论 Connection Slot 数量是:
$$
4\times4096
16384
但这个数字不能直接理解成: ```text 最多 16384 个客户端。 ``` *** ** * ** *** ### 18. 为什么 worker_connections 不能直接等于客户端数 因为 Nginx 官方明确指出: `worker_connections` 包含: * Client Connection; * Proxied Server Connection; * 其他连接。 例如一次典型反向代理: ```text Client ↓ connection 1 Nginx ↓ connection 2 Backend ``` 在请求活跃期间,一个请求可能同时需要: ```text Client-side Connection + Upstream Connection ``` 所以粗略容量关系可以写成: ##
C_{\text{worker}}
C_{\text{client}}
C_{\text{upstream}}
C_{\text{other}}
*** ** * ** *** ### 19. 一个简单容量估算 假设: ```text worker_processes = 8 worker_connections = 8192 ``` 理论 Connection Slot: ##
8\times8192
65536
如果业务主要是反向代理,并且高峰期大多数 Client 同时需要一个 Upstream Connection,可以粗略考虑: Cclient+Cupstream≈2Cactive request C_{\\text{client}} + C_{\\text{upstream}} \\approx 2C_{\\text{active request}} Cclient+Cupstream≈2Cactive request 因此真正能够承载的活跃 Client 数会明显低于: 65536 65536 65536 。 同时还需要预留: * Idle Keep-Alive; * Monitoring; * Internal Connection; * Graceful Reload 期间旧 Worker 的连接; * Upstream Pool。 所以最终容量必须通过压测确定。 *** ** * ** *** ### 20. File Descriptor 为什么很重要 Socket 在 Linux 中对应: **File Descriptor。** 所以: Connections↑⇒FD↑ Connections\\uparrow \\Rightarrow FD\\uparrow Connections↑⇒FD↑ 即使设置: ```nginx worker_connections 100000; ``` 如果 OS 对 Worker 的: ```text RLIMIT_NOFILE ``` 只有: ```text 1024 ``` Nginx 也不可能真正打开 100000 个 Socket。 Nginx 提供: ```nginx worker_rlimit_nofile ... ``` 用于调整 Worker 的最大 Open File Limit。 实际还需要检查系统层: ```text ulimit -n ``` 等限制。 *** ** * ** *** ### 21. 长连接对内存有什么影响 每一个连接都至少会产生: ```text Connection State Socket State Timers Protocol State ``` 如果启用 TLS,还会增加: ```text TLS Session State Buffers ``` HTTP/2 连接还要维护: * Stream State; * HPACK State; * Flow-control Window; * Frame Processing State。 因此可以粗略理解: Memory≈C×Mper connection+Mrequest+Mcache Memory \\approx C \\times M_{\\text{per connection}} + M_{\\text{request}} + M_{\\text{cache}} Memory≈C×Mper connection+Mrequest+Mcache 其中: C C C 是并发连接数。 实际每连接占用需要通过目标配置和压测测量。 *** ** * ** *** ### 22. Idle Keep-Alive 为什么也会消耗容量 假设用户请求已经完成。 TCP Connection 保持: ```text OPEN ``` 等待下一次请求。 此时虽然 CPU 消耗很低,但仍占用: ```text FD Connection Slot Socket Memory Timer ``` Nginx 的 `stub_status` 中: ```text Waiting ``` 就是当前等待下一请求的空闲 Client Connection。 因此出现: ```text Waiting = 100000 ``` 说明大量连接正在空闲等待。 *** ** * ** *** ### 23. keepalive_timeout 控制什么 客户端侧: ```nginx keepalive_timeout 75s; ``` 表示空闲 Keep-Alive Client Connection 在 Server 端可以保持多久。 如果 Timeout 太长: ```text Idle Connections ↑ FD ↑ Connection Slots ↑ Memory ↑ ``` 如果 Timeout 太短: ```text Connection Reuse ↓ TCP Handshake ↑ TLS Handshake ↑ Latency ↑ ``` 因此它是: ResourceUsage↔ConnectionReuse ResourceUsage \\leftrightarrow ConnectionReuse ResourceUsage↔ConnectionReuse 之间的权衡。 *** ** * ** *** ### 24. keepalive_requests 为什么也重要 Nginx 允许限制: ```text 一个 Keep-Alive Connection 最多处理多少 Request。 ``` 当前官方默认: ```text keepalive_requests 1000 ``` 原因之一是: 长期不关闭 Connection 会使某些 Per-connection Memory Allocation 更晚释放。 所以不能简单配置: ```text keepalive_requests 无限大 ``` 希望最大化复用。 需要同时考虑内存释放。 *** ** * ** *** ### 25. keepalive_time 和 keepalive_timeout 有什么区别 可以简单理解: #### 25.1 keepalive_timeout 关注: **Idle 多久以后关闭。** ```text 没有新Request ↓ 等待 timeout ↓ Close ``` #### 25.2 keepalive_time 关注: **这个 Connection 最长允许持续使用多久。** 即使一直有 Request: ```text Request Request Request ... ``` 达到最大 Lifetime 后,也会在合适时机关闭。 因此两个参数控制的是不同维度。 *** ** * ** *** ### 26. Upstream Keep-Alive 又是什么 架构: ```text Client ↓ Nginx ↓ Application Server ``` Nginx 也可以复用: ```text Nginx ↔ Backend ``` 之间的 TCP Connection。 否则每个请求都: ```text connect() ↓ request ↓ response ↓ close() ``` 会不断产生: * TCP Handshake; * TLS Handshake; * Ephemeral Port; * Backend Accept Cost。 Upstream Keep-Alive 可以降低这些开销。 *** ** * ** *** ### 27. Upstream Keep-Alive Pool 需要注意什么 Nginx 的: ```nginx keepalive N; ``` 在 Upstream Context 中控制: **每个 Worker 最多缓存多少条空闲 Upstream Keep-Alive Connection。** 一个非常重要的面试点: ```text keepalive 32 ``` 并不代表: > Nginx 到 Backend 最多只有 32 条连接。 它限制的是: ```text Idle Cached Connections ``` 总活跃 Upstream Connection 仍可能更高。 如果需要限制 Backend 同时处理的 Active Connection,需要结合: ```text max_conns ``` 等机制。 *** ** * ** *** ### 28. 2026 年 Nginx Upstream Keep-Alive 有一个版本变化 截至 2026 年: **Nginx 1.29.7 起 HTTP Upstream Keep-Alive 已默认启用。** 官方文档当前默认缓存: ```text 32 ``` 条空闲 Upstream Connection / Worker。 同时 HTTP Proxy 默认已经使用: ```text HTTP/1.1 ``` 因此很多旧教程中的: ```nginx proxy_http_version 1.1; proxy_set_header Connection ""; ``` 对于当前版本已经不再是普遍必需配置。 面试中如果涉及具体版本,应先确认实际部署版本。 *** ** * ** *** ### 29. Upstream Pool 太小会怎样 如果并发很高,Keep-Alive Cache 太小: ```text 大量Backend Connection不能复用 ``` 会增加: * Connect Rate; * TCP Handshake; * TLS Handshake; * Backend Accept; * TIME_WAIT; * Latency。 表现可能是: ```text Connection Reuse Rate ↓ Backend Connect Latency ↑ ``` *** ** * ** *** ### 30. Upstream Pool 太大又会怎样 如果配置很多 Worker: ```text worker_processes = 16 ``` 并且每个 Worker 缓存: ```text keepalive = 1000 ``` 理论空闲 Upstream Connection Cache 可能很大。 Backend 端会看到大量: ```text Idle Connections ``` 持续消耗: * Backend FD; * Memory; * Socket State。 所以 Upstream Pool 应根据: * Worker 数; * Backend 数; * 并发; * Backend Connection Capacity; * Connection Reuse Rate; 共同调整。 *** ** * ** *** ### 31. 长连接会怎样影响 Nginx Reload Nginx Graceful Reload 时: ```text Old Workers → 不再接受新连接 → 等待已有请求/连接退出 New Workers → 开始处理新流量 ``` 长生命周期 Connection 会让旧 Worker 存活更久。 因此生产环境还需要考虑: ```text worker_shutdown_timeout ``` 等 Graceful Shutdown 策略。 这在 WebSocket、SSE、长时间 HTTP/2 Connection 等场景尤其重要。 *** ** * ** *** ### 32. WebSocket/SSE 和普通 Keep-Alive 要区分 "长连接"在面试里可能有不同含义。 #### 32.1 HTTP Keep-Alive 请求完成后连接保持空闲,等待下一请求。 #### 32.2 HTTP/2 Connection 一条 Connection 上长期承载多个 Stream。 #### 32.3 WebSocket 连接建立后持续双向通信。 #### 32.4 SSE Server 长时间保持 Response 并不断发送事件。 这些场景对: * Active Connection; * Idle Timeout; * Buffer; * Upstream; * Graceful Shutdown; 影响不同。 面试时最好先说明自己讨论的是哪一种。 *** ** * ** *** ### 33. 怎么监控 Nginx 长连接 开源 Nginx 的 `stub_status` 可以提供: ```text Active connections Reading Writing Waiting accepts handled requests ``` 其中: #### 33.1 Active Connections 当前 Client Connection 总量,包括 Waiting。 #### 33.2 Reading 当前正在读取 Request Header 的连接。 #### 33.3 Writing 当前正在向 Client 返回 Response 的连接。 #### 33.4 Waiting 当前等待下一 Request 的 Idle Keep-Alive Client Connection。 如果: ```text Waiting / Active ``` 长期非常高,可以检查 Keep-Alive 策略是否过于激进。 *** ** * ** *** ### 34. 为什么 accepts 和 handled 也值得看 Nginx 官方指出: 正常情况下: ```text accepts ≈ handled ``` 如果: ```text handled < accepts ``` 可能表示出现资源限制。 例如: ```text worker_connections ``` 达到上限。 所以容量压测时可以同时观察: accepts−handled accepts-handled accepts−handled 。 *** ** * ** *** ### 35. 除连接数之外还需要看哪些指标 我会同时观察: ```text Active Connections Idle / Waiting Connections FD Usage Worker Memory CPU Network Throughput TLS Handshake Rate Upstream Active Connections Upstream Keepalive Connections Backend Connect Time Request Latency P95 / P99 5xx Connection Reset Timeout ``` 如果系统支持进一步的 Event Loop 指标,也可以监控: ```text Event-loop Delay / Stall ``` 但这一项需要具体监控实现提供,不能仅靠 `stub_status` 得到。 *** ** * ** *** ### 36. 一个常见容量估算思路 假设: ```text 100000 Client Connections ``` 其中: ```text 90000 Idle 10000 Active ``` 假设 Active Request 基本都需要 Backend: ```text 10000 Upstream Connections ``` 粗略 Connection 数量已经达到: 100000+10000=110000 100000+10000=110000 100000+10000=110000 还没有计算其他内部资源。 如果有: ```text worker_processes=8 ``` 平均每 Worker: ##
110000/8
13750
$$
个 Connection。
那么:
text
worker_connections = 4096
显然不足。
这只是容量估算。
最终仍然需要真实压测确认 Worker 分布、Connection Reuse 和 Memory。
37. 怎么做压测验证
我不会只改:
nginx
worker_connections 100000;
然后认为系统支持 10 万连接。
会做分级压测。
37.1 Connection Ramp
例如:
text
1k
5k
10k
20k
50k
100k
逐级增加。
37.2 每一级观察
记录:
- Connection Success Rate;
- P50/P95/P99;
- FD;
- Memory;
- CPU;
- Waiting;
- Backend Connections;
- Error Rate。
37.3 找 Saturation Point
例如当:
text
50k → 60k
时:
text
Latency suddenly rises
handled < accepts
5xx increases
说明系统正在接近资源瓶颈。
38. 一个很重要的反例:HTTP/2 一条 TCP 连接可以有很多请求
所以看到:
text
HTTP/2 Client Connections = 1000
不能推出:
text
Concurrent Requests = 1000
一条 Connection 可以存在多个 Concurrent Stream。
容量模型要区分:
text
Physical TCP Connections
和:
text
Concurrent HTTP Requests / Streams
这也是 HTTP/2 与 HTTP/1.1 容量分析的重要区别。
39. limit_conn 在 HTTP/2 下也要注意语义
Nginx 的 limit_conn 官方文档特别说明:
在 HTTP/2 / HTTP/3 中,每个并发 Request 会被这个模块视作一个独立的 Connection 进行计数。
因此:
text
limit_conn
的业务限制语义不能简单等同于:
text
真实TCP连接数量。
做 HTTP/2 限流时需要确认使用的指标到底是:
- Physical Connection;
- Concurrent Stream;
- Request;
- User;
- IP。
40. 面试官如果问"HTTP/2 为什么通常更快"
可以回答:
HTTP/2 的核心提升之一是 Multiplexing。HTTP/1.1 Pipelining 要求响应按照请求顺序返回,所以前面的慢响应会产生应用层 Head-of-Line Blocking。HTTP/2 给每个请求建立独立 Stream,再把 HEADERS、DATA 等 Frame 在同一个 TCP Connection 上交错传输,所以慢 Stream 不会在 HTTP 层直接卡住其他 Stream。
同时 HTTP/2 使用 Binary Framing 和 HPACK Header Compression,可以降低协议和重复 Header 开销,也减少了 HTTP/1.x 为并发而建立多条 TCP Connection 的需求。
但 HTTP/2 仍运行在 TCP 上,所以 TCP Segment 丢失时,同一 TCP Connection 上的多个 Stream 仍可能受到 Transport-layer HOL 影响。
41. 面试官如果问"长连接是不是越多越好"
可以回答:
长连接可以降低 TCP/TLS Handshake 和建连开销,提高连接复用率,但它会长期占用 Nginx Connection Slot、FD、Socket 和一定内存,所以需要在复用收益和资源占用之间平衡。
Nginx 使用 Event-driven Non-blocking I/O,一个空闲长连接不会对应一个独立线程,因此它非常适合高并发连接。但
worker_connections仍限制每个 Worker 能打开的总连接数,而且这里还包括到 Upstream 的连接,同时受RLIMIT_NOFILE约束。我会结合
keepalive_timeout、keepalive_requests、Upstream Keepalive Pool、Worker Connection 和 FD Limit 调优,并通过 Active/Waiting Connection、FD、内存、P99、Backend Connect Time 和 Connection Reuse 等指标进行压测验证。
42. 面试时可以压缩成下面这段
HTTP/1.1 Pipelining 虽然允许在同一 TCP Connection 连续发送多个请求,但响应仍然必须按请求顺序返回,所以前面的慢请求会造成应用层 Head-of-Line Blocking。
HTTP/2 为每个 Request/Response 建立独立 Stream,并使用 Binary Frame。不同 Stream 的 Frame 可以在同一 TCP Connection 上交错发送,因此一个慢 Stream 不会在 HTTP 层阻止其他 Stream 继续传输。同时 HTTP/2 使用 HPACK 压缩重复 Header,可以降低协议开销,也减少了 HTTP/1.x 为并发而建立大量 TCP Connection 的需求。
但是 HTTP/2 底层仍然是 TCP。TCP 提供可靠、有序的字节流,所以一个 Segment 丢失后,后续数据需要等待重传,同一个 HTTP/2 Connection 上的多个 Stream 仍可能受到 TCP 层 Head-of-Line Blocking 影响。
长连接对 Nginx 的影响主要是 Connection Slot、File Descriptor、Socket/TLS State 和 Memory。Nginx 本身是 Event-driven Non-blocking 架构,所以一条空闲连接不会占一个线程,但资源占用仍然存在。worker_connections 是每个 Worker 的总连接上限,而且 Client 和 Upstream Connection 都会占这个额度,实际还受 RLIMIT_NOFILE 限制。
调优时我会关注 worker_connections、FD Limit、keepalive_timeout、keepalive_requests 和 Upstream Keepalive Pool,同时监控 Active、Waiting、FD、Memory、P95/P99、Backend Connect Time 和 Connection Reuse Rate。
一句话总结:
HTTP/2 用 Stream Multiplexing 缓解 HTTP 应用层队头阻塞,但 TCP 层 HOL 仍存在;Nginx 的事件驱动模型适合长连接,但长连接仍会持续消耗 Connection、FD 和内存,因此必须做容量规划、超时控制和连接池调优。
43. 当前资料能够确定到什么程度
43.1 原表直接支持
原表明确支持:
- HTTP/2 单连接多路复用;
- Binary Framing;
- Header Compression;
- 缓解 HTTP/1.1 应用层 HOL;
- TCP 丢包仍影响同连接多个 Stream;
- 长连接占用 Nginx Connection;
- 长连接占用 FD;
- 长连接占用内存;
- 调整
worker_connections; - 调整 Keep-Alive / Idle Timeout;
- 调整 Upstream Connection Pool;
- 监控 Active Connections;
- 监控 Event-loop Delay;
- 监控 Connection Reuse Rate。
43.2 外部资料补充
本次外部核验进一步补充:
- HTTP/1.1 Pipelining 的响应顺序要求;
- HTTP/2 Stream 和 Frame 的正式定义;
- HPACK;
- HTTP/2 Flow Control;
worker_connections同时计算 Client 和 Upstream;RLIMIT_NOFILE;- Nginx Event-driven Worker 模型;
keepalive_requests;keepalive_time;keepalive_timeout;- Upstream Keepalive Cache 的确切语义;
- Nginx 1.29.7 的 Upstream Keep-Alive 默认行为变化;
stub_status的 Active / Reading / Writing / Waiting;- HTTP/2 下
limit_conn的特殊计数语义。
44. 来源
- 原始题目表,第49题(T5):HTTP/2 如何缓解 HTTP/1.1 阻塞;长连接对 Nginx 影响。
- RFC 9112 --- HTTP/1.1:定义 HTTP/1.1 Pipelining 和响应顺序规则,并讨论 Head-of-Line Blocking 与多连接。
- RFC 9113 --- HTTP/2:定义 Stream、Multiplexing、Binary Framing 和 Flow Control,并明确 TCP Head-of-Line Blocking 仍然存在。
- RFC 7541 --- HPACK: Header Compression for HTTP/2:定义 HTTP/2 Header Compression。
- Nginx Core Documentation ---
worker_connections/worker_rlimit_nofile:定义单 Worker 连接上限,并明确 Upstream Connection 同样计入。 - Nginx HTTP Core Module ---
keepalive_requests/keepalive_time/keepalive_timeout:定义客户端持久连接生命周期和资源释放策略。 - Nginx Upstream Module ---
keepalive/max_conns:定义 Upstream Idle Keepalive Cache 与 Active Connection Limit 的区别。 - Nginx
stub_statusModule:定义 Active、Reading、Writing、Waiting 等 Connection 指标。 - Nginx Architecture Documentation:说明 Worker 使用 Non-blocking Event-driven 模型处理大量连接。