2、网络基础:TCP三次握手与四次挥手对延迟的影响
说到抢单系统的延迟优化,TCP握手是个绕不开的话题。
我见过不少团队,业务代码写得飞快,结果一压测,延迟全卡在网络层了。说白了,TCP的三次握手和四次挥手,就是抢单路上第一道坎。
2.1 三次握手:抢单的第一道坎
先看一个典型的抢单场景:用户点击「抢单」按钮,客户端向服务端发起TCP连接。
三次握手的过程,你肯定背过:
- SYN:客户端发送SYN包,进入SYN_SENT状态
- SYN+ACK:服务端回复SYN+ACK,进入SYN_RCVD状态
- 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 四次挥手:被忽视的延迟陷阱
四次挥手很多人不重视。但抢单系统里,连接释放不当,照样会拖慢整体响应。
挥手过程:
- 主动方发送FIN,进入FIN_WAIT_1
- 被动方回复ACK,进入CLOSE_WAIT
- 被动方发送FIN,进入LAST_ACK
- 主动方回复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握手与挥手
下面这张图,是我自己画的。它把三次握手和四次挥手放在一起对比,方便你理解延迟到底出在哪一步。
从图上你能看到:三次握手需要1.5个RTT,四次挥手需要1个RTT(不算TIME_WAIT)。加起来就是2.5个RTT的纯网络开销。
我的建议:抢单系统一定要做连接池。别让每次抢单都重新握手。另外,如果服务端压力大,尽量让客户端主动关闭连接,避免服务端陷入TIME_WAIT风暴。
好了,这一章就聊到这儿。TCP握手和挥手对延迟的影响,说白了就是「每一次握手都有代价,每一次挥手都有坑」。理解了这些,后面咱们聊优化方案时,你就能知道每个方案到底在解决什么问题。
公众号:蓝海资料掘金营,微信deep3321