我那个请求-响应的小包通信,延迟总是莫名其妙地多出 40 毫秒,抓包才发现是 Nagle 算法和延迟确认这两个好心的优化打起来了:一次 TCP 小包延迟的深度复盘

我有个请求-响应式的小包通信,逻辑上应该很快,可监控显示延迟经常莫名多出约 40 毫秒,而服务端处理只要零点几毫秒。代码翻烂了也没找到哪里慢,最后 tcpdump 抓包看时间戳才明白:这 40ms 不在我的代码里,而消耗在 TCP 协议栈里两个各自合理的优化的相互作用上——发送端的 Nagle 算法(有未 ACK 数据时攒着不发新小包、等 ACK)和接收端的延迟确认(收到数据先不回 ACK、等回程数据捎带或等 40ms 定时器)。在小包一来一回模式下,client 等 ACK、server 等回程数据,两边互相干等,直到延迟确认定时器超时强制发 ACK,凭空多出 40ms。这篇复盘从故障现场讲到 Nagle 和延迟确认为什么会打架、为什么特定模式才触发,再到延迟敏感就开 TCP_NODELAY 禁用 Nagle、应用层合并写、遇整齐的固定延迟优先抓包的完整正解,以及各自正确的优化叠加可能产生负面涌现、抽象是有漏洞的、局部善意叠加不出全局和谐要从全局审视协调的认知。

我那个请求-响应的小包通信,延迟总是莫名其妙地多出 40 毫秒,抓包才发现是 Nagle 算法和延迟确认这两个"好心"的优化打起来了:一次 TCP 小包延迟的深度复盘

那个延迟是性能压测时"怎么算都对不上"才暴露的:我有个内部的请求-响应式通信(client 发个小请求、server 回个小响应,一来一回),逻辑上应该很快。可监控显示:这个调用的延迟,经常莫名其妙地多出大约 40 毫秒(有时是 200ms),而服务端处理明明只要零点几毫秒。我把代码翻烂了也没找到哪里慢,最后上 tcpdump 抓包,盯着时间戳看,才看明白,后背发凉:这 40ms,不是花在我的代码上,而是消耗在了 TCP 协议栈里两个"各自都很合理"的优化机制的相互作用:一个是发送端的 Nagle 算法,一个是接收端的 延迟确认(Delayed ACK)Nagle 算法:为了避免发送大量小包浪费带宽,它会"攒一攒"——在还有数据"已发出但没收到对方 ACK"时,先不发新的小包,等凑够一个大包或收到 ACK 再发;延迟确认:接收端为了减少 ACK 包的数量,收到数据后不立即回 ACK,而是等一小会儿(常见 40ms),希望"顺便搭着回程的数据一起把 ACK 捎回去";而当我的通信模式恰好是"发一个小包→等响应"时,这俩就互相干等:发送端因 Nagle 在等对方的 ACK 才肯发下一个小包,接收端因延迟确认在等(根本不会来的)回程数据才肯发 ACK——两边都在等对方,直到接收端的 40ms 延迟确认定时器超时、被迫发出 ACK,僵局才打破;这凭空多出的 40ms,就是这么来的。根本原因是:两个单独看都合理的优化(Nagle 攒小包、延迟确认省 ACK),在"小包请求-响应"这种特定模式下,组合成了一个"互相等待"的死结。问题的根,是 Nagle 算法和延迟确认在小包请求-响应模式下相互作用、互相干等,直到延迟确认定时器超时,凭空引入约 40ms 延迟。这篇就把这次"TCP 小包延迟"的坑,从头到尾复盘一遍。

故障现场:小包请求-响应,凭空多 40ms

问题在于 Nagle 算法和延迟确认在小包一来一回模式下互相等待:

# ✗ 现象: 一个请求-响应式小包通信, 延迟莫名多出约40ms
# - client发一个小请求, server处理(零点几ms)后回一个小响应;
# - 监控: 端到端延迟经常 ≈ 40ms (本该<1ms); 服务端处理时间正常, 时间不知去哪了。

# 抓包(tcpdump)看时间戳, 发现40ms卡在"等一个ACK"上。

# 为什么? Nagle算法(发送端) + 延迟确认(接收端) 的相互作用:

# 【Nagle算法】(发送端的优化, 默认开启):
#   - 目的: 避免发送大量小包(每个小包都有40字节TCP/IP头, 1字节数据发1个包太浪费);
#   - 规则: 若有"已发送但还没被ACK确认"的数据在路上, 就【不发】新的小包, 攒着,
#           直到 ①攒够一个满包(MSS) 或 ②之前的数据被ACK了, 才发。

