流量控制是路由器上报文的接收和发送机制及排队系统的统称。这包括在一个输入接口上决定以何种速率接收何种报文,在一个输出接口上以何种速率、何种顺序输出何种报文。
整流。整流器通过延迟数据包来使流量保持在一定速率。整流就是让包在输出队列上被发送之前进行延时,然后一定的速率发送,使网络流量保持在一定的速率之下,这是大部分用户进行流量控制的目的。
调度。调度就是对队列中的输入输出报文进行排列。最常的调度方法就是FIFO(先进先出),更广泛的来说,在输出队列上的任何流量控制都可以被称作调度,因为报文被排列以被输出。
分类。分类就是将流量进行划分以便区别处理,例如拆分后放到不同的输出队列中。在报文的接收、路由、发送过程中,网络设备可以用多种方式来分类报文。分类包括对报文进行标记,标记可以在边际网络中由一个单一的控制单元来完成,也可以在每一跳中都进行标记。
监管。监管作为流量控制的一部分,就是用于限制流量。监管常用于网络边际设备,使某个节点不能使用多于分配给它的带宽。监管器以特定的速率接收数据包,当流量超过这一速率时就对接收的数据包执行相应的动作。最严格的动作就是丢弃数据包,尽管该数据包可以被重新分类。
标记。标记流量控制在数据包中插入了DSCP部分,在一个可管理网络中,其可被其它路由器利用和识别(通常用于DiffServ,差分服务)。
分组交换网络和电路交换网络的一个重要不同之处是:分组交换网络是无状态的,而电路交换网络(比如电话网)必须保持其状态。分组交换网络和IP网络一样被设计成无状态的,实际上,无状态是IP的一个根本优势。
无状态的缺陷是不能对不同类型数据流进行区分。但通过流量控制,管理员就能够基于报文的属性对其进行排队和区别。它甚至能够被用于模拟电路交换网络,将无状态网络模拟成有状态网络。
有很多实际的理由去考虑使用流量控制,并且流量控制也有很多有意义的应用场景。下面是一些利用流量控制可以解决或改善的问题的例子,下面的列表不是流量控制可以解决的问题的完整列表,此处仅仅介绍了一些能通过流量控制来解决的几类问题
通过HTB分类(HTB class)和分类(classifying)并配合filter,来限制指定用户、服务或客户端的带宽。
通过提升ACK报文的优先级,以及使用wondershaper来最大化非对称线路上的TCP吞吐量。
通过HTB分类(HTB class)中的(优先级)PRIO机制来提高延时敏感型应用的性能。
通过监管器(policer)加上带丢弃动作的过滤器(filter)来使某种类型的流量被丢弃。
首先需要注意的是:Linux tc只对egress方向实现了良好的控制,而对ingress方向控制有限,简而言之,控发不控收。
令牌桶。这是个非常重要的因素。为了控制出队的速率,一种方式就是直接统计队列中出队的报文或字节数,但是为了保证精确性就需要复杂的计算。在流量控制中广泛应用的另一种方式就是令牌桶,令牌桶以一定的速率产生令牌,报文或字节出队时从令牌桶中取令牌,只有取到令牌后才能出队。
我们可以打一个比方,一群人正排队等待乘坐游乐场的游览车。让我们想象现在有一条固定的道路,游览车以固定的速度抵达,每个人都必须等待游览车到达后才能乘坐。游览车和游客就可以类比为令牌和报文,这种机制就是速率限制或流量整形,在一个固定的时间段内只有一部分人能乘坐游览车。
继续上面的比方,设想有大量的游览车正停在车站等待游客乘坐,但现在没有一个游客。如果现在有一大群游客同时过来了,那么他们都可以马上乘上游览车。在这里,我们就可以将车站类比为桶,一个桶中包含一定数量的令牌,桶中的令牌可以一次性被使用完而不管数据包到达的时间。
让我们来完成这个比方,游览车以固定的速率抵达车站,如果没人乘坐就会停满车站,即令牌以一定的速率进入桶中,如果令牌一直没被使用那么桶就可以被装满,而如果令牌不断的被使用那么桶就不会满。令牌桶是处理会产生流量突发应用(比如HTTP)的关键思想。
使用令牌桶过滤器的排队规则(TBF qdisc,Token Bucket Filter)是流量整形的一个经典例子(在TBF小节中有一个图表,通过该图表可以形象化的帮助读者理解令牌桶)。TBF以给定的速度产生令牌,当桶中有令牌时才发送数据,令牌是整流的基本思想。
分类只存在于可分类排队规则(classful qdisc)(例如,HTB和CBQ)中。分类可以很复杂,它可以包含多个子分类,也可以只包含一个子qdisc。在超级复杂的流量控制应用场景中,一个类中再包含一个可分类qdisc也是可以的。
任何一个分类都可以和任意多个filter相关联,这样就可以选择一个子分类或运用一个filter来重新排列或丢弃进入分类中的数据包。
叶子分类是qdisc中的最后一个分类,它包含一个qdisc(默认是pfifo)并且不包含任意子分类。任何包含子分类的分类都是内部分类而不是子分类。
Linux的过滤器可以允许用户利用一个或多个过滤器将数据包分类至输出队列上。它包含了一个分类器实现,常见的分类器如u32,u32分类器可以允许用户基于数据包的属性来选择数据包。
无论是qdisc,还是class, 都需要有一个唯一标识符。就是所说的句柄。它们都采用major:minor格式来命名,注意他们都是以十六进制解析。对于他们的使用,在栗子中会做具体说明。
HTB是一种classful qdisc,是一种分层分类流控方法,是Linux常用的一种流控配置。接下来就来看一下使用配置:
default 这是HTB排队规则的一个可选参数,默认值为0, 当值为0时意味着会绕过所有和rootqdisc相关联的分类,然后以最大的速度出队任何未分类的流量。 rate 这个参数用来设置流量发送的最小期望速率。这个速率可以被当作承诺信息速率(CIR), 或者给某个叶子分类的保证带宽。 ceil 这个参数用来设置流量发送的最大期望速率。租借机制将会决定这个参数的实际用处。 这个速率可以被称作“突发速率”。 burst 这个参数是rate桶的大小(参见令牌桶这一节)。HTB将会在更多令牌到达之前将burst个字节的数据包出队。 cburst 这个参数是ceil桶的大小(参见令牌桶这一节)。HTB将会更多令牌(ctoken)到达之前将cburst个字节的数据包出队。 quantum 这个是HTB控制租借机制的关键参数。正常情况下,HTB自己会计算合适的quantum值,而不是由用户来设定。对这个值的轻微调整都会对租借和整形造成巨大的影响,因为HTB不仅会根据这个值向各个子分类分发流量(速率应高于rate,小于ceil),还会根据此值输出各个子分类中的数据。 r2q 通常,quantum 的值由HTB自己计算,用户可以通过此参数设置一个值来帮助HTB为某个分类计算一个最优的quantum值。 mtu prio
入向的流控常见做法是通过把接口的流量重定向到ifb设备,然后在ifb的egress上做流控,间接达到控制入向的目的。简单的使用示例如下:

