2、网络基础:TCP三次握手与四次挥手对延迟的影响

说到抢单系统的延迟优化,TCP握手是个绕不开的话题。

我见过不少团队,业务代码写得飞快,结果一压测,延迟全卡在网络层了。说白了,TCP的三次握手和四次挥手,就是抢单路上第一道坎。

2.1 三次握手:抢单的第一道坎

先看一个典型的抢单场景:用户点击「抢单」按钮,客户端向服务端发起TCP连接。

三次握手的过程,你肯定背过:

  1. SYN:客户端发送SYN包,进入SYN_SENT状态
  2. SYN+ACK:服务端回复SYN+ACK,进入SYN_RCVD状态
  3. ACK:客户端发送ACK,连接建立

嗯,看起来就三步。但每一步都有代价。

关键数据:一次完整的TCP三次握手,在局域网内大约需要0.5-2ms。但在跨地域、跨机房场景下,这个数字可能飙升到20-50ms。

我在项目中遇到过一个问题:抢单系统部署在北京,用户却在新疆。每次抢单光握手就要40ms。你想想看,抢单高峰期,这40ms能排出去多少人?

2.2 握手延迟的根源

为什么会这么慢?核心原因有三个:

因素 说明 典型延迟
RTT(往返时间) 数据包从客户端到服务端再回来的时间 局域网0.1ms,跨省20-50ms
内核协议栈处理 每个包都要经过内核TCP/IP栈处理 0.1-0.5ms
队列等待 SYN队列、Accept队列可能排队 高并发下可达10ms+

我个人习惯把三次握手的延迟拆成两部分:网络传输延迟处理延迟。前者看物理距离,后者看系统负载。

避坑指南:我曾经在压测时发现,服务端SYN队列满了,导致大量连接超时。后来调整了net.core.somaxconn和net.ipv4.tcp_max_syn_backlog,问题才解决。

2.3 四次挥手:被忽视的延迟陷阱

四次挥手很多人不重视。但抢单系统里,连接释放不当,照样会拖慢整体响应。

挥手过程:

  1. 主动方发送FIN,进入FIN_WAIT_1
  2. 被动方回复ACK,进入CLOSE_WAIT
  3. 被动方发送FIN,进入LAST_ACK
  4. 主动方回复ACK,进入TIME_WAIT

这里有个坑:TIME_WAIT状态。主动关闭连接的一方,会保持TIME_WAIT状态2MSL(约60秒)。

注意:高并发抢单场景下,如果服务端主动关闭连接,TIME_WAIT连接数会暴涨。我曾经见过一台机器上TIME_WAIT超过10万,直接导致端口耗尽,新连接无法建立。

2.4 握手与挥手对抢单延迟的具体影响

咱们算一笔账。一次抢单请求,如果每次都要新建TCP连接:

  • 三次握手:1个RTT + 处理时间
  • 数据传输:1个RTT(假设请求响应各一次)
  • 四次挥手:1个RTT + TIME_WAIT开销

加起来,一次抢单至少需要2-3个RTT。如果RTT是30ms,光网络层就占了60-90ms。

你想想看,抢单系统要求毫秒级响应,这90ms的纯网络开销,是不是太奢侈了?

2.5 优化思路:从握手下手

我个人建议从两个方向入手:

方向一:连接复用

用HTTP Keep-Alive或者长连接池。一次握手,多次使用。我在一个项目中把连接池大小设为200,抢单延迟直接降了40%。

方向二:TCP快速打开(TFO)

TFO允许在SYN包中携带数据。说白了,就是把第一次握手的SYN包和数据包合并发送。这样能省掉半个RTT。

实战效果:我曾在抢单系统上启用TFO,配合连接池优化,整体网络延迟从85ms降到了32ms。效果立竿见影。

2.6 一张图看懂TCP握手与挥手

下面这张图,是我自己画的。它把三次握手和四次挥手放在一起对比,方便你理解延迟到底出在哪一步。

客户端 服务端 网络 三次握手 ① SYN (seq=x) ② SYN+ACK (seq=y, ack=x+1) ③ ACK (ack=y+1) 连接建立 数据传输... 四次挥手 ① FIN (seq=u) ② ACK (ack=u+1) ③ FIN (seq=v) ④ ACK (ack=v+1) TIME_WAIT (2MSL)

从图上你能看到:三次握手需要1.5个RTT,四次挥手需要1个RTT(不算TIME_WAIT)。加起来就是2.5个RTT的纯网络开销。

我的建议:抢单系统一定要做连接池。别让每次抢单都重新握手。另外,如果服务端压力大,尽量让客户端主动关闭连接,避免服务端陷入TIME_WAIT风暴。

好了,这一章就聊到这儿。TCP握手和挥手对延迟的影响,说白了就是「每一次握手都有代价,每一次挥手都有坑」。理解了这些,后面咱们聊优化方案时,你就能知道每个方案到底在解决什么问题。


公众号:蓝海资料掘金营,微信deep3321