Nginx 的 Keepalive 功能通过复用已经建立的 TCP 连接来处理多个 HTTP 请求,避免了为每个请求都重新建立连接的巨大开销,从而显著提升了每秒查询率(QPS)。
其性能提升主要源于避免了以下两个耗时的过程:
-
避免 TCP 三次握手
- 没有 Keepalive:每个 HTTP 请求都需要经历一次完整的 TCP 三次握手(SYN, SYN-ACK, ACK),这会引入至少一个网络往返时间(RTT)的延迟。
- 启用 Keepalive:在第一个请求完成后,TCP 连接会保持打开状态。后续的请求可以直接复用这个"空闲"的连接,省去了重复的握手过程。
-
避免 TLS 握手(针对 HTTPS)
- 如果服务是 HTTPS,每次新建 TCP 连接后还需要进行一次昂贵的 TLS 握手,这个过程涉及复杂的加密计算,会消耗大量的 CPU 资源。
- 启用 Keepalive 后,TLS 会话也可以被复用,极大地降低了 CPU 负载和请求延迟。
简单来说,Keepalive 将"建立连接 → 发送请求 → 关闭连接 "的模式,变成了"建立连接 → 发送多个请求 → 关闭连接",大大减少了系统资源的消耗和请求的等待时间。
🔧 Nginx Keepalive 详解与配置
Nginx 的 Keepalive 配置分为两部分:客户端到 Nginx 和 Nginx 到后端服务器(Upstream)。
1. 客户端 ↔ Nginx 的长连接
这部分控制 Nginx 如何处理来自浏览器等客户端的连接。
keepalive_timeout: 定义 Nginx 与客户端的空闲连接保持打开的时长。- 示例 :
keepalive_timeout 65;(保持65秒)
- 示例 :
keepalive_requests: 定义在一个长连接上最多可以处理多少个请求,超过后连接会被关闭。这可以防止单个连接占用资源过久。- 示例 :
keepalive_requests 1000;
- 示例 :
配置位置 : 通常放在 http 块中。
http {
# ... 其他配置 ...
keepalive_timeout 65;
keepalive_requests 1000;
}
2. Nginx ↔ 后端服务器 (Upstream) 的长连接
这部分是提升 QPS 的关键,它控制 Nginx 作为反向代理时,如何与后端的 Java、Python、Go 等应用服务器复用连接。
keepalive: 在upstream块中定义,表示 Nginx 为每个工作进程保留的、与后端服务器的空闲连接池大小。- 示例 :
keepalive 32;(建议从 16-64 开始调优)
- 示例 :
proxy_http_version: 在location块中,必须设置为1.1,因为 HTTP/1.1 默认支持长连接。- 示例 :
proxy_http_version 1.1;
- 示例 :
proxy_set_header Connection "": 这是最关键的一步!它的作用是清空请求头中的Connection字段。如果不设置,Nginx 可能会将客户端的Connection: close头传递给后端,导致后端在处理完一个请求后就关闭连接,使得keepalive配置失效。
配置位置 : upstream 块和 location 块协同配置。
# 定义后端服务器组
upstream my_backend {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
keepalive 32; # 启用长连接池,大小为32
}
server {
listen 80;
location / {
proxy_pass http://my_backend;
# 启用 upstream keepalive 的关键配置
proxy_http_version 1.1;
proxy_set_header Connection "";
# 其他代理配置...
proxy_set_header Host $host;
}
}
TCP 连接的唯一性
一个 TCP 连接是由一个唯一的"四元组"来标识的:
- 源 IP 地址 (Source IP)
- 源端口号 (Source Port)
- 目标 IP 地址 (Destination IP)
- 目标端口号 (Destination Port)
只要这四个值中任何一个不同,就代表一个全新的、独立的 TCP 连接。
当两个不同的客户端(例如,两个不同的浏览器,或者两台不同的电脑)访问同一个 Nginx 服务器时,它们的源 IP 地址必然是不同的。因此,它们与 Nginx 建立的 TCP 连接从一开始就是两个完全独立的连接,物理上就不可能复用。
一个形象的比喻
您可以将 Nginx 服务器想象成一个餐厅,而 TCP 连接就是餐桌。
- Keepalive (长连接) :指的是同一位客人(同一个客户端) 在吃完一道菜(完成一个HTTP请求)后,不必离开餐厅,可以坐在**同一张餐桌(同一个TCP连接)**上继续点下一道菜。这节省了重新找位、带位的时间。
- 不同客户端 :指的是两位不同的客人(两个不同的客户端)。他们不可能也不应该坐在同一张餐桌上吃饭。餐厅(Nginx)会为每一位新来的客人提供一张新的、独立的餐桌(建立一个新的TCP连接)。
所以,Keepalive 优化的是"一位客人"的用餐体验,而不是让"所有客人"挤在一张桌子上。
Nginx 的实际工作机制
Nginx 作为高性能服务器,其核心能力之一就是高并发处理。它会为每一个 incoming 的客户端连接创建一个独立的上下文来处理请求。
- 客户端 A 向 Nginx 发起请求,Nginx 接受后,会建立一个 TCP 连接(我们称之为
连接A)。 - 客户端 B 也向 Nginx 发起请求,Nginx 会接受并建立另一个全新的 TCP 连接(我们称之为
连接B)。 - 如果客户端 A 启用了 Keepalive,那么它在短时间内发起的第二个、第三个请求,会继续使用
连接A。 - 同样,客户端 B 的后续请求也会复用
连接B。
连接A 和 连接B 在 Nginx 内部是完全隔离的,它们的数据流、状态和生命周期都互不干扰。
Nginx 反向代理 和 Upstream Keepalive
- 客户端 → Nginx :这个阶段,连接是一对一的,不同客户端的连接绝对不能复用。
- Nginx → 后端服务器 (Upstream) :这个阶段,Nginx 会维护一个连接池 。为了高效地与后端应用服务器(如 Tomcat, Gunicorn)通信,Nginx 会预先建立一批长连接放在池子里。
- 来自客户端A 的请求,经过 Nginx 处理后,可能会通过连接池中的
连接1转发给后端。 - 紧接着,来自客户端B 的请求,也可能会通过连接池中的
连接1转发给后端。
- 来自客户端A 的请求,经过 Nginx 处理后,可能会通过连接池中的
在这种情况下,是 Nginx 在复用它与后端服务器之间的连接 ,来服务不同的客户端。但请注意,对于客户端来说,它们各自与 Nginx 的连接依然是独立的。这个复用过程对客户端是完全透明的。
| 特性 | Nginx Keepalive (长连接) |
|---|---|
| 作用层面 | 网络层 (TCP 连接) |
| 核心目的 | 复用 TCP 连接,减少网络握手开销 |
| 解决的问题 | 提升 QPS,降低网络延迟 |
| 必要性 | 必要。优化所有 HTTP 请求的基础设施。 |