做下载站的朋友应该都有过这种经历:服务器配置不差——几十核CPU、大内存、万兆网卡,但一到高峰期,连接数就死活上不去,大量请求超时,用户抱怨下载中断。看监控CPU才跑30%,内存也够用,问题到底出在哪?
其实十有八九是Linux内核的默认参数在拖后腿。默认配置跑个开发环境或者小流量业务还行,到了高并发场景,那就是在给自己挖坑。
先看清问题在哪
遇到连接数上不去的情况,第一件事不是急着改参数,而是先看现场。
# 查看当前连接状态分布
ss -s
这个命令会告诉你总连接数、各种状态的分布。你大概率会看到一堆TIME_WAIT,或者estab数量卡在一个不上不下的数值就再也涨不上去了。
再细看一下backlog队列的情况:
ss -lnt | grep :你的端口号
如果Recv-Q那一列的数字长期接近甚至等于Send-Q的值,说明监听队列满了,新连接进来直接排队排不上,被内核丢弃了。
还有个常见的坑是文件描述符限制。用ulimit -n看一眼,如果输出是1024,那不用想了,肯定不够。每个Socket连接都要占一个文件描述符,默认1024的上限意味着你的服务器同时最多只能维持一千来个连接,这在下载站场景下连开胃菜都算不上。
内核参数这样调
下面是生产环境验证过的一套参数配置,直接照抄问题不大,但建议根据你的实际业务量微调。
编辑 /etc/sysctl.conf 文件,添加或修改以下内容:
# ========== 连接队列相关 ==========
# 全连接队列最大值,就是ESTABLISHED状态但还没被应用accept的连接
# 默认128,高并发下至少要开到65535
net.core.somaxconn = 65535
# SYN半连接队列最大值,应对SYN Flood和突发建连请求
net.ipv4.tcp_max_syn_backlog = 8192
# 网卡接收数据包队列,突发流量大时防止丢包
net.core.netdev_max_backlog = 16384
# ========== TIME_WAIT和连接回收 ==========
# 允许复用TIME_WAIT状态的连接(仅限客户端角色生效)
net.ipv4.tcp_tw_reuse = 1
# 缩短FIN_WAIT2状态超时时间,默认60秒,调小可加快连接释放
net.ipv4.tcp_fin_timeout = 30
# TIME_WAIT状态的最大数量,超过后系统会直接回收
net.ipv4.tcp_max_tw_buckets = 200000
# ========== TCP缓冲区 ==========
# 接收缓冲区:最小、默认、最大(单位字节)
net.ipv4.tcp_rmem = 4096 87380 16777216
# 发送缓冲区:最小、默认、最大
net.ipv4.tcp_wmem = 4096 65536 16777216
# 单个Socket最大缓冲区
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
# ========== 系统文件描述符 ==========
# 系统级最大文件描述符
fs.file-max = 1000000
# ========== 安全与防御 ==========
# SYN Cookies防攻击,队列满时启用避免丢SYN
net.ipv4.tcp_syncookies = 1
# 拥塞控制算法,高带宽高延迟场景用BBR效果明显
net.ipv4.tcp_congestion_control = bbr
几个关键参数稍微解释一下,免得你改的时候心里没底。
somaxconn 是最容易忽略的坑。你的Nginx或Tomcat在配置文件里可能把backlog设成了1024甚至更高,但如果内核的somaxconn只有128,应用层的设置会被内核限死,实际上起作用的还是128。这就是为什么很多人改了应用配置没效果的根本原因。
tcp_max_syn_backlog 对应的是半连接队列。下载站的用户来自五湖四海,网络环境复杂,握手阶段丢包重传很常见,这个队列如果太小,正常的建连请求都可能被丢掉。
tcp_tw_reuse 很多人不敢开,怕出问题。其实在Linux 4.x以上的内核里,这个参数已经相当安全了,而且只对客户端角色有效。下载站服务器发起回源连接时,开启它可以有效缓解端口耗尽的问题。但注意,tcp_tw_recycle在较新内核已经被移除了,不要再去碰它,尤其是在NAT环境下会引发灾难性后果。
让配置生效并验证
配置写好后,执行:
sysctl -p
这条命令会重新加载 /etc/sysctl.conf 里的所有配置,并立即生效。
验证一下是否改对了:
# 逐个检查关键参数
sysctl net.core.somaxconn
sysctl net.ipv4.tcp_max_syn_backlog
sysctl net.ipv4.tcp_tw_reuse
# 或者批量查看
sysctl -a | grep -E 'somaxconn|tcp_max_syn_backlog|tcp_tw_reuse'
除了内核参数,别忘了改进程的文件描述符限制。编辑 /etc/security/limits.conf:
* soft nofile 1000000
* hard nofile 1000000
改了之后需要重新登录才会生效,可以用 ulimit -n 确认。
应用程序也得配合
内核参数调完了,Nginx或者你的下载服务本身也得跟上。
如果你用的是Nginx,在listen指令里明确指定backlog:
listen 80 backlog=65535;
如果是Tomcat,修改server.xml里的acceptCount:
<Connector port="8080" acceptCount="500" .../>
应用层的backlog值不要超过内核的somaxconn,否则会被内核截断,起了个寂寞。
调完怎么看效果
改完参数重新压测,或者等高峰期再看一次ss -lnt。如果Recv-Q不再长期逼近Send-Q的值,ss -s看到的TIME_WAIT数量被控制住了,同时连接数能稳定突破之前的瓶颈,说明调优到位了。
另外,如果下载站主要走大文件传输,建议把拥塞控制算法换成BBR。对高带宽、高延迟的网络环境,BBR比传统的Cubic能多榨出不少带宽。确认是否启用成功:
sysctl net.ipv4.tcp_congestion_control
# 输出应为 net.ipv4.tcp_congestion_control = bbr
最后提醒一句:这套配置是生产环境验证过的,但每个业务模型不同,最好在测试环境先压一遍,观察内存使用情况。缓冲区调大了确实能提升吞吐,但同时也会吃掉更多内存,别让系统把内存耗尽了就好。
推荐文章