ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

TCP/IP协议核心原理与网络排障实战指南

TCP/IP协议核心原理与网络排障实战指南 干网络运维和后台开发这么多年每次面试年轻同事问一句“TCP/IP协议是什么”十个人里有九个能背出“四层模型、三次握手”可真遇到线上接口变慢、连接卡住的时候不少人就懵了。TCP/IP协议是互联网真正在跑的那套底层规则它规定数据怎么切开装箱、怎么找路、怎么保证不丢不乱也直接决定了我们写代码时要不要操心消息边界、连接开销、重传延迟。这篇文章把TCP/IP协议的原理和应用场景摊开讲从四层骨架到抓包排障尽量让刚接触网络的实习生跟得上也让写过几年代码的人看完真有收获。内容适合网络运维、后端开发、测试以及准备面试的朋友读完你至少能明白日常那些“莫名其妙”的网络问题其实都有迹可循。1. 先搞懂TCP/IP的四层骨架后面才不迷路1.1 四层模型为什么比七层更实用很多教材一上来就是OSI七层模型但现实世界跑的是TCP/IP四层模型这不是巧合。OSI七层把通信过程拆得非常细可实际落地时有些层根本没法独立存在比如表示层和会话层的活儿基本都被应用层自己干掉了物理层和数据链路层又经常被网卡驱动绑在一起。所以TCP/IP在RFC 1122里只定义了四层网络接口层、网际层、传输层、应用层。有些教材为了把物理介质讲清楚会把网络接口层拆成物理层和链路层变成五层这也没错但我排障时脑子里始终保留四层就够用了。我常跟新人打一个比方四层模型像寄快递。应用层是你在电商下单填了收件人地址写的是“我要什么内容”传输层是快递公司给你装箱、编号、开回执保证包裹不丢不乱网际层是运输干线决定这一单走哪条路、下一站交给谁网络接口层是快递员骑着电动车在你家和转运站之间跑腿。每一层的“下一跳”都不关心包裹里装的是什么只管把东西安全交给相邻的那一环。分层的价值就在于各层可以独立演化。传输层不想操心你的数据是走光纤还是Wi-Fi网际层不想知道你在传网页还是打游戏应用层也不用关心底层路由发生了多少次转发。真出问题时分层能把排查范围迅速缩小连不上先判断是链路问题、DNS问题、端口问题还是服务本身挂了。这种逐层定位的思路依赖的就是清晰的协议分层。OSI七层TCP/IP四层核心协议/设备应用层、表示层、会话层应用层HTTP、HTTPS、DNS、FTP、SSH传输层传输层TCP、UDP网络层网际层IP、ICMP、ARP接口处数据链路层、物理层网络接口层以太网帧、交换机、网卡OSI里的表示层和会话层在TCP/IP体系里没有单独成层加密、压缩、会话状态统统交给应用程序自行处理。这不代表这些功能消失了而是设计上“简化边界、把复杂度往上推”。比如TLS逻辑上跑在TCP之上、HTTP之下抓包时你会看到它紧挨着TCP头但协议栈里没有专门给TLS留一个层位。理解了这一点看抓包列表时才不会对着层次发懵。1.2 每层靠什么干活地址、端口与封装网络接口层最常见的是以太网靠MAC地址在同一个广播域里找到目标设备。它的主要工作是给上层数据套上以太网帧头加上源MAC、目的MAC和帧尾校验。这个层里有个很容易被忽略的配角叫ARP负责把IP地址翻译成MAC地址。你ping局域网内一台机器时抓包能看到第一条往往不是ICMP请求而是ARP广播“谁的192.168.1.1请告诉我你的MAC地址。”网际层的核心是IP协议负责寻址和路由但本身不做可靠性保证属于“尽力而为”。IPv4地址有32位IPv6地址有128位它们的共同使命是把数据包从源机器一路转发到目的机器。IP层要干的事包括把上层数据切成不超过MTU的包以太网典型MTU是1500字节在路由表里选择下一跳通过TTL字段防止数据包无限兜圈子。我们常用的ping命令内核就是通过ICMP协议实现ICMP属于网际层配套工具排障时非常重要。传输层就是TCP和UDP的战场。两者最大的区别一个面向连接、可靠、有序一个无连接、尽力交付。这一层引入了端口号16位取值范围0到65535。端口用来在一台机器的众多进程之间区分数据该交给谁。比如服务器同时跑了Nginx和MySQL请求到达网卡后TCP头里的目的端口是80内核就把数据丢给Nginx目的端口是3306就丢给MySQL。很多新手在这里犯迷糊IP地址解决的是“找哪台机器”端口解决的是“找机器上的哪个进程”两个维度缺一不可。端口资源耗尽也是线上常见事故尤其在高并发短连接场景下后续章节会专门展开。数据从发送端到接收端每一层都会加头这叫封装接收端从下往上逐层拆头这叫解封装。应用层把业务数据交给TCPTCP加上包含源端口、目的端口、序号、标志位的TCP头再交给IPIP加上包含源IP、目的IP、TTL的IP头最后链路层加上以太网头和帧尾。你在Wireshark里看到的每一个包其实是带着所有层头的完整帧。能一眼分辨出哪段是TCP头、哪段是IP头是抓包分析的基本功这个功夫后面实战环节能直接派上用场。2. 三次握手与四次挥手把可靠连接讲透2.1 三次握手的本质以及少一次会怎样三次握手是TCP建立连接的核心流程我结合seq和ack讲因为这两个概念面试和排障都会用到。第一步客户端发一个SYN包携带自己的初始序列号seqx。第二步服务端收到后回复SYNACK携带自己的初始序列号seqy同时把确认号ack设为x1。第三步客户端再回一个ACKseqx1acky1。到这里连接才算建立双方进入ESTABLISHED状态。很多人背下了这个流程却不明白为什么必须三次。假设只有两次握手客户端发了一个SYN因为网络拥塞迟迟没到客户端等不及超时重发SYN服务端收到重发的SYN后建立连接、传完数据并释放。可就在这时最初那个迟到的SYN又飘到服务端了服务端以为客户端要发起新连接于是分配资源、回复SYNACK进入ESTABLISHED白白占着一个啥也不是的连接。有了第三次握手客户端不会回复最终ACK服务端就知道这个连接不该建立。所以三次握手本质上是不可靠信道上的双向确认机制一次确认对方能收一次确认自己能发最后一次让双方对齐初始序号。序列号解决的是字节流顺序问题。TCP把数据看成连续的字节流每个字节都有一个序号TCP头里的seq标记本段第一个字节的序号。一个1MB的文件会被切成很多段发送每段seq不同接收方按序号排序才能还原原文件。乱序到达靠序号重排重复到达靠序号去重确认号ack表示“我期待收到的下一个字节序号”是累积确认。滑动窗口和重传机制都建立在这套序号体系上后面章节会展开。实操里我建议自己抓一次握手看看。用tcpdump抓指定端口tcpdump -nn -i any port 80 tcp[tcpflags] tcp-syn ! 0。你会发现客户端发SYN的seq是个随机初始值服务端回包里的ack正好等于这个seq加1。如果线上服务端出现大量SYN_RECV状态堆积说明握手卡在第二步很可能是半连接队列满了或者遭受了SYN Flood典型症状是客户端连接一直处于SYN_SENT接口超时甚至拒绝连接。排查时用netstat -s看SYN丢包的计数器再配合ss -lnt看连接队列的溢出情况基本能定位。2.2 四次挥手和TIME_WAIT这个老熟人断开连接为什么是四次而不是三次因为TCP是全双工的两个方向的数据通道要分别关闭。主动关闭方发FIN意思是“我这一侧的数据发完了”被动方回ACK说“我知道了但我还有数据要发”被动方把剩余数据发完再发FIN主动方回ACK然后进入TIME_WAIT。我常跟团队说这就像下班收拾工位你说“我先走了”FIN同事说“好的慢走”ACK同事把自己桌面的活干完说“我也走了”FIN你回一句“拜拜”ACK。四次缺一不可。TIME_WAIT是面试高频也是线上问题常客。主动关闭方收到对端FIN后不能立刻关闭要等2MSL两倍报文最大生存时间常见约60秒。原因有二第一确保最后一个ACK万一丢了被动方重发FIN时还能再补一个ACK不让对端卡在LAST_ACK第二让旧连接里所有迟到的报文在网络中自然消失避免污染下一个使用相同四元组的新连接。如果不等这2MSL新连接很可能收到旧连接的脏数据导致诡异的数据错乱。线上常见现象是服务器端堆积大量TIME_WAIT多半出现在高并发短连接场景。TIME_WAIT本身不可怕它是TCP正确性的保障可怕的是端口被占满导致新连接无端口可用。对策优先级应该是先让业务用长连接或者连接池减少主动断开如果实在要大量短连接再考虑客户端侧开启tcp_tw_reuse注意这个参数只在发起连接的方向有效不能消除TIME_WAIT只是安全复用。千万别为了消除TIME_WAIT去改短2MSL那样会把脏数据风险引进来线上会出现更难排查的偶发错乱。顺带要记住一个状态TIME_WAIT只出现在主动关闭方。如果服务器是主动断连的一方你会看到大量TIME_WAIT如果服务器是被动方连接一直不收口通常表现为大量CLOSE_WAIT。CLOSE_WAIT堆积说明服务端收到了FIN但应用进程没有调用close关掉socket这基本是代码层面连接资源泄漏查业务逻辑里的连接释放任何内核参数都救不了。我见过很多团队在内核参数上折腾一整天最后发现是连接池没回收方向完全反了。3. 可靠性设计窗口、确认与拥塞控制3.1 确认号、滑动窗口和“传输像发货”TCP的可靠性建立在“确认重传”上发送方发出数据接收方回ACK发送方收到ACK才继续。问题是如果一包一停整个链路利用率会低得离谱。所以TCP引入了滑动窗口允许发送方一次发多个数据包不需要等每个包的ACK回来窗口大小随时可调。接收方在TCP头的Window字段里告诉发送方“我的缓冲区还能收多少”这个叫接收窗口rwnd。窗口单位是字节不是包数。可以用三根指针理解滑动窗口已发送已确认、已发送未确认、还可发送。每当ACK到达右边界就向前推发送方继续发送新数据。如果某个包超时没收到确认或者收到连续重复ACK就触发重传。窗口越大吞吐越高但接收方缓冲区压力也越大网络拥塞时丢包反而增多。所以发送方还有一个自己维护的拥塞窗口cwnd代表“当前网络能承受的数据量”。实际能发的数据量取rwnd和cwnd的较小值这两套窗口机制一个管接收端能力一个管网络容量缺一不可。我常把这个过程比作批量进货。网络顺畅时平台允许你一次下单100件窗口就大网络开始卡顿、丢件时平台提醒你少买点窗口就收紧。TCP就是这个“平台”它不断根据反馈调整你能发多少货。另外要注意TCP头的Window字段只有16位理论上最大65535字节现代高速链路早就超出这个量级了。所以TCP在握手时通过Window Scale选项把窗口放大几十倍甚至几百倍很多老内核默认开启不了这个选项大带宽链路吞吐上不去第一嫌疑就是它。抓包时在Wireshark里能看到每个TCP段的“Window size value”和“Calculated window size”后者的数值往往大得多就是因为乘了缩放因子。3.2 拥塞控制四件套慢启动、拥塞避免、快重传、快恢复TCP拥塞控制经历了慢启动、拥塞避免、快重传、快恢复四个阶段现代Linux默认的CUBIC算法也是在这个骨架上演化来的。连接刚建立时发送方不知道网络水深水浅所以cwnd从一个很小的初始值开始常见是10个MSS每收到一个ACKcwnd指数翻倍。这个“慢”不是说增长慢而是起步慢实际上翻倍速度惊人几个往返就能冲到高位。当cwnd到达慢启动阈值ssthresh就切换进入拥塞避免增长方式变成每经过一个RTT只增加一个MSS线性试探上限。指数增长是为了快速探测可用带宽线性增长是为了逼近上限时不至于一头撞墙。当发生超时说明网络严重拥塞连ACK都回不来TCP会把ssthresh设为当前cwnd的一半cwnd归为1重新慢启动。这属于最保守的应对。后来业界觉得一丢包就归零太伤吞吐于是有了快重传和快恢复。快重传的逻辑是只丢了某一个包后面的包顺利到达接收方每收到一个乱序包就重复返回“我期待收到那个丢失字节的ACK”。发送方连续收到三个重复ACK不必傻等超时立即重传丢失段。收到重复ACK说明网络还有余量只是某个包被丢了所以快恢复把cwnd减半而不是归1然后进入拥塞避免线性增长。这套组合拳让TCP在丢包后能更快恢复吞吐。作为应用开发者不需要精确计算每一步的cwnd但必须理解一个核心结论网络丢包会触发TCP退避导致吞吐骤降。尤其在高带宽长链路场景一次丢包带来的重传和窗口收缩可能让应用吞吐掉一个数量级。如果你的服务端应用频繁出现大流量尖峰先别急着怀疑代码抓包看看有没有重复ACK、有没有重传、有没有乱序很可能问题在链路上。我见过不少“半夜告警”和“高峰必卡”的案例最后都是链路丢包率超标闹的跟业务代码毫无关系。3.3 TCP栈里的隐藏行为Nagle、延迟ACK与队头阻塞TCP内部有几个经常给应用层“上课”的隐藏机制第一个是Nagle算法。它诞生于网络带宽极贵的年代思路很简单如果发送方已经有未确认的小数据包在途后续小包就不立即发送先攒在缓冲区里等前一个包的ACK回来或者攒够一个MSS再发出去。这对交互式应用影响很大比如一个请求体很小的接口你可能会观察到偶发40ms左右的延迟典型的Nagle算法和延迟ACK互相等待的“锁死”现场。解决方式很直接对延迟敏感的小包交互开启TCP_NODELAY禁用Nagle。这个选项在Java里是setTcpNoDelay(true)在Linux下就是setsockopt设置TCP_NODELAY。延迟ACK是另一个容易被误解的机制。接收方收到数据后不会立刻回ACK而是等最多200ms左右的窗口期如果这段时间里有数据要发就搭顺风车把ACK捎带出去。它在很多场景下能减少ACK包数量但对某些“写一小段-等回应”的应用模式会把本来可以立即继续的流程拖慢。抓包时如果看到某一端先收了数据隔了200ms才回ACK多半就是延迟ACK在起作用。这类问题的排查经验是同时检查两端的Nagle和延迟ACK设置很多用户态网络库已经在默认配置里处理好了但自己用原生态socket写服务时要格外小心。TCP还有一个绕不开的设计缺陷就是队头阻塞。因为TCP保证字节流有序接收方必须把数据重排成完整顺序才能交给应用层一旦某个包丢失后续即使已经到的数据也要等在缓冲区里直到重传成功。这个特性保证了可靠性但也意味着一条TCP连接里一个丢包会阻塞后续所有数据。HTTP/2虽然实现了多路复用多个请求可以共享一条TCP连接并行传输但只要底层还是TCP队头阻塞就依然存在。这也是HTTP/3选择底层改用基于UDP的QUIC的核心原因之一。了解了这个背景你在全站升级HTTP/2之后发现某些弱网场景依然卡顿就不会觉得奇怪了。顺带再补一个经常被误解的“粘包半包”问题。TCP是字节流没有消息边界应用层发送两次writeTCP完全可能把它们合并成一次传输也可能把一个消息切成两段。所谓粘包和半包本质都是应用层拿不到完整消息。解决思路不是去改TCP而是让应用层协议自带边界识别能力用固定长度、用分隔符、或者最常用的“长度字段消息体”。HTTP协议头就以\r\n\r\n作为边界。很多新手在这里问能不能关掉Nagle来解决粘包答案是治标不治本消息边界必须由应用层协议自己定义。4. 应用场景怎么选TCP、UDP与QUIC4.1 TCP和UDP的选择清单TCP适合不能丢数据的场景网页浏览、文件传输、数据库连接、邮件推送这些业务对顺序和完整性要求极高多等几十毫秒可以接受但数据绝对不能错。UDP适合能容忍少量丢失、但对实时性要求很高的场景实时音视频、游戏同步、DNS查询、NTP校时。有些应用索性在UDP之上自己实现了可靠传输比如一些直播系统自己做了丢包重传和前向纠错就是为了在低延迟和可靠性之间拿到更优解。选择传输层协议本质是在“可靠”和“及时”之间做取舍。维度TCPUDP连接状态面向连接需要握手挥手无连接即发即走可靠性确认重传、顺序保证不保证可能丢、乱、重头部开销20字节不含选项8字节流量/拥塞控制有网络拥塞会自动退避无应用自己控制典型场景网页、文件、数据库、消息音视频、游戏、DNS实际工作中不要因为UDP“不可靠”就拒绝它也不要因为TCP“稳”就把所有流量都往上堆。一个强实时多人同步游戏如果硬用TCP一个包的丢包重传可能让所有玩家同时卡顿这比偶尔飘一下的体验差多了。反过来一个要求严格落库的交易系统如果图快走UDP业务层得自己补一堆重传和校验逻辑成本往往比直接用TCP高得多。选型前先回答三个问题数据允许丢吗延迟更重要还是完整性更重要我有没有精力在应用层补可靠机制答案清楚了协议自然就出来了。4.2 三个典型场景的协议设计参考场景一是大文件上传下载。核心诉求是完整和一致首选TCP。HTTP大文件传输时注意长肥链路上TCP窗口要足够大否则带宽再高也跑不满。可以检查连接的发送/接收缓冲区现代内核默认的缓冲区在低延迟局域网够用但在跨地域大带宽链路上可能需要调大tcp_wmem、tcp_rmem的上限。同时确认TCP SACK选择性确认开启这样丢包重传的成本比老式累积重传低很多。网络中断续传一般依赖HTTP的Range请求业务层自行记录断点和传输层关系不大。这个场景不建议禁用延迟ACK大文件传输本来就是批量数据禁用延迟ACK只会增加ACK包数量。场景二是实时音视频通话。核心诉求是低延迟少量丢包可以接受甚至可以通过音频前向纠错和视频插帧来弥补。这里UDP几乎是必然选择同时要自己建立重传策略比如只有关键帧丢失才重传普通帧跳过即可配合FEC在每N个数据包里插入冗余包让接收端少依赖重传就能恢复部分丢包。WebRTC就是这么干的。如果团队不想从零造轮子也可以考虑QUIC它在UDP之上提供了类似TCP的可靠性同时解决了队头阻塞连接迁移做得很优雅正适合实时通信。场景三是大批量IoT设备小包上报。每台设备每隔几秒上报几十字节设备数量一多连接数会非常吓人。无脑用TCP短连接第一个被打垮的就是连接管理模块其次是NAT表项很多物联网平台上量之后都踩过这个坑。常见方案是用MQTT over TCP加连接保活把大量设备通过长连接维护起来或者干脆UDP上报加应用层确认。无论选哪条路都要在业务层准备好心跳、断线重连、协议版本兼容这些能力。协议选型不是一步定终身很多时候是先选一个能跑起来的方案再根据真实的网络规模和故障率迭代。稍微说下QUIC这个新选项。HTTP/3把传输层换成了QUIC底层跑UDP解决了两大痛点TCP队头阻塞和握手延迟。TCP配合TLS建立一条HTTPS连接通常需要一到两个RTT而QUIC在单个RTT内完成传输层和TLS握手还在连接迁移上优于TCP——手机从Wi-Fi切到4GTCP连接大概率断开重连QUIC连接却能平滑延续。不过QUIC也有自己的脾气它依赖UDP对端到端链路的友好度有些老旧网络设备对UDP包处理能力差甚至直接丢落地时需要灰度验证。如果你在建新项目认真评估QUIC是值得的但不用迷信绝大多数现有业务跑在TCP上并没有性能瓶颈换协议反而增加风险。5. 网络变慢别瞎猜抓包排查与调优实战5.1 离不开的抓包三板斧排障三板斧抓包看现象、看状态计数、看内核统计。抓包工具我主要用tcpdump和Wireshark。服务端抓入向包用tcpdump -nn -i eth0 port 8080 -w /tmp/trace.pcap客户端抓出向包用tcpdump -nn -i any host 目标IP把抓包结果存成pcap文件再用Wireshark打开分析。抓包文件别抓太久几十秒到几分钟就够了文件太大会让分析寸步难行可以先用过滤条件把范围收窄比如只抓某个端口、某个IP、甚至某个TCP流。用Wireshark时我最关心几个标志性信息TCP Retransmission代表重传TCP Dup ACK代表重复确认TCP ZeroWindow代表对端接收窗口为0、应用层处理不过来了TCP Out-Of-Order代表乱序TCP Fast Retransmission代表快重传TCP Previous segment lost代表前序数据包丢失。过滤表达式也要熟练tcp.stream eq 0只看第一条TCP流tcp.flags.syn 1 tcp.flags.ack 0只看SYN包tcp.analysis.retransmission只看重传包。这套过滤语法是我日常最高频用到的几组。除了抓包还要会看系统和网卡的状态。netstat -anpt | grep 8080看连接状态分布ss -s汇总socket统计ethtool -S eth0 | grep -E drop|err|reset看网卡丢包计数sar -n DEV 1 5看实时PPS和带宽。链路问题导致的丢包往往先体现在网卡统计里然后才表现为TCP层的重传。如果所有工具都指向正常还得查一下CPU软中断和内存分配高PPS下丢包经常是因为主机处理能力到顶了。tcpdump -nn -i eth0 port 8080 -w /tmp/trace.pcap tcpdump -nn -i any host 10.0.0.5 -c 100 ethtool -S eth0 | grep -E drop|err|reset ss -s5.2 一份常见故障自查表我整理了一张高频故障自查表遇到问题先对着表找线索能省很多时间。注意不要孤立看某一个状态要结合连接队列、网卡计数器、应用日志做交叉判断。症状大概率原因处理思路SYN_RECV堆积、连接打不开半连接队列满可能SYN Flood或服务端accept太慢查ss -lnt的Recv-Q、netstat -s的SYN丢包计数必要时启用syncookies、调大backlog接口偶发超时、大量重传链路丢包、网卡异常、对端处理能力不足看重传率、ethtool -S统计、CPU软中断逐段ping做二分定位大量TIME_WAIT短连接服务主动断开连接优先改长连接客户端侧可考虑tcp_tw_reuse不改2MSL大量CLOSE_WAIT服务端应用没关闭socket查连接池、资源释放代码内核参数救不了ZeroWindow频繁应用消费数据速度跟不上排查应用读写逻辑、缓冲区大小、阻塞点乱序和Dup ACK多路径负载均衡不保序、多链路看持续性和影响面必要时调整链路或引入应用层容忍机制这张表不用死记理解状态机才是关键。netstat输出的每一个state都对应TCP状态机里的一个真实位置SYN_RECV说明半连接已经收到回包等应用层acceptESTABLISHED说明数据通道正常FIN_WAIT_2说明对方已经发FIN但还没收到最终ACK。状态堆积的位置不同物理世界里的原因也完全不同。5.3 一个线上接口变慢的排查实录分享一个我印象很深的案例。线上网关接口在高峰期偶发2到3秒超时开发同学第一反应是数据库慢查询可调用链拉出来数据库和缓存都正常。后来抓包网关入向包完全正常出向后端的链路却出现大量Dup ACK和Retransmission。用ethtool看网卡统计发现rx_dropped持续上涨dmesg里偶尔能看到NIC reset。结论不是应用代码问题而是网卡驱动配合虚拟网桥在高PPS下丢包。后面升级驱动、打开多队列、调整ring buffer参数丢包率立刻降下来接口超时消失。这个案例有两点很值得记住。第一先看现象再猜原因抓包是最直接的现场还原别在应用日志里考古一整晚才发现问题根本不在应用。第二排障要建立“物理层-链路层-网际层-传输层-应用层”的排查顺序数据链路层的丢包往往表现为传输层的重传你看到的“超时”只是一个结果真正的病灶藏在更底层。看到Wireshark报TCP Previous segment lost时也要回头检查是不是中间链路或负载均衡设备把包扔了而不是一上来就怪后端业务。5.4 内核参数调整要谨慎网上随便抄几个sysctl参数就上生产是我最反对的做法。内核参数是把手术刀不是锤子。几个值得了解的参数带一点使用心得。net.ipv4.tcp_syncookies用来应对SYN Flood能在半连接队列满时尽量保住新连接但开启SYN Cookie会丢失部分TCP选项协商不能作为默认常态。net.ipv4.tcp_tw_reuse允许客户端复用TIME_WAIT状态的连接前提是开启了TCP时间戳选项它只对发起连接方有效不能替代长连接策略。注意tcp_tw_recycle早就被现代内核移除了旧资料里再看到这个参数不要捡起来用NAT环境下它会破坏连接。连接队列方面net.core.somaxconn和应用的backlog参数决定全连接队列上限高并发服务容易在这里卡住表现为连接建立慢。缓冲区方面net.ipv4.tcp_rmem和tcp_wmem控制TCP收发缓冲区范围一般建议让内核自动调节手动乱改可能限制吞吐。net.ipv4.tcp_sack选择性确认在丢包场景能明显提升性能现代内核默认开启别轻易关。调整任何参数前先记录调整前的吞吐、RTT、丢包率作为基线然后小步调、压测验证、灰度观察。还要记住很多网络参数只对新建连接生效改完不重启进程或者不等待旧连接自然消亡你看到的数值不会变。MTU和MSS也是一对常见坑。MTU是最大传输单元UDP对MTU大小非常敏感因为IP层分片后接收端重组成本高很多设备直接丢弃分片。TCP在握手时通过MSS协商限制了最大段大小通常设为MTU减去40字节。当中间链路有隧道封装或者PPPoE拨号时实际MTU可能变成1480甚至更低TCP的MSS没有协商对的话就会出现大包被丢、小包正常的诡异现象。现象是网页能开、很多接口超时、ping不带载荷没问题。排查方法很简单ping -M do -s 1472 目标IP如果能通说明MTU 1500链路没问题如果不通那就逐级减小包大小找到临界值再调整MTU或MSS。6. 最后分享一点我的个人体会TCP/IP协议这套东西我建议大家不要急着把所有细节一次背完而是带着问题抓包、看状态、做实验。我自己的实践路径是用Python或nc写个简单服务本地tcpdump抓三次握手和四次挥手故意开一个socket只收不读观察ZeroWindow怎么把发送端“踩刹车”再用网络损伤工具模拟丢包观察快重传和慢启动的变化。两三个晚上折腾下来TCP在你眼里就不再是抽象概念而是一条看得见摸得着的流水线。抓包时多留意时间戳和RTT的分布很多性能问题真正要看的不是单个包而是包与包之间的时间节奏。真到了线上出问题能抓包就别靠猜能看内核统计就别只盯应用日志这一点我反复跟团队强调也确确实实帮我省下过无数个通宵。
返回列表