# 【延迟确认 Delayed ACK】(接收端的优化, 默认开启):
#   - 目的: 减少纯ACK包的数量(ACK可以"搭便车"——和回程的数据包一起发, 省一个包);
#   - 规则: 收到数据后【不立即回ACK】, 而是等一小会儿(典型40ms, 有的200ms),
#           希望这段时间内本端正好有数据要发回去, 把ACK捎上; 等不到就到点单独发ACK。

# 【两者撞在一起 → 互相干等】(在"发小包→等响应"模式下):
#   1. client发小包1; server收到, 但因【延迟确认】先不回ACK(想等回程数据捎带);
#   2. client想发小包2, 但因【Nagle】"包1还没被ACK", 不发, 等ACK;
#   3. → client在等server的ACK, server在等(它以为会有的)回程数据来捎ACK;
#      两边都在等对方 → 僵持;
#   4. 直到server的【延迟确认定时器超时(~40ms)】, 被迫单独发出ACK → client才收到ACK、继续;
#   5. → 凭空多了这40ms。

# 关键: Nagle(攒小包等ACK)和延迟确认(等回程数据捎ACK)各自合理, 但在小包请求-响应模式下
#       互相等待、直到延迟确认定时器超时, 凭空引入约40ms延迟 —— 经典的"两个优化打架"。

第一次抓包看懂"这 40ms 是 Nagle 和延迟确认互相干等等出来的"时,我又荒谬又恍然:"我一直在自己代码里找那 40ms,做梦也没想到它躺在 TCP 协议栈里、是两个我从没关心过的'优化'凑一块儿憋出来的。"这个坑最隐蔽的地方在于:不在你的代码里(代码逻辑、服务端处理都正常),藏在更底层的 TCP 协议栈中,普通的代码层面排查根本看不到,必须抓包看时间戳;而且这 40ms 是个"固定的、可疑的整数"(40ms/200ms 这种"整齐"的延迟,往往是某个定时器在作怪的信号)下面就来拆解,这两个机制以及该怎么解决。

第一件事:搞懂 Nagle 与延迟确认为什么会打架

我顺着这次事故,把 Nagle 算法、延迟确认及其相互作用彻底理清了。

Nagle 算法 与 延迟确认(Delayed ACK) 为什么会相互作用出延迟?

【核心: Nagle攒小包"等ACK才发下一个", 延迟确认"等回程数据捎ACK"; 小包请求-响应下两者互相干等到定时器超时, 多40ms】

1. 两个机制各自的善意目的:
   - Nagle算法(发送端): 减少小包数量, 提高带宽利用率(避免1字节数据配40字节包头的浪费);
   - 延迟确认(接收端): 减少纯ACK包数量(让ACK搭着回程数据"便车", 省下独立的ACK包)。
   - 它们【单独看都是合理的优化】, 都是为了"少发包、省网络资源"。

2. 它们的"等待"条件:
   - Nagle: "有数据在途中(未被ACK)时, 不发新的小包" → 在等【对方的ACK】;
   - 延迟确认: "收到数据后, 先不回ACK, 等回程数据捎带或等定时器(40ms)" → 在等【本端的回程数据】。

3. 在"小包请求-响应"模式下, 形成死锁式等待:
   - 这种模式: 发一点 → 等回应 → 再发一点; 数据是"一来一回的小包";
   - client发了小包, 在等server的ACK后才肯发下一个(Nagle);
   - server收到小包, 在等"回程数据"来捎ACK(延迟确认), 可应用层这会儿没数据要发回;
   - → client等ACK, server等回程数据; 谁也不动 → 卡住, 直到server的延迟确认定时器超时(40ms)强制发ACK。

4. 为什么是"特定模式"才触发:
   - 大流量、持续发数据时: Nagle能攒满包就发、ACK也能搭便车, 不卡;
   - 偏偏"小包、一来一回、且回程数据不是立刻就有"的请求-响应模式, 才会触发这个干等。

5. 解决思路: 打破这个互相等待
   - 最常用: 发送端开 TCP_NODELAY(禁用Nagle) → 小包立刻发, 不再等ACK;
   - 对"延迟敏感的请求-响应/RPC"协议, 几乎都应该 TCP_NODELAY;
   - 也可: 应用层合并写(一次write把请求写完整, 别分多次小write); 或调整延迟确认(不通用)。

