文章目录
- 1、单机最大TCP连接数
- 1.1、如何标识一个TCP连接
- 1.2、client最大tcp连接数
- 1.3、server最大tcp连接数
- 1.3.1、理论连接数
- 1.3.2、实际连接数
- 1.4、单台服务器支撑的最大TCP并发连接数
- 1.4.1、进程限制句柄限制
- 查看进程句柄数限制
- 临时修改
- 重启后失效的修改(不过我在CentOS 6.5下测试,重启后未发现失效)
- 永久修改
- 1.4.2、全局限制
- 2、tcp内核参数调优
- 2.1、查看`net.ipv4`参数
- 2.2、推荐配置
- 2.3、参数解释
- 2.3.1、 建立连接阶段
- 2.3.2、 数据传输阶段
- 2.3.3、 断开连接阶段
1、单机最大TCP连接数
1.1、如何标识一个TCP连接
在确定最大连接数之前,先来看看系统如何标识一个tcp连接。系统用一个4四元组来唯一标识一个TCP连接:
- local ip
- local port
- remote ip
- remote port
1.2、client最大tcp连接数
client每次发起tcp连接请求时,通常会让系统选取一个空闲的本地端口(local port),该端口是独占的,不能和其他tcp连接共享。在操作系统中,端口号的的数据类型是unsigned short
,所以端口号的范围只有0~65535,其中0-1024是预留端口号,不可使用,其他的端口都是可以使用的。也就是说,在链接发起端,受端口号的限制理论上最多可以创建64000左右链接。
那么有没有办法超过这个限制呢,答案是肯定的!
通过TCP标识的四元组可以看到,对于链接发起端,影响链接数的是本地ip和port,端口号受限于65535,已经没办法增加了。那我们可以增加本地ip来达到这个目的。一般情况下,服务器的一个网卡上只绑定了一个ip,对外通信都使用这个ip进行。其实网卡是支持一个绑定多个IP的(必须确保ip是有效的且未使用的)
ifconfig eth0:1 10.0.0.5
以上命令可以在eth0网卡上增加一个ip 10.0.0.5,服务器网卡每增加一个ip,就可以允许在这个ip上再创建65535左右的链接数。
最大的TCP链接数=所有有效ip排列组合的数量*端口数量64000
1.3、server最大tcp连接数
1.3.1、理论连接数
server通常固定在某个本地端口上监听,等待client的连接请求。不考虑地址重用(unix的SO_REUSEADDR选项)的情况下,即使server端有多个ip,本地监听端口也是独占的,因此server端tcp连接4元组中只有remote ip(也就是client ip)和remote port(客户端port)是可变的,因此最大tcp连接为客户端ip数×客户端port数,对IPV4,不考虑ip地址分类等因素,最大tcp连接数约为2的32次方(ip数)×2的16次方(port数),也就是server端单机最大tcp连接数约为2的48次方。
1.3.2、实际连接数
上面给出的是理论上的单机最大连接数,在实际环境中,受到机器资源、操作系统等的限制,特别是sever端,其最大并发tcp连接数远不能达到理论上限。在unix/linux下限制连接数的主要因素是内存和允许的文件描述符个数(每个tcp连接都要占用一定内存,每个socket就是一个文件描述符),另外1024以下的端口通常为保留端口。在默认2.6内核配置下,经过试验,每个socket占用内存在15~20k之间。
影响一个socket占用内存的参数包括:
net.core.rmem_max=16777216
net.core.wmem_max=16777216
net.ipv4.tcp_rmem=4096 87380 16777216
net.ipv4.tcp_wmem=4096 65536 16777216
net.ipv4.tcp_mem=
grep skbuff /proc/slabinfo
1.4、单台服务器支撑的最大TCP并发连接数
在linux下编写网络服务器程序的朋友肯定都知道每一个tcp连接都要占一个文件描述符,一旦这个文件描述符使用完了,新的连接到来返回给我们的错误是“Socket/File:Can’t open so many files”。
1.4.1、进程限制句柄限制
查看进程句柄数限制
ulimit -n
输出 1024,说明对于一个进程而言最多只能打开1024个文件,所以你要采用此默认配置最多也就可以并发上千个TCP连接。
临时修改
ulimit -n 1000000
这种临时修改只对当前登录用户目前的使用环境有效,系统重启或用户退出后就会失效。
重启后失效的修改(不过我在CentOS 6.5下测试,重启后未发现失效)
编辑 /etc/security/limits.conf 文件
root soft nofile 1000000
root hard nofile 1000000
* soft nofile 1000000
* hard nofile 1000000
永久修改
编辑/etc/rc.local,在其后添加如下内容
ulimit -SHn 1000000
1.4.2、全局限制
执行 cat /proc/sys/fs/file-nr
输出 9344 0 592026,分别为:1.已经分配的文件句柄数,2.已经分配但没有使用的文件句柄数,3.最大文件句柄数。但在kernel 2.6版本中第二项的值总为0,这并不是一个错误,它实际上意味着已经分配的文件描述符无一浪费的都已经被使用了 。
我们可以把这个数值改大些,用 root 权限修改 /etc/sysctl.conf 文件:
fs.file-max = 1000000
net.ipv4.ip_conntrack_max = 1000000
net.ipv4.netfilter.ip_conntrack_max = 1000000
执行命令生效
sysctl -p
2、tcp内核参数调优
2.1、查看net.ipv4
参数
cat /proc/sys/net/ipv4/tcp_rmem
修改则修改文件/ect/sysctl.conf
Linux下查看tcp连接数及状态命令
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
2.2、推荐配置
net.ipv4.ip_local_port_range = 1024 65536
net.core.rmem_max=16777216
net.core.wmem_max=16777216
net.ipv4.tcp_rmem=4096 87380 16777216
net.ipv4.tcp_wmem=4096 65536 16777216
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_window_scaling = 0
net.ipv4.tcp_sack = 0
net.core.netdev_max_backlog = 30000
net.ipv4.tcp_no_metrics_save=1
net.core.somaxconn = 262144
net.ipv4.tcp_syncookies = 0
net.ipv4.tcp_max_orphans = 262144
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2
2.3、参数解释
2.3.1、 建立连接阶段
- net.ipv4.tcp_syn_retries
控制三次握手第一步客户端发送syn得不到服务端响应时重传syn的次数,如果是内网环境,中间链路少,网络稳定,服务端无响应很可能是服务端应用出了问题,重传多次的意义不大,还会加大服务端压力,可以调低重传次数,让客户端尽快去尝试连接其他服务端。 - net.ipv4.tcp_syncookies
开启SYN Cookies,默认开启,建议保持默认值,可以提升SYN Flood攻击的防护能力。 - net.ipv4.tcp_synack_retries
控制三次握手第二步服务端发送syn+ack得不到客户端响应时重传syn+ack的次数,如果是内网环境,中间链路少,网络稳定,客户端无响应很可能是客户端出了问题,重传多次的意义不大,可以调低重传次数。 - net.ipv4.tcp_max_syn_backlog
控制半连接队列大小,所谓半连接是指还没有完成TCP三次握手的连接。服务端收到了客户端的SYN包后,就会把这个连接放到半连接队列中,然后再向客户端发送SYN+ACK,为了应对新建连接数暴增的场景,建议调大,半连接队列溢出观察方法:netstat -s | grep “SYNs to LISTEN” - net.core.somaxconn
全连接队列=min(somaxconn,backlog),所谓全连接,是指服务端已经收到客户端三次握手第三步的ACK,然后就会把这个连接放到全连接队列中,全连接队列中的连接还需要被 accept()系统调用取走,服务端应用才可以开始处理客户端的请求,建议适当调大,全连接队列溢出观察方法:netstat -s | grep “listen queue” - net.ipv4.tcp_abort_on_overflow
当全连接队列满了之后,新的连接就会被丢弃掉。服务端在丢弃新连接时,默认行为是直接丢弃不去通知客户端,有的时候需要发送reset来通知客户端,这样客户端就不会再次重试,至于是否需要给客户端发送reset,是由tcp_abort_on_overflow参数控制,默认为0,即不发送reset给客户端,如非特殊需求,建议保持默认值。 - net.ipv4.tcp_timestamps
0关闭,1关闭
tcp_timestamps的本质是记录数据包的发送时间。基本的步骤如下
- 发送方在发送数据时,将一个timestamp(表示发送时间)放在包里面
- 接收方在收到数据包后,在对应的ACK包中将收到的timestamp返回给发送方(echo back)
- 发送发收到ACK包后,用当前时刻now - ACK包中的timestamp就能得到准确的RTT。当然实际运用中要考虑到RTT的波动,因此有了后续的(Round-Trip Time Measurement)RTTM机制
2.3.2、 数据传输阶段
- net.ipv4.tcp_wmem
tcp发送缓冲区大小,包含min、default、max三个值,内核会控制发送缓冲区在min-max之间动态调整,可根据实际业务场景和服务器配置适当调大,如果设置了socket的SO_SNDBUF,动态调整功能失效,一般不建议设置。 - net.core.wmem_max
socket发送缓冲区的最大值,需要设置net.core.wmem_max的值大于等于 net.ipv4.tcp_wmem的max值。 - net.ipv4.tcp_mem
系统中所有tcp连接最多可消耗的内存,有三个值,当TCP总内存小于第1个值时,不需要内核进行自动调节,在第1和第2个值之间时,内核开始调节缓冲区的大小,大于第3个值时,内核不再为TCP分配新内存,此时无法新建连接,需要注意的是,三个值的单位都是内存页,也就是4KB。 - net.ipv4.tcp_rmem
tcp接收缓冲区大小,包含min、default、max三个值,内核会控制接收缓冲区在min-max之间动态调整,可根据实际业务场景和服务器配置适当调大,如果设置了socket的 SO_RECVBUF或者关闭了net.ipv4.tcp_moderate_rcvbuf,动态调整功能失效。 - net.core.rmem_max
socket接收缓冲区的最大值,需要设置net.core.rmem_max的值大于等于net.ipv4.tcp_rmem 的max值。 - net.ipv4.tcp_moderate_rcvbuf
接收缓冲区动态调整功能,默认打开,建议保持默认配置。 - net.ipv4.tcp_window_scaling
扩充滑动窗口,tcp头部中,窗口字段只有2个字节,最多只能达到2的16次方,即65535字节大小的窗口,打开此开关可以扩充窗口大小,默认打开,建议保持默认配置。 - net.ipv4.tcp_keepalive_probes
keepalive探测失败后通知应用前的重试次数,建议适当调低。 - net.ipv4.tcp_keepalive_intvl
keepalive探测包的发送间隔时间,建议适当调低。 - net.ipv4.tcp_keepalive_time
最后一次数据包到keepalive探测包的间隔时间,建议适当调低。 - net.ipv4.tcp_available_congestion_control
查看内核支持的拥塞控制算法。 - net.ipv4.tcp_congestion_control
配置拥塞控制算法,默认cubic,内核4.9版本后支持BBR,弱网络条件下建议配置成BBR。
2.3.3、 断开连接阶段
- net.ipv4.tcp_fin_timeout
是从Fin_WAIT_2到TIME_WAIT的超时时间,长时间收不到对端FIN包,大概率是对端机器有问题,不能及时调用close()关闭连接,建议调低,避免等待时间太长,资源开销过大。 - net.ipv4.tcp_max_tw_buckets
系统TIME_WAIT连接的最大数量,根据实际业务需要调整,超过最大值后dmesg会有报错TCP: time wait bucket table overflow。 - net.ipv4.tcp_tw_reuse
允许TIME_WAIT状态的连接占用的端口用到新建连接中,客户端可开启。 - net.ipv4.tcp_tw_recycle
开启后,TIME_WAIT状态的连接无需等待2MSL时间就可用于新建连接,在NAT环境下,开启tcp_tw_recycle参数会触发PAWS机制导致丢包,建议不开启,事实上,内核在4.1版本后就把这个参数删除了。
参考博客:
TCP单机最大连接数优化