centos7 支持百万长连接,需要调优哪些参数?
首先来看一个高并发场景下的 “经典问题”: too many open files, 产生这个问题的根本原因是: 短时间内打开大量网络 (文件) 连接,超过了操作系统对单个进程允许打开的文件描述符(file descriptor)数量限制。
想要单机支持 100 万链接,需要调优哪些参数呢?
解决方案
Soft open files 是 Linux 系统参数,影响系统单个进程能够打开最大的文件句柄数量。
这里输出1024 是centos7默认装机后的配置,如果是云厂商的话这里会是65535。
[root@localhost ~]# ulimit -n
1024
表示单个进程同时最多只能维持 1024 个网络 (例如 TCP) 连接。
可以通过增大该参数,来支持更大的网络连接数量。
1. 临时性调整
只在当前会话 (终端) 中有效,退出或重启后失效
[root@localhost ~]# ulimit -n 1048576
[root@localhost ~]# ulimit -n
1048576
2. 永久性设置
修改配置文件 /etc/security/limits.conf:
$ sudo vim /etc/security/limits.conf# 追加如下内容 (例如支持百万连接)
# 重启永久生效# 单个进程可以打开的最大进程数量
# 表示可以针对不同用户配置不同的值
# 当然实际情况中,网络应用一般会独享整个主机/容器所有资源
# 调整文件描述符限制
# 注意: 实际生效时会以两者中的较小值为准 (所以最好的方法就是保持两个值相同)
* soft nofile 1048576
* hard nofile 1048576
root soft nofile 1048576
root hard nofile 1048576
运行 sysctl -p 命令生效,重启之后仍然有效。
Linux 内核参数调优
想要单机支持 100 万链接,除了刚才的 文件描述符数量 参数调优之外,还需要针对部分内核参数进行调优。
打开系统配置文件 /etc/sysctl.conf,增加 (或修改) 以下配置数据,参数名称及其作用已经写在了注释中。
# 设置系统的 TCP TIME_WAIT 数量,如果超过该值
# 不需要等待 2MSL,直接关闭
net.ipv4.tcp_max_tw_buckets = 1048576# 将处于 TIME_WAIT 状态的套接字重用于新的连接
# 如果新连接的时间戳 大于 旧连接的最新时间戳
# 重用该状态下的现有 TIME_WAIT 连接,这两个参数主要针对接收方 (服务端)
# 对于发送方 (客户端) ,这两个参数没有任何作用
net.ipv4.tcp_tw_reuse = 1
# 必须配合使用
net.ipv4.tcp_timestamps = 1# 启用快速回收 TIME_WAIT 资源
# net.ipv4.tcp_tw_recycle = 1
# 能够更快地回收 TIME_WAIT 套接字
# 此选项会导致处于 NAT 网络的客户端超时,建议设置为 0
# 因为当来自同一公网 IP 地址的不同主机尝试与服务器建立连接时,服务器会因为时间戳的不匹配而拒绝新的连接
# 这是因为内核会认为这些连接是旧连接的重传
# 该配置会在 Linux/4.12 被移除
# 在之后的版本中查看/设置会提示 "cannot stat /proc/sys/net/ipv4/tcp_tw_recycle"
# net.ipv4.tcp_tw_recycle = 0# 缩短 Keepalive 探测失败后,连接失效之前发送的保活探测包数量
net.ipv4.tcp_keepalive_probes = 3# 缩短发送 Keepalive 探测包的间隔时间
net.ipv4.tcp_keepalive_intvl = 15# 缩短最后一次数据包到 Keepalive 探测包的间隔时间# 减小 TCP 连接保活时间
# 决定了 TCP 连接在没有数据传输时,多久发送一次保活探测包,以确保连接的另一端仍然存在
# 默认为 7200 秒
net.ipv4.tcp_keepalive_time = 600# 控制 TCP 的超时重传次数,决定了在 TCP 连接丢失或没有响应的情况下,内核重传数据包的最大次数
# 如果超过这个次数仍未收到对方的确认包,TCP 连接将被终止
net.ipv4.tcp_retries2 = 10# 缩短处于 TIME_WAIT 状态的超时时间
# 决定了在发送 FIN(Finish)包之后,TCP 连接保持在 FIN-WAIT-2 状态的时间 (对 FIN-WAIT-1 状态无效)
# 主要作用是在 TCP 连接关闭时,为了等待对方关闭连接而保留资源的时间
# 如果超过这个时间仍未收到 FIN 包,连接将被关闭
# 更快地检测和释放无响应的连接,释放资源
net.ipv4.tcp_fin_timeout = 15# 调整 TCP 接收和发送窗口的大小,以提高吞吐量
# 三个数值分别是 min,default,max,系统会根据这些设置,自动调整 TCP 接收 / 发送缓冲区的大小
net.ipv4.tcp_mem = 8388608 12582912 16777216
net.ipv4.tcp_rmem = 8192 87380 16777216
net.ipv4.tcp_wmem = 8192 65535 16777216# 定义了系统中每一个端口监听队列的最大长度
net.core.somaxconn = 65535# 增加半连接队列容量
# 除了系统参数外 (net.core.somaxconn, net.ipv4.tcp_max_syn_backlog)
# 程序设置的 backlog 参数也会影响,以三者中的较小值为准
net.ipv4.tcp_max_syn_backlog = 65535# 全连接队列已满后,如何处理新到连接 ?
# 如果设置为 0 (默认情况)
# 客户端发送的 ACK 报文会被直接丢掉,然后服务端重新发送 SYN+ACK (重传) 报文
# 如果客户端设置的连接超时时间比较短,很容易在这里就超时了,返回 connection timeout 错误,自然也就没有下文了
# 如果客户端设置的连接超时时间比较长,收到服务端的 SYN+ACK (重传) 报文之后,会认为之前的 ACK 报文丢包了
# 于是再次发送 ACK 报文,也许可以等到服务端全连接队列有空闲之后,建立连接完成
# 当服务端重试次数到达上限 (tcp_synack_retries) 之后,发送 RST 报文给客户端
# 默认情况下,tcp_synack_retries 参数等于 5, 而且采用指数退避算法
# 也就是说,5 次的重试时间间隔为 1s, 2s, 4s, 8s, 16s, 总共 31s
# 第 5 次重试发出后还要等 32s 才能知道第 5 次重试也超时了,所以总共需要等待 1s + 2s + 4s+ 8s+ 16s + 32s = 63s
# 如果设置为 1
# 服务端直接发送 RST 报文给客户端,返回 connection reset by peer
# 设置为 1, 可以避免服务端给客户端发送 SYN+ACK
# 但是会带来另外一个问题: 客户端无法根据 RST 报文判断出,服务端拒绝的具体原因:
# 因为对应的端口没有应用程序监听,还是全队列满了
# 除了系统参数外 (net.core.somaxconn)
# 程序设置的 backlog 参数也会影响,以两者中的较小值为准
# 所以全连接队列大小 = min(backlog, somaxconn)
net.ipv4.tcp_abort_on_overflow = 1# 增大每个套接字的缓冲区大小
net.core.optmem_max = 81920
# 增大套接字接收缓冲区大小
net.core.rmem_max = 16777216
# 增大套接字发送缓冲区大小
net.core.wmem_max = 16777216# 增加网络接口队列长度,可以避免在高负载情况下丢包
# 在每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数量
net.core.netdev_max_backlog = 65535# 增加连接追踪表的大小,可以支持更多的并发连接
# 注意:如果防火墙没开则会提示 error: "net.netfilter.nf_conntrack_max" is an unknown key,忽略即可
net.netfilter.nf_conntrack_max = 1048576# 缩短连接追踪表中处于 TIME_WAIT 状态连接的超时时间
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30
运行 sysctl -p 命令生效,重启之后仍然有效。
注意事项
如果系统已经使用了参数 net.ipv4.tcp_syncookies, 参数 net.ipv4.tcp_max_syn_backlog 将自动失效。
