Nginx与Linux内核性能调优实战
在一次部署中,我们使用了两个Nginx实例,一个运行在CentOS 7.6,另一个运行在Windows Server 2008 R2,用于进行流量转发。流量拓扑如下:
随着业务量的急剧增长,转发服务开始出现大量的502错误,同时Nginx也报告了大量的error: 10054 An existing connection was forcibly closed by the remote host错误。在阿里云监控中,我们也观察到流量和TCP连接数陡增,并且出现了大量的TIME_WAIT状态的连接。经过深入排查,我们采取了一系列优化措施。
一、Nginx配置优化
首先,我们针对Nginx报错的10054错误,查找相关的解决方案。当Nginx作为反向代理时,为了支持长连接,需要确保客户端到Nginx以及Nginx到后端服务器的连接都尽可能保持长连接。
1. 保持客户端到Nginx的长连接
通过调整Nginx的keepalive_timeout和keepalive_requests参数来优化:
http {
keepalive_timeout 120s;
keepalive_requests 1000;
}
keepalive_timeout: 指定客户端连接在服务器端保持开启的超时时间(默认75秒)。设置为0则禁用keep-alive客户端连接。该参数主要控制浏览器和Nginx之间TCP通道的空闲等待时间。keepalive_requests: 指定一个keep-alive连接上可服务的最大请求数。达到此数量后,连接会被关闭,迫使客户端重新建立连接。默认值为100。
2. 保持Nginx到后端服务器的长连接
默认情况下,Nginx与后端服务器之间使用HTTP/1.0的短连接。为了启用长连接,需要进行如下配置:
location / {
proxy_http_version 1.1;
proxy_set_header Connection "keep-alive";
proxy_pass http://your_backend_server;
}
proxy_http_version 1.1: 启用HTTP/1.1协议,这是支持长连接的基础。proxy_set_header Connection "keep-alive": 显式地向后端服务器发送Keep-Alive头部。
3. Nginx的keepalive指令
keepalive指令用于设置到上游服务器的空闲keep-alive连接的最大数量。当超过此数量时,最近最少使用的连接会被关闭。该指令不限制一个worker进程与上游服务器建立连接的总数。
注意:keepalive参数的设置需要谨慎,尤其在高QPS场景下。可以根据QPS和平均响应时间估算所需的连接数,然后将keepalive设置为该数量的10%-30%。
4. TIME_WAIT状态的产生原因与Nginx配置关系
- Nginx端出现大量TIME_WAIT:
keepalive_requests设置过小,导致高并发下Nginx主动关闭与客户端的连接。keepalive设置过小,导致Nginx在高并发下频繁关闭与后端服务器的连接。- 后端服务器端出现大量TIME_WAIT:
- Nginx未启用与后端的长连接(未设置
proxy_http_version 1.1和proxy_set_header Connection "keep-alive"),导致后端每次关闭连接。
二、操作系统层面调优
1. Windows系统TIME_WAIT连接优化
问题:Windows Server默认动态端口范围较小(16384个),且TCP默认TIME_WAIT时长较长(4分钟),在高并发连接下容易导致端口耗尽。
解决方案:
- 查看当前动态端口配置:
netsh interface ipv4 show dynamicport tcp - 增大动态端口范围:
netsh int ipv4 set dynamicport tcp start=15000 num=50000(无需重启) - (可选)降低TIME_WAIT时间:修改注册表
HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters,新增TcpTimedWaitDelay(REG_DWORD),设置为30(秒)。
2. Linux系统TIME_WAIT连接优化
TCP连接状态详解:
- 三次握手 (建立连接): SYN -> SYN/ACK -> ACK
- 四次挥手 (终止连接): FIN -> ACK -> FIN -> ACK (TCP连接是全双工的,每个方向的关闭都需要单独进行)
- 常见状态:
LISTEN: 服务器等待连接。SYN_RCVD: 服务器收到SYN,等待ACK。SYN_SENT: 客户端发送SYN,等待SYN/ACK。ESTABLISHED: 连接已建立。FIN_WAIT_1: 主动关闭方发送FIN,等待对方ACK。FIN_WAIT_2: 主动关闭方收到ACK,等待对方FIN。TIME_WAIT: 收到对方FIN,发送ACK,等待2MSL后返回CLOSED。这是为了确保对方收到了最后的ACK,并允许重发丢失的ACK。MSL是TCP报文在网络上存在的最长时间(通常为30秒-2分钟)。CLOSE_WAIT: 被动关闭方收到FIN,发送ACK,等待应用程序关闭连接。LAST_ACK: 被动关闭方发送FIN,等待对方最后的ACK。
检查当前连接状态:
netstat -ano|awk -F " " '{print $6}'|sort |uniq -c |sort -r -n
通过调整内核参数解决大量TIME_WAIT问题:
编辑/etc/sysctl.conf文件,添加或修改以下参数:
# 开启SYN Cookies,防止SYN Flood攻击
net.ipv4.tcp_syncookies = 1
# 允许TIME-WAIT sockets重用
net.ipv4.tcp_tw_reuse = 1
# 开启TIME-WAIT sockets的快速回收(谨慎使用,可能导致问题)
# net.ipv4.tcp_tw_recycle = 1
# TIME-WAIT状态的连接保持时间(秒)
net.ipv4.tcp_fin_timeout = 30
# IP本地端口范围,用于向外连接
net.ipv4.ip_local_port_range = 1024 65535
# SYN队列长度
net.ipv4.tcp_max_syn_backlog = 8192
# 系统同时保持TIME_WAIT套接字的最大数量
net.ipv4.tcp_max_tw_buckets = 5000
运行sysctl -p使配置生效。
三、阿里云 Linux 内核优化实战
对于阿里云ECS实例,可以进一步优化内核参数。
1. sysctl.conf 优化
编辑/etc/sysctl.conf或在/etc/sysctl.d/目录下创建新文件,并添加以下参数:
# 最大打开文件数
fs.file-max = 2097152
# 减少内存交换使用
vm.swappiness = 30
# 允许分配所有物理内存
vm.overcommit_memory = 1
# 最大内存映射区域数
vm.max_map_count = 262144
# 开启SYN Cookies
net.ipv4.tcp_syncookies = 1
# TIME_WAIT socket的最大数目
net.ipv4.tcp_max_tw_buckets = 50000
# 激活TCP窗口扩充
net.ipv4.tcp_window_scaling = 1
# TCP缓冲区内存设置
net.ipv4.tcp_mem = 786432 2097152 3145728
net.ipv4.tcp_rmem = 4096 4096 16777216
net.ipv4.tcp_wmem = 4096 4096 16777216
# Socket缓冲区默认值和最大值
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
# ACCEPT等待队列长度
net.core.netdev_max_backlog = 65535
# 系统最大并发连接数
net.core.somaxconn = 65535
# 不属于任何进程的socket最大数量
net.ipv4.tcp_max_orphans = 65535
# SYN等待队列长度
net.ipv4.tcp_max_syn_backlog = 65535
# 禁用TCP timestamps (在高并发下重要)
net.ipv4.tcp_timestamps = 0
# SYN/SYN-ACK重试次数
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_syn_retries = 1
# 允许回收TCP连接
net.ipv4.tcp_tw_recycle = 1
# 允许重用TCP连接
net.ipv4.tcp_tw_reuse = 1
# 服务端主动关闭后,客户端释放连接的超时时间
net.ipv4.tcp_fin_timeout = 5
# TCP保持空闲keepalive时长
net.ipv4.tcp_keepalive_time = 30
# 系统作为TCP客户端连接自动使用的端口范围
net.ipv4.ip_local_port_range = 10240 65535
运行sysctl -p加载配置。
查询参数:sysctl -a | grep <参数名>
2. ulimits 优化
限制系统打开文件的最大数量(文件描述符)。
编辑/etc/security/limits.conf文件,添加或修改以下行:
root hard nofile 65535
* soft nofile 65535
* hard nofile 65535
* soft nproc 65535
* hard nproc 65535
断开SSH连接并重新登录,或重启服务使配置生效。使用ulimit -a检查。
