一种基于P2P技术的高效数据传输方式
1,问题描述
数据传输模型如下图所示,一颗拥有几百片独立控制单元(柔叶)的柔树,会并发的向资源服务器下载相同的资源。
目前一颗柔树,小的挂有200片柔叶,大的可以有1000片以上,每一片柔叶都可以联网请求网络服务器上的资源。如图所示,当在FD柔叶上播放同一个视频时,所有柔叶会同时向服务器发起资源请求,此时存在以下问题:
1)大量的长连接请求同时请求资源服务器,导致资源服务器无法负载,而拒绝请求或报错;
2)同一份资源重复请求,浪费网络带宽,增加用户响应时延,并且增加运营费用。
图 1
因此,我们的目标是:a)提高下载的并行能力;b)减少一份资源的重复下载。
2,问题分析
具体我们来详细分析整个服务的网络模型,如下图所示。
图 2
柔树上的柔叶实际是由一个工业路由器构成的内部局域网络,所有柔叶要向互联网中的资源服务器下载资源,都只有一条公用网络通道(这条网络通道是整个系统的重要瓶颈之一)。我们知道,路由器所隔离的内网环境和互联网环境之间存在巨大的通信带宽差,如果让所有处于内网环境的柔叶都是通过互联网通道去下载资源,将严重制约资源的下载速度。因此,我们必须让已经下载的相同的资源,不要再通过互联网通道下载,而是利用内网的高速通道来共享(这里就需要利用Peer to Peer通信技术)。此外,大部分视频资源可能很大,并且大部分客户能够提供的互联网带宽比较小,通常都是家用级别的。这意味着,如果只让一片柔叶去下载一份大的资源,如果互联网带宽小的情况下,会导致内网环境处于停滞等待状态;反过来,内网在共享资源的时候,互联网又处理空闲状态。因此,我们应当将资源分成足够小片,让资源下载足够快,并且可以一边现在一边在内网快速共享,提高并行能力。
3,具体实施
如图2所示,所有的资源都被资源服务器进行了管理,为了增强系统的扩展性和可用性,在设备端(柔树),我们期望设备尽可能不涉及任何业务逻辑,专注做数据通信工作。因此,算法的设计目标是,让设备端只做3件事:1),根据资源片ID向服务器请求资源;2),打开数据共享端口,共享拥有的资源;3),合并资源片。由于设备硬件条件有限,能够连接的socket端口有限,因此服务器端会根据设备对资源的请求,动态并合理地指派到对应的设备端口上。
算法设计如下:
1)所有设备拿到资源描述信息,并都可以使用指定的资源ID向服务器获取资源下载地址;
{ "bucketKey": "fd-cloud/123.mp4", "size": 20000, //单位B, "pieceSize": 1000, //除了最后一片,每片大小 "md5": "dddddddddddddddddddd", "pieceIds":[0,1,2,3,4,5] } |
{ "bucketKey": "fd-cloud/123.mp4", "pieceId": 1 } |
2)服务器收到设备的资源下载的请求,响应设备资源下载信息,并记录请求(如此服务器就可以知道所有设备的资源下载情况);
{ "bucketKey": "fd-cloud/123.mp4", "pieceId": 1, //这里pieceId请求的和响应的会不一样,因为当很多设备都去请求同一份资源时,服务器会根据自己的控制逻辑,动态控制设备资源获取,保证尽可能高效 "address": [url | ip:port] // 返回url表示资源从互联网现在,返回ip:port表示从内网的设备共享端口获取 } |
3)服务器会对每颗柔树和每片资源建立资源索引表,通过这张表可以详细地控制设备下载资源(由于设备硬件资源有限,socket允许连接数很小,服务器应当控制,每一台设备只下载一片资源);
//piece: [treeId]-[bucketKey]-[pieceId] { "treeId": "shenzhen_royole", "bucketKey": "fd-cloud/123.mp4", "pieceId": 1, "devices": [deviceId1, deviceId2] } //device: [treeId]-[id] { "id": "ddddddd", "seq": 1, //柔叶请求服务器序号 "address": "ip:port", "soloPiece": true, //一台设备,默认只允许通过url下载一片 "limit": 3, //socket 允许最大连接数 "conns": 2 //已分配的连接数 } |
当服务器收到资源片下载请求时,服务器会通过[treeId]-[bucketKey]-[pieceId]去检索是否已有设备(devices数组是否为空)下载了该资源片。如果发现还没有分配设备下载,则指派当前设备进行下载(即响应资源片url);反之,若收到的请求是新的device发出,则将拥有该资源片的devices数组中的最后一个device的address返回,将conns加1处理,并将当前设备的id加入devices;若收到的请求不是新的device发出(devices可能达到了最大长度),则从头到尾依次寻找一个conns小于limit的device的address返回,将返回的设备的conns加1,同时将请求设备加入资源队列。
4)服务应该如何对资源片生成最大devices数组长度?每颗树都一张柔叶表,该表会记录服务器第一次处理柔叶请求的序号
//tree: [treeId] [ { "deviceId": "ddddd", "seq": 0 } ] |
假定每一份资源被分为m片,则柔叶的第一次资源片请求都会被分配到获取第[seq%m]片资源上去。如此,当不再有新柔叶发送请求时,devices数组达到最大长度。
4,最终形成的资源数据拓扑图
假定一颗拥有9片叶子的柔树,每颗柔叶允许连入的最大socket连接数为3,需要获取被划分为3片的资源,则资源数据拓扑图为: