今天和同事聊到这个话题,突然想到了另一种用法。

NODELAY 的基本作用这里不再赘述,下面介绍一种在特定场景下应当关闭它的情况。

假设每个包的大小为 100 字节,socket 缓冲区为 1MB,NODELAY 延迟设置为 40ms。进程并发模型为 15 个网络 IO 线程 + 1 个游戏逻辑主线程(30W QPS),实测性能比约为 15:1。IO 线程收到一个包后,投递给主线程处理。

应用场景:网关服务器进程将消息转发到后端服务器进程,主线程处理速度为 30W QPS,所有线程 CPU 吃满。

  1. 在这个场景中,主线程一个 NODELAY 周期(40ms)内可以处理约 1.2W 个包。如果关闭 NODELAY,将会出现下面的情况。
  2. 缓冲区大小为 1MB,可容纳约 1W 个包。忽略网卡效率等因素,意味着一次发送行为可以携带 1W 个包的数据,因此上下文切换只发生一次而非 1W 次。30W QPS 下只需切换 30 次上下文、发送 30 次,性能与 IO 的配比变为 1:1,直接减少了 14 个 IO 线程,IO 次数也只有 30 次。
  3. 即使加上 MTU 的限制,一次发送行为也能携带约 15 个包,性能与 IO 配比同样趋近 1:1,可减少 14 个 IO 线程,IO 次数约为 2W 次,恰好将一个 IO 线程吃满。

总结

这种做法适用于服务器内部转发、延迟不敏感的场景,多见于非即时战斗类业务,目的是提升信道传输能力。

这一思路有很多延伸应用,例如 Redis 的 pipeline、应用层二次打包等,都是利用批量聚合来提升信道传输效率,而不仅仅依赖 NODELAY 本身。