背景
线上有一套高并发的系统经常出现访问超时、服务不可用的告警,访问链路如下:client-> nginx-> web server,所以第一时间排查了nginx服务器,这台服务器主要是做前端web和后端的反向代理。排查nginx日志时发现大量的报错
,负载均衡时无法分配请求的地址。
(99: Cannot assign requested address) while connecting to upstream
排查问题时,发现系统存在几万多个time_wait状态连接
netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}'
由于time_wait连接会占用本地端口,而本地默认的最大端口数是65535,所以导致了端口不够用的情况
time_wait什么是
在 TCP 连接的生命周期中,TIME_WAIT 是连接终止过程中的一个状态
TIME_WAIT 状态有两个主要目的:
1、确保最后一个 ACK 包的可靠传输
2、允许旧的重复数据包消失
如何优化
1、系统内核参数调优
vi /etc/sysctl.conf
...
# 减少 TIME_WAIT 持续时间
net.ipv4.tcp_fin_timeout = 30
# 启用端口复用
net.ipv4.tcp_tw_reuse = 1
# 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭
net.ipv4.tcp_tw_recycle = 1
其他优化:
net.ipv4.ip_local_port_range = 1024 65535 增加可用端口范围,让系统拥有的更多的端口来建立链接,这里有个问题需要注意,对于这个设置系统就会从1025~65535这个范围内随机分配端口来用于连接,如果我们服务的使用端口比如8080刚好在这个范围之内,在升级服务期间,可能会出现8080端口被其他随机分配的链接给占用掉,这个原因也是文章开头提到的端口被占用的另一个原因
net.ipv4.ip_local_reserved_ports = 7005,8001-8100 针对上面的问题,我们可以设置这个参数来告诉系统给我们预留哪些端口,不可以用于自动分配。
...
#使内核参数生效
sysctl -p
2、nginx反向代理调整短链接为长链接
短连接是指SOCKET连接后发送后接收完数据后马上断开连接
长连接指建立SOCKET连接后不管是否使用都保持连接,但安全性较差
长连接比短连接从根本上减少了关闭连接的次数,减少了TIME_WAIT状态的产生数量,在高并发的系统中,这种方式的改动非常有效果,可以明显减少系统TIME_WAIT的数量
当使用nginx作为反向代理时,为了支持长连接,需要做到两点:
1、保持client到nginx的长连接
2、upstream 保持nginx到server的长连接
http {
#1、保持client到nginx的长连接
keepalive_timeout 60; # TCP长连接存活时间,默认75秒
keepalive_requests 1000; # 一个keep-alive连接上可以服务的请求的最大数量,超出的连接将被关闭,默认100
upstream myservers {
ip_hash;
server 192.168.0.21:8001 max_fails=5 fail_timeout=300s weight=1;
server 192.168.0.22:8001 max_fails=5 fail_timeout=300s weight=5;
server 192.168.0.23:8001 max_fails=5 fail_timeout=300s weight=5;
}
server {
listen 80;
location / {
# 2、upstream 保持nginx到server的长连接
proxy_http_version 1.1; # http 1.1协议才开始支持长连接
proxy_set_header Connection keep-alive; # http 1.0中Connection默认close,但在http1.1中默认keep-alive
proxy_pass http://myservers;
}
}
}