对于多任务多连接多线程实现限速的实现方法及思考

对于多任务多连接多线程实现限速的实现方法及思考

前言

最近在写毕业设计,我的毕业设计就是用Rust语言实现一个Bittorrent客户端协议及其拓展协议,顺便写个Web让这个玩意能跑起来用。

总之就是要实现一个类似迅雷的下载器。下载器嘛,肯定要有限速功能的,不然吃满带宽导致其他应用饿死(BT下载尤其如此,因为是多连接多线程多任务下载,网络好的情况下真的很容易完全吃满网速导致其需要网络的应用根本不能用,不仅下载带宽会被吃满,上传带宽也会被吃满)。

通常实现下载限速的方式就是固定时间内传输固定字节数量。对于单任务单连接单线程来说,很好实现,直接传输完了就开始sleep就完事了。但是对于多任务多连接多线程来说,如何做到让所有正在传输的线程的总和加起来的速度不超过设定值是一个让我头疼的问题。

所以我需要设计一个限速的方法或者说架构,来实现多线程多任务多连接下载。思考之前也在往上搜索了相关的资料,没有找到适合这个场景的方法。只能自己想一个喽。

系统架构

低耦合高内聚。不同的模块之间虽然是包含关系,大部分操作通过信息的传递来进行控制。
主进程可以设置全局限速,TASK可以设置TASK限速及TASK本身优先级,connect也可以单独设置限速及优先级。(优先级本质上是资源分配权重)

实现思路

什么是速度?什么是限速?速度本质上是资源,限速本质上是有限资源的分配!

在BT下载中,速度通常来说分别有下载速度以及上传速度,这都是速度,只是方向不同,因此统一用速度来代替下载和上传两种速度,下载和上传除了方向不作区分。

  • 物理中的定义,速度分为平均速度和瞬时速度。瞬时速度就是时间无限小之内的位移除以时间。那么在下载中的速度一般指瞬时速度,但是一般来说,如果要得到瞬时速度,那么基本上程序都用来算速度而不是逻辑执行以及网络传输了。所以这里的速度一般指的就是固定时间内,字节传输的平均速度。假设1s传输了100字节,那就是100Byte Per Second (100bps)。因此软件实现测速就是固定间隔时间,计算在这段时间内传输的总字节数。很简单的事情。

  • 再深入一些,什么是限制速度?限制速度本质上就是限制一段时间内传输的字节数量。也就是限制流量。想到这一刻的时候,我豁然开朗!对啊,速度也是一种资源,和内存一样,是一种可以消耗的资源。

  • 因此在多线程多模型多连接的传输场景下,实现限速,就可以使用“速度是资源”这一思想。要做的就是资源的分配,而且这种资源的分配不会像内存分配那样产生内部碎片和外部碎片。因为流量本身就是连续的,是一种流。根本不用实现类似buddy系统+slab系统这种较为复杂的资源分配器。也不存在资源的返还,用了就是用了,不用还的。

资源分配方式

在我的设想中,资源(速度)的分配主要就分为两种方式。

  1. 主动分配被动接受
  2. 主动申请被动分配

主动分配,被动接受

  1. 实现思路
    在第一种方式中,主进程每隔一段时间扫描所有task,然后根据限速及优先级综合分配资源。TASK线程每隔一段时间扫一遍connect,将自己有的资源分配给connect。

    在这种方式中,我们首先要保证公平性,也就是如果两个task优先级一样,并且连接性能理想化(也就是假设不给限速,带宽就是无限大)。需要分配的资源是相同的,也就是在实际下载中,速度相同。如果优先级不同,则根据优先级分配资源。

    同样的我们还需要保证高效率低浪费,对于同一个task下的所有connect,假设connect连接优先级均等同,那么这时候哪个连接的性能越好,其速度应该越高。因为如果均使用平均分配的方式,那么连接性能好的connect会饥饿,但是连接差的connect会过饱。多劳多得,本质上也是一种公平。

    同时,还需要防止资源的“垄断”。因为如果连接性能越好,获得的资源越多,那么资源越多的情况下有时候会反应更好的连接性能,这时候就造成了垄断。

    因此,还需要一个方案来测算真实连接性能。

    在一段时间内,有传输时间,有等待资源获取时间。那么有效的时间就是传输时间,而不是资源获取时间。因此实际连接性能应该根据 (流量/有效传输时间) 来测算,这样可以保持公平性。

    资源的浪费会导致达不到设置的速度。

  2. 架构优点

    • 可以实现较复杂的优先级设置及资源分配模式
    • 天然实现速度测量
  3. 架构缺点及问题

    • 测算的开销可能较大,而且测算是有误差的。
    • 反应慢,不能及时做出分配,在线路性能不稳定的时候,资源的等待时间可能会过长。
    • 实现复杂
    • 等待补充,读者也可以在评论区提出呀

主动申请,被动分配

参考内存分配的方式,内存分配一般都是程序向操作系统提出申请,然后操作系统作出反应来分配内存。

  1. 实现思路
    固定每次分配的流量大小,每次申请均返回固定大小的资源。当connect用完资源后才去申请。task响应connect的申请,主进程响应task的申请。

    要保证一段时间内保证可以分配匹配限速的资源数量,不然会导致task的流量堆积。

  2. 架构优点

    • 响应速度快
    • 资源利用率高
    • 实现简单
  3. 架构缺点

    • 复杂的分配逻辑难以实现
    • 需要额外实现速度测量
    • 等待补充,读者也可以在评论区提出呀

代码实现

主动分配,被动接受

todo

主动申请,被动分配

todo

UPDATE 2023.6.18

最后的实现也和这个差距较大。
其实,要限制下载速度,只需要对请求发送速率限制即可。
要限制上传速率,对请求的响应作出限制即可。
之前还是想复杂了。

others

如果错误之处,一定要指出来!
感谢你的阅读。

posted @ 2023-03-15 22:55  Alyjay  阅读(311)  评论(0编辑  收藏  举报