HTTP/2 如何缓解 HTTP/1.1 阻塞;长连接对 Nginx 有什么影响?

第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_timeoutkeepalive_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_timeoutkeepalive_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. 来源

  1. 原始题目表,第49题(T5):HTTP/2 如何缓解 HTTP/1.1 阻塞;长连接对 Nginx 影响。
  2. RFC 9112 --- HTTP/1.1:定义 HTTP/1.1 Pipelining 和响应顺序规则,并讨论 Head-of-Line Blocking 与多连接。
  3. RFC 9113 --- HTTP/2:定义 Stream、Multiplexing、Binary Framing 和 Flow Control,并明确 TCP Head-of-Line Blocking 仍然存在。
  4. RFC 7541 --- HPACK: Header Compression for HTTP/2:定义 HTTP/2 Header Compression。
  5. Nginx Core Documentation --- worker_connections / worker_rlimit_nofile:定义单 Worker 连接上限,并明确 Upstream Connection 同样计入。
  6. Nginx HTTP Core Module --- keepalive_requests / keepalive_time / keepalive_timeout:定义客户端持久连接生命周期和资源释放策略。
  7. Nginx Upstream Module --- keepalive / max_conns:定义 Upstream Idle Keepalive Cache 与 Active Connection Limit 的区别。
  8. Nginx stub_status Module:定义 Active、Reading、Writing、Waiting 等 Connection 指标。
  9. Nginx Architecture Documentation:说明 Worker 使用 Non-blocking Event-driven 模型处理大量连接。
相关推荐
我的小卷呀5 小时前
Linux ip 命令底层逻辑与实战指南
linux·运维·c语言·网络协议·tcp/ip·网络安全
Soonyang Zhang6 小时前
使用wintun构建tcp中转服务
网络·网络协议·tcp/ip
木辰風7 小时前
启动了不安全的HTTP方法(漏洞、springmvc)
网络·网络协议·http
ITxiaobing20238 小时前
技术选型参考:从IPinfo出发,聊聊IP情报服务的替代方案与选型逻辑
网络·网络协议·tcp/ip
z_mazin8 小时前
逆向一种私有二进制序列化格式:从零到字节级解析器
网络·python·网络协议·算法·rpc
Jeremy_WW9 小时前
QSFP/QSFP-DD/OSFP 通用管理接口规范(CMIS)解读:11 Page 13h IV
网络·网络协议·信息与通信·光模块·cmis
Arms20610 小时前
https配置备忘
网络协议·http·https
比兔代理10 小时前
游戏加速、直播拉流、视频下载场景下的代理IP技术方案
网络·http·游戏
鱼很腾apoc11 小时前
【Linux】第15期 讲解IP网络层核心原理与机制
linux·服务器·网络协议·学习·tcp/ip·ip·网络层