Web 性能优化:TCP
- 🤐 Web 性能优化:TCP
- Web 性能优化:TLS
- Web 性能优化:HTTP
现代网络协议栈中最靠近应用侧的,除了应用层协议,就数传输层协议了。传输层有两大协议:TCP、UDP。两个协议最大的区别是:tcp 提供可靠传输,udp 提供不可靠传输。
经过几十年的打磨,tcp 取得了辉煌的成就,目前绝大部分的 Web 应用都建立在 TCP 传输机制之上,除了保证数据一定会送达,tcp 还提供了按序到达、拥塞控制、数据完整性保障等能力。要优化性能,是避不开 tcp 的,这快优化主要是在系统层面做。
如何优化呢 ?
三次握手
每个 tcp 传输都始于三次握手,正常情况下,需要等到握手完成后才能开始传输数据。客户端可以在发送完 ack 后立即发送数据包,但服务端必须要等接收到这个 ack 后,才能开始发送数据,如此一来,数据传输就增加了一个网络往返时间的延迟。
如果我们进行的是大文件传输,或者多个请求共享一个长连接,自然可以通过拉长时间来分摊成本,但在大量短连接的场景,则代价就显得很大了。
针对这个问题,提出了 TCP 快速打开(TFO,TCP Fast Open):允许在握手的 SYN 报文中携带数据。这种方案也有其限制,所以默认是不开启的。
另一种避免三次握手延迟的方案是复用 tcp 连接,如今 http/1.1 默认是长连接机制。
接收窗口(rwnd)
为了预防发送端发送了过多的数据给接收端,导致接收端无法处理,连接的双方在发送报文时均需要通告自己的接收窗口(rwnd)大小,以便对端可以及时调整。第一次连接时,发送的接收窗口大小都是系统设置好的默认值。
即使带宽足够,如果通告的接收窗口过小,也无法充分利用起来。目前结合 TCP 窗口缩放,接收窗口大小已经可以设置到 1G。
拥塞窗口(cwnd)
在 tcp 连接建立之初,不用一包一 ack,可以一口气向网络上发送多少数据呢?这个问题由慢启动算法负责。
根据 慢启动算法(slow start),有一个预设的保守值 — 拥塞窗口大小(cwnd),用来约束初次发送的数据量,该值不会通告给对端,但发送端在发送时会根据 min(rwnd, cwnd) 值来决定可以发送的数据量。目前 cwnd 的值一般会设置为 10 个 tcp 段,这就是前段时间 hackernews 上讨论比较多的 Why-your-website-should-be-under-14kb-in-size 这篇博文的理论依据(10 x 1460 = 14600 bytes ≈ 14 kb)—— 多好的理论联系实际的例子 🤨。
慢启动算法是对网络可用带宽的试探,每次收到一个 ack 拥塞窗口就增长两个大小,一段时间后达到对端的接收能力极限(rwnd)后稳定下来。在使用长连接时,在连接空闲一段时间后,拥塞窗口会因为 SSR(Slow-Start Restart)机制而被重置回最初的默认值(如 10 个 tcp 段),从而开启又一次慢启动过程。在互联网这种不稳定的环境中保守一点是可以理解的,但在网络环境相对稳定的企业内网,则增加了不必要的延迟,可以考虑禁用 SSR 以减少延迟。
队首阻塞
tcp 协议保障报文的顺序,简化了上层的编程模型,但实际传输过程中,报文并不是按序到达的,由于路由路径不同,或者中途有报文丢失,导致序号靠后的报文先到达接收端,先到达的报文需要等待前序报文到达后,才能组装为完整的数据提交给应用,这就引入了不可预知的延迟。
如果无需按序交付、能容忍分组丢失、对网络延迟或抖动要求高,可能 udp 是一种更好的选择。
无招胜有招
解决网络传输问题最好的方法就是 没有网络传输。
砍需求、砍功能点、复用 tcp 连接、非必要不传输数据、压缩必须要传输的数据、把服务器再靠近一点点。
参考:
《Web 性能权威指南》