首页 原创 深1度 24小时 洞见 突发 娱乐 传媒 IT 电脑 软件 手机 通信 科教 游戏 科技 网络 物联网 智汽车 云计算 大数据 医疗 电商 数码 金融 教育 交通 物流 消费 AI 区块链
当前位置: 首页 > 云计算 > 正文

智汇华云 | 浅谈TCP优化

2022-10-14 10:23:16 来源:   

  背景

  我们在日常开发中经常会使用到TCP相关技术,但我们往往不会去关心TCP连接本身,下面会给大家介绍一下TCP的一些优化点。TCP 协议是由操作系统实现,所以操作系统提供了不少调节 TCP 的参数如何正确有效的使用这些参数,我们可以从TCP三次握手来提升TCP的性能。

  TCP三次握手的性能提升

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

  三次握手的过程在一个 HTTP 请求的平均时间占比 10% 以上,在网络状态不佳、高并发或者遭遇 SYN 攻击等场景中,如果不能有效正确的调节三次握手中的参数,就会对性能产生很多的影响。如何正确有效的使用这些参数,来提高 TCP 三次握手的性能,这就需要理解”三次握手的状态变迁”,这样当出现问题时,先用 netstat命令查看是哪个握手阶段出现了问题

  客户端和服务端都可以针对三次握手优化性能,但优化方式不同,接下来分别针对客户端和服务端介绍一下如何进行优化

  客户端优化

  三次握手建立连接的首要目的是「同步序列号」。只有同步了序列号才有可靠传输,TCP 许多特性都依赖于序列号实现,比如流量控制、丢包重传等,这也是三次握手中的报文称为 SYN 的原因

  客户端作为主动发起连接方,首先它将发送 SYN 包,于是客户端的连接就会处于 SYN_SENT 状态。客户端在等待服务端回复的 ACK 报文,正常情况下,服务器会在几毫秒内返回 SYN+ACK ,但如果客户端长时间没有收到 SYN+ACK 报文,则会重发 SYN 包,重发的次数由 TCP_syn_retries 参数控制,默认是 5 次

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

  可以根据网络的稳定性和目标服务器的繁忙程度修改 SYN 的重传次数,调整客户端的三次握手时间上限。比如内网中通讯时,就可以适当调低重试次数,尽快把错误暴露给应用程序。

  服务端优化

  服务端收到 SYN 包后,服务端会立马回复 SYN+ACK 包,表明确认收到了客户端的序列号,同时也把自己的序列号发给对方。此时,服务端出现了新连接,状态是 SYN_RCV。在这个状态下,Linux 内核就会建立一个「半连接队列」来维护「未完成」的握手信息,当半连接队列溢出后,服务端就无法再建立新的连接。

  我们可以通过该 netstat -s 命令给出的统计结果中, 可以得到由于半连接队列已满,引发的失败次数,若失败次数一直增加,那我们需要增加半连接对了。要想增大半连接队列,不能只单纯增大tcp_max_syn_backlog 的值,还需一同增大 somaxconn 和 backlog,也就是增大 accept 队列。否则,只单纯增大 tcp_max_syn_backlog 是无效的。增大 tcp_max_syn_backlog 和 somaxconn 的方法是修改 Linux 内核参数:

  增大 backlog 的方式,每个 Web 服务都不同,比如 Nginx 增大 backlog 的方法如下:

  改变了如上这些参数后,要重启 Nginx 服务,因为 SYN 半连接队列和 accept 队列都是在 listen() 初始化的。

  SYN_RCV状态的优化

  客户端接收到服务器发来的 SYN+ACK 报文后,就会回复 ACK 给服务器,同时客户端连接状态从 SYN_SENT 转换为 ESTABLISHED,表示连接建立成功。服务器端连接成功建立的时间还要再往后,等到服务端收到客户端的 ACK 后,服务端的连接状态才变为 ESTABLISHED。如果服务器没有收到 ACK,就会重发 SYN+ACK 报文,同时一直处于 SYN_RCV 状态。

  当网络繁忙、不稳定时,报文丢失就会变严重,此时应该调大重发次数。反之则可以调小重发次数。修改重发次数的方法是,调整 tcp_synack_retries 参数:

  tcp_synack_retries 的默认重试次数是 5 次,与客户端重传 SYN 类似,它的重传会经历 1、2、4、8、16 秒,最后一次重传后会继续等待 32 秒,如果服务端仍然没有收到 ACK,才会关闭连接,故共需要等待 63 秒。服务器收到 ACK 后连接建立成功,此时,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到 accept 队列,等待进程调用 accept 函数时把连接取出来。

  Accept队列调整

  丢弃连接只是 Linux 的默认行为,我们还可以选择向客户端发送 RST 复位报文,告诉客户端连接已经建立失败。打开这一功能需要将 tcp_abort_on_overflow 参数设置为 1。

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

  0 :如果 accept 队列满了,那么 server 扔掉 client 发过来的 ack ;

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

  如果要想知道客户端连接不上服务端,是不是服务端 TCP 全连接队列满的原因,那么可以把tcp_abort_on_overflow 设置为 1,这时如果在客户端异常中可以看到很多 connection reset by peer 的错误,那么就可以证明是由于服务端 TCP 全连接队列溢出的问题。通常情况下,应当把 tcp_abort_on_overflow 设置为 0,因为这样更有利于应对突发流量。

  所以,tcp_abort_on_overflow 设为 0 可以提高连接建立的成功率,只有你非常肯定 TCP 全连接队列会长期溢出时,才能设置为 1 以尽快通知客户端。

  总结

  TCP优化三次握手可以从四个角度入手出发进行调整,调整SYN报文的重传次数、调整SYN半连接队列长度、调整SYN+ACK报文的重传次数、调整accpet队列长度。

标签:
广告、内容合作请点这里:寻求合作