1.1 著名的 CBQ 队列规定web
除了能够分类以外,CBQ 也是一个整形器。若是想把一个 10Mbps 的链接整造成 1Mbps 的速率,就应该让链路 90%的时间处于闲置状态,必要的话就强制,以保证 90% 的闲置时间。但闲置时间的测量很是困难,因此 CBQ 就采用了它一个近似值——来自硬件层的两个传输请求之间的毫秒数——来代替它。这个参数能够近似地表征链路的繁忙程度。 算法
1.1.1 CBQ 整形的细节服务器
CBQ 的工做机制是确认链路的闲置时间足够长,以达到下降链路实际带宽的目的。为此,它要计算两个数据包的平均发送间隔。有效闲置时间的测量使用EWMA(exponential weighted moving average, 指数加权移动均值)算法,也就是说最近处理的数据包的权值比之前的数据包按指数增长。UNIX 的平均负载也是这样算出来的。计算出来的平均时间值减去 EWMA 测量值,得出的结果叫作“avgidle”。最佳的链路负载状况下,这个值应当是 0,数据包严格按照计算出来的时间间隔到来。网络
在一个过载的链路上,avgidle 值应当是负的。若是这个负值太严重,CBQ 就会暂时禁止发包,称为“overlimit”(越限)。相反地,一个闲置的链路应该有很大的 avgidle 值,这样闲置几个小时后,会形成链路容许很是大的带宽经过。为了不这种局面,一般用 maxidle 来限制 avgidle 的值不能太大。 spa
理论上讲,若是发生越限,CBQ 就会禁止发包一段时间(长度就是事先计算出来的传输数据包之间的时间间隔),而后经过一个数据包后再次禁止发包。code
下面是配置整形时须要指定的一些参数:blog
avpkt 队列
平均包大小,以字节计。计算 maxidle 时须要,maxidle 从 maxburst 得出。ip
bandwidth 路由
网卡的物理带宽,用来计算闲置时间。
cell
包间隔发送单元的大小。
maxburst
这个参数的值决定了计算 maxidle 所使用的数据包的个数。在 avgidle 跌落到 0 以前,这么多的数据包能够突发传输出去。这个值越高,越可以容纳突发传输。没法直接设置 maxidle 的值,必须经过这个参数来控制。
minburst
如前所述,发生越限时 CBQ 会禁止发包。实现这个的理想方案是根据事先计算出的闲置时间进行延迟以后,发一个数据包。然而,UNIX 的内核通常来讲都有一个固定的调度周期(通常不大于10ms),因此最好禁止发包的时间稍长一些,而后突发性地传输 minburst 个数据包,而不是一个一个地传输。等待的时间叫作 offtime。从大的时间尺度上说,minburst 值越大,整形越精确。可是,从毫秒级的时间尺度上说,就会有越多的突发传输。
minidle
若是 avgidle 值降到 0,也就是发生了越限,就须要等待,直到 avgidle 的值足够大才发送数据包。为避免因关闭链路过久而引发的意外突发传输, 在 avgidle 的值过低的时候会被强制设置为 minidle 的值。参数 minidle 的值是以负微秒记的。10 表明 avgidle 被限制在-10us 上。
mpu
最小包传输包大小——由于即便是 0 长度的数据包,在以太网上也要生成封装成 64 字节的帧,而须要必定时间去传输。为了精确计算闲置时间,CBQ 须要知道这个值。
rate
实际分配的带宽。
1.1.2 CBQ 在分类方面的行为
除了使用上述 idletime 近似值进行整形以外,CBQ 还能够象 PRIO 队列那样,把各类类赋予不一样的优先级,优先权数值小的类会比优先权值大的类被优先处理。每当网卡请求把数据包发送到网络上时,都会开始一个 WRR(weighted round robin,加权轮转)过程,从优先权值小的类开始。那些队列中有数据的类就会被分组并被请求出队。在一个类收到容许若干字节数据出队的请求以后,再尝试下一个相同优先权值的类。
下面是控制 WRR 过程的一些参数:
allot
最大传输单元加 MAC 头的大小。当从外部请求一个 CBQ 发包的时候,它就会按照“priority”参数指定的顺序轮流尝试其内部的每个类的队列规定。当轮到一个类发数据时,它只能发送必定量的数据。“allot”参数就是这个量的基值。
prio
CBQ 能够象 PRIO 设备那样工做。其中“prio”值较低的类只要有数据就必须先服务,其余类要延后处理。
weight
“weight”参数控制 WRR 过程。每一个类都轮流取得发包的机会。若是其中一个类要求的带宽显著地高于其余的类,就应该让它每次比其余的类发送更多的数据。CBQ 会把一个类下面全部的 weight 值加起来后归一化,因此数值能够任意定,只要保持比例合适就能够。一般把“速率/10”做为参数的值来使用。归一化值后的值乘以“allot”参数后,决定了每次传输多少数据。
须要注意的是,在一个 CBQ 内部全部的类都必须使用一致的主号码。
1.1.3 决定链路的共享和借用的 CBQ 参数
除了纯粹地对某种数据流进行限速以外,CBQ 还能够指定哪些类能够向其它哪些类借用或者出借一部分带宽。
isolated/sharing
凡是使用“isolated”选项配置的类,就不会向其兄弟类出借带宽。选项“sharing”是“isolated”的反义选项。
bounded/borrow
一个类也能够用“bounded”选项配置,意味着它不会向其兄弟类借用带宽。选项“borrow”是“bounded”的反义选项。
一个典型的状况就是一个链路上有多个客户都设置成了“isolated”和“bounded”,那就是说他们都被限制在其要求的速率之下,且互相之间不会借用带宽。在这样的一个类的内部的子类之间是能够互相借用带宽的。
1.1.4 配置范例
这个配置把 WEB 服务器的流量控制为 5Mbps、SMTP 流量控制在 3Mbps。并且两者一共不得超过 6Mbps,互相之间容许借用带宽。网卡带宽为 100Mbps。
# sudo tc qdisc add dev enp0s5 root handle 1: cbq bandwidth 100Mbit avpkt 1000 cell 8 # sudo tc class add dev enp0s5 parent 1:0 classid 1:1 cbq bandwidth 100Mbit rate 6Mbit weight 0.6Mbit prio 8 allot 1514 cell 8 maxburst 20 avpkt 1000 bounded
这里设置了根为 1:0,而且绑定了类 1:1。也就是说整个带宽不能超过 6Mbps。
# sudo tc class add dev enp0s5 parent 1:1 classid 1:3 cbq bandwidth 100Mbit rate 5Mbit weight 0.5Mbit prio 5 allot 1514 cell 8 maxburst 20 avpkt 1000 # sudo tc class add dev enp0s5 parent 1:1 classid 1:4 cbq bandwidth 100Mbit rate 3Mbit weight 0.3Mbit prio 5 allot 1514 cell 8 maxburst 20 avpkt 1000
这里创建了 2 个类。两个类都没有配置成“bounded”,但它们都链接到了类 1:1 上,而 1:1 设置了“bounded”。 因此两个类的总带宽不会超过 6Mbps。须要注意的是,同一个 CBQ 下面的子类的主号码都必须与 CBQ 本身的主号码相一致。
# sudo tc qdisc add dev enp0s5 parent 1:3 handle 30: sfq # sudo tc qdisc add dev enp0s5 parent 1:4 handle 40: sfq
缺省状况下,两个类都有一个 FIFO 队列规定。这里把它换成 SFQ 队列,以保证每一个数据流都公平对待。
# sudo tc filter add dev enp0s5 parent 1:0 protocol ip prio 1 u32 match ip sport 80 0xffff flowid 1:3 # sudo tc filter add dev enp0s5 parent 1:0 protocol ip prio 1 u32 match ip sport 25 0xffff flowid 1:4
这里规定了根上的过滤器,保证数据流被送到正确的队列规定中去。注意:上面命令先使用了“tc class add”在一个队列规定中建立了类,而后使用“tc qdisc add”在类中建立队列规定。那些没有被那两条规则分类的数据流被 1:0 直接处理,没有限制。若是 SMTP+web 的总带宽需求大于 6Mbps,那么这 6Mbps 带宽将按照两个类的 weight 参数的比例状况进行分割:WEB 服务器获得 5/8 的带宽,SMTP 获得 3/8 的带宽。从这个例子来讲,能够这么认为:WEB 数据流老是会获得 5/8*6Mbps=3.75Mbps 的带宽。
1.2 一个完整的 CBQ 队列规定实例
流量需求:
流量控制器上的以太网卡(enp0s5)的IP地址为 10.211.55.17, 在其上创建一个 CBQ 队列。假设包的平均大小为 1000 字节,包间隔发送单元的大小为 8 字节,可接收冲突的发送最长包的数目为 20 字节。
加入有三种类型的流量须要控制:
(1)是发往主机 1 的,其 IP 地址为1 0.211.55.20。其流量带宽控制在 8Mbit,优先级为 2;
(2)是发往主机 2 的,其 IP 地址为 10.211.55.21。其流量带宽控制在 1Mbit,优先级为 1;
(3)是发往子网 1 的,其子网号为 10.211.55.0。 子网掩码为 255.255.255.0。流量带宽控制在 1Mbit,优先级为 6。
建立 CBQ 队列主要分为四个步骤:创建队列、创建分类、建立过滤器、创建路由。在创建队列以前,须要对网卡的队列规则进行清除,具体操做以下:
清除网卡全部队列规则
# sudo tc qdisc del dev enp0s5 root 2> /dev/null > /dev/null
创建队列
# sudo tc qdisc add dev enp0s5 root handle 1: cbq bandwidth 10Mbit avpkt 1000 cell 8 mpu 64
将一个 cbq 队列绑定到物理网络设备 enp0s5 上,其编号为 1:0;物理网络设备 enp0s5 的实际带宽为 10Mbit,包的平均大小为 1000 字节;包间隔发送单元的大小为 8 字节,最小传输包大小为 64 字节。
创建分类
建立根分类 1:1,分配带宽为 10Mbit,优先级别为 8:
# sudo tc class add dev enp0s5 parent 1:0 classid 1:1 cbq bandwidth 10Mbit rate 10Mbit maxburst 20 allot 1514 prio 8 avpkt 1000 cell 8 weight 1Mbit
该队列的最大可用带宽为 10Mbit,实际分配的带宽为 10Mbit,可接收冲突的发送最长包数目为 20 字节;最大传输单元加 MAC 头的大小为 1514 字节,优先级别为 8,包的平均大小为 1000 字节,包间隔发送单元的大小为 8 字节,至关于实际带宽的加权速率为 1Mbit。
建立分类 1:2,其父类为 1:1,分配带宽为 8Mbit,优先级别为 2:
# sudo tc class add dev enp0s5 parent 1:1 classid 1:2 cbq bandwidth 10Mbit rate 8Mbit maxburst 20 allot 1514 prio 2 avpkt 1000 cell 8 weight 800Kbit split 1:0 bounded
该队列的最大可用带宽为 10Mbit,实际分配的带宽为 8Mbit,可接收冲突的发送最长包数目为 20 字节;最大传输单元加 MAC 头的大小为 1514 字节,优先级别为 2,包的平均大小为 1000 字节,包间隔发送单元的大小为 8 字节,至关于实际带宽的加权速率为 800Kbit,分类的分离点为 1:0,且不可借用未使用带宽。
建立分类 1:3,其父分类为 1:1,分配带宽为 1Mbit,优先级别为 1:
# sudo tc class add dev enp0s5 parent 1:1 classid 1:3 cbq bandwidth 10Mbit rate 1Mbit maxburst 20 allot 1514 prio 2 avpkt 1000 cell 8 weight 100Kbit split 1:0
该队列的最大可用带宽是 10Mbit,实际分配的带宽为 1Mbit,可接收冲突的发送最长包数目为 20 字节;最大传输单元加 MAC 头的大小为 1514 字节,优先级别为 1,包的平均大小为 1000 字节,包间隔发送单元的大小为 8 字节,至关于实际带宽的加权速率为 100Kbit,分类的分离点为 1:0。
建立分类 1:4,其父分类为 1:1,分配带宽为 1Mbit,优先级别为 6:
# sudo tc class add dev enp0s5 parent 1:1 classid 1:3 cbq bandwidth 10Mbit rate 1Mbit maxburst 20 allot 1514 prio 2 avpkt 1000 cell 8 weight 100Kbit split 1:0
该队列的最大可用带宽为 10Mbit,实际分配的带宽为 1Mbit,可接收冲突的发送最长包数目为 20 字节;最大传输单元加 MAC 头的大小为 1514 字节,优先级别为 6,包的平均大小为 1000 字节,包间隔发送单元的大小为 8 字节,至关于实际带宽的加权速率为 100Kbit,分类的分离点为 1:0。
建立过滤器
(1)应用路由分类器到 cbq 队列的根,父分类编号为 1:0;过滤协议为 ip,优先级别为 100,过滤器为基于路由表:
# sudo tc filter add dev enp0s5 parent 1:0 protocol ip prio 100 route
(2)创建路由映射分类 1:2 , 1:3 , 1:4
# sudo tc filter add dev enp0s5 parent 1:0 protocol ip prio 100 route to 2 flowid 1:2 # sudo tc filter add dev enp0s5 parent 1:0 protocol ip prio 100 route to 3 flowid 1:3 # sudo tc filter add dev enp0s5 parent 1:0 protocol ip prio 100 route to 4 flowid 1:4
创建路由
(1)发往主机 10.211.55.20 的数据包经过分类 2 转发(分类 2 的速率 8Mbit)
# sudo ip route add 10.211.55.20 dev enp0s5 via 10.211.55.17 realm 2
(2)发往主机 10.211.55.21 的数据包经过分类 3 转发(分类 3 的速率 1Mbit)
# sudo ip route add 10.211.55.21 dev enp0s5 via 10.211.55.17 realm 3
(3)发往子网 10.211.55.0/24 的数据包经过分类 4 转发(分类 4 的速率 1Mbit)
# sudo ip route add 10.211.55.0/24 dev enp0s5 via 10.211.55.17 realm 4