Linux内核性能优化TCP调优

Posted Xd聊架构

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux内核性能优化TCP调优相关的知识,希望对你有一定的参考价值。

文章目录


前言

TCP 是面向连接的、可靠的、双向传输的传输层通信协议,所以在传输数据之前需要经过三次握手才能建立连接。

接下来,将以三个角度来阐述提升 TCP 的策略,分别是:

  • TCP 三次握手的性能提升;
  • TCP 四次挥手的性能提升;
  • TCP 数据传输的性能提升;

一、TCP三次握手原理

三次握手的过程在一个 HTTP 请求的平均时间占比 10% 以上,在网络状态不佳、高并发或者遭遇 SYN 攻击等场景中,如果不能有效正确的调节三次握手中的参数,就会对性能产生很多的影响。

如何正确有效的使用这些参数,来提高 TCP 三次握手的性能,这就需要理解「三次握手的状态变迁」,这样当出现问题时,先用 netstat 命令查看是哪个握手阶段出现了问题。


三次握手建立连接的首要目的是「同步序列号」。

只有同步了序列号才有可靠传输,TCP 许多特性都依赖于序列号实现,比如流量控制、丢包重传等,这也是三次握手中的报文称为 SYN 的原因,SYN 的全称就叫 Synchronize Sequence Numbers(同步序列号)。

客户端作为主动发起连接方,首先它将发送 SYN 包,于是客户端的连接就会处于 SYN_SENT 状态。

当服务端收到 SYN 包后,服务端会立马回复 SYN+ACK 包,表明确认收到了客户端的序列号,同时也把自己的序列号发给对方。

此时,服务端出现了新连接,状态是 SYN_RCV。在这个状态下,Linux 内核就会建立一个「半连接队列」来维护「未完成」的握手信息,当半连接队列溢出后,服务端就无法再建立新的连接。


二、TCP四次挥手原理

客户端和服务端双方都可以主动断开连接,通常先关闭连接的一方称为主动方,后关闭连接的一方称为被动方。

可以看到,四次挥手过程只涉及了两种报文,分别是 FIN 和 ACK:

  • FIN 就是结束连接的意思,谁发出 FIN 报文,就表示它将不会再发送任何数据,关闭这一方向上的传输通道;
  • ACK 就是确认的意思,用来通知对方:你方的发送通道已经关闭;

四次挥手的过程:

  • 当主动方关闭连接时,会发送 FIN 报文,此时发送方的 TCP 连接将从 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 报文后,被动方的连接就会关闭。

你可以看到,每个方向都需要一个 FIN 和一个 ACK,因此通常被称为四次挥手。
这里一点需要注意是:主动关闭连接的,才有 TIME_WAIT 状态。

关闭的连接的方式通常有两种,分别是 RST 报文关闭和 FIN 报文关闭。

如果进程异常退出了,内核就会发送 RST 报文来关闭,它可以不走四次挥手流程,是一个暴力关闭连接的方式。

安全关闭连接的方式必须通过四次挥手,它由进程调用 close 和 shutdown 函数发起 FIN 报文(shutdown 参数须传入 SHUT_WR 或者 SHUT_RDWR 才会发送 FIN)。


三、性能优化

1.TCP三次握手优化

tcp_syn_retries

对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃。不应该大于255。
通常,第一次超时重传是在 1 秒后,第二次超时重传是在 2 秒,第三次超时重传是在 4 秒后,第四次超时重传是在 8 秒后,第五次是在超时重传 16 秒后。没错,每次超时的时间是上一次的 2 倍。

当第五次超时重传后,会继续等待 32 秒,如果仍然服务端没有回应 ACK,客户端就会终止三次握手。所以,总耗时是 1+2+4+8+16+32=63 秒,大约 1 分钟左右。

默认值:5
建议值:net.ipv4.tcp_syn_retries = 3
3次也就是间隔15s会放弃重传(实际时间可能稍高,因为需要包括报文传输和处理的时间)

tcp_max_syn_backlog

