在一台12核macOS笔记本上,KeibiDrop文件系统的开发团队把传输层从TCP换成QUIC,结果得到了一组乍看自相矛盾的数据:在真实网络链路里,随机定位并读取视频文件的某个片段,QUIC的延时只有109毫秒,而同样的操作在TCP上花了大约3秒——快了28倍。但一旦切换到批量文件传输,同一条链路里QUIC单流的吞吐却只有TCP的十六分之一,慢到几乎没法用。
这次测试的起点,是一个点对点加密文件系统的核心难题:如果用户挂载了远端节点的文件,像本地磁盘一样随时打开、拖进度条,那么传输协议必须先保证“连得上”,再谈“跑得快”。TCP连接靠的是源IP、源端口、目的IP、目的端口和协议号组成的五元组。笔记本电脑从咖啡馆Wi-Fi切换到手机热点,IP地址变了,五元组更新,TCP连接就会直接断开,正在读取的40 GB视频文件传了一半就得重来。QUIC用连接ID标识一条会话,不绑定网络层的地址,因此可以在IP变更时把连接迁移到新的套接字上,上层流不中断。
KeibiDrop原本在自研的加密TCP连接上跑gRPC,这个加密层恰好对上层暴露了标准的net.Conn接口。把QUIC塞进去甚至不需要改动业务层的任何一行代码:直接用一个quic.Stream适配成net.Conn,因为QUIC流本身就是可靠有序的字节流,不需要额外加帧。唯一需要补的就只有LocalAddr和RemoteAddr两个方法,从quic.Conn里取出来即可。这样一来,gRPC完全感知不到底下已经从TCP流换成了QUIC流。
第一个意外出现在macOS本机回环测试里。QUIC传输的吞吐卡在大约137 MB/s就再也上不去了,原因是macOS没有UDP分段卸载(USO),每一次sendmsg系统调用都只能推送一个很小的数据报,CPU很快就被系统调用占满。开发团队翻到了一个未正式文档化的批量发送接口 sendmsg_x,由Apple在2016年引入的网络框架中提供,允许一次调用送出多个数据报。把这个批量接口用上之后,吞吐立刻拉到了接近700 MB/s。这个数值已经追平了同环境里TCP的表现,说明瓶颈不在QUIC协议本身,而在操作系统对UDP发送路径的优化程度。
过程中还踩到一个并发死锁:在同时关闭一个QUIC流并向它写入数据的时候,程序会卡死。排查下来,是流关闭和写入竞争了同一个内部锁。修复方法很简单,在关闭流之前先执行一次流重置(Reset),让写入路径知道流已经终止,锁的竞争就解开了。
连接迁移的能力随后在真实环境中得到了验证。测试链路是一台位于巴塞罗那的笔记本电脑,通过72毫秒往返延迟的互联网连接到一台4核Linux VPS。在传输途中人为切换网络接口,抓包工具显示的QUIC包计数器平滑过渡到新地址对应的套接字上,gRPC通道没有重建,上层的文件读取调用也未感知到底层地址的变化。
真正的反差在随机读取和批量传输的对决中变得格外尖锐。为了让文件系统能在拖拽进度条时几乎瞬时响应,KeibiDrop设计了激进的预取策略:一旦用户打开文件,后台就开始全速拉取后续数据。在Wi-Fi存在一定丢包的广域网链路里,单一QUIC流的批量吞吐被丢包重传和拥塞控制拖累得厉害,实测比TCP慢了16倍之多。但如果用户在预取的海量数据流中间插入一次随机定位请求,TCP只有一个流,这个seek请求只能排在预取数据后面等。QUIC给了每一次seek请求自己单独的流,不需要排队,109毫秒的延时背后是两段较短的数据交换,不受预取流拥塞的影响。而在相同的条件下,TCP上的seek调用往往要等上3秒,直到后续的预取块传输完毕。
这组数据直接决定了KeibiDrop接下来的传输架构:交互式的按需读取路径保留在QUIC上,靠它的连接迁移能力和seek隔离特性确保用户在任意网络间切换时都能流畅操作大文件;而批量传输路径则回退到TCP,用成熟的拥塞控制算法保证在有损链路上的传输效率。两条路径同时工作,各自解决各自最擅长的问题。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.