HTB Linux队列使用方法
源文地址:http://luxik.cdi.cz/~devik/qos/htb/manual/userg.htm
1.介绍
HTB是一种在linux里替换CBQ qdisc的方案,其更易懂、更合理并且更快。对于CBQ和HTB都可以帮你控制输出带宽的使用。两种都可以允许在一个物理链接上模拟几个慢速的链接,并在几个通道上发送不同类型的数据。在两种算法都必须指定如何区分物理链路到模拟链路的映射和如何决定哪条模拟链路用于发送指定的数据包。
注意:tc工具(不仅HTB)用简称表示速率单位。kbps意味着kilobytes,kbit意味着kilobits;qdisc是一种排队规则。
2.链路共享
问题:我们有两个客户,A和B,他俩都通过eth0链接到Internet。我们想分配60kbps到B,并且分配40kbps到A。接下来,再把A的带宽30kbps用于www,10kbps用于其它。任何未使用的带宽可以被其它须要的类使用。
HTB确保——所有类的服务都被给予所需要的最少带宽(the amount of service provided to each class is at least the minimum of the amount it requests and the amount assigned to it)。当一个类所请求的带宽少于分配给它的带宽时,可以把没使用的部分分配给有需要的服务。
注意:一些文献中用了“借”这样的词,本文也使用这个术语,这个术语看起来并不是很严谨,因为并不需要“归还”资源。
根据上面的需求,如上图所示,使用下面的命令建立HTB队列:
tc qdisc add dev eth0 root handle 1: htb default 12
这条命令使HTB队列绑定到eth0设备上,并且给予一个句柄(handle) 1:。这只是一个标识符,用于它下面的语句指向它。default 12意示着没有归属的流量都被分到类1:12中。
注意:一般来说(不仅仅是HTB,对于所有在TC下的QDISC和类),句柄被写成x:y,这里的x是一个整数,它代表qdisc;y也是一个整数,它代表属于x qdisc的类。对qdisc的句柄来说必须有一个0值对应y,对class的句柄来说必须有一个非0值对应y。上面的“1:”可以看做“1:0”。
tc class add dev eth0 parent 1: classid 1:1 htb rate 100kbps ceil 100kbps
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 30kbps ceil 100kbps
tc class add dev eth0 parent 1:1 classid 1:11 htb rate 10kbps ceil 100kbps
tc class add dev eth0 parent 1:1 classid 1:12 htb rate 60kbps ceil 100kbps
第一行建立了“根”类,在qdisc “1:”下面的“1:1”就是类根类。根类是一个使用了htb排队方法的类,使其成为父类。根类与其它使用htb排队方法的类一样,允许它下面的子类互借流量,但是一个根类不能借给另一个根类流量。我们可以将其它三个类直接建立在htb qdisc下,但是那样的话它们之间是不能互借带宽的。在本例中我们希望它们可以互借带宽,所以我们必须在根类下建立额外的类,并且将实际的数据放到这些类中。
注意:有人可能会问为什么要一直重复dev eth0?我们不是已经使用handle或parent了么?那是因为不同的网卡下可能会有相同的句柄号。
这时必须把不同的流量指派到不同的类中,这时已经和HTB qdisc无关了。如:(详情看tc-filter文档,翻意过程中还没有找到这个文档在哪里)
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 \ match ip src 1.2.3.4 match ip dport 80 0xffff flowid 1:10 tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 \ match ip src 1.2.3.4 flowid 1:11
注意:U32分类器有一个没有被记录的设计缺陷——导致在U32分类器在不同prio值时使用“tc filter show”
时有重复的记录。
上面的过滤器没有定义1:12这个类,那是因为在根类上建立了默认分类。所有未定义的分类都会到1:12这个类里。
现在可以将一些队列绑定到叶子类上,如果没有明确指定队列类型,默认队列是pfifo。
tc qdisc add dev eth0 parent 1:10 handle 20: pfifo limit 5 tc qdisc add dev eth0 parent 1:11 handle 30: pfifo limit 5 tc qdisc add dev eth0 parent 1:12 handle 40: sfq perturb 10
上面是我们需要的所有命令。让我们看一下如果我们在每个类上以90kbps的速度发送数据包,然后在一个类上停止发送数据包会发生了什么。
沿着图片的底部注释着类似“0:90k”的字样。红色的“1”指明当前的类的速度。冒号之前是类(0表示类1:10,1表示类1:11,2表示类1:12),冒号之后是所在时间点新的速率。例如,在时间0时0类的速率是90k,时间3时是0k,之后在时间6时是90k。
在该图中,最初所有的类都是90kb。因为这个速率高于任何一个指定的速率,所以每个类都被限定在其指定的速率中。在时间3时,当我们停止了类0发送数据包,分配给类0的速率按比例重新分给其它两个类,1部分给了类1,6部分给了类2(1part class 1 to 6 parts class 2,在http://lartc.org/ 下载的文档中得知,流量分割的比例是按当前分配的流量占总体流量的多少分配的,上面的总流量是100,子类又将100分成三分,分别是60:30:10,所以如果30这部分的流量分给60和10时,比例是按6:1分配)(类1的增长是很难看出来的,因为只有4kbps)。同理,在时间9时,停止类1的流量,它的带宽被重新分配给另外两个类(同样在类0里很难看出增长)。在时间15,很容易看出分配给类2的流量,其3个部分给类0,1个部分给类1。在时间18,类1和类2都停止,所以类0获得了全部的90kbps。
现在应该介绍一下什么是quantum了。当更多的类想要借用带宽时,在为获得同样资源的竞争类提供服务之前,每个类都会被给予一定数量的字节数。这个数字被称做“quantum”
。你应该已经见过了,在几个类竞争父类带宽时,它们会获得一定比例的quantum。 quantum须要尽可能的小但要比MTU大——为了精确的操作,这点非常重要。
通常你无须手动指定quantum,因为HTB会选择一个预置值。计算类的quantum是通过速率除以全局参数r2q得到的。它的默认值10,并且因为典型的MTU值是1500。默认值对于速度15kBps(120kbit)是不错的!当建造QDISC时小于最小速率,指定r2q为1,对于12kbit这是不错的配置。当你添加或更改qdisc时,如果你需要手动指定quantum值。当指定了quantum,命令行的r2q被忽略。
当A和B是同一个客户时,上面的方案看上去还是不错的,但是如果A和B是不同公司,当A的有多余流量时,不想“借给”B,要怎么办?这就需要HTB类的分层!
3.共享层次
如图所示,上面的问题可以通过类的分层解决。客户A现在有自己的类。回忆一下上面的“所有类的服务都被给予最少所需带宽(the amount of service provided to each class is at least the minimum of the amount it requests and the amount assigned to it)”。
如果HTB类是其它HTB类的父类,则不能应用这条规则,该规则只能对叶子节点有用。对于HTB类是其它HTB类的父类,这种情况我们称作“内部类”,它他们规则是:带宽至少是它的子类所需量的总和的最小值。(the amount of service is at least the minumum of the amount assigned to it and the sum of the amount requested by its children.)。在这个例子中分配了40kbps到客户A。这意味着如果A请求的速率少于分配给WWW,剩余部分将会分给A的其它服务,至少A的总速率是40kbps。
注意:包过滤器(filter)规则可以分配给内部节点。那么你必须绑定其它filter列表到内部节点。最后你会到达叶子节点或1:0类。父类的速率应该假定为子类速率之和。
命令如下:
tc class add dev eth0 parent 1: classid 1:1 htb rate 100kbps ceil 100kbps tc class add dev eth0 parent 1:1 classid 1:2 htb rate 40kbps ceil 100kbps tc class add dev eth0 parent 1:2 classid 1:10 htb rate 30kbps ceil 100kbps tc class add dev eth0 parent 1:2 classid 1:11 htb rate 10kbps ceil 100kbps tc class add dev eth0 parent 1:1 classid 1:12 htb rate 60kbps ceil 100kbps
如果A的总流量不超过分配给它的总数,那么剩余部分会分给B。
4.速率峰值(ceil)
ceil参数指定了一个类能使用的最大带宽。这个值限制了类可以借用多少带宽。默认ceil和rate的值一样(这就是为什么要单独指定)。
注意:ceil值应该一直是高于rate的。同样一个类的ceil值应该至少高于它的任意子类的ceil值。
5.突发流量(burst)
网络硬件只能一次发送一个包,并且依赖于硬件速率。链路共享软件可以使用这个能力使多条链路运行在不同的速率(更低的)上。因此,不能真正的实时度量rate和ceil值,但是可以在一个更长的时间里发送更多的包,然后得到一个比较准的平均值。实际上流量是一个类以最大速度一次发送少量包,其它类也是同样的操作。burst和cburst参数控制着一次以最大速度(硬件速度)能发送数据的总量。
当你给一个父类设置的burst小于它的某个子类,那么你是希望父类阻塞一段时间流量。HTB会记住这些阻塞突量,最多1分钟。(HTB will remember these negative bursts up to 1 minute.)
你要问为什么需要burst?因为它可以改善拥塞链路上的响应时间,这个方法既廉价又简单。
注意:一个类的burst和cburst应该是一直至少高于它的任意子类的。
限制:当你在一台低时序(low resolution timer)电脑上使用高速率时,你需要为所有的类设置最小的burst和cburst。在i386构架时序是10ms,在Alphas构架时序是1ms。最小burst可以通过公式“最大速率 * 时序”。所以10Mbit速率在i386构架上burst需要12kb(10*1000kb/8 * 0.01 =12.5kb)
如果你设置的burst太小,你会遭遇得到的速率比你设置的值要小。如果没有指定burst,tc会自动计算一个最小值。
6.优先带宽共享
优先流量有两个方面。首先,它影响了剩余带宽的分配。直到现在为止我们已知的剩余带宽分配工作是根据速率来做的。现在使用刚才的配置(不设置ceil和burst)并且设置除了SMTP(SMTP为0,0有更高的优先级)外所有类的优先级为1。
从共享带宽的观点来说,规则是:类的优先级高会先得到剩余带宽。但是这个规则会保证rate和ceil仍然有效。
这同样有另一方面的问题——数据包延迟。这在网卡上相当难度量,因为它太快了。不过现在我们用两个HTB队列和三个类来模拟这种情况:
# qdisc for delay simulation tc qdisc add dev eth0 root handle 100: htb tc class add dev eth0 parent 100: classid 100:1 htb rate 90kbps # real measured qdisc tc qdisc add dev eth0 parent 100:1 handle 1: htb AC="tc class add dev eth0 parent" $AC 1: classid 1:1 htb rate 100kbps $AC 1:2 classid 1:10 htb rate 50kbps ceil 100kbps prio 1 $AC 1:2 classid 1:11 htb rate 50kbps ceil 100kbps prio 1 tc qdisc add dev eth0 parent 1:10 handle 20: pfifo limit 2 tc qdisc add dev eth0 parent 1:11 handle 21: pfifo limit 2
注意:HTB做为另一个HTB的子类,不同于在同一个HTB下一个类是另一个类的子类。因为在HTB中发送数据是根据硬件设备尽可能快的发出。所以类中的延迟是受限于设备,而不是设备的父类。
在HTB队列下的HTB子队列,导至外导HTB模拟新硬件伴随着大量的延迟。
这个例子的结论就是说:当你对一个类给予更高的优先级,那么其它的类将会有更大的延迟。
那我们应该怎么做呢?应该对视频、音频、TELNET和SSH等服务给予更高的优先级。。
7.理解队列状态
tc工具允许你在linux下收集队列的状态。但不幸的是作者并没有解释统计结果,所以通常无法使用它们。这里我尝试帮你理解 HTB的统计结果。
首先,整个HTB的统计。下面是一个小小的片段,来自第3节那个模拟。
# tc -s -d qdisc show dev eth0 qdisc pfifo 22: limit 5p Sent 0 bytes 0 pkts (dropped 0, overlimits 0) qdisc pfifo 21: limit 5p Sent 2891500 bytes 5783 pkts (dropped 820, overlimits 0) qdisc pfifo 20: limit 5p Sent 1760000 bytes 3520 pkts (dropped 3320, overlimits 0) qdisc htb 1: r2q 10 default 1 direct_packets_stat 0 Sent 4651500 bytes 9303 pkts (dropped 4140, overlimits 34251)
overlimits:告诉你延迟了多少个数据包。
direct_paackets_stat:告诉你有多少个数据包已经被发到这个队列中。
其它的自己理解去吧!PS:原文就是这么写的!(•ิ_•ิ
tc -s -d class show dev eth0 class htb 1:1 root prio 0 rate 800Kbit ceil 800Kbit burst 2Kb/8 mpu 0b cburst 2Kb/8 mpu 0b quantum 10240 level 3 Sent 5914000 bytes 11828 pkts (dropped 0, overlimits 0) rate 70196bps 141pps lended: 6872 borrowed: 0 giants: 0 class htb 1:2 parent 1:1 prio 0 rate 320Kbit ceil 4000Kbit burst 2Kb/8 mpu 0b cburst 2Kb/8 mpu 0b quantum 4096 level 2 Sent 5914000 bytes 11828 pkts (dropped 0, overlimits 0) rate 70196bps 141pps lended: 1017 borrowed: 6872 giants: 0 class htb 1:10 parent 1:2 leaf 20: prio 1 rate 224Kbit ceil 800Kbit burst 2Kb/8 mpu 0b cburst 2Kb/8 mpu 0b quantum 2867 level 0 Sent 2269000 bytes 4538 pkts (dropped 4400, overlimits 36358) rate 14635bps 29pps lended: 2939 borrowed: 1599 giants: 0
overlimits显示了由于rate/ceil的原因不能发送数据包。
rate,pps告诉你通过类的实时速率(10秒内的平均值)
lended该类捐赠出去的流量。
borrowed是该类从parent借来的流量。
giants是大于mtu包的数量,mtu的大小会影响HTB的精度,但在mtu不准的情况下,HTB同样可以工作。(mtu默认是1600字节)
8.发现
- 经测式一个qdisc可以有ffff个类。
- filter只能放在qdisc中,它们并不关心发生了什么。(原自tc man page:It is important to notice that filters reside within qdiscs – they are not masters of what happens.)
-
tc qdisc add dev INNER root handle f: htb default ffff tc class add dev INNER parent f: classid f:1 htb rate 1mbps ceil 1mbps #172.16.2.x一边分类 tc class add dev INNER parent f:1 classid f:10 htb rate 500kbps ceil 500kbps tc qdisc add dev INNER parent f:10 handle 1: htb #172.16.2.x side, 80port side 下面两句要调换位置才可以,也就是tc class在上面tc qdisc在下面;由此可以qdisc要建立在class上,不能单独为某一qdisc建立子qdisc tc qdisc add dev INNER parent 1:10 handle 10: htb tc class add dev INNER parent 1: classid 1:10 htb rate 200kbps ceil 200kbps
- here