网络物理层丢包是一种需要偿还的债务,可以容忍低劣的传输质量,这为 UDP 类服务提供了空间,而对于 TCP 类服务,可以用另外两类代价来支付:
- 主机端采用轻率的 GBN 策略恢复丢包,节省 CPU 资源,但浪费网络带宽;
- 主机端采用极其谨慎的 SACK 策略恢复丢包,浪费 CPU 资源,但节省带宽;
无论哪种,都属于异常处理路径,可靠传输协议,丢包总要恢复,省是省不了的,要么用 CPU 换带宽,要么用带宽换 CPU,TCP 协议已经可以将策略完美配置在两个极端。
上述两类策略我特意加上 “轻率” 和 “谨慎” 修饰,以表明标准版 GBN 和 SACK 是恢复丢包的最小代价,CPU 和 带宽是决策的两端。我写这个目的在于表明但凡你稍微给 GBN 或 SACK 加戏,就要付出额外代价,结局是既浪费了 CPU 又浪费了带宽。
来举两个例子,先看 GBN,我们知道 GBN 几乎不费 CPU,只要超时或 3 次 dupack 立马就重传 snd_una 到 snd_nxt 间所有段,如果你发明一个新的 GBN 算法,加入了比超时和 3 次 dupack 更复杂的丢包判断,由于重传动作省不了,就额外增加了丢包判断的 CPU 消耗。
再看 SACK,从相关 RFC 中我们看到 TCP SACK 谨慎到鉴别 scoreboard 每一个段的状态,虽然 RACK 后有所松散,但依然保持谨慎的底色,这意味着作为 GBN 反面的 SACK 不希望浪费一个段的带宽,但如此复杂的状态鉴定显然需要更多 CPU,如果你发明了一个更加 “高效” 的 SACK 算法,希望丢包能更加快速恢复,势必要冒险重传不该重传的段,精确鉴定省不了,多发的额外重传段却浪费了带宽。
那么是否存在一个交易点,在这个点位,重传动作更快开始,恰到好处的精确丢包判定,恰到好处的精确重传。理论上这个点肯定存在,但实际上根本找不到,因为端到端传输协议携带的信息量根本不足以找到这个点,TCP/IP 框架下的传输协议的网络度量在本质上是不可观测的,比如 RTT 和 delivery rate 根本无法测准。
若要解决测不准问题,要么需要协议携带更多信息,这增加了协议头开销,要么端网协同,这增加了转发节点的计算和状态存储开销,本质上还是交易,在分布式复杂系统中,买卖的收益是递减的,也就是说增加一个机制会引入该机制本身的消耗,而它带来的收益却只是解决一个不值得解决的问题。
在现实中,我们经常看到类似不值得做的事,只要它工作的还不至于糟糕,就让它继续工作。
解决丢包重传问题的根本方法就是升级底层网络,提高线路质量,提高交换容量。自下而上才能起到优化效果,而自上而下只能规避问题,是为 workaround,这一点上,TCP 用最小代价处理(而不是解决,解决只能靠物理层,链路层)丢包恢复已经足够好。如果算法上没有明显的缺陷,优化效果几乎只能自下而上呈现,而 TCP GBN 和 TCP SACK 没有缺陷。
So?还做什么 TCP 传输优化,现有信息量约束下,每做一个新策略都是在同时浪费 CPU 和带宽,同时浪费公司的钱。
还有另一类丢包值得关注,即拥塞丢包,它不是问题,相反,它是功能。就像感冒和醉酒一样,拥塞丢包是一种功能性信号,如何处理拥塞也像如何治疗感冒和醒酒一样常规。在统计复用系统中,没有任何机制可以避免拥塞丢包,问题在于用最小的代价回应拥塞,答案都在 AIMD 或测度方法里,不赘述。
浙江温州皮鞋湿,下雨进水不会胖。