一句话: Nagle(攒小包等ACK)和延迟确认(等回程数据捎ACK)各自合理, 但在小包请求-响应模式下互相干等到
   延迟确认定时器超时(约40ms); 对延迟敏感的请求-响应通信, 开TCP_NODELAY禁用Nagle即可打破僵局。

这套认知,是整个坑的根。两个机制各自的善意目的:Nagle(发送端)减少小包数量提高带宽利用率、延迟确认(接收端)减少纯 ACK 包数量让 ACK 搭便车;单独看都是合理的优化它们的等待条件:Nagle 在等"对方的 ACK"才发下一个小包、延迟确认在等"本端的回程数据"来捎 ACK小包请求-响应下形成死锁式等待:client 等 server 的 ACK,server 等回程数据来捎 ACK(可这会儿应用层没数据要回),谁也不动直到 server 的延迟确认定时器超时(40ms)强制发 ACK为什么特定模式才触发:大流量持续发数据时 Nagle 能攒满包、ACK 能搭便车不卡;偏偏小包一来一回模式才触发。解决思路:发送端开 TCP_NODELAY 禁用 Nagle(小包立刻发),对延迟敏感的请求-响应/RPC 几乎都应 TCP_NODELAY;也可应用层合并写。一句话:Nagle(攒小包等 ACK)和延迟确认(等回程数据捎 ACK)各自合理,但在小包请求-响应模式下互相干等到延迟确认定时器超时(约 40ms);对延迟敏感的请求-响应通信,开 TCP_NODELAY 禁用 Nagle 即可打破僵局。

第二件事:正解——TCP_NODELAY 禁用 Nagle,或应用层合并写

搞懂了原理,正解就清晰了:对延迟敏感的请求-响应/RPC 通信,开 TCP_NODELAY 禁用 Nagle(让小包立即发);并在应用层尽量"一次写完整请求",别拆成多次小 write

// ====== 正解一: 开 TCP_NODELAY 禁用 Nagle(最常用) ======
// Java 原生 Socket:
Socket socket = new Socket();
socket.setTcpNoDelay(true);     // ★ 禁用Nagle, 小包立即发送, 不再等ACK

// Netty:
bootstrap.option(ChannelOption.TCP_NODELAY, true);
// 服务端:
serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true);

// → 开了TCP_NODELAY后, 发送端的小包立刻发出, 不再"等上一个被ACK", 打破了和延迟确认的僵局;
//   代价: 小包变多一点(带宽利用率略降), 但换来了低延迟——对请求-响应/RPC这是值得的。
# ====== 正解二: 应用层"合并写", 别拆成多次小write ======
# - 很多Nagle相关延迟, 是因为应用层把一个逻辑请求拆成了多次小write(如先write头、再write体);
#   每次write都可能触发一个小包, 和Nagle/延迟确认纠缠;
# - 把一个完整请求【一次性write出去】(先在内存拼好, 一次发), 减少小包、减少往返;
# - 用带缓冲的写(BufferedOutputStream), 攒够再flush, 也能减少小包(但注意别忘flush)。

# ====== 选型与注意 ======
# 1. 延迟敏感的请求-响应/RPC/交互式协议(如游戏、金融、内部RPC): 开 TCP_NODELAY(禁Nagle);
#    多数RPC框架(gRPC等)默认已开TCP_NODELAY, 自己裸写socket时才要注意;
# 2. 吞吐敏感、大量小数据但不在意单次延迟的: 可保留Nagle(攒包省带宽);
# 3. 应用层合并写: 无论是否禁Nagle, 都是好习惯(减少包数、系统调用);
# 4. 别盲目两个都关: 一般禁Nagle(TCP_NODELAY)就够; 改对端的延迟确认不通用、不可控;
# 5. 排查这类"整齐的固定延迟"(40ms/200ms), 优先抓包看是不是卡在等ACK上。

# ====== 一个识别信号 ======
# - 延迟是"可疑的整数"(40ms、200ms这种), 且和你的代码逻辑/数据量无关 → 大概率是某个定时器/协议机制,
#   而非你的代码慢; 这类问题要往"协议栈/底层机制"上想, 而非死磕业务代码。

# 核心: 延迟敏感的请求-响应通信开TCP_NODELAY禁用Nagle(小包立即发, 打破和延迟确认的僵局);
#   应用层合并写减少小包; 遇到"整齐的固定延迟(40/200ms)"优先抓包, 往协议栈机制上排查。

