网络流量限速是一个经久不衰的话题,Linux 内核中已经实现了若干种流量限速的方式。
最简单的方式是通过定期采集速率,在超过指定的速率后直接丢包,但这种方案效果不佳,不能精准地将流量控制在指定的速率。
更成熟的方案都是把需要延迟发送的数据包缓冲在队列中,在合适的时间再进行发送,因此 Linux 内核中的 Traffic Control(简称 TC)层就成了实现透明的网络流量限速的最佳位置。
由于历史原因,TC 只有在出口方向上实现了有队列的 Qdisc (Queue discipline),所以我们在这里提到的限速方案也都是在出口方向上实现的。入口方向上的限速比较困难,除了 TC 层缺少队列之外,还很难对流量的源头进行限速,因此我们在这里不予讨论。
传统的 TC 限速,是通过给网络设备添加单一的具有限速功能的 Qdisc (比如 HTB、TBF 等)来完成的。这些方案有一个共同的缺点,即依赖一把设备全局的 Qdisc spinlock 来进行同步。
这把锁很难进行优化,主要有两个原因:
上述传统方案在发送流量较大的时候会碰到这个全局锁的性能瓶颈。
针对传统方案的弊端,Linux 内核提供了一个「拆散」这把全局 spinlock 的软件方案:mq Qdisc。mq Qdisc 是一个很特殊的 Qdisc,它为网络设备的每一个硬件队列分别创建一个软件 Qdisc,再通过一个 ->attach()
操作将它们挂载到各个硬件队列上,如图所示:
这样一来,每一个硬件队列上的 Qdisc(上图中「child Qdisc」)都有各自的 spinlock,一个锁被「拆」成了多个锁,从而改善了设备全局 spinlock 带来的性能问题。
需注意的是,mq Qdisc 本身并不实现任何限速机制,仅仅提供了一个框架,须和其它具有限速功能的 child Qdisc(HTB、SFQ 等)配合使用。
这样做有一个缺点:现在设备上的一个 Qdisc 被「拆」成了多个 child Qdisc,我们就只能分别对这些 child Qdisc 配置限速规则,从而失去了传统方案里对设备流量的全局控制。
针对传统 HTB 方案的缺点,Mellanox 网卡推出了一个通过硬件来实现限速的方案,给 HTB Qdisc 添加了「offload 模式」,模拟 mq Qdisc 的 ->attach()
操作:
每个硬件队列分别对应了一个 HTB 树形结构中最下层的流量类型 (leaf class)。传统 HTB Qdisc 里的分类逻辑现在被移到了 clsact Qdisc 里,例如:
$ tc qdisc add dev $DEV clsact
$ tc filter add dev $DEV egress protocol ip flower dst_port 80 \
action skbedit priority 1:10
梳理一下这种方案下的发包流程:
skb->priority
;->ndo_select_queue()
成员函数根据 skb->priority
将数据包分发到不同的硬件队列(对应不同的 HTB leaf class)里;但同样的,无论是基于 mq Qdisc 的软件方案,还是依赖于硬件特性的 offload 方案,它们都有一个最大的弊端:流量的类型、限速规则绑定在各个硬件队列上,没有办法根据业务的需求灵活地分配带宽。
ifb 方案为每一种流量类型新建一个软件 ifb 设备,在原发送设备的 clsact Qdisc 上对流量进行分类,通过 mirred action 将不同类型的流量转发到对应的 ifb 设备,再由各个 ifb 设备上的 TBF Qdisc 完成限速:
由于 ifb 设备的数量不受网络设备硬件队列个数的限制,ifb 方案(相比 mq Qdisc 方案和硬件 offload 方案)可以更灵活地满足业务所需的限速需求。然而,由于各个 ifb 设备上的限速由 TBF Qdisc 实现,仍然可能会出现多个 CPU 同时竞争同一把 spinlock 的情况:
如上图所示,假设某一时刻运行在 2 号、3 号和 4 号 CPU 上的进程同时发送 B 类流量(对应到 1 号 ifb 设备),那么这三个 CPU 就会竞争 1 号 ifb 设备上 TBF Qdisc 的 spinlock。
字节跳动系统部 STE 团队进行了以下验证:
经验证,server 端 64.4 秒内实际接收到的平均流量为 466 Mbits/sec。
针对上述各类方案的缺点,Eric Dumazet 等人提出了一种使用 clsact Qdsic + eBPF filter + mq Qdisc + fq Qdisc 的 EDT (Earliest Departure Time) 方案:
梳理一下这种方案下的发包流程:
skb->tstamp
);与 mq Qdisc 等方案相比,EDT 方案的一个优点,在于流量类别与硬件队列之间不存在对应关系:
如上图所示,发送同一类流量的 2 号、3 号、4 号 CPU 有机会把流量发送到不同的硬件队列上,有效解决了某一类大流量同时从多个 CPU 发送到某一个(或某几个)硬件队列(或 ifb 等虚拟设备)上带来的锁竞争问题。
需要注意的是,不管是发送相同还是不同类流量的两个 CPU 仍然有可能竞争同一个 fq Qdisc spinlock。
字节跳动系统部 STE 团队对 EDT 方案也做了类似的验证:
经验证,server 端 62.8 秒内实际接收到的平均流量为 489 Mbits/sec。简单来说,EDT 方案与 ifb 方案相比,「竞争 1 把锁」变成了「竞争 8 把锁」,从而在性能上获得了一定的优势。
通过在 eBPF filter 程序中修改 skb->queue_mapping
,我们可以将 CPU 与发送设备的硬件队列一一绑定(例如 CPU 1 只能发送到硬件队列 1,等等),从而进一步减缓 CPU 之间的锁竞争。事实上,如果机器上 CPU 的数量小于等于发送设备的硬件队列数,这样操作可以完全避免 CPU 之间的锁竞争。
但这也带来一个问题,考虑:假设我们让 CPU 1 只能发到硬件队列 1,CPU 2 只能发到硬件队列 2。发送某类流量的进程甲,上一时刻运行在 CPU 1 上,发了一个数据包 A 到硬件队列 1 上;下一时刻它被调度到 CPU 2 上,发了一个数据包 B 到硬件队列 2 上。由于硬件队列 1 和 2 分别有自己的 fq Qdisc,维护着各自的数据包出队顺序,我们无法保证数据包 A 会先于数据包 B 出队——也就可能会有发包乱序的问题。
总的来说,我们认为 EDT 方案有三个优点:
按照 IP 目的地址把流量分为不同类型,并为每一类配置 100 Mbits/sec 的带宽:
#include <linux/types.h>
#include <bpf/bpf_helpers.h>
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/pkt_cls.h>
#include <linux/swab.h>
#include <stdint.h>
#include <linux/ip.h>
struct bpf_elf_map {
__u32 type;
__u32 size_key;
__u32 size_value;
__u32 max_elem;
__u32 flags;
__u32 id;
__u32 pinning;
__u32 inner_id;
__u32 inner_idx;
};
#define NS_PER_SEC 1000000000ULL
#define PIN_GLOBAL_NS 2
#ifndef __section
# define __section(NAME) \
__attribute__((section(NAME), used))
#endif
struct bpf_elf_map __section("maps") rate_map = {
.type = BPF_MAP_TYPE_HASH,
.size_key = sizeof(__u32),
.size_value = sizeof(__u64),
.pinning = PIN_GLOBAL_NS,
.max_elem = 16,
};
struct bpf_elf_map __section("maps") tstamp_map = {
.type = BPF_MAP_TYPE_HASH,
.size_key = sizeof(__u32),
.size_value = sizeof(__u64),
.max_elem = 16,
};
int classifier(struct __sk_buff *skb)
{
void *data_end = (void *)(unsigned long long)skb->data_end;
__u64 *rate, *tstamp, delay_ns, now, init_rate = 12500000; /* 100 Mbits/sec */
void *data = (void *)(unsigned long long)skb->data;
struct iphdr *ip = data + sizeof(struct ethhdr);
struct ethhdr *eth = data;
__u64 len = skb->len;
long ret;
now = bpf_ktime_get_ns();
if (data + sizeof(struct ethhdr) > data_end)
return TC_ACT_OK;
if (eth->h_proto != ___constant_swab16(ETH_P_IP))
return TC_ACT_OK;
if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end)
return TC_ACT_OK;
rate = bpf_map_lookup_elem(&rate_map, &ip->daddr);
if (!rate) {
bpf_map_update_elem(&rate_map, &ip->daddr, &init_rate, BPF_ANY);
bpf_map_update_elem(&tstamp_map, &ip->daddr, &now, BPF_ANY);
return TC_ACT_OK;
}
delay_ns = skb->len * NS_PER_SEC / (*rate);
tstamp = bpf_map_lookup_elem(&tstamp_map, &ip->daddr);
if (!tstamp) /* unlikely */
return TC_ACT_OK;
if (*tstamp < now) {
*tstamp = now + delay_ns;
skb->tstamp = now;
return TC_ACT_OK;
}
skb->tstamp = *tstamp;
__sync_fetch_and_add(tstamp, delay_ns);
return TC_ACT_OK;
}
char _license[] SEC("license") = "GPL";
# 1. 编译 example.c
clang -O2 -emit-llvm -c example.c -o - | llc -march=bpf -filetype=obj -o example.o
# 2. 安装 mq/fq Qdisc
tc qdisc add dev $DEV root handle 1: mq
NUM_TX_QUEUES=$(ls -d /sys/class/net/$DEV/queues/tx* | wc -l)
for (( i=1; i<=$NUM_TX_QUEUES; i++ ))
do
tc qdisc add dev $DEV parent 1:$(printf '%x' $i) \
handle $(printf '%x' $((i+1))): fq
done
# 3. 安装 clsact Qdisc, 加载 example.o
tc qdisc add dev $DEV clsact
tc filter add dev $DEV egress bpf direct-action obj example.o sec .text
# 4. (使用后) 卸载 Qdisc,清理 eBPF map
tc qdisc del dev $DEV clsact
rm -f /sys/fs/bpf/tc/globals/rate_map
tc qdisc del dev $DEV root handle 1: mq
我们把所有的方案放到一张表中对比不难看出 EDT 方案的总体优势:
EDT 方案的简洁性也决定了它只需要在内核中执行很少的代码,更复杂的带宽借用逻辑、burst 处理逻辑等都可以放到用户空间,进一步避免了在内核中使用额外的锁进行同步。
EDT 方案的不足之处是目前还无法和下层的 Qdisc 直接进行交互,从而实现更复杂的公平性逻辑。但是,字节跳动系统部 STE 团队正在开发的基于 eBPF 的 Qdisc 可以通过共享 eBPF map 达到这个目的,从而会让这个方案变得更加完善。
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/64iz3X40ZDtna0HEhtisig
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。