tcp_max_syn_backlog 是内核保持的未被 ACK 的 SYN 包最大队列长度,超过这个数值后,多余的请求会被丢弃。

默认值:1024
建议值:net.ipv4.tcp_max_syn_backlog = 4096

somaxconn

somaxconn 是一个 socket 上等待应用程序 accept() 的最大队列长度。

默认值:128
建议值:net.core.somaxconn= = 65535

syncookies

当syn_backlog队列溢出时,启用cookies来处理,可防范少量SYN攻击

syncookies 参数主要有以下三个值:

  • 0:表示关闭该功能;
  • 1:表示仅当 SYN 半连接队列放不下时,再启用它;
  • 2:表示无条件开启功能;

默认值:0
建议值:net.ipv4.tcp_syncookies = 1

tcp_synack_retries

如果服务器没有收到 ACK,就会重发 SYN+ACK 报文,synack即为重试次数,原理同tcp_syn_retries

默认值:5
建议值:net.ipv4.tcp_synack_retries = 2

tcp_abort_on_overflow

accept 队列已满,可以选择向客户端发送 RST 复位报文,告诉客户端连接已经建立失败。

tcp_abort_on_overflow 共有两个值分别是 0 和 1,其分别表示:

  • 0 :如果 accept 队列满了,那么 server 扔掉 client 发过来的 ack ;
  • 1 :如果 accept 队列满了,server 发送一个 RST 包给 client,表示废掉这个握手过程和这个连接;

默认值:0
建议值:net.ipv4.tcp_abort_on_overflow = 0

2.TCP四次挥手优化

tcp_orphan_retries

四次挥手主动方发送 FIN 报文后,连接就处于 FIN_WAIT1 状态,正常情况下,如果能及时收到被动方的 ACK,则会很快变为 FIN_WAIT2 状态。

但是当迟迟收不到对方返回的 ACK 时,连接就会一直处于 FIN_WAIT1 状态。此时,内核会定时重发 FIN 报文,其中重发次数由 tcp_orphan_retries 参数控制(注意,orphan 虽然是孤儿的意思,该参数却不只对孤儿连接有效,事实上,它对所有 FIN_WAIT1 状态下的连接都有效)

默认值:0,特指8次
建议值:net.ipv4.tcp_orphan_retries= 0

tcp_max_orphans

当进程调用了 close 函数关闭连接,此时连接就会是「孤儿连接」,因为它无法在发送和接收数据。Linux 系统为了防止孤儿连接过多,导致系统资源长时间被占用,就提供了 tcp_max_orphans 参数。如果孤儿连接数量大于它,新增的孤儿连接将不再走四次挥手,而是直接发送 RST 复位报文强制关闭。

默认值:8192
如果遇到恶意DOS攻击,FIN 报文根本无法发送出去时,可设置此值

tcp_fin_timeout

对于 close 函数关闭的孤儿连接,由于无法在发送和接收数据,所以这个状态不可以持续太久,而 tcp_fin_timeout 控制了这个状态下连接的持续时长。

默认值:60(秒)
建议值:net.ipv4.tcp_fin_timeout= 15

tcp_max_tw_buckets

定义系统在同一时间最多能有多少 TIME_WAIT 状态,当超过这个值时,系统会直接删掉这个 socket 而不会留下 TIME_WAIT 的状态。

tcp_tw_reuse

依赖 TCP 时间戳,即 net.ipv4.tcp_timestamps = 1,tw_reuse 会根据 TCP 时间戳决定是否复用 TIME_WAIT socket,在 tw_buckets 满的时候,会根据 TCP 时间戳决定是否复用 TIME WAIT socket,选取一个已经持续1秒以上的连接,复用这个五元组,这个选项只对客户端(反代服务器连接 upstream 时也可认为是客户端)有效。

默认值:0
建议值:net.ipv4.tcp_tw_reuse = 1

tcp_timestamps

通常需要开启,用于精确计算RTT,避免序列号回绕时序列号碰撞而产生错误,而且tw_reuse也是需要基于tcp timestamp开启来使用。

默认值:1
建议值:net.ipv4.tcp_timestamps = 1

3.TCP数据传输优化

tcp_window_scaling

tcp_window_scaling表示设置tcp/ip会话的滑动窗口大小是否可变。参数值为布尔值,为1时表示可变,为0时表示不可变。tcp/ip通常使用的窗口最大可达到 65535 字节,对于高速网络,该值可能太小,这时候如果启用了该功能,可以使tcp/ip滑动窗口大小增大数个数量级,从而提高数据传输的能力。

默认值:1
建议值:net.ipv4.tcp_window_scaling = 1

tcp_wmem

tcp_wmem包含3个整数值,分别是:min,default,max
Min:为TCP socket预留用于发送缓冲的内存最小值。每个TCP socket都可以使用它。
Default:为TCP socket预留用于发送缓冲的内存数量,默认情况下该值会影响其它协议使用的net.core.wmem中default的 值,一般要低于net.core.wmem中default的值。
Max:为TCP socket预留用于发送缓冲的内存最大值。该值不会影响net.core.wmem_max,今天选择参数SO_SNDBUF则不受该值影响。默认值为 128K。

默认值:4096 16384 131072
建议值:net.ipv4.tcp_wmem = 4096 65536 4194304

tcp_rmem

tcp_rmem包含3个整数值,分别是:min,default,max
Min:为TCP socket预留用于接收缓冲的内存数量,即使在内存出现紧张情况下TCP socket都至少会有这么多数量的内存用于接收缓冲。
Default:为TCP socket预留用于接收缓冲的内存数量,默认情况下该值影响其它协议使用的 net.core.wmem中default的 值。该值决定了在tcp_adv_win_scale、tcp_app_win和tcp_app_win的默认值情况下,TCP 窗口大小为65535。
Max:为TCP socket预留用于接收缓冲的内存最大值。该值不会影响 net.core.wmem中max的值,今天选择参数 SO_SNDBUF则不受该值影响。

默认值:4096 87380 174760
建议值:net.ipv4.tcp_rmem = 4096 87380 4194304

tcp_mem

tcp_mem包含3个整数值,分别是:low,pressure,high
Low:当TCP使用了低于该值的内存页面数时,TCP不会考虑释放内存。
Pressure:当TCP使用了超过该值的内存页面数量时,TCP试图稳定其内存使用,进入pressure模式,当内存消耗低于low值时则退出 pressure状态。
High:允许所有tcp sockets用于排队缓冲数据报的页面量。
一般情况下这些值是在系统启动时根据系统内存数量计算得到的。

默认值:24576 32768 49152
建议值:net.ipv4.tcp_mem = 786432 1048576 1572864


四、参数汇总

上述TCP调优参数汇总,将如下内容写入:/etc/sysctl.conf,执行sysctl -p 命令使配置文件生效。

注意:

  • 不要以为我们在/etc/sysctl.conf 中配置了a.b.c = 1 ;sysctl -p 之后就可以在/proc/sys/就生成对应的目录和文件,结果是error的
  • sysctl -p 不会重新载入未定义的配置的默认值的,只会更新配置文件中配置的内容,不会影响没有配置到的内容。
net.ipv4.tcp_syn_retries = 3
net.ipv4.tcp_max_syn_backlog = 4096
net.core.somaxconn= = 65535
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_fin_timeout= 15
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_wmem = 4096 65536 4194304
net.ipv4.tcp_rmem = 4096 87380 4194304
net.ipv4.tcp_mem = 786432 1048576 1572864

结尾

  • 感谢大家的耐心阅读,如有建议请私信或评论留言。
  • 如有收获,劳烦支持,关注、点赞、评论、收藏均可,博主会经常更新,与大家共同进步

以上是关于Linux内核性能优化TCP调优的主要内容,如果未能解决你的问题,请参考以下文章

linux内核参数优化调优

内核参数调优

linux内核优化参数

linux性能优化实战-网络性能调优

TCP/IP及内核参数优化调优

高流量大并发Linux TCP 性能调优