修复的核心,是"延迟敏感就 TCP_NODELAY 禁 Nagle,并在应用层合并写"正解一:开 TCP_NODELAY 禁用 Nagle(最常用)——socket.setTcpNoDelay(true)(Netty 用 ChannelOption.TCP_NODELAY),小包立即发出、不再等 ACK,打破和延迟确认的僵局;代价是小包略多但换来低延迟,对请求-响应值得正解二:应用层合并写——把一个完整请求一次性 write 出去(别拆成多次小 write)、用带缓冲的写攒够再 flush选型:延迟敏感的 RPC/交互式协议开 TCP_NODELAY(多数 RPC 框架默认已开,裸写 socket 才注意)、吞吐敏感不在意延迟可保留 Nagle、合并写是好习惯、一般禁 Nagle 就够、整齐的固定延迟优先抓包识别信号:延迟是可疑的整数(40ms/200ms)且和代码逻辑无关→大概率是定时器/协议机制,往协议栈上想而非死磕业务代码归根结底:延迟敏感的请求-响应通信开 TCP_NODELAY 禁用 Nagle(小包立即发、打破和延迟确认的僵局);应用层合并写减少小包;遇到整齐的固定延迟优先抓包、往协议栈机制上排查。

第三件事:网络性能排查中其他容易被忽略的底层机制

排查后我把网络性能、TCP 相关其他容易被忽略的底层机制也系统梳理了一遍。

网络性能中其他容易被忽略的底层机制

# 1. Nagle+延迟确认(本文): 小包请求-响应卡40ms。→ TCP_NODELAY。

# 2. TCP慢启动: 连接刚建立时拥塞窗口小, 发不快(大文件开头慢)。→ 连接复用(同547篇)摊薄。

# 3. 连接没复用每次握手(同547篇): 建连开销。→ keep-alive/连接池。

# 4. 缓冲区/窗口太小: 发送/接收缓冲区或TCP窗口小, 限制吞吐(尤其高带宽高延迟链路)。→ 调大窗口。

# 5. DNS解析慢/未缓存: 每次请求都解析。→ 缓存DNS。

# 6. TLS握手开销: 每次新连接的TLS协商。→ 会话复用/连接复用。

# 7. 队头阻塞(HTTP/1.1): 一个连接上的请求要排队。→ 多路复用(HTTP/2)。

# 8. 没设超时(同355篇): 慢调用拖垮自己。→ 连接/读超时。

# 共同根源: 网络通信的实际性能, 受一大堆"藏在协议栈/操作系统/中间设备里"的机制影响——
#   这些机制大多是为"通用场景"做的优化, 但在你的"特定场景"下可能反而成为瓶颈或陷阱;
#   它们不在应用代码里, 不抓包/不懂底层就看不见。

# 核心: 排查网络性能, 不能只盯应用代码——要往下看协议栈/OS层的机制(Nagle/慢启动/窗口/握手/延迟确认);
#   理解这些通用优化在你场景下的影响, 必要时针对性调整(如TCP_NODELAY); 善用抓包看清"时间到底花在哪"。

排查让我把网络性能的其他底层机制也梳理清了。一、Nagle+延迟确认(本文)。二、TCP 慢启动三、连接没复用每次握手四、缓冲区/窗口太小五、DNS 解析慢六、TLS 握手开销七、队头阻塞八、没设超时它们的共同根源是:网络通信的实际性能受一大堆"藏在协议栈/操作系统/中间设备里"的机制影响——这些机制大多是为通用场景做的优化,但在你的特定场景下可能反而成为瓶颈或陷阱;它们不在应用代码里,不抓包/不懂底层就看不见核心是:排查网络性能不能只盯应用代码——要往下看协议栈/OS 层的机制(Nagle/慢启动/窗口/握手/延迟确认);理解这些通用优化在你场景下的影响,必要时针对性调整(如 TCP_NODELAY);善用抓包看清"时间到底花在哪"下面这张图,是这次 Nagle 延迟坑的成因与解法:

第四件事:Nagle 与延迟确认对比表

这次踩坑后,我把 Nagle 算法和延迟确认这两个机制对比成一张表。

维度 Nagle 算法 延迟确认(Delayed ACK)
在哪一端 发送端 接收端
目的 减少小包数量(省带宽) 减少 ACK 包数量(让 ACK 搭便车)
做法 有未 ACK 数据时攒着不发小包 收到数据先不回 ACK, 等一会儿
在等什么 等对方的 ACK 等本端的回程数据(捎 ACK)
单独看 合理优化 合理优化
撞一起(小包一来一回) 互相干等 到 40ms 定时器才解

这张表把两个机制钉清了。核心是:整个问题的精髓,在于"两个各自都正确、都在优化的机制,放在一起、在特定场景下,却产生了谁都不想要的坏结果"——Nagle 没错(它在省带宽),延迟确认也没错(它在省 ACK 包),错的是它们俩"对'该等什么'的假设互相矛盾"(一个等对方先动,另一个也等对方先动);这是一种典型的"局部最优的叠加, 不等于全局最优"它给我的最大启发是:多个"单独看都正确、都有益"的组件/优化/规则,组合在一起时,未必还正确、有益——它们可能因为"各自的假设/行为相互冲突"而产生谁都没预料到的负面涌现(死锁、互相等待、振荡、性能塌陷);"1+1" 在系统里 不总是等于 2, 有时是 0、甚至是负数;"组合的正确性", 是一个独立于"各部分正确性"的、需要单独验证的东西这给了我一种系统集成时的清醒:把多个"各自靠谱"的部件/机制/优化组合到一起时,不能想当然地认为"它们都对, 合起来当然也对"——而要专门审视"它们之间会不会相互作用、产生意外的负面后果?它们各自的假设是否在对方面前还成立?";尤其警惕"两个都在'等对方先动'/都在'退让'/都在'抢占'"这类对称性冲突;"单独验证组合的正确性、警惕局部最优叠加出的全局问题",是驾驭复杂系统集成的关键意识认清各自正确的优化叠加可能产生负面涌现、单独验证组合的正确性——是这个坑带给我的认知。

第五件事:这次事故暴露的"抽象层背后的真实代价"

这次让我反思更深一层:我能写出这 bug,是因为我把"网络通信"当成了一个"发数据就到"的简单抽象。我把"抽象的网络"和"真实的网络"对比成表。

维度 我脑中"抽象的网络" 真实的网络(协议栈)
发数据 write 了就发出去了 可能被 Nagle 攒着、被缓冲
延迟 就是物理传输时间 还含握手/ACK/排队/各种定时器
一来一回 瞬间往返 受窗口/确认机制影响
可靠性 发了就到 重传/拥塞控制/丢包
本质 一根透明的管道 一套复杂的、有自己脾气的协议栈

这张表道出了认知的根源。核心是:我能踩这个坑,是因为我心里的"网络",是一个被高度简化的抽象——"我把数据 write 进 socket,它就嗖地发到对面了";这个抽象在 99% 的时候够用,让我不用关心底层;可正是这层"便利的抽象",遮蔽了底层真实的复杂性(Nagle、ACK、窗口、定时器),当这些被遮蔽的细节"泄漏"出来咬我时(那 40ms),我便在自己的抽象里怎么也找不到答案它给我的深刻启发是:我们依赖的每一层抽象(网络是管道、内存是无限的、函数调用是瞬时的、数据库是个黑盒),都为了让我们专注上层而隐藏了下层的复杂与代价;但抽象是"有漏洞的(leaky)"——在性能、边界、异常等情况下,被隐藏的底层细节会"泄漏"上来,反过来影响你(Joel 说的 Law of Leaky Abstractions);"用着抽象、却完全不懂抽象之下是什么", 在平时无碍, 但在抽象泄漏时就会束手无策这给了我一种使用抽象的清醒:享受抽象带来的便利时,要对"它之下还有一层真实的、有自己代价和脾气的实现"保持敬畏和基本的了解——不必时刻深究, 但当出现"抽象层面解释不了的诡异问题(尤其性能、延迟)"时, 要有意识地"掀开抽象、往下看一层"(抓包、看协议栈、看 OS);"知道抽象会泄漏、并在它泄漏时有能力下沉一层去看真实",是应对复杂系统疑难问题的关键能力认清抽象是有漏洞的、抽象泄漏时要有能力下沉一层看真实——是这个 40ms 延迟坑带给我的认知。

第六件事:遇到诡异网络延迟时,我现在的自检习惯

现在每当我遇到"解释不了的网络延迟",我都会先按这张图问自己:

这张图的精髓,是"整齐的固定延迟优先抓包往协议栈想,请求-响应小包卡 ACK 就 TCP_NODELAY"可疑整数延迟抓包看协议栈、卡等 ACKNagle+延迟确认开 TCP_NODELAY、建连慢连接复用这套习惯,让我从"延迟问题只在代码里找"变成了"整齐的固定延迟先怀疑底层机制、抓包看真实"——核心始终是:请求-响应小包延迟莫名多 40ms 多半是 Nagle 和延迟确认互相干等,开 TCP_NODELAY 禁用 Nagle;遇到整齐的固定延迟优先抓包、往协议栈机制上排查。

我立下的几条规矩

这场"请求莫名多 40ms"的事故,换来了我做网络通信时,刻进骨子里的几条铁律:

  1. Nagle 算法(发送端攒小包等 ACK)和延迟确认(接收端等回程数据捎 ACK)各自合理。
  2. 在小包请求-响应模式下,二者互相干等,直到延迟确认定时器超时(约 40ms)。
  3. 延迟敏感的请求-响应/RPC 开 TCP_NODELAY 禁用 Nagle,小包立即发。
  4. 应用层尽量一次 write 完整请求,别拆成多次小 write。
  5. 遇到"整齐的固定延迟"(40ms/200ms),优先怀疑定时器/协议机制,抓包看真实。
  6. 多个各自正确的优化叠加,可能产生意外的负面后果,单独验证组合的正确性。
  7. 抽象会泄漏,出现抽象层解释不了的问题时,要有能力往下看一层。

写在最后

回头看,这场由"两个好心的优化打架"引发的、凭空多 40ms 的事故,真正教给我的,远不止"开 TCP_NODELAY"这一个技巧。它让我对"两个'各自都在为好的目标努力'的部分, 放在一起, 却可能因为'彼此都在等对方先行动'而陷入僵局; 善意的、局部的优化, 拼不出全局的和谐",有了一次刻骨的体会。我被这个坑震撼,是因为它太"无辜"了——Nagle 算法在尽职尽责地"省带宽",延迟确认也在尽职尽责地"省 ACK 包",它们俩都没做错任何事,都在为"更高效的网络"这个好目标努力;可偏偏,它们俩"努力的方式"都包含了"我先等等、看对方会不会先动"这个动作——当两个都信奉"我先等等"的家伙碰到一起,就谁也不肯先迈步,一起干等,直到一个定时器不耐烦地打破沉默;灾难,不是源于谁的错误,而是源于两份善意的"等待"恰好对撞这让我领悟到一个关于"局部善意与全局协调"的深刻认知:一个系统里,每个部分都"追求自己局部的最优/善意的目标",并不能自动保证整个系统达到全局的最优/和谐——局部的善意/优化,若缺乏全局的协调,完全可能叠加成全局的僵局、内耗甚至灾难;这在技术系统(两个优化打架)、在协作(每个人都'等对方先表态')、在很多领域都一样;"各自为好" 不等于 "合起来好"这给了我一种构建和审视系统的全局视角:设计或诊断一个由多方组成的系统时,不能只确认"每一方是否在做正确/有益的事",更要从全局视角审视"各方的行为放在一起, 是否协调?会不会相互掣肘、相互等待、相互抵消?"——必要时引入"打破对称、明确谁先动"的协调机制(就像 TCP_NODELAY 让发送方别再等);"超越局部的善意, 去关照全局的协调一致",是让一个多方系统真正高效运转、而非陷入'人人都对、整体却卡死'的关键智慧认清局部善意叠加不出全局和谐、要从全局审视各方行为是否协调并打破对称僵局——这,是我用一次 40ms 延迟的事故,换来的、关于网络协议、也关于如何让多方系统协调运转的、最朴素也最深刻的领悟。如果这篇复盘,能让你下次的请求-响应通信凭空慢 40ms 时,立刻想到去抓包、去 TCP_NODELAY,那我对着那 40ms 抓包的这段时间,就值了。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

我扣库存的逻辑是先查出当前库存、减一、再写回,平时好好的,一上量就超卖、库存对不上账:一次数据库并发更新丢失、读改写非原子导致超卖的深度复盘

2026-6-2 21:21:46

技术教程

我的 Java 服务在容器里跑着跑着就被干掉重启,kubectl 显示 OOMKilled,可服务器明明有几十 G 内存,排查发现是 JVM 根本不知道自己被关在了一个小盒子里的深度复盘

2026-6-2 21:33:49

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索