问题练习 -- 如何快速分发大文件到大量服务器

设计目标

将大文件快速分发到大量服务器

demo请参考:https://github.com/caojunxyz/exercise-file-transmit

分析

大文件有多大?大量服务器有多少,百万台?这两个关键数据都会对问题的复杂度产生 至关重要的影响。为了简化问题,我们将文件大小限定到1G左右,服务器规模限制到100-10000台。 我们假设服务器之间的网络带宽都为100Mbit, 且服务器有足够的内存处理文件分发。实现会特别 注意减少内存开销,但文件大小与整个分发过程中内存的使用情况未能做精确计算。

  1. 假设分发服务器为Master, 需要将1000MB的文件分发到目标服务器Node,它们之间的带宽为100Mbit。 理论耗时大致为: 1000/(10010) = 100s
  2. 如果目标服务器的数量为100,M依次将1000MB文件传输到这100个服务器, 理论耗时大致为:100s x 100 = 10000s (~2.8小时)。显然,随着规模的扩大, 耗时越来越难以接收。
  3. 经过以上2点分析,最直接的瓶颈就在M的带宽,我们的解决办法核心就是突破M的带宽限制。 n台目标服务器之间的带宽资源是可以充分利用的。

设计

分组

将目标服务器进行分组,组间、组内都可以互相分发文件。

文件分块

有了分组的设计,如果还是互相传输1000MB大小的文件,那么节点之间可以开始文件传输的最短等待时间也至少是100s, 累计等待时间会远大于100s。为了减少等待时间,我们对文件进行分块,将文件分成很多小块进行分发,然后进行组装。

分组策略

将100台服务器分为10组,每组10台服务器,其中有一个Leader节点,其它为Normal节点。 分组Id和节点Id都从0开始。 1. M将文件块block#n Push给分组i(i = n % 10)的Leader。 2. Leader收到block后进行校验并组装(写到文件中),组装成功后向其它Leader节点以及本 Group中的其中一个普通节点广播消息(消息内容为它拥有*block#n*)。 3. 收到广播消息的节点向该节点Pull该*block#n*,并进行校验并组装,组装成功后重复2的广播操作, 但广播对象不包含2中广播消息的节点(因为它已经拥有该block)。 4. 当节点接收完所有文件块后,进行文件大小和sha1校验。 5. Leader节点可以定时(例如每秒)向它所在Group的普通节点查询当前组装进度,Master节点可以向所有Leader节点 查询一个Group的所有节点进度。

实现

核心有两个部分: 1. 文件分块与组装的逻辑 2. 节点间的Push和Pull的调度

注意的点: 1. 不能并发操作同一个文件,特别注意io.Seek的时候也需要加锁保护 2. 文件尽快落地

优化

  1. 在实际使用中,目标机器上的程序应该是一个常驻的后台进程。每个文件分发应该是 一个Session,但是目前没有完整的实现Session的管理。
  2. 分组根据节点之前的实际网络情况来动态确定,例如一个局域网内的节点应该尽可能 分到一个组。
  3. 故障恢复,断点续传。如果Leader节点挂了,应该动态切换新的Leader。
  4. 更好的用户操作及监控接口,例如Web方式。

Contents