Dragonfly 中 P2P 传输协议优化

简介: 静态限流策略的理想情况是: `perPeerRateLimit` 设置为20M , `totalRateLimit` 设置为 100M ,且该节点目前运行了 5 个或更多的 P2P 下载任务,这种情况下可以确保所有任务总带宽不会超过 100M ,且带宽会被有效利用。

图片

文|孙珩珂

上海交通大学

本文1987字 阅读 10 分钟

01 优化背景

此前 Dragonfly 的 P2P 下载采用静态限流策略,相关配置项在 dfget.yaml 配置文件中:

# 下载服务选项。
download:  
# 总下载限速。  
totalRateLimit: 1024Mi  
# 单个任务下载限速。  perPeerRateLimit: 512Mi

其中 perPeerRateLimit 为单个任务设置流量上限, totalRateLimit 为单个节点的所有任务设置流量上限。

静态限流策略的理想情况是: perPeerRateLimit 设置为20M , totalRateLimit 设置为 100M ,且该节点目前运行了 5 个或更多的 P2P 下载任务,这种情况下可以确保所有任务总带宽不会超过 100M ,且带宽会被有效利用。

图片

这种限流策略的缺点是:若perPeerRateLimit 设置为 20M , totalRateLimit 设置为 100M ,并且当前该节点只运行了一个下载任务,那么该任务的最大下载速度为 20M ,和最大带宽 100M 相比,浪费了 80% 的带宽。

图片

因此,为了最大限度地利用带宽,需要使用动态限流来确保任务数量少时能能充分利用总带宽,而任务数量多时也能公平分配带宽。最终,我们设计出一套根据上下文进行动态限流的算法,其中上下文指各任务在过去一秒内使用的带宽,此外,算法还考虑到了任务数量、任务剩余大小、任务保底带宽等因素,性能相比原来的静态限流算法有显著提升。

02 相关代码分析

perPeerRateLimit 配置项最终赋值给 peerTaskConductor 的pt.limiter ,由 peerTaskConductor 的 DownloadPiece() 函数里进行限速,pt.waitLimit() 进行实际限流工作,底层调用 Go 自带的限流函数 WaitN() 。

TotalRateLimit 配置项则在创建 Daemon 时被赋值给 pieceManager 的pm.limiter ,在 pieceManager 的 DownloadPiece() 和 processPieceFromSource() 函数中用到的 pm.limiter ,而这两个函数都会由 peerTaskConductor 调用,也就是说 P2P 下载会先进行总限速,之后再进行每个任务单独限速。

图片

根据以上分析,Dragonfly 进行任务限速的逻辑为,每个peer task(peerTaskConductor)会有单独的限速 perPeerRateLimit ,同时 pieceManager 会有 TotalRateLimit 的总限速,以此达到单任务单独限流,同时限制所有任务总带宽的效果。

### 03 优化方案

为了解决此前静态限流算法总带宽利用率不佳的缺点,需要将其改进为动态限流算法,即总带宽限速仍恒定,但每个任务的单独带宽限速需要根据上下文适度、定期调整,已达到最大化利用总带宽、同时相对公平分配带宽的目的。

在经过数个改版后,最终我们确定了根据上下文进行限流的 sampling traffic shaper 动态限流算法。具体方案为,每个任务的单任务限流交由 TrafficShaper 组建进行统一管理, TrafficShaper 维护当前正在运行的所有任务,并且定期(每秒)更新这些任务的带宽。

具体来说,上下文指每个任务在上一秒使用的带宽、每个任务的剩余大小、任务数量、任务保底带宽(不能低于 pieceSize )等因素, TrafficShaper 会根据这些上下文公平地、效率最大化地为每个任务分配其下一秒的带宽(具体分配方案详见下一小节),实现动态限流的效果。

04 优化实现

定义 TrafficShaper 接口如下:

// TrafficShaper allocates bandwidth for running tasks dynamically
type TrafficShaper interface {
   // Start starts the TrafficShaper
   Start()   
   // Stop stops the TrafficShaper
   Stop()   
   // AddTask starts managing the new task
   AddTask(taskID string, ptc *peerTaskConductor)
   // RemoveTask removes completed task
   RemoveTask(taskID string)   
   // Record records task's used bandwidth
   Record(taskID string, n int)
   // GetBandwidth gets the total download bandwidth in the past second
   GetBandwidth() int64
}

该接口有两种实现,第一种是 samplingTrafficShaper 即基于上下文的 traffic shaper ,第二种是 plainTrafficShaper 只记录带宽使用情况,除此之外不做任何动态限流工作,用于和 samplingTrafficShaper 对比性能提升。

同时,将相关配置项修改为如下内容:

# 下载服务选项。
download:  
# 总下载限速。
totalRateLimit: 1024Mi
# 单个任务下载限速。
perPeerRateLimit: 512Mi
# traffic shaper类型,有sampling和plain两种可选  trafficShaperType: sampling

图片

Traffic shaper 的具体运行逻辑为,由peerTaskManager维护trafficShaper,在创建peerTaskManager时,根据配置初始化trafficShaper,并且调用Start()函数,启动trafficShaper,具体来说,新建time.NewTicker,跨度为 1 秒,也即每秒trafficShaper都会调用updateLimit()函数以动态更新所有任务的带宽限流。

