文章目录
TCP三次握手的性能提升
调整SYN报文重传次数
- echo 5>/proc/sys/net/ipv4/tcp_syn_retries
- 每次重传的时间点是上次重传的两倍
调整SYN半连接队列长度
- 如何查看半连接队列是否已满 netstat- -s | grep “SYNs to LISTEN” ,这是一个被丢弃连接的累计值,如果在上升说明半连接队列已经满了
- 要想增大半连接队列,不能只单纯增大 tcp_max_syn_backlog 的值,还需一同增大 somaxconn 和 backlog,也就是增大 accept 队列。否则,只单纯增大 tcp_max_syn_backlog 是无效的。
- 其实可以使用syncookies可以绕开半连接队列,但是可能要遭受syn攻击,一般选用他的1参数“表示仅当 SYN 半连接队列放不下时,再启用它” echo 1> /proc/sys/net/ipv4/tcp_syncookies
调整SYN+ACK报文重传次数
- 调整 tcp_synack_retries 参数,默认重传5次,echo 1> /proc/sys/net/ipv4/tcp_synack_retries
调整accept队列长度
-
accept满了默认行为为丢弃数据包,但是我们也可以选择像客服端发送RST复位报文 echo 1> /proc/sys/net/ipv4/tcp_abort_on_overflow .通常情况下,应当把 tcp_abort_on_overflow 设置为 0,因为这样更有利于应对突发流量,因为ack会重发。设置为1只是方便我们观察客服端情况判断是否是队列已满;
-
如何调整长度呢? ==min(somaxconn, backlog)
- somaxconn 是 Linux 内核的参数,默认值是 128,可以通过 net.core.somaxconn 来设置其值
- backlog 是 listen(int sockfd, int backlog) 函数中的 backlog 大小;
-
如何查看长度呢?
-
ss -ltn进行查看
- -l 代表listen,-n 不解析服务器 ,-t 只显示tcp
-
-
如何查看由于 accept 连接队列已满,而被丢弃的连接?
- netstat- -s | grep overflowed ,同上面查看半连接队列
绕过三次握手
- TCP fast open技术,主要的依据就是cookie保存信息,以便下次快速发送;
- 怎样打开呢? echo 3> /proc/sys/net/ipv4/tcp_fastopen ,3代表无论客服端和服务端都可以使用FAST OPEN功能。感兴趣去看看参数1,2有啥作用;
TCP四次挥手的性能提升
明白shutdown和colse的区别
-
close 函数意味着完全断开连接,完全断开不仅指无法传输数据,而且也不能发送数据。此时,调用了 close 函数的一方的连接叫做「孤儿连接」,如果你用 netstat -p 命令,会发现连接对应的进程名为空。
-
而shutdown ,它可以控制只关闭一个方向的连接。它有以下参数:
- SHUT_RD(0) 关闭读方向。如果接收缓冲区有已接收的数据,则将会被丢弃,并且后续再收到新的数据,会对数据进行 ACK,然后悄悄地丢弃。也就是说,对端还是会接收到 ACK,在这种情况下根本不知道数据已经被丢弃了。
- SHUT_WR(1):关闭连接的「写」这个方向,这就是常被称为「半关闭」的连接。如果发送缓冲区还有未发送的数据,将被立即发送出去,并发送一个 FIN 报文给对端。
- SHUT_RDWR(2):相当于 SHUT_RD 和 SHUT_WR 操作各一次,关闭套接字的读和写两个方向。
主动方的优化
-
FIN_WAIT1 的优化
-
调整FIN报文重传次数
- echo 5> /proc/sys/net/ipv4/tcp_orphan_retries 默认为8次
-
调整孤儿链接的上限个数
- 如果遇见恶意攻击,当我们调用close之后,FIN报文可能没有办法发出去(发动缓冲区还有数据or 接收窗口为0)
- 那么就可以调整孤儿连接的最大数tcp_max_orphans
- Linux 系统为了防止孤儿连接过多,导致系统资源长时间被占用,就提供了 tcp_max_orphans 参数。如果孤儿连接数量大于它,新增的孤儿连接将不再走四次挥手,而是直接发送 RST 复位报文强制关闭。
-
-
FIN_WAIT2 的优化
-
调整FIN_WAIT2状态的时间
- 如果连接是用 shutdown 函数关闭的,连接可以一直处于 FIN_WAIT2 状态
- 对于 close 函数关闭的孤儿连接,由于无法在发送和接收数据,所以这个状态不可以持续太久,而 tcp_fin_timeout 控制了这个状态下连接的持续时长,默认值是 60 秒(2MSL)
- tcp_fin_timeout
-
-
TIME_WAIT的优化
-
调整time_wait状态是上限次数
-
time_wait的作用
- 防止具有相同「四元组」的「旧」数据包被收到;(简单来说就是放置A给B的数据被接替C的客服端收了)
- 保证「被动关闭连接」的一方能被正确的关闭,即保证最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭
-
为什么是2MSL?,maximum segment lifetime,定义了一个在网络中的生存时间 ;而2MSL是至少允许报文丢失一次(一个MSL为30s)
-
为什么不是8MSL?
- 因为连续两次丢包的概率太小了,比如一个丢包率达到百分之一的糟糕网络,连续两次丢包的概率只有万分之一
-
time_wait太多怎么办呢?
- 虽然 TIME_WAIT 状态有存在的必要,但它毕竟会消耗系统资源。如果发起连接一方的 TIME_WAIT 状态过多,占满了所有端口资源,则会导致无法创建新连接。
- 客户端受端口资源限制:如果客户端 TIME_WAIT 过多,就会导致端口资源被占用,因为端口就65536个,被占满就会导致无法创建新的连接
- 服务端受系统资源限制:由于一个 四元组表示TCP连接,理论上服务端可以建立很多连接,服务端确实只监听一个端口 但是会把连接扔给处理线程,所以理论上监听的端口可以继续监听。但是线程池处理不了那么多一直不断的连接了。所以当服务端出现大量 TIME_WAIT 时,系统资源被占满时,会导致处理不过来新的连接;
-
如何改?
- echo 5000 > /proc/sys/net/ipv4/tcp_max_tw_buckets,当服务器并发数增多的时候,可以适当的增大这个值,但是也不是越大越好,因为内存和端口都是有限的;
-
-
复用time_wait状态的连接
-
有一种方式可以在建立新连接时,复用处于 TIME_WAIT 状态的连接,那就是打开 tcp_tw_reuse 参数。但是需要注意,该参数是只用于客户端(建立连接的发起方),因为是在调用 connect() 时起作用的,而对于服务端(被动连接方)是没有用的。
-
其是协议角度安全可控的
- 只适用于连接发起方,也就是 C/S 模型中的客户端
- 对应的 TIME_WAIT 状态的连接创建时间超过 1 秒才可以被复用。
-
使用前提还有双方都打开TCP时间戳
-
echo 1 >/proc/sys/net/ipv4/tcp_timestamps
-
引入时间戳带来的好处
- 我们在前面提到的 2MSL 问题就不复存在了,因为重复的数据包会因为时间戳过期被自然丢弃;
- 同时,它还可以防止序列号绕回,也是因为重复的数据包会由于时间戳过期被自然丢弃;
-
-
-
-
被动方的优化
- 大多数应用程序并不使用 shutdown 函数关闭连接。所以,当你用 netstat 命令发现大量 CLOSE_WAIT 状态。就需要排查你的应用程序,因为可能因为应用程序出现了 Bug,read 函数返回 0 时,没有调用 close 函数。
- colse_wait状态重传FIN也是根据tcp_orphan_retries参数来进行设置的
- 如果被动方迅速调用 close 函数,那么被动方的 ACK 和 FIN 有可能在一个报文中发送,这样看起来,四次挥手会变成三次挥手,这只是一种特殊情况,不用在意。(注意哦,是迅速)
- 两方发送 FIN 报文时,都认为自己是主动方,所以都进入了 FIN_WAIT1 状态,FIN 报文的重发次数仍由 tcp_orphan_retries 参数控制。接下来,双方在等待 ACK 报文的过程中,都等来了 FIN 报文。这是一种新情况,所以连接会进入一种叫做 CLOSING 的新状态,它替代了 FIN_WAIT2 状态。接着,双方内核回复 ACK 确认对方发送通道的关闭后,进入 TIME_WAIT 状态,等待 2MSL 的时间后,连接自动关闭。
TCP 数据传输的性能提升
TCP连接
-
TCP连接时是由内核维护的,内核会为每个连接建立内存缓冲区
- 如果连接的内存配置过小,就无法充分利用网络带宽,TCP传输效率就会降低
- 如果连接的内存配置过大,很容易间服务器是资源耗光,这样就会导致新的来凝结无法建立
滑动窗口是怎样影响传输速度的?
-
TCP因为有超时重传机制,所以TCP报文发出去了之后,并不会立即从内存中删除,因为重传的时候还会利用到它;
-
通过TCP报文格式,我们知道窗口大小为16位,也就是能够表达64KB的大小,而这个在当今的网速下明显是不够用的,所以在 TCP 选项字段定义了窗口扩大因子,用于扩大TCP通告窗口,使 TCP 的窗口大小从 2 个字节(16 位) 扩大为 30 位,所以此时窗口的最大值可以达到 1GB(2^30)
-
扩大方式: ehco 1> /proc/sys/net/ipv4/tcp_window_scaling ,默认就是打开的
-
那么是不是滑动窗口可以无限的扩大?
- 这是不可能的,因为网络的传输能力是有限的,当发送方依据发送窗口,发送超过网络处理能力的报文时,路由器会直接丢弃这些报文。因此,缓冲区的内存并不是越大越好。
- 内核缓冲区决定了滑动窗口的上限,缓冲区可分为:发送缓冲区 tcp_wmem 和接收缓冲区 tcp_rmem。
-
所以TCP 的传输速度,受制于发送窗口与接收窗口,以及网络设备传输能力
如何确定最大的传输速度?
-
首先理解:网络时延积=RTT*带宽,它决定网络中飞行报文的大小
- 比如最大带宽是 100 MB/s,网络时延(RTT)是 10ms 时,意味着客户端到服务端的网络一共可以存放 100MB/s * 0.01s = 1MB 的字节。
-
由于发送缓冲区大小决定了发送窗口的上限,而发送窗口又决定了「已发送未确认」的飞行报文的上限。因此,发送缓冲区不能超过「带宽时延积」。
-
怎样调整发送缓冲区?
- echo “4096 16384 4194304” >/proc/sys/net/ipv4/tcp_wmem 自动开启的
- 第一个是动态范围的最小值(byte),而二个数值位初始化值,第三个为动态范围的最大值;
- 发送缓冲区是自行调节的,当发送方发送的数据被确认后,并且没有新的数据要发送,就会把发送缓冲区的内存释放掉
-
怎样调整接受缓冲区?
- echo “4096 16384 4194304” >/proc/sys/net/ipv4/tcp_rmem
- 如果空闲内存(tcp_mem)很多,就可以自动把缓冲区增大一些,这样传给对方的接收窗口也会变大,因而提升发送方发送的传输数据数量;
- 反正,如果内存(tcp_mem)很紧张,就会减少缓冲区,这虽然会降低传输效率,可以保证更多的并发连接正常工作;
- 接受缓冲区开启需要配置:配置 tcp_moderate_rcvbuf 为 1 来开启调节功能
-
怎么调节TCP内存范围?
-
echo “88560 118080 177120” > /proc/sys/net/ipv4/tcp_mem
-
上面三个数字单位不是字节,而是「页面大小」,1 页表示 4KB
- 当 TCP 内存小于第 1 个值时,不需要进行自动调节;
- 在第 1 和第 2 个值之间时,内核开始调节接收缓冲区的大小;
- 大于第 3 个值时,内核不再为 TCP 分配新内存,此时新连接是无法建立的;
-
-
高并发服务器
- 在高并发服务器中,为了兼顾网速与大量的并发连接,我们应当保证接收缓冲区的动态调整的最大值达到带宽时延积,而接收缓冲区最小值保持默认的 4K 不变即可。而对于内存紧张的服务而言,调低默认值是提高并发的有效手段。
- 如果这是网络 IO 型服务器,那么,调大 tcp_mem 的上限可以让 TCP 连接使用更多的系统内存,这有利于提升并发能力。需要注意的是,tcp_wmem 和 tcp_rmem 的单位是字节,而 tcp_mem 的单位是页面大小。而且,千万不要在 socket 上直接设置 SO_SNDBUF 或者 SO_RCVBUF,这样会关闭缓冲区的动态调整功能。
以上内容总结于微信公众号:小林coding,一个很优秀的coder和订阅号up主;