Dragonfly 中 P2P 传输协议优化

news2025/3/1 3:31:59

文|孙珩珂

上海交通大学

本文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 反向通道详解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/9422.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java-IO流学习

文章目录前言1.Java-IO流简介2.流的基本概念3.创建文件的三种方式3.1根据一个文件路径直接创建一个文件。3.2 根据父目录 File 对象,在子路径创建一个文件3.3 根据父目录路径,在子路径下生成文件4.获取文件信息5.目录与文件操作5.1文件删除5.2目录删除5.…

【Linux】实验二 Makefile 的编写及应用

静态和动态库的转换可以参考这篇文章哦!! 文章目录实验二 Makefile 的编写及应用实验目的实验内容具体步骤:一、进入文件夹二、生成各个.c .h文件1. exam.h2. exam.c3. mat.h4. mat.c5. main.c三、编译建立的文件1. 只编译不链接 main.o2. 使…

Vue实现任意内容展开 / 收起功能组件

博主介绍 📢点击下列内容可跳转对应的界面,查看更多精彩内容! 🍎主页:水香木鱼 🍍专栏:后台管理系统 文章目录 简介:这是一篇有关【Vue实现任意内容展开 / 收起功能组件】的文章&am…

1.jdk,数据类型,运算符

Java语言跨平台性的解释 一次编译,到处运行 将.java文件编译成.class文件,然后就可以在linux,windows运行。 java的跨平台的实现是通过在不同的操作系统上的不同的jvm差异来实现跨平台的 jdk jdk jre(jvmjavase核心类库&…

MySQL基础操作总结_CRUD

文章目录 1.新增 insert 1.1 单行数据全列插入 1.2 多行数据指定列插入 2.查询 select 2.1 全列查询 2.2 指定列查询 2.3 查询字段包含表达式 2.4 别名 2.5 去重:distinct 2.6 排序:order by 2.7 条件查询 2.7.1 where条件查询 2.7.2 and与or查询 2.7.3 范围查…

Java简介

Java是一门面向对象的编程语言,不仅吸收了C语言的各种优点,还摒弃了C里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论&#xff0…

Matplotlib 基础入门知识点总结

目录 1、绘图的一些基本命令展示 2、Matplotlib 绘制网格 3、plt.gca() 对坐标轴的操作 4、 图表的样式参数设计 5、创建图形对象 6、绘制多子图 1.add_axes():添加区域 2. subplot()函数,可以均等的划分画布 3.subplots()函数详解 7、柱形图的…

踩坑List.addAll抛出UnsupportedOperationException

转载标明出处: 公司内部博客,作者 居xx, 在此表示感谢 1、我自己遇到的问题 try { // 不加try..catch报错 java.lang.UnsupportedOperationException: null ,原因未知list.addAll(a.getResult());list.addAll(b.getResult());list.addAll(c.getResult());list.addAll(d.getResu…

双软认证是指哪两项证书认证

一、双软认证是指哪两项证书认证: “双软认证”:指软件产品等登记和软件企业认定;企业申请双软认证,除了获得软件企业和软件产品的认证资格外,也可以使企业享受国家对软件行业提供的税收优惠政策。 二、认证的好处 1、…

后量子密码学中的模数是多少?

密码学:后量子密码学中的模数是多少? 只要是从事网络安全或安全软件开发的人,就都可能已经了解过公钥加密以及在20世纪70年代末和80年代前后创建的方法。现在我们可能需要学习更多的理论,因为我们所学的方法可能会受到量子计算机的…

网页字体图标用法

目录前言引用流程1、打开网页2、选择自己喜欢的图标加入购物车3、下载图标4、引用引用前言 1、接下来介绍引用图标都是免费的,可以放心食用。 2、标签可以自己设计,用css、svg画图或者引用图片都可以。但我们设计的很难超过专业人员的设计。引用图片的画…

深入理解mvcc机制(详解)

深入理解mvcc机制一,MVCC定义1,undolog日志2,undolog版本控制链3,readView3.1,readview简介3.2,readview和undolog结合使用规则3.3,readview和undolog基本使用4,总结一,M…

Java学习----前端3

今日学习内容如下&#xff1a; JavaScript JavaScript是一种基于对象和事件驱动的客户端脚本语言动态、弱类型、基于原型&#xff0c;内置了支持类 解释器称为 JS 引擎&#xff0c;内置于浏览器中 ECMA 欧洲计算机制造商协会Hello world <input type"button" on…

与git相关错误的究极解决方案

没有科学上网导致的git推送和拉取错误&#xff0c;严重阻碍了搬砖进度&#xff0c;遇到的与git有关的错误&#xff1a; 1. gnutls_handshake() failed: The TLS connection was non-properly terminated. 2. Failed to connect to github.com port 443:connection timed out …

Postman 如何获取请求结果并设置到请求头中

目录1.设置环境变量2.获取token&#xff0c;并赋值给环境变量3.将环境变量添加到请求头Postman&#xff1a; 是一款用于接口调试和测试的开发工具&#xff0c;功能强大&#xff0c;使用简单。 无论是开发人员进行接口调试&#xff0c;还是测试人员做接口测试&#xff0c;Postma…

本地生活小程序有什么功能_本地生活小程序的优势

对于平台运营方 痛点&#xff1a;社区团购毛利偏低 零售行业竞争激烈&#xff0c;单纯依靠社区团购卖货整体毛利率较低 手中大量用户&#xff0c;缺少好的变现模式&#xff0c;迫切需要提升盈利能力。 优势&#xff1a;提升盈利&#xff0c;解决流量变现 本地生活属于轻资产…

8Manage:千万别忽视了供应商绩效管理

供应商绩效管理是采购管理流程中的一部分&#xff0c;现代企业几乎都会对供应商实行绩效考核。绩效管理的主要目的是了解供应商的表现、促进供应商改进&#xff0c;并为工业商奖励、供应商优化提供依据&#xff0c;以此来发现优质的供应商&#xff0c;及时改进不合格的供应商。…

Python控制自己的手机摄像头拍照,并把照片自动发送到邮箱

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 又到了学Python时刻~ 今天这个案例&#xff0c;就是控制自己的摄像头拍照&#xff0c; 并且把拍下来的照片&#xff0c;通过邮件发到自己的邮箱里。 想完成今天的这个案例&#xff0c;只要记住一个重点&#xff1a;你…

CSDN的MD编辑器【写作技巧】

CSDN的MD编辑器【写作技巧】在线LaTeX公式的编辑器快捷键&#xff0c;结合快捷键提高效率写作技巧在线LaTeX公式的编辑器 推荐去https://www.latexlive.com/在线LaTeX公式编辑器 将写好的公式代码复制过来。 然后复制在CSDN编辑器里面前后加上符合$就行了 前后加两个该符合就会…

Java环境安装即配置

一、java1.8的安装步骤 1 安装目录创建java文件夹 在想要安装java的目录下创建一个java文件夹,不能出现特殊符号和汉字、空格等 2 java文件夹内创建jdk和jre Java安装需要两部分,一个jdk一个jre 所以在java文件夹中创建两个文件夹 3 解压安装包 通过百度网盘分享的文件&#…