updateLimit() 函数会遍历所有运行中的任务,得出每个任务上一秒消耗的带宽以及所有任务消耗的总带宽,随后根据任务上一秒使用的带宽、任务剩余大小等因素,按比例分配带宽,具体来说首先根据上一秒该任务使用带宽以及该任务剩余大小的最大值确定下一秒该任务带宽,接着所有任务带宽根据总带宽按比例缩放,得到下一秒的真实带宽;同时需要确保每个任务的带宽不低于该任务的 pieceSize ,以免出现持续饥饿状态。

在 peerTaskManager 的 getOrCreatePeerTaskConductor() 函数中,若新建任务,需要带宽,那么调用 AddTask() 更新所有任务的带宽,即按照已有任务的平均任务分配带宽,然后再根据总带宽上限将所有任务的带宽等比例进行缩放;根据平均带宽分配新任务带宽的优势为,避免了已经有一个任务占满了所有带宽,有新任务进来时,带宽会被压缩到很小 **的情况;同时,不是平均分配带宽,而是按需等比例分配,可以确保带宽需求量大的任务仍然带宽最多。在 peerTaskManager 的 PeerTaskDone() 函数中,任务完成,不再占用带宽,调用 RemoveTask() 按比例扩大所有任务的带宽。

最后, peerTaskManager 停止时,调用 Stop 函数,停止运行 traffic shaper 。

05 优化结果

测试 traffic shaper 相比原有的静态限流策略在单个任务、多个任务并发、多个任务交错等多种情况下的性能提升,测试结果如下:

图片
注:若不特殊注明,单任务限流为4KB/s,总限流为10KB/s

可以看到, traffic shaper 在单任务、多任务不相交、单任务低带宽等情况下相比静态限流策略性能提升明显,为 24%~59% 。在多个任务并发、多个任务交错等情况下和静态限流策略性能相当。综上,实验证明 sampling traffic shaper 能很好地解决任务数量较少时总带宽被大量浪费的情况,同时在任务数量较多以及其他复杂情况时依旧能保证和静态限流算法持平的效果。

PR 链接(已合并):
https://github.com/dragonflyoss/Dragonfly2/pull/1654

本周推荐阅读

Dragonfly 基于 P2P 的文件和镜像分发系统

深入 HTTP/3(2)|不那么 Boring 的 SSL

Go 代码城市上云——KusionStack 实践

MOSN 反向通道详解

相关文章
|
2月前
|
网络协议 网络安全 网络架构
分布式基础-网络通信协议讲解
分布式基础-网络通信协议讲解
分布式基础-网络通信协议讲解
|
2月前
|
网络协议
UDP协议在网络通信中的独特应用与优势
UDP(用户数据报协议)作为关键的传输层协议,在网络通信中展现出独特优势。本文探讨UDP的无连接性及低开销特性,使其在实时性要求高的场景如视频流、在线游戏中表现优异;其不保证可靠交付的特性赋予应用程序自定义传输策略的灵活性;面向报文的高效处理能力及短小的包头设计进一步提升了数据传输效率。总之,UDP适用于高速、实时性强且对可靠性要求不高的应用场景,为网络通信提供了多样化的选择。
|
3月前
|
JSON Dubbo Java
【Dubbo协议指南】揭秘高性能服务通信,选择最佳协议的终极攻略!
【8月更文挑战第24天】在分布式服务架构中,Apache Dubbo作为一款高性能的Java RPC框架,支持多种通信协议,包括Dubbo协议、HTTP协议及Hessian协议等。Dubbo协议是默认选择,采用NIO异步通讯,适用于高要求的内部服务通信。HTTP协议通用性强,利于跨语言调用;Hessian协议则在数据传输效率上有优势。选择合适协议需综合考虑性能需求、序列化方式、网络环境及安全性等因素。通过合理配置,可实现服务性能最优化及系统可靠性提升。
52 3
|
6月前
|
网络协议 安全 算法
谷歌出品!读懂 QUIC 协议:更快、更高效的通信协议
谷歌出品!读懂 QUIC 协议:更快、更高效的通信协议
1059 0
|
6月前
|
存储 网络协议 数据中心
|
6月前
|
编解码 移动开发 C++
RTMP协议深度解析:从原理到实践,掌握实时流媒体传输技术
RTMP协议深度解析:从原理到实践,掌握实时流媒体传输技术
1172 0
RTMP协议深度解析:从原理到实践,掌握实时流媒体传输技术
|
12月前
|
存储 XML JSON
高效的网络传输协议设计protobuf
高效的网络传输协议设计protobuf
126 1
|
网络协议 安全 数据安全/隐私保护
直播app源码,QUIC协议:改善性能与安全性
由于QUIC协议作为一个多种协议集成的一项新型协议创新,被应用到直播app源码平台中,能帮助平台提高性能与安全性,提供给用户更可靠、更优质的直播体验。
直播app源码,QUIC协议:改善性能与安全性
EMQ
|
Kubernetes Cloud Native 物联网
在 Kubernetes 上体验 EMQX 5.0 的 MQTT over QUIC 特性
本文将介绍如何在 Kubernetes 上部署 EMQX 集群并开启 MQTT over QUIC 功能。
EMQ
140 0
在 Kubernetes 上体验 EMQX 5.0 的 MQTT over QUIC 特性
|
tengine 网络协议 算法
带你读《2022龙蜥社区全景白皮书》——5.5.2 面向HTTP 3.0时代的高性能网络协议栈
带你读《2022龙蜥社区全景白皮书》——5.5.2 面向HTTP 3.0时代的高性能网络协议栈
181 2