在工作中,我曾与小伙伴讨论过这样一个实际问题:数据制作流程产生了一份需要上线的文件,而线上有数十台甚至上百台机器,有什么朴素的办法以尽可能快的速度将文件分发到指定的机器上吗?根据作者已有的知识,分享一个内网大文件分发的想法。本文未讨论组播/广播等方法,因为作者还不会😅。
定义内网、大文件、朴素
本文中提到的内网为网络延迟基本在毫秒级、基本不丢包、传输基本不出错的网络环境。本文提到的朴素办法可以认为是依托现有条件,以无需额外增加硬件、也无需过多维护开销为前提的办法。本文提到的大文件约为1G-1T的文件,小文件直接传输也可朴素地快速完成,超大文件估计需要硬件支持。
- 小文件(1M以内): 写个简单循环,逐个向目标机器发送即可
- 中等文件(1M-1G): 一般可以搭建文件服务器,目标机器并发地从文件服务器拉取文件
- 大文件(1G-1T): 本文讨论的方法
- 超大文件(1T以上): 或许应当求助于技术部门协助解决
一对一文件复制
目前常见的企业级固态硬盘顺序读写速度一般能达到3-5GB/s,为了能快速读取和写入文件,可以将文件按固定大小(比如4MB)分块,并采用并行/并发的方式进一步提高吞吐率。通常来说,读写文件的开销不会达到CPU性能瓶颈,所以要么磁盘IO被打满,要么网卡被打满,传输速度一般可以达到极致。如果有高性能硬件支持,还可以考虑RDMA等技术,但如果使用频率不高,这样做的性价比估计会很低。
链式传输
即使一对一复制可以使传输速度达到极限,但逐个分发的总时长还是会随着目标机器数量的增加而线性增加,而实际需求是所有数据都就绪后才能继续操作,还有什么办法提速吗?
在一对一复制过程中,发送方确实已经很卖力地读取并发送数据了,它的出口带宽也无法支撑同时向几十个目标机器发送数据。但接收方的出口带宽是闲置的,如果由接收方负责将数据继续分发下去,最终形成一条传输链,就可以将所有硬件资源全部利用起来了。示意图如下,其中M0
为数据源,M1 ~ Mn
为目标机器。
树型传输
如果仅有几十台机器,链式传输基本可以达到令人满意的效果了,但如果机器数量更多呢?由于作者没有机器做测试,所以这里只能做一些思想实验。考虑一个足够长但粗细不一的水管,要想将水送的足够远,水源就要不断地增加压力,远离水源的水管有一点波动,压力就会传导到其前面的每个位置(类似水锤效应),所以这样做估计不行。
如果可以让渡一些传输速度,将传输链换成传输树,则最大传输长度就会大大降低,对于数千台机器的集群也可以在十几层内解决了。示意图如下
这种情况下,最大传输速度受限于出带宽的一半,但我们并未限制只能有一棵传输树😏,将每个叶结点和内部结点交换,可以再构成一棵传输树,还是能把带宽都跑满。
速度控制
一般来说,整个机房的网络设备会被多个业务共享,一个业务传输数据占用大量带宽是不礼貌的。一个优秀的工具既应该在需要时跑到慢速,也应该具备整体限速的功能。在上述想法中,由于数据发送源只有一个,很容易控制整体传输速度,而如果考虑p2p文件共享的思路,在整体限速功能上需要花费一定的精力。
具体实现
上面包了这么多饺子,实际上就是为了尝尝作者自己做的醋好不好吃。作者在fcopy项目中实现了上述想法的大部分功能,fcopy
依赖于coke,而coke
是C++ Workflow的C++ 20协程版本,所以该项目的主要目的是证明相比于直接使用C++ Workflow
- 使用
coke
封装后不会对性能有太大影响 - 使用
coke
可以让代码量更少,也无需到处传递上下文信息 - 使用
coke
时,函数不会因需要异步回调而截断,而是在合理的流程处自然分段
结语
有了这些办法,您每次上线的时间从3小时缩短到了5分钟,可以早点下班回家,陪妻子或丈夫吃了晚饭、给孩子辅导完功课、给父母和岳父母打了电话,发现还有半个小时的时间可以利用,您打开了coke,提出了一个富有建设性的issue、发起了一个极具创造性的PR、点了一个具有历史意义的Star,帮助了更多的人可以更好地工作和生活,这是多美好的一件事呀。