ARTICLE DETAIL

资讯详情

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

TCP/IP协议栈完全指南:架构、调优与排障实战

TCP/IP协议栈完全指南:架构、调优与排障实战 干了十几年网络相关的开发和运维见过太多人把TCP/IP协议栈背得滚瓜烂熟一到线上排查就两眼一抹黑。抓包抓到眼瞎调参调到崩溃最后只能重启大法。说白了协议栈这玩意儿不是考试用的是你每天都要跟它打交道的东西。它决定了你的接口为什么慢、你的视频为什么卡、你的游戏为什么掉线甚至是你的物联网设备为什么连不上。这篇东西不想讲成RFC文档的翻译版我想从一个实际干活的工程师角度把TCP/IP协议栈的体系架构、核心机制、现代应用优化和排障经验串一遍。你能搞清楚它每一层在干什么、为什么要这么干、出了问题怎么定位这才是这篇长文的真正价值。1. 协议栈整体架构四层模型到底怎么协作1.1 为什么必须分成四层很多人刚接触TCP/IP时都有个疑问不就是发个数据吗搞这么复杂干什么直接一根线连过去不就完了。但现实网络根本不是点对点那么简单。你的手机连家里Wi-FiWi-Fi连光猫光猫连运营商的局端设备中间要穿越一堆路由器和交换机最后才到达目标服务器。这中间任何一层都可能出问题如果所有功能都揉在一起你连问题出在哪里都无从查起。分层的好处就体现在这里每一层只关心自己的事下层给上层提供服务上层不需要知道下层怎么实现。这个设计思路跟公司组织架构很像。老板要发一封信给另一个公司的老板老板不用自己跑去送信他交给秘书秘书交给快递员快递员负责把信送到对方公司前台前台再转交给对方老板。每一层只需要管好自己的交接就行。TCP/IP的四层模型应用层、传输层、网络层、网络接口层本质就是这样一套分工明确、接口清晰的协作体系。我见过一些工程师在排查问题时分不清到底该看哪一层。域名解析出问题他去看服务器端口通不通TCP握手超时他跑去看业务日志。这就是典型的分层意识不够。建立分层思维比记住每个协议头部的每个字段重要的多。1.2 数据包的封装与解封装快递盒的层层包装如果要说TCP/IP协议栈最核心的机制那就是封装Encapsulation与解封装Decapsulation。每层协议在发送数据时会往上层传下来的数据前面加上自己的头部信息。应用层的数据到达传输层TCP/UDP加一个传输层头部到了网络层IP协议再加一个IP头部到了网络接口层以太网协议再加上以太网帧头和帧尾。我习惯用一个快递包裹的类比来解释这个过程。你写一封信信纸是应用层数据你把信装进信封写上收件人地址和寄件人地址这相当于TCP头源端口、目的端口然后你把信封放进快递袋快递单上写上从哪个城市寄到哪个城市、走哪条运输线路这相当于IP头源IP、目的IP最后快递袋上还要贴一个面单供快递员扫码这相当于MAC地址和帧头。到了收件那端每一层再逐层拆开包装把数据交给上一层处理。这个过程就叫解封装。这个机制的底层逻辑其实就一句话每一层只需要关心自己的路由信息。MAC地址负责在同一个局域网内找到下一跳设备IP地址负责在整个互联网找到目标主机端口号负责在目标主机上找到对应的进程。三层各管一段数据才能在不同网络中可靠地传递。1.3 各层常见协议及其应用场景应用层是离用户最近的一层HTTP、HTTPS、DNS、SSH、FTP、WebSocket这些都是这一层的协议。这一层的设计原则很简单为具体业务服务。HTTP做网页传输DNS做域名解析SSH做远程登录每个协议都有自己的一套规则。传输层的两个核心协议是TCP和UDP它们选型是很多开发天天纠结的问题。TCP面向连接、提供可靠传输、有拥塞控制和流量控制适合对数据完整性要求高的场景。UDP无连接、传输快、开销小适合实时性要求高但能容忍少量丢包的场景。我做过一个实时对战类项目最开始用的TCP后来发现弱网环境下延迟波动太大了最后切到UDP自研了一套带ACK和重传的可靠UDP方案效果立竿见影。网络层最核心的是IP协议负责寻址和路由。IP分两种IPv4和IPv6。IPv4地址是32位理论上最多有43亿个地址听着不少但全球设备数量早就远超这个量级了。IPv6地址是128位主要是为了解决地址枯竭问题同时它还简化了报文头部设计提高了转发效率。网络接口层往下就是真正干苦力活的负责把数据转换成电信号、光信号或者无线信号通过物理介质发送出去。ARQ协议、PPP协议、以太网协议都在这一层。这里我想强调一点很多人把OSI七层模型和TCP/IP四层模型混在一起背这个没什么问题只要不弄混就行。但在实际排障中我几乎只按TCP/IP四层来思考因为抓包工具展示的层次结构就是四层的变体。OSI模型多出来的会话层、表示层在实际应用中已经被应用层吞并了纠结它没有实际意义。2. TCP核心机制深度拆解2.1 三次握手与四次挥手为什么是3次而不是2次TCP是面向连接的协议所以在传输数据前必须先建立连接。三次握手是TCP连接建立的必经过程第一步客户端发起SYN包序列号设为x表示“我想建立连接”。第二步服务端收到后回复SYNACK包序列号设为y确认号设为x1表示“我收到了你的请求我也很想建立连接”。第三步客户端收到后回复ACK包确认号设为y1然后连接建立成功。这个问题最常被问到的就是为什么不能只握两次手我实际工作中是这么理解的。如果只握两次手存在一个经典问题服务器无法确认客户端的接收能力是否正常。设想一下客户端第一次发SYN请求因为网络问题超时了没过多久又重发了一个SYN此时服务器如果只确认第二次SYN就直接建立连接那第一个SYN在网络中延迟到达后会引起混乱。三次握手让双方都能确认彼此的发送能力和接收能力都正常同时交换初始序列号为后续的可靠传输打好基础。四次挥手的过程也是很多人容易犯迷糊的地方。TCP是全双工的所以关闭连接需要双方各自关闭自己的发送通道。客户端发FIN表示“我的数据发完了”服务端回复ACK表示“我知道了”服务端再发FIN表示“我的数据也发完了”客户端回复ACK确认。这里有一个最被关心的状态就是TIME_WAIT。主动关闭连接的一端会进入TIME_WAIT状态要停留2个MSL最大报文段生存时间之后才真正关闭。为什么需要这个TIME_WAIT两个原因第一个保证最后一次ACK能够到达对端如果ACK丢了对端会重发FIN此时还能来得及响应。第二个让本次连接中产生的所有报文在网络中自然消失避免出现在新的连接中造成数据混乱。我处理过一个线上案例某接口大量出现TIME_WAIT状态的连接导致本地端口被占满新连接无法建立解决方案就是调整TIME_WAIT的复用参数和端口范围这个后面讲调优的时候再展开。2.2 可靠传输确认、重传、滑动窗口与序列号TCP的可靠性靠的不是玄学是确认ACK、重传Retransmission、序列号Sequence Number和滑动窗口Sliding Window这套组合拳。发送方给每个字节编一个序号接收方收到后回一个ACK带上期望收到的下一个序号。发送方如果发现超时没收到ACK就会重传。这就像你寄了一封挂号信邮局给你一个单号你每隔几天查一次物流信息如果一直显示“运输中”没有更新你就会打电话催问。TCP的超时重传机制就是干这个的。滑动窗口是TCP实现高效传输的关键。如果不做窗口控制那发送方必须等每次数据被确认后才能发下一条效率极低。滑动窗口机制允许发送方在不等待确认的情况下连续发送窗口内的所有数据只有窗口滑过去才需要等待ACK。窗口大小由接收方的接收能力接收缓冲区剩余空间决定接收方会在ACK包里带上窗口大小告诉发送方“我还能收多少数据”。这里面有一个我踩过好多次坑的地方窗口大小不是单纯由应用层决定的还跟内核的接收缓冲区、套接字选项有关。你应用层明明设置了很大的读取缓冲区但如果内核套接字的接收缓冲区太小TCP依然会把窗口縮得很小导致吞吐量上不去。调优时一定要同时看应用层缓冲区和内核缓冲区不能只看一头。2.3 拥塞控制慢启动、拥塞避免、快重传与快恢复拥塞控制解决的是另一个问题网络中间设备路由器、交换机的处理能力是有限的如果发送方一股脑把数据全发出去中间设备处理不过来就会丢包。丢包后发送方重传让网络更拥堵恶性循环之下整个网络就瘫痪了。TCP通过拥塞控制算法来探测网络的承受上限动态调整发送速率避免把网络打爆。慢启动Slow Start是TCP开始传输时的一个阶段。发送方先是把拥塞窗口设成一个很小的值通常是10个报文段每收到一个ACK就增加一点窗口指数级增长直到达到慢启动阈值ssthresh或者发生丢包。这个过程叫“慢启动”但实际增长很快目的是快速探测网络的可用带宽。拥塞避免Congestion Avoidance阶段窗口增长速度变慢从指数变为线性一条一条地试探网络的极限。一旦检测到丢包或延迟增加拥塞窗口会减半甚至直接归1然后重新开始。快重传和快恢复是两个用于减少重传成本的机制。如果接收方收到乱序报文会立即发送重复ACK发送方连续收到3个重复ACK就直接重传丢失的报文而不必等到超时。快恢复则是把拥塞窗口减半而不是清零让传输速率不至于大幅跌落。现代TCP的拥塞控制算法已经演化出很多变体比如Cubic、BBR、Reno等。Cubic在Linux默认启用适合高带宽高延迟的长肥网络BBR是Google开发的通过估计瓶颈带宽和往返时间来控制发送率在弱网环境和高丢包率场景表现更好。我在做跨运营商网络加速时实测过把拥塞控制算法从Cubic切到BBR大文件传输速度能提升30%到50%代价是BBR对中间设备队列占用的策略更激进某些网络环境下可能造成额外的排队延迟需要根据场景权衡。3. 现代网络环境下的协议栈调优3.1 定位协议栈瓶颈从抓包与监控入手很多人一上来就改内核参数这是本末倒置的做法。调优前第一件事是先搞清楚瓶颈到底在哪一层。我一般会按以下顺序排查先看应用层。接口本身处理慢不慢数据库查询有没有慢日志业务代码有没有锁竞争这些可以用APM工具、慢查询日志、火焰图来分析。应用层的问题往往不需要动协议栈把代码优化好就解决了。再看传输层。使用抓包工具看TCP的握手时延、重传率、窗口变化、乱序情况。如果发现大量TCP重传多半是因为链路有丢包如果窗口长期很小多半是接收方处理不过来如果延迟很高可能是拥塞控制算法选型不佳。最后看网络层和链路层。使用ping、traceroute、MTR命令检查网络路径上的延迟和丢包率。如果发现某个中间节点丢包严重问题就不在你的服务器上而在运营商网络路径上面这时候调自己服务器的参数是没用的。我推荐每一个做网络性能调优的人都养成一个习惯先抓包再动手。抓包数据能告诉你TCP连接的真实状态这是最接近事实的数据比任何监控指标都靠谱。Wireshark的过滤功能要熟练tcp.analysis.retransmission、tcp.window_size、tcp.analysis.zero_window这些是排障时的高频过滤条件。3.2 Linux内核TCP参数调优实战Linux系统在默认配置下TCP性能是为通用场景设计的不一定适合你的业务特征。以下这几个参数是我在实战中经常调整的net.core.rmem_max和net.core.wmem_max这两个控制了套接字缓冲区的最大值。如果你的业务是高吞吐传输默认值往往太小了。我处理过一个文件传输服务最初吞吐只有200Mbps把rmem_max和wmem_max从默认的208KB调大到16MB后直接飙到800Mbps以上效果立竿见影。net.ipv4.tcp_rmem和net.ipv4.tcp_wmem这两个是TCP读写缓冲区的动态范围格式是“最小值 默认值 最大值”。调优时要注意TCP缓冲区不是固定大小的内核会根据网络情况自动调整但调整的上限受rmem_max影响。建议把默认值调到4MB以上最大值调到16MB左右。net.ipv4.tcp_slow_start_after_idle0这个参数控制TCP连接在空闲一段时间后是否重新进入慢启动阶段。默认值是1也就是说空闲后连接会回到慢启动状态传输速率会下降。对于连接复用率高的场景比如HTTP长连接、数据库连接池建议设为0避免慢启动导致的每次重连后性能下降。net.ipv4.tcp_fastopen3启用TCP快速打开TFO机制这个功能允许在TCP握手的SYN包中携带应用数据减少一次RTT的往返时间。对于小请求高频调用的场景比如页面加载、API调用能直接提升响应速度。我在实际项目里测过开启TFO后平均请求时延降低了约20%到30%前提是服务端和客户端都支持。net.ipv4.ip_local_port_range这个参数控制了本机可用的临时端口范围。默认是32768到60999对于高并发的TCP客户端来说不够用。在TIME_WAIT状态的连接会占用端口如果端口范围太小就会出现“Cannot assign requested address”的错误。建议扩大到1024到65535。net.ipv4.tcp_tw_reuse1这个参数允许从TIME_WAIT状态复用连接减少TIME_WAIT对端口资源的占用。需要明确的是它只对客户端连接有意义也就是发起方连接服务端TIME_WAIT状态不会因为这个参数被减少。这也是我被很多人问过的一个误区。调参不是一次性完成就一劳永逸每一项都需要结合压测结果和经验值做微调。拿我做过的一个推送网关项目举例最初TCP握手平均耗时在80ms左右开启TFO后降到55ms把接受队列长度net.core.somaxconn从128提到4096SYN队列溢出问题直接消失。这些小改动叠加起来对整体服务质量的影响是非常显著的。3.3 从TCP到TCP/IP协议栈现代演进QUIC与HTTP/3如果你关注网络协议近几年的发展一定会注意到HTTP/3和QUIC。QUIC基于UDP传输在用户空间实现了TCP的可靠传输、流量控制、拥塞控制等机制同时自带TLS加密和多路复用。它最核心的优势在于解决了TCP连接建立延迟与队头阻塞的问题。传统HTTP/2虽然支持多路复用但当其中一条流的数据丢失时TCP整体会降低传输速率导致其他流的传输也受影响这就是队头阻塞。QUIC在用户空间为每个流独立设置传输控制机制一条流丢包不阻塞其他流的传输这对弱网环境下的动态内容加载有明显改善。还有一个关键点QUIC支持连接迁移。TCP连接是四元组源IP、源端口、目的IP、目的端口绑定的手机切换Wi-Fi和4G网络时IP会变TCP连接就断了。QUIC用连接ID来标识连接不依赖IP地址切换网络时连接可以无缝保持。这对移动端应用的体验提升不是一星半点。当前的新版安卓和iOS系统对HTTP/3的支持已经比较成熟了服务端如果用的是nginx或Cloudflare等支持QUIC的网关可以逐步灰度开启HTTP/3。我当时在一个视频点播项目里做了一组对照测试启用HTTP/3后TCP协议栈的高延迟样本数量降低了50%用户播放卡顿率从2.3%降到了1.1%效果相当明显。但注意QUIC也不是银弹。它本身是基于UDP的所以对网络中间设备的NAT超时、UDP丢包率都比较敏感。某些老旧路由器、防火墙操作系统可能不支持或者限制UDP流量导致QUIC在某些网络环境下反而更慢。部署时要做好兼容性评估配置TCP回退机制。4. 协议栈在更多领域的落地与比较4.1 IoT与嵌入式场景下的TCP/IP协议栈裁剪TCP/IP协议栈不是只有PC和服务器才用。物联网设备、智能家居、工业网关、汽车电子控制单元到处都有它的身影。但这些设备跟PC环境差异巨大内存可能只有几百KBCPU频率可能只有几十M赫兹操作系统可能根本没有不能直接扛一个完整Linux的协议栈。在嵌入式领域常见的方案是使用轻量级协议栈比如lwIPlightweight IP和uIP它们专门为内存受限的设备设计。lwIP提供了完整的TCP/IP实现支持多接口、DNS、DHCP等占用资源却只有几十KB。我在一个使用C开发的安卓中间件项目里需要在安卓Native层跑一个精简协议栈来处理终端通信当时对比了几种方案最终选择在Native层封装lwIP既保证了跨平台性又避免了Java层GKI通用内核接口版本的变更影响这部分属于安卓GKI相关的工程化问题整体稳定性还是很不错的。裁剪的关键点是搞清楚你的应用到底需要哪些协议功能。如果一个设备只需要通过UDP上报状态那可以把TCP的实现全部裁剪掉如果设备需要通过MQTT接入云端那只需要TCP TLS MQTT这几个模块。裁剪越狠内存占用越少但后续调试和功能扩展就越麻烦。我的经验是先跑通完整功能测量内存占用和CPU开销再按实际使用场景一点点裁剪千万别上来就精简到后面想加个功能都得翻半天代码。4.2 蓝牙、CAN等协议栈与TCP/IP的关系搜索热词里看到了蓝牙协议栈、CAN协议栈这些协议栈跟TCP/IP不是一回事但很多人容易混淆。简单区分一下TCP/IP协议栈是网络通信的通用协议栈面向互联网覆盖了不同网络之间的传输典型场景是电脑访问网页、手机连服务器。蓝牙协议栈面向短距离无线通信覆盖的是设备之间的点对点或低功耗广播通信它的协议分层和TCP/IP完全不一样比如它有L2CAP、SDP、GATT等层次不适合做TCP/IP替代品。CAN协议栈面向工业控制与车载网络是一种现场总线协议特点是实时性强、抗干扰好、数据帧短小。如果你要在CAN总线上做通信需要的是CANopen协议栈而不是TCP/IP。不过随着车载以太网的普及TCP/IP正在逐步下沉到汽车领域与CAN协议栈并存于整车网络之中。我理解这些热词背后的需求大概是大家搜索“协议栈”时看到各种协议栈类型分不清它们之间的关系。这里我的建议是先问清楚自己的项目到底在什么场景下通信。远程数据交换请用TCP/IP设备在办公室或家里近距离通信用蓝牙/Wi-Fi工业现场抗干扰要求极高用CAN/Modbus。协议技术没有绝对的好坏只有匹配不匹配。4.3 安卓与C中间件场景中的协议栈选型C安卓中间件这个词在热词里也出现了这个在移动端和车机项目里还是比较常见的。安卓应用层Java或Kotlin可以直接使用Java标准库的Socket API但这些API有时不能满足性能或者底层控制的需求这时候就需要在Native层用C实现自己的网络模块或者对接第三方的协议栈实现。在C中间件里做网络通信有几种选择。直接调用Linux内核提供的Socket API这是最基础的做法性能好、可控性高缺点是你需要自己处理线程模型、缓冲区管理、超时重传等细节。封装第三方跨平台网络库比如Boost.Asio、libevent、libuv这些库封装了异步IO和事件循环代码可读性和跨平台性大大提高。集成一个完整的协议栈库比如lwIP或者PicoTCP适合需要灵活控制协议行为的场景。我在一个车载娱乐项目中遇到过一个问题系统里的网络中间件和上层安卓应用使用的是不同端口段因为中间件没处理好成对出现的收发端口逻辑导致上层应用偶发连不上服务器。最后排查下来就是端口分配逻辑在Native层没有做全局统一管理各模块各自为政。这个问题让我明白了一个道理在中间件层设计网络模块时端口管理、连接生命周期管理、超时策略这些横切关注点必须放在一个统一的框架里去控制不能每个模块都自己搞一套。5. 常见问题与排查技巧实录5.1 高频问题速查表这么多年的网络排查经验我把遇到的高频问题整理成了一张速查表。这张表不是我拍脑袋编的几乎每一条都对应着我处理过的真实线上事故TCP连接建立超时或失败可能原因SYN队列溢出、防火墙拦截、目标主机负载过高排查命令ss -s查看连接状态、netstat -s查看SYN丢弃计数、dmesg查看内核日志关键参数net.core.somaxconn、net.ipv4.tcp_max_syn_backlog大量TIME_WAIT连接占满端口可能原因短连接请求量过大主动关闭连接一方端口来不及释放排查命令netstat -tan | grep TIME_WAIT | wc -l关键参数net.ipv4.tcp_tw_reuse、net.ipv4.ip_local_port_range传输速度上不去可能原因TCP窗口过小、MTU问题、拥塞控制算法不适合当前网络、中间链路丢包排查命令抓包分析tcp.window_size、ping大包测试丢包率、MTR检查路径关键参数net.ipv4.tcp_rmem、net.ipv4.tcp_wmem、net.ipv4.tcp_mtu_probing视频或语音卡顿可能原因UDP丢包或延迟抖动过大、协议栈缓冲区配置不当、中间设备限速排查命令抓包看RTCP协议反馈、测试端到端往返时延抖动关键处理考虑启用FEC前向纠错、使用WebRTC的拥塞控制、或者改用QUIC连接被重置Connection reset by peer可能原因对端程序崩溃、对端主动关闭、防火墙发送RST排查命令抓包看RST包来源、查看对端应用日志关键处理检查服务端健康检查机制排查是否有防火墙安全策略这张表里的每一个问题处理思路都是“从现象到链路从监控到抓包从应用到内核”按这个顺序做排查基本不会漏掉关键线索。5.2 抓包分析实战从建立到断开全程复现抓包是理解协议栈最有效的手段没有之一。我拿一次典型的HTTP请求为例演示一下怎么用Wireshark完整分析一次TCP连接的从建立到断开第一步打开Wireshark选择对应的网卡设置过滤条件host 10.0.0.8目标服务器IP然后发起HTTP请求。第二步观察四次握手第一条记录是客户端发起的SYN包包序号为0窗口大小为64240第二条是服务端回应的SYNACK包第三条是客户端的ACK包。这三条记录就构成了TCP三次握手。第三步观察HTTP请求和响应。请求从客户端发出响应从服务端发回中间可能会有多个TCP报文段每个段都有对应的序号和确认号。第四步观察连接关闭的四次挥手。注意看FIN包的发出方是谁、ACK包的响应顺序、最后进入TIME_WAIT状态的是哪一端。实际抓包分析时我最常用的三个Wireshark过滤表达式tcp.analysis.retransmission过滤所有TCP重传包。大量重传意味着链路丢包或者对端处理不过来优先排查路径路由和MTU。tcp.analysis.zero_window接收窗口为零的数据包。长期出现零窗口说明对端应用不消费数据重点排查对端应用卡死或缓冲区未读取。tcp.analysis.ack_rtt统计ACK包往返时间。这个指标能够衡量网络路径的实时延迟比ping更接近真实业务时延。抓包分析的最终目标是把协议的每一次交互都还原成可理解的执行序列看清数据在哪一步丢失、在哪一步延迟、在哪一步被重置。一旦掌握这个能力你就能用协议栈的语言跟系统对话。5.3 嵌入式协议栈调试中的经验与误区嵌入式场景调试TCP/IP协议栈和PC场景差异很大。首先是日志输出受限很多设备没有串口终端或者串口波特率低输出大量日志会干扰实时任务。我做过一个方案在协议栈的调试输出层做了一个环形缓冲把关键事件记录在内存中配合远程调试指令按需导出既保证实时性又拿到了足够的信息。其次是嵌入式协议栈的API和Linux标准Socket API往往有差异。lwIP的函数名、参数类型、返回值语义跟BSD Socket并不完全一致迁移代码时如果直接改函数名不改调用逻辑很容易出问题。我特别提醒一点lwIP默认没有开启SO_REUSEADDR的语义如果你的应用依赖这个特性必须在lwipopts.h中打开相关配置否则很可能遇到端口占用异常。常见的嵌入式协议栈调试误区是对时间敏感问题缺乏因果分析。嵌入式设备网络问题常表现为偶发性的联网失败或超时这类问题如果抓不到现场几乎无法定位。我的做法是在协议栈源码中增加事件标记把关键状态机迁移、超时触发、缓冲区分配失败这类信息记录到日志系统再结合外部示波器或逻辑分析仪做时序对照才把偶发问题变成可复现问题。还有一点要强调嵌入式设备很多时候不是协议栈本身有问题而是下层物理层的信号质量问题。比如无线模块的天线性能差导致丢包率高、SPI接口速率不够导致数据读取不过来这些会有时也表现为TCP重传率偏高抓包又看不出来。排查时不要只盯着协议栈还要看底层驱动的错误计数驱动层面的丢包往往是协议栈性能恶化的根源。6. 我踩过的一些坑留给你的参考最后再分享几个我在实际项目中踩过的坑算是个人的备忘。第一个是关于TCP_NODELAY的。我做WebSocket长连接时一开始没有设置TCP_NODELAY导致小数据包的发送延迟非常明显因为Nagle算法会合并小包要等前一个包的ACK才发下一个包。后来在代码里开启TCP_NODELAY数据延迟立刻降下来了。但要注意开启TCP_NODELAY后大量小包会直接打出去可能增加网络开销对高频小请求场景尤其明显。自己要做权衡。第二个是关于连接池的超时时间。我见过一个项目连接池里的TCP连接长时间空闲后被防火墙悄无声息地断开但客户端不知道还在往已死的连接上写数据结果出现反复发送失败。后来在业务层加了应用层心跳机制通过ping/pong消息来维持连接活性问题才彻底解决。TCP本身的保活机制keepalive默认是2小时对很多实时业务来说太长了应用层心跳是最可靠的方案。第三个是关于防火墙对TCP半开连接的处理。不少安全设备会清理长时间不活动的半开连接导致TCP连接被RST重置。排查这类问题时光看客户端日志可能只能看到“Connection reset by peer”还需要看服务端的错误统计和防火墙日志才能定位到是哪一层设备做了什么操作。第四个是关于双栈IPv4/IPv6场景的。现代安卓应用和云服务都在逐步启用IPv6但有些中间件只实现了IPv4的Socket逻辑在双栈主机上可能连接失败或路由到错误的网络。如果你在做新项目建议从一开始就把IPv6支持设计进去不要等项目上线后再打补丁。网络协议栈这个东西表面看是知识问题实际是经验问题。你背会了多少个RFC不重要关键是在一个慢接口、一次卡顿、一场事故里你能不能快速判断是哪一层出了问题、怎么调怎么改。上面的内容覆盖了从架构到调优到排障的主要路径但你真正动手时肯定会遇到文档里没有的场景。那时候我的建议很简单打开抓包工具跟着数据走一遍协议会告诉你答案。
返回列表