TCP性能调优实现原理及过程解析
三次握手阶段
客户端syn包的重试次数
sysctl -w net.ipv4.tcp_syn_retries=6
相关介绍
第 1 次重试发生在 1 秒钟后,接着会以翻倍的方式在第 2、4、8、16、32 秒共做 6 次重试,最后一次重试会等待 64 秒,如果仍然没有返回 ack,才会终止三次握手。所以,总耗时是 1+2+4+8+16+32+64=127 秒,超过 2 分钟。
服务端半连接池大小
sysctl -w net.ipv4.tcp_max_syn_backlog=16384
服务端半连接池满了以后是否开启syncookie机制
sysctl -w net.ipv4.tcp_syncookies=1
相关介绍
如果 syn 半连接队列已满,默认会丢弃连接并不是这样,开启 syncookies 功能就可以在不使用 syn 队列的情况下成功建立连接。
syncookies 是这么做的:服务器根据当前状态计算出一个值,放在己方发出的 syn+ack 报文中发出,当客户端返回 ack 报文时,取出该值验证,如果合法,就认为连接建立成功,如下图所示。
- 0 表示关闭该功能;
- 2 表示无条件开启功能;
- 1 则表示仅当 syn 半连接队列放不下时,再启用它。
注意:由于 syncookie 仅用于应对 syn 泛洪攻击(攻击者恶意构造大量的 syn 报文发送给服务器,造成 syn 半连接队列溢出,导致正常客户端的连接无法建立),这种方式建立的连接,许多 tcp 特性都无法使用。所以,应当把 tcp_syncookies 设置为 1,仅在队列满时再启用。
服务端syn+ack包的重试次数
net.ipv4.tcp_synack_retries=5
相关介绍
tcp_synack_retries 的默认重试次数是 5 次,与客户端重发 syn 类似,它的重试会经历 1、2、4、8、16 秒,最后一次重试后等待 32 秒,若仍然没有收到 ack,才会关闭连接,故共需要等待 63 秒。
服务端全连接队列的大小
取决于min(backlog, /proc/sys/net/core/somaxconn),在linux内核2.2版本以后,listen 函数的 backlog 参数就可以设置 accept 队列的大小。
另外backlog 参数还受限于 linux 系统级的队列长度上限,当然这个上限阈值也可以通过 somaxconn 参数修改,somaxconn是内核的参数,默认是128。
sysctl -w net.core.somaxconn=32768
四次挥手阶段
接下来我们把先关闭连接的一方叫做主动方,后关闭连接的一方叫做被动方。
四次挥手的流程:
其实四次挥手只涉及两种报文:fin 和 ack。fin 就是 finish 结束连接的意思,谁发出 fin 报文,就表示它将不再发送任何数据,关闭这一方向的传输通道。ack 是 acknowledge 确认的意思,它用来通知对方:你方的发送通道已经关闭。当主动方关闭连接时,会发送 fin 报文,此时主动方的连接状态由 established 变为 fin_wait1。当被动方收到 fin 报文后,内核自动回复 ack 报文,连接状态由 established 变为 close_wait,顾名思义,它在等待进程调用 close 函数关闭连接。当主动方接收到这个 ack 报文后,连接状态由 fin_wait1 变为 fin_wait2,主动方的发送通道就关闭了。再来看被动方的发送通道是如何关闭的。当被动方进入 close_wait 状态时,进程的 read 函数会返回 0,这样开发人员就会有针对性地调用 close 函数,进而触发内核发送 fin 报文,此时被动方连接的状态变为 last_ack。当主动方收到这个 fin 报文时,内核会自动回复 ack,同时连接的状态由 fin_wait2 变为 time_wait,linux 系统下大约 1 分钟后 time_wait 状态的连接才会彻底关闭。而被动方收到 ack 报文后,连接就会关闭。
主动方的优化
等待ack,fin包的重发次数
主动方发送 fin 报文后,连接就处于 fin_wait1 状态下,该状态通常应在数十毫秒内转为 fin_wait2。只有迟迟收不到对方返回的 ack 时,才能用 netstat 命令观察到 fin_wait1 状态。此时,内核会定时重发 fin 报文,其中重发次数由 tcp_orphan_retries 参数控制(注意,orphan 虽然是孤儿的意思,该参数却不只对孤儿连接有效,事实上,它对所有 fin_wait1 状态下的连接都有效),默认值是 0,特指 8 次:
net.ipv4.tcp_orphan_retries = 0
孤儿连接的数量
net.ipv4.tcp_max_orphans = 16384
相关介绍
tcp_max_orphans 定义了孤儿连接的最大数量。当进程调用 close 函数关闭连接后,该连接是在 fin_wait1 状态,这个连接都与该进程无关了,它变成了孤儿连接。linux 系统为防止孤儿连接过多,导致系统资源长期被占用,就提供了 tcp_max_orphans 参数。如果孤儿连接数量大于它,新增的孤儿连接将不再走四次挥手,而是直接发送 rst 复位报文强制关闭。
孤儿连接的定义:由进程调用close关闭的连接称为孤儿连接,另外shutdown 函数也可以关闭连接,这二者都会向对方发送 fin 报文(shutdown 参数须传入 shut_wr 或者 shut_rdwr 才会发送 fin),区别在于 close 调用后,哪怕对方在半关闭状态下发送的数据到达主动方,进程也无法接收。如果你用 netstat -p 命令,会发现连接对应的进程名为空(与进程无关!)。而 shutdown 函数调用后,即使连接进入了 fin_wait1 或者 fin_wait2 状态,它也不是孤儿连接,进程仍然可以继续接收数据。
等待fin的时间
net.ipv4.tcp_fin_timeout = 60
相关介绍
当连接收到 ack 进入 fin_wait2 状态后,就表示主动方的发送通道已经关闭,接下来将等待对方发送 fin 报文,关闭对方的发送通道。这时,如果连接是用 shutdown 函数关闭的,连接可以一直处于 fin_wait2 状态。但对于 close 函数关闭的孤儿连接,这个状态不可以持续太久,而 tcp_fin_timeout 控制了这个状态下连接的持续时长。
time_wait相关参数
相关介绍
time_wait 是主动方四次挥手的最后一个状态。当收到被动方发来的 fin 报文时,主动方回复 ack,表示确认对方的发送通道已经关闭,连接随之进入 time_wait 状态,等待 60 秒后关闭。
time_wait状态最大连接数
当 time_wait 的连接数量超过该参数时,新关闭的连接就不再经历 time_wait 而直接关闭。
net.ipv4.tcp_max_tw_buckets = 5000
是否复用time_wait状态的端口
复用 time_wait 状态的端口,如果服务器会主动向上游服务器发起连接的话,就可以把 tcp_tw_reuse 参数设置为 1,它允许作为客户端的新连接,在安全条件下使用 time_wait 状态下的端口。
net.ipv4.tcp_tw_reuse = 1
当然,要想使 tcp_tw_reuse 生效,还得把 timestamps 参数设置为 1,它满足安全复用的先决条件(对方也要打开 tcp_timestamps ):
net.ipv4.tcp_timestamps = 1
老版本的 linux 还提供了 tcp_tw_recycle 参数,它并不要求 time_wait 状态存在 60 秒,很容易导致数据错乱,不建议设置为 1。
net.ipv4.tcp_tw_recycle = 0
所以在 linux 4.12 版本后,直接取消了这一参数。
其他配置
允许系统打开的端口范围
sysctl -w net.ipv4.ip_local_port_range=1024 65000
系统全局允许分配的最大文件句柄数
sysctl -w fs.file-max=2097152
sysctl -w fs.nr_open=2097152
echo 2097152 > /proc/sys/fs/nr_open
允许当前会话或进程打开文件句柄数
ulimit -n 1048576
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。