接收方驱动传输:数据中心短消息如何实现低延迟调度
这篇论文我前后读了两遍。第一遍读的时候感觉它非常激进——把传输层的调度权从发送方手里直接交到接收方手里还要求交换机和网卡提供多个硬件优先级队列第二遍再读发现它的设计逻辑其实高度自洽而且精准命中了数据中心网络流量的核心矛盾绝大多数报文都很短但它们恰恰是被一视同仁地对待才导致高延迟、大抖动、频繁超时。这是一篇发表在NSDI 2021上的论文标题是《A New Approach to Datacenter Packet Transport》。它在网络系统方向属于那种一看标题就觉得不简单的工作不是修修补补地优化TCP窗口也不是换个拥塞信号算法而是从传输单位、控制方向、调度粒度三个层面重新设计了一套数据中心包传输方案。如果你平时接触数据中心网络、分布式存储、RPC框架调优或者对DCTCP、pFabric这类方案有了解这篇文章值得认真读一遍。1. 这篇论文要解决什么痛数据中心里没人疼的短消息先看一组论文开篇就抛出来的数据特征在典型的数据中心内部流量里绝大多数RPC请求的消息体非常小通常在几十字节到几KB之间99%的消息小于几百KB。但与此同时消息数量极其庞大短消息的数量占比能到99%以上。这意味着数据中心的包传输系统面对的核心负载不是长连接大文件搬运而是成千上万条小消息的快速送达。场景上很好想象某个分布式存储系统要处理一次读操作客户端发送一个几KB的读请求存储节点返回几KB到几十KB的数据某次Web搜索聚合上游服务需要并行查询多个后端后端各自返回一小块结果。整个过程里几乎没有一条大流慢慢传的机会全是短平快的子请求。问题在于这些小消息一旦和其他大流量混在同一个网络里就会面临严重的排队、丢包、重传和头部阻塞。论文在论述背景时有一张经典的流量分布图清楚展示了两个特征一是消息长度呈现明显的短小密集分布二是不同大小的消息数量相差悬殊。基于这个特征作者抛出一个核心质疑既然绝大多数消息这么小为什么要把它们和那些大消息放在同一个拥塞控制框架里处理为什么不能让小消息走快速通道、让大消息主动让路这就是论文切入问题的起点。它对标的是现有数据中心传输方案的共同毛病要么基于TCP的字节流模型把RPC消息硬塞进长连接里要么像DCTCP那样只用ECN做早期拥塞反馈仍然保留发送方驱动、流为单位、按拥塞窗口发数据的框架要么像pFabric那样假想硬件可以支持成千上万个优先级队列现实中根本没有这种交换机。换句话说论文不是提出一个小补丁而是认为传输层的设计目标应该围绕消息而不是字节流调度权应该围绕接收方而不是发送方优先级应该围绕剩余数据量而不是连接完成时间。这一节的结论也代表整篇论文的立场数据中心包传输需要一次架构层面的重构而不是参数调整。2. 核心设计理念把调度权从发送方交到接收方手里论文最颠覆的地方是提出由接收方来决定什么时候发、发多少。这和TCP、DCTCP等所有经典方案的思路正好相反。为什么发送方驱动不可靠论文分析了很关键的一点发送方并不知道它发出去的包到底在网络上排了多久。在TCP里发送方只能通过确认、超时、ECN标记这类间接信号来猜测拥塞而且在交换机缓冲队列已经堆积之后才能收到反馈——这是典型的事后反应。在数据中心的incast场景里大量发送方同时向同一个接收方发数据交换机队列会瞬间堆积起来哪怕每一条流都只有几个包只要同时到达的数量够多队列就能把尾延迟拉高几个数量级。接收方驱动的优势在于接收方是最终数据汇聚的地方它比任何单个发送方都清楚自己收到了多少、还有多少没到、哪些消息更重要。因此接收方把调度权收归自己直接告诉每个发送方你现在可以发X个包、你暂时不要发。论文的设计里有一个很容易被忽略但很关键的词消息message不是流flow。从消息粒度出发接收方看到的是一批待完成的请求它可以像一个任务调度器一样规划完成顺序。作者用了一个很朴素但极其有效的策略优先处理剩余数据量更小的消息也就是倾向于先完成那些差几包就能收完的小任务。这和操作系统里的短作业优先SJF是同一个思想——让短任务先结束整体平均完成时间就会显著下降。看到这里可能有人要问接收方怎么知道剩余数据量这就靠消息头信息了。每条消息在开始发送时会先带一个包含总长度的头包接收方根据这个长度就知道了该消息总共要收多少数据再加上自己回收到的字节数就能估算出剩余量。调度器维护一个待完成消息列表按剩余字节排序然后依次给对应的发送方发放信用额度。这套接收方主导的思路在后来的论文里也被反复引用它和NDP、Homa这些同门方案一起构成了学界讨论接收方驱动传输的基本框架。和早期接收方驱动的拥塞控制机制不同论文没有把接收方当做一个被动确认者而是让它成为真正的带宽分配者。3. 协议机制拆解优先级映射、信用控制和两个Grant这一节是论文的技术核心拆开看其实由四个部分组成优先级队列、消息剩余量映射、接收方Grant机制、以及发送端的发送流程。3.1 优先级映射让每个包自己知道自己有多着急协议规定交换机为每个端口提供多个优先级队列论文实验中用8个数据包进入交换机时根据一个规则选择队列。传统方案里优先级往往对应着应用层类别或者流ID而在这套设计里优先级直接由消息剩余大小决定。作者给出一个映射思路消息越小剩余越小对应的优先级越高。举例来说一个总长度只有几百字节、只剩最后一两个包还没收完的消息它的包会被放入最高优先级队列后面的其他包都要给它让路。反过来一条大消息刚开始发送的包就会被放进低优先级队列。这样保证了一个最直接的宏观效果短消息几乎不会被长消息挡在后面。这个设计在概念上很像pFabric提出的fabric-wide优先级但论文做了一个非常现实的处理把优先级数量压缩到了商用交换机能接受的8个左右而不是每一条流一个优先级。代价是精度下降收益是可部署性大幅提升。为了把连续的消息剩余量映射到有限的优先级上论文采取了对数分桶的思路——取消息剩余字节的对数再量化到优先级这样极端小的消息总是被映射到最高优先级而相对大小差异足够大的消息能在队列上拉开层次。3.2 两个Grant区分开始发和继续发两种行为接收方给发送方的信用授权论文里分成两种第一种叫unscheduled grant无计划授权。它的作用是允许发送方在消息刚开始时直接发出一批数据不需要等待接收方的逐包确认。这批数据大概覆盖了初始的一部分字节设计思路是如果每条消息都要等接收方点头才开始发那第一条消息就会白白多一个RTT的延迟。对于短消息来说这个RTT可能占整体延迟的很大比例所以初始授权必须存在。论文对这种初始授权做了限制防止网络在没有调度信息时被过度灌入数据。第二种叫scheduled grant计划授权。接收方根据当前消息剩余量和网络负载状况在合适的时机给发送方发放后续额度。scheduled grant才是真正的调度动作它告诉发送方你现在可以再发多少数据包。发送方只有在收到这个授权后才能推进消息后续部分的发送。把两种Grant放在一起看实际上是把传输过程分成了两个阶段第一个阶段是无计划冲刺第二个阶段是接收方精确管控。这种混合设计的精妙之处在于既避免了纯接收方驱动造成的首包延迟又避免了纯发送方驱动造成的盲目突发。3.3 发送端流程一个发送方如何配合接收方工作一个发送方的行为在论文里有非常清晰的描述。从上层应用角度看发送方拿到的消息通常来自RPC栈它先把消息分成固定大小的包每个包除了载荷之外还携带消息ID、消息总长度、消息内的字节偏移。前几个包论文中称为scheduled包之前的初始部分属于unscheduled发送直接进入网络。当发送方收到接收方的scheduled grant后它会按照授权额度从消息队列里取包发出。这个发送过程不是一次性全部发出而是要严格匹配接收方给的额度。如果发送方同时维护多个消息它需要按照接收方对不同消息的授权顺序来发送不能自己随意调整。这里有一个细节值得注意在消息刚开始时接收方可能还没有来得及产生scheduled grant所以网络上的初始数据包允许盲目发送但后续的数据必须由grant驱动。这种机制从全局看相当于是让接收方统一控制了网络窗口杜绝了每个发送方各自为政、单独维护拥塞窗口造成的叠加浪费。3.4 快速恢复与不必要的复杂性处理论文还讨论了故障场景下的机制。当接收方长时间没有发送grant或者发送方长时间没有收到确认时协议会触发超时重传。传统TCP在超时后往往要缓好久才能重新启动发送而这里因为接收方驱动每个发送方的发送状态本身就是由接收方的授权决定的本地没有那套复杂的cwnd计算。重传的逻辑会简单很多发送方只是保持消息在队列里等待下一个授权。比较让我意外的是论文对网络内聚合只做了一些概念性讨论没有完全依赖它来实现incast控制。incast问题主要靠优先级和接收方调度来解决当大量短响应同时涌向一个接收方时接收方会通过grant机制控制不同发送方的发送速率让它们的数据稍微错开即便有瞬时堆积高优先级的小消息也会插队先走。这种思路比传统的拥塞窗口退避要主动得多。4. 性能评估靠数据说话短消息快多少、大消息慢多少论文的实验部分做得相当扎实它对比了TCP、DCTCP、pFabric、NDP和这套新方案在多种工作负载下测公平共享、尾部延迟、吞吐量等指标。4.1 实验配置与负载模型实验用的是模拟方式拓扑是典型的三层数据中心网络模拟了构建在商用交换机上的多路径配置。论文针对负载做了很细致的区分一是基于Web搜索类RPC的请求-响应负载消息普遍偏小短消息占比极高二是数据挖掘类任务的负载特征是有一定数量的中大消息混合三是缓存类访问模式响应的消息大小呈双峰分布大多很短。之前很多传输论文喜欢用单条流长时间传输来测吞吐论文换个思路它测的是一组RPC消息的平均/尾部完成时间这其实才更贴近应用的真实体验。因为上层调用的SLO通常绑定的是这次RPC在多少毫秒内完成而不是这条TCP连接的平均吞吐有多高。4.2 核心结论尾部延迟的数量级差别论文最直观的实验结果是在负载较低时新方案和DCTCP差距不算大因为网络本来就不拥塞一旦负载上升到中等水平DCTCP的99%尾部延迟开始急剧抬升而新方案仍然能保持比较平缓的延迟曲线。在部分高负载场景下尾部延迟差距甚至达到一个数量级以上。这个结果并不神秘。DCTCP虽然能提前响应ECN但它本质上还是所有流平等排队、发送方猜测窗口在大规模incast下很难避免队列堆积。新方案则通过接收方调度和优先级插队把短消息从排队泥潭里解放出来。值得注意的是大消息的完成时间并没有被牺牲太多因为小消息插队虽然会暂时抢占低优先级队列的带宽但单个短消息占用的传输资源其实很小对长消息造成的时间增量是有限的。4.3 一个绕不开的话题优先级抢占带来的带宽浪费论文也坦诚讨论了优先级机制的一个副作用高优先级数据包插队时可能打断交换机低优先级队列的连续传输导致线路上的传输间隙。比如一个低优先级大包传输到一半高优先级小包抢占了链路低优先级包只能分成两段传产生额外的等待。这种现象在论文中被称为碎片化或传送浪费。作者承认这是设计的一部分不是bug。他们的观点是为了尾部延迟的大幅改善牺牲少量链路利用效率是划算的。实际测量中这种浪费在高负载下会稍高但仍然在可接受范围内。这种坦诚的讨论方式我觉得很值得学习——它没有把方案包装成完美无缺而是明确告诉读者每个设计决策都是有代价的。5. 读后的思考这个方法为什么还没有大规模统治数据中心论文读完后我最大的感觉是学术逻辑闭环但工程落地依然是难题。这也是为什么论文虽然影响很大但你在真实数据中心里至今很难看到它完整跑起来。5.1 硬件优先级队列的支撑度问题整套方案对硬件的要求集中在优先级队列上。论文实验只用了8个优先级这是很多商用交换机能提供的数量。但难点在于除了交换机端口队列之外接收方网卡也要能识别并处理多种优先级的数据包要有相应的调度能力。目前很多数据中心里使用的网卡并没有为这种接收方驱动多优先级模型优化这直接影响部署成本。更微妙的是优先级队列只是机制层面真正发挥调度优势还需要上层应用配合。比如应用发送请求时要能显式关联消息要能区分不同优先级否则协议栈无法拿到消息总长度之类的元数据。这意味着从RPC框架到传输层都要联调改动光这一条就已经劝退很多团队了。5.2 和现有TCP生态的兼容成本数据中心不是一张白纸线上已经有大量基于TCP的中间件、负载均衡器、防火墙、可视化和监控工具。新协议和TCP在语义上有本质差异端口、连接、流的建模方式都不一样不能低成本地板平移。论文对此没有给出渐进式迁移方案它更像是一个理想形态的完整设计。因此论文的实际价值更像一个方向指引接收方调度、消息粒度、多优先级映射这些思想完全可以拆开来一部分一部分应用到现有系统里。比如很多云厂商都在做的RPC级流量调度、服务网格里的直连网络优化本质上吸收的就是类似的思路。5.3 这版设计里最值得借鉴的三个思想我读完以后认为有三点特别值得迁移到其他系统设计中。第一明确传输单位。TCP以字节流为单位导致用户态要自己做消息边界和定长解析而是消息粒度让调度真正贴合应用语义。这个思想不仅适用网络传输在分布式存储的副本同步、跨可用区数据复制里同样可以借鉴。第二把调度权放到信息最全的位置。发送方只能看到自己的队列接收方能看到所有消息的进度所以在关键路径上谁掌握的信息多谁就应该做决策。这个原则如今在大规模分布式系统里越来越常见论文算是一个落地范式。第三用优先级来表达相对紧迫感而不是给每个流定量分配带宽。带宽分配是手段完成时间是目标。优先级机制天然适合延迟敏感型短消息优先通过比复杂的带宽计算调度器简单得多。5.4 我个人的阅读建议如果你要读这篇论文我建议不要一口气啃完协议细节先看前三章的背景和理解把为什么接收方主导更好这个问题想清楚再去看机制部分。机制部分容易让人陷进很多细节比如scheduled包头的格式、重传计时器的设置等等这些看个大概就行真正要抓住的是优先级映射和grant划分两条主线。还有一个不容易注意的点论文写作风格非常清晰每一个设计选择都配有动机。对照着TCP的缺陷去读会更容易体会作者的推演过程。比如TCP为什么出现队头阻塞因为字节流顺序很重要新方案为什么允许乱序交付因为消息本身可以按块乱序到达后再组装调度灵活性就起来了。这种回到第一性原理的推演方式比背协议状态机有价值得多。读完之后我对数据中心网络传输的认知确实更新了一遍。回头再看那些优化TCP参数提升数据中心性能的实践总觉得还是停留在表层修修补补。这篇论文指出的方向是目前为止我看到的最系统、最自洽的一套替代性设计方案。虽然离大规模落地还有距离但很难说未来的数据中心不会真的往这个方向演进。