多种Linux网络限速方案
网路流量限制速度是个经典老话题,在Linux内核中已有几种实现方法来限制流量。 最简单的方法是采用定时采集率,在超过规定速率后直接丢包,但是这种方法效果不好,无法精确地把流量控制在规定的速率上。
多种Linux网络限速方案
比较成熟的方法是将需要延迟发送的分组缓冲到队列中,并在适当的时候再次发送,所以Linux内核中的TrafficControl(TC)层成为实现透明网络流量限制的最好位置。
TC仅在出口方向上实现了具有队列Qdisc(Queuediscipline),因此我们所提到的所有速度限制也都是以出口为导向的。进气口方向的限速是比较困难的,除流量控制层缺乏排队外,对流量源的限制也非常困难,所以本文不作讨论。
1.传统速度限制方案。
常规的TC速度限制,就是在一个单独的Qdisc上增加一个Qdisc,比如HTB,TBF等等。这两种方法都存在一个共同的缺点,即依赖于全局设备的Qdiscspinlock。
分组入队、出队均为写操作,且均非原子操作,因此需要使用锁进行同步;
这两个Qdisc支持全局设备速度限制,其中一些部分(比如HTBQdisc)允许不同通信类型(class)之间相互借用带宽,这就需要使用全局锁来进行统一协调。
这类全局锁在传输量很大时,都会遇到性能瓶颈。
2.mqQdisc模式。
为了解决传统方案的缺点,Linux内核提供了一个“分散”这个全局spinlock的软件方案:mqQdisc。mqQdisc是一种特殊的Qdisc,它为每个网络设备的每个硬件队列创建一个软件Qdisc,然后通过->attach()操作将它们安装到单独的硬件队列中。
通过这种方式,Qdisc在每个硬件队列(上图中的“childQdisc”)都有自己的spinlock,其中一个“拆分”为多个锁,这就改善了设备全局spinlock的性能问题。
值得注意的是,mqQdisc本身没有实现任何速度限制机制,它只是提供了一个可以与其他childQdisc(HTB,SFQ等)配合使用的框架。
这种做法有一个缺点:现在设备上的Qdisc被“拆掉”到多个childQdisc中,我们只能分别对这些childQdisc配置限速规则,因此在传统的方案中无法全局控制设备的流量。
3.HTB硬件安装方案。
Mellanox网卡针对传统HTB方案的不足,推出了一种通过硬件实现速度限制的方案,为HTBQdisc增加“offload模式”,模拟mqQdisc的->attach()操作。
每一个硬件队列都对应HTB树结构中最低级的流量类型(leafclass)。在传统HTBQdisc中分类逻辑现在被移至clsactQdisc中,
设置skb->priority,安装在clsactQdisc上的filter可以将包分类;
Mellanox网卡驱动注册的->ndo_select_queue()成员函数根据skb->priority将包分发到不同的硬件队列(对应于不同的HTBleafclass);
限制在硬件上最终完成。
但是,不管是基于mqQdisc的软件方案,还是offload方案,都有一个最大的缺点:数据流的类型,限制速度规则被限制在单独的硬件队列中,无法灵活地分配带宽以满足业务需求。
4.ifb计划。
ifb方案为每个流量类型创建一个新的软件ifb设备,流量在原始发送设备的clsactQdisc上被分类,使用mirredaction将不同类型的通信流转发给相应ifb设备,单个ifb设备上的TBFQdisc则可完成:
因为网络设备硬件队列数目不受ifb设备数量的限制,ifb方案(相对于mqQdisc方案和硬件offload方案)能够更灵活地满足业务要求的速度限制需求。但是,由于TBFQdisc对各ifb设备的速度限制仍有可能出现多个CPU同时在同一个spinlock中竞争的情形:
假定某个时间点在2号、3号和4号CPU上的进程同时发送类别B的通信(相当于1号ifb设备),那么三个CPU将与TBFQdisc中的spinlock竞争。
字节跳动系统部STE小组确认如下:
将clsactQdisc安装到eth0设备上,设置mirred规则,将出口的方向A类流量指向ifb0;
将TBFQdisc安装到ifb0设备上,并为A类通信设置500Mbits/sec带宽限制;
运行96个iperfclient进程将分别绑定到96个CPU,向eth0设备发送A类通信。
经过确认,在server端64.4秒内实际收到的平均流量是466Mbits/sec。
5.EDT计划。
EricDumazet等人针对以上几类方案的不足,提出了一种EDT(EarliestDepartureTime)方案,使用clsactQdsic+eBPFfilter+mqQdisc。
根据这一计划整理的承包程序:
信息包首先统一地通过clsactQdisc出口的一种eBPFfilter。该eBPFfilter程序包含了主要的速度限制逻辑;它将根据特定分组所属通信类别的带宽计算该包的预期发送时间,并为其添加时间戳(skb->tstamp);
然后,数据包被分发到设备的不同的硬件队列中,并且具有各自的fqQdisc;
最终,fqQdisc会根据时间戳来进行分组排序,保证每一个包最终离开的时间不会早于分组上的时间戳,从而实现速度限制。
EDT方案相对于mqQdisc等算法的优势之一,就是流量类别和硬件队列之间没有对应关系。
给2号、3号和4号CPU相同的通信量提供了向不同的硬件队列发送通信的机会,有效地解决了一种类型的大通信同时从多个CPU向某个(或某些)硬件队列(如ifb)带来的锁定竞争问题。
一定要注意,无论发送同一或不同类通信量的两个CPU仍有可能与相同的fqQdiscspinlock竞争。