charlieroro

Overview of Concepts

本章将介绍流量控制,研究出现流量控制的原因及其优缺点,并介绍流量控制的关键概念。

了解Linux的流量控制的目的:一是为了更好地理解底层对报文的处理逻辑,二是在流量控制中使用了很多很好的流量处理方法,可以学习一下这些方法和思想。

2.1. 什么是流量控制

流量控制是指在路由器上接收和传输数据包的队列系统和机制的统称。包括决定(如果和)输入接口上以哪种速率接收哪个包,以及决定在输出接口上以哪种顺序传输那些包。

在最简单的模型中,流量控制包含简单的队列,该队列收集了所有的报文,并在硬件(或底层设备)可以接收报文时尽快让这些报文出队列。这种队列即FIFO。它就像一个进入高速公路的收费站,每辆车必须停下并缴纳过路费,而此时其他汽车也必须等待。

注:Linux下默认的qdisc为pfifo_fast,它比FIFO更加复杂。

在各种软件中都有队列的影子。队列是一种组织挂起的任务或数据(参见Section 2.5, “Queues”)的方式。由于网络链接通常会以序列化的方式携带数据,因此需要一个队列来管理出站的数据包。

在台式机和一台高效的网络服务器共享(到因特网的)同一上行链路的情况下,可能会对带宽资源产生竞争。例如,服务器填充路由器上的输出队列的速度可能比通过链路传输数据的速度还要快,此时路由器开始丢包(缓冲已经满了),这样台式机(可能是交互应用的用户)可能会面临丢包和高延迟。通过划分内部队列来服务这两种不同的应用,就可以在两个应用间更好地共享网络。

流量控制是一组允许管理员对这些队列进行精细控制的工具和网络设备的排队机制的统称。虽然这些工具重新分配流量和包的能力是强大的,同时也可能会很复杂,但最好留有足够的带宽。

术语Quality of Service (QoS)通常作为ip层的流量控制的代名词。

2.2. 为什么使用流量控制

流量控制工具允许实现者对传输到网络中的数据包或网络流应用首选项、组织或业务策略,进而管理网络资源,如吞吐量或延迟。

从根本上说,由于网络中的分组交换,流量控制变得非常必要。

为了简要说明数据包交换的新颖性和巧妙性,考虑一下在整个20世纪构建起来的电路交换电话网络。为了发起一个呼叫,网络设备需要了解建立呼叫的规则。当一个呼叫者尝试发起连接时,网络会使用这些规则来为整个呼叫或连接期间保留一个电路。当有一个使用该资源的通话占线时,其他呼叫或呼叫者都不能使用该资源,意味着由于资源不可用,许多设备可能会因为单个部件而阻塞通话的建立。

回到分组交换网络,这是20世纪中期的一项发明,后来被广泛使用,并且在21世纪几乎无处不在。分组交换网络与基于电路的网络有一个非常重要的区别,即网络设备处理的数据单位不再是电路,而是一小块的数据,称为数据包。分组交换网络只需要处理一小部分的工作:读取目的地标识,并传输该数据包。

分组交换网络有时会被认为为无状态的,这是因为它不需要跟踪网络上所有的活动的流。因此,缺乏对特定数据包或网络流重要性的区分是这种分组交换网络的一个弱点。网络可能会因数据包的相互竞争而超载。

简单来说,流量控制工具允许根据数据包的属性,通过不同的方式将数据包放入网络。不同的工具用于解决不同的问题,可以组合多个工具来实现复杂的规则,满足偏好或业务目标。

下面是一些常见问题的例子,可以用于解决或改善这些工具。

下面并没有给出流量控制的所有解决方案,仅给出了可以使用流量控制工具解决的常见问题,用于最大化利用网络。

常用的流量控制方案

  • 将总带宽限制为某个值:TBF,和带子类的HTB
  • 限制特定用户、服务或客户的带宽:HTB 类和带filter分类
  • 最大化非对称链路上的TCP吞吐量;提升传输的ACK包的优先级:wondershaper
  • 为特定应用或用户预留带宽:带子类的HTB和分类。
  • 偏好延迟敏感的流量:HTB类中的PRIO
  • 管理超额的带宽:HTB租借
  • 允许公平分配未预留的带宽:HTB租借
  • 确保丢弃特定类型的流量:给filter添加policer,使用drop动作。

需要注意的是,有时候最好订购更多的带宽,流量控制并不能解决所有的问题。

2.3 优点

正确引入流量控制可以更加可靠地对网络资源地利用进行预测,并可以减少对这些资源的不稳定竞争,这样就可以实现流量控制配置的目标。即使在为更高优先级的交互式流量提供服务同时,也可以为批量下载分配合理的带宽;即使低优先级的数据传输(如邮件),也可以分配到一定的带宽,而不会对其他类型的流量造成巨大的影响。

如果流量控制中的配置代表了用户的策略,那么该用户(或应用)就应该知道后续会网络的影响。

2.4 缺点

使用流量控制的最大缺点之一是其复杂性。实践中,有一些方法可以用来熟悉流量控制工具,简化关于流量控制及其机制的学习曲线,但如何确定一个流量控制的错误配置仍然是一个相当大的挑战。

当正确配置流量控制时,可以公平地分配网络资源。但不合理的使用可能导致对资源的分裂性争夺。

路由器上支持流量控制方案所需的计算资源必须能够处理维护流量控制结构的成本的增加。幸运的是,它的成本增量很小,但随着配置和复杂度的增加,其成本可能显著增加。

对于个人来说,不需要考虑引入量流量控制带来的培训成本,但对于一个公司来说,相比引入流量控制,采购更多的带宽可能是一个更简单的解决方案(员工的培训成本可能要远高于采购带宽的成本)。

2.5 队列

所有的流量控制都会用到队列,它是调度算法不可或缺的一部分。一个队列是一个位置(或缓冲),包含有限数目的元素,等待相应的动作或服务。在网络中,一个队列是报文(单位)等待被硬件(服务)传输的地方。在最简单的模型中,报文根据先进先出的方式进行传输。在计算机网络(和更普遍的计算机科学)的学科中,这种队列被称为FIFO。

如果没有其他机制,队列是不会为流量控制提供任何优化。此时一个队列只有两个需要关注的动作:任何到达队列的报文(或单位)都会在队列中排队;为了从队列中移除一个元素,则需要对其执行出队列操作。

当结合其他机制时,队列可以提供更加丰富的功能,如延迟包容,重新排列,丢弃,以及优先处理多个队列中的数据包。一个队列可能会使用子队列,用来处理更加复杂的调度行为。

从上层的软件的角度来看,当一个报文入队列后,该队列对待传输报文的处理行为和处理顺序对上层软件来说是无关紧要的。因此,对上层来说,整个流量控制队列系统可能只是一个单一的队列,只有对使用了流量控制的那一层来说,流量控制结构才是可见的。

下图展示了一个高度简化的Linux网络栈的传输路径上的队列图:

2.6 流

一条流指两个主机之间的特定连接或会话。两个主机之间的任何(唯一的)报文集都可以看作是一条流。TCP使用源IP和端口,目的IP和端口来表示一条流,UDP流也是类似的。

流量控制机制经常会将流量划分为不同的类,并以聚合流的方式(如DiffServ)对这些流进行聚合和传输。类似的,可能会基于单个流来平均分配带宽。

当尝试在一组竞争流中平均分配带宽时,对流的处理就变得很重要,尤其是在某些应用故意构建大量流时。

2.7 令牌和桶

整流机制的两个关键概念是令牌和桶。

为了控制出队列的速率,实现中可以在每个元素出队列时计算出列的报文数或字节数(虽然这样会使用复杂的定时器和工具进行精确限制)。除了计算当前使用量和时间,还有一种方法广泛用到流量控制中,即以一定速率生成令牌,只有存在可用的令牌是才允许报文或字节出队列。

考虑一个游乐园游乐设施,人们排队等候体验游乐设施。让我们将该设施想象成一个轨道,在这个轨道上,推车会通过一个固定的轨道。这些推车以固定的速率排在队伍的前头。为了享受乘坐的乐趣,每个人必须等待一辆可用的推车,每个推车类似于一个令牌,人类似于一个报文。这种机制就是限流或整流机制。 在特定时期内,只有一定数量的人可以体验骑行。

为了扩展这个类比,想象游乐园里有一条空的线路,而轨道上有大量的推车准备载客,如果大量的人一起进入队列,很多(也许全部)人可以体验乘坐,因为此时有一定数量的可用的推车。推车的数量是一个类似于桶的概念。一个桶包含很多令牌,可以使用桶中现有的令牌,而无需等待。

为了完成这个类比,游乐园里的推车(我们的令牌)的到达的速率是固定的,且可用的推车不超过桶大小。因此,令牌会以固定速率填充到桶,如果没有使用令牌,则桶可以被填满。如果使用了令牌,则桶不会被填满。桶是支持突发流量(如HTTP)的一个关键概念。

TBF qdisc是一个典型的整流器(关于TBF包括一个图表,它可以帮助以可视化的方式展示令牌和桶的概念)。TBF会生成速率令牌,只有当令牌可用时才能传输报文。令牌是一个通用的整流概念。

当队列不需要令牌时,这些令牌会被收集起来,并在后续需要时使用。无限制地收集令牌会抵消整流带来的好处,因此需要限制收集的令牌的数量。队列中的令牌可用于需要出队列的报文或字节。 这些无形的令牌存储在无形的桶中,可以存储的令牌数量取决于桶的大小。

这也意味着,在任意时刻都可能存在一个满token的桶,可预测的流量可以使用小的桶,突发流量可以使用大的桶(除非目标是为了降低流的突发)。

总之,令牌使用一定速率来生成,最大可用的令牌数由桶的大小来决定。通过这种方式可以处理突发流量,使得传输的流量变得平滑。

令牌和桶是息息相关的,用于 TBF (classless qdiscs的一种) 和HTB (classful qdiscs的一种)。在tcng语言中,二色和三色标识法就是令牌桶的应用。

2.8 报文和帧

网络上传输的数据的术语取决于其所在的网络层。尽管在此处给出了报文和帧的技术上的区别,但本文并不作区分。

帧通常用于描述二层网络上转发的数据单位。以太接口,PPP接口和T1接口都将二层数据单位称为帧。帧是流量控制的实际单位。

从另一方面将,报文时上层协议的概念,表示三层数据单位。本文档的使用了报文。

2.9 NIC,网络接口控制器

一个网络接口控制器是一个计算机硬件组件,与前面的软件组件不同,它将一个计算机连接到一个计算机网络。网络控制器使用特定的数据链路层和物理层标准实现了通信所需的电子电路,如 Ethernet, Fibre Channel, Wi-Fi or Token Ring。流量控制必须处理NIC接口的物理限制和特征。

2.9.1 网络栈的巨包

大多数NICs都有一个固定的传输单位(MTU),即物理媒介可以传输的最大帧。对于以太网来说,默认为1500字节,但对于支持巨型帧的以太网来说,其MTU可以达到9000字节。在IP网络栈中,MTU作为发送或传输报文时的大小限制。例如,如果一个应用向TCP socket写入了2000字节的数据,那么IP栈需要创建两个IP报文来保证报文小于或等于1500字节的MTU。当需要传输大于MTU的数据时,会导致创建大量的小报文,并传输到 驱动队列

为了避免在传输路径上对大报文处理产生的开销,Linux内核实现了几类优化:TCP分段卸载(TSO),UDP分片卸载(USO)以及通用的分段卸载 (GSO)。所有这些优化都允许IP栈创建的报文大于传出的NIC上的MTU。对于IPv4,创建并放到驱动队列中的报文可以达到65536字节。在TSO和UFO场景下,NIC硬件负责将单个大报文切分为可以在物理接口上传输的小报文。对于没有硬件支持的NIC,GSO会在报文进入驱动队列之前对其进行相同的操作。

回顾一下,驱动队列包含一个固定数目的描述符,每个描述符指向大小不同的报文,由于TSO, UFO 和GSO 允许更大长度的报文,因此这些优化会大大增加驱动队列中保存的字节数(即驱动中的描述符可能指向一个大于MTU的报文,后续会在NIC中进行报文切割)。

2.10 饥饿和延迟

IP栈和硬件(参见4.2章节的驱动队列,和5.5章节对启动队列的管理)之间的队列引入了两个问题:饥饿和延迟。

NIC驱动程序从队列中取出数据包进行传输,但队列是空的,此时硬件会错失一次传输的机会,进而导致系统吞吐量下降,这种情况称为饥饿。注意,当系统不需要传输任何数据时,队列也是空的,这是正常情况,并不归类为饥饿。与避免饥饿相关的复杂情况是,正在填充队列的IP栈和消耗队列的硬件驱动程序是异步运行的,更糟糕的是,填充和获取事件的间隔会随着系统的负载和外部状况(如网口的物理媒介)而变化。例如,在一个繁忙的系统上,IP栈向缓冲区添加报文的机会将变少,这会导致硬件在更多数据包入队列之前耗尽缓冲区。基于这种原因,使用比较大的缓冲可以降低饥饿的概率,并保证高吞吐量。

当使用一个大的队列来为一个繁忙的系统保持高吞吐量时,同时也会引入大量延迟。

上图展示了一个驱动队列,其中几乎填满了高带宽,大流量(蓝色)下的TCP段。队列的最后一个报文来自一个VoIP或游戏流(黄色)。像VoIP或游戏这样的交互式应用通常会以固定间隔的时间发送小的报文,它们是延迟敏感型的。而高带宽数据传输会产生更高的报文速率和更大的报文,更高的报文速率会填满交互式报文之间的缓冲,导致交互式报文的传输被推迟。为了描述这种行为,考虑下面这种场景:

  • 网口上允许传输的速率为5 Mbit/sec 或 5,000,000 bits/sec。
  • 大流量上的每个报文是 1,500 bytes 或12,000 bits。
  • 交互式流量上的每个报文是500 bytes。
  • 队列的深度为128个描述符。
  • 此时队列中有127个大流量报文和1个交互式报文。

鉴于上述假设,耗尽127个大流量报文并给交互式报文创造传输机会的时间为(127 * 12,000) / 5,000,000 = 0.304 seconds (对于根据ping来衡量的延迟结果为304毫秒),这样的延迟对交互式应用来说是不可接受的,且不代表完整的往返时间(仅仅是交互式报文在队列中等待传输前的时间)。如前面所述,当启用TSO, UFO 或 GSO时,驱动队列中的报文大小可以大于1500字节,这将导致延迟更加严重。

为驱动队列选择一个合适的大小可以看作是一个Goldilocks 问题,为了保证吞吐量而不能大小,为了保证延迟而不能太大。

2.11 吞吐量和延迟之间的关系

在所有流量控制系统中,吞吐量和延迟都存在一定的关系。网络链路上传输的的最大信息速率称为带宽,但是对于网络上用户来说,实际获得的带宽还有一个专用术语,吞吐量。

延迟

  • 发送者传输和接收者解码或接收数据之间的延迟,总是非负或非0的值。

  • 原则上,延迟是单向的,但几乎整个Internet网络社区都在谈论双向延迟--发送方发送数据和通过某种方式确认收到数据之间的时间延迟,如ping

  • 以毫秒计算延迟;在以太网上,延迟通常是0.3到1ms之间,在广域网上,延迟为5到300ms之间。

吞吐量

  • 衡量发送者和接收者之间成功传输的数据总量

  • 以bit/sec为单位进行衡量;

    注:延迟和吞吐量是常用的计算术语。例如,应用程序开发人员在尝试构建响应工具时会提到了用户感知的延迟。数据库和文件系统人员会提到磁盘吞吐量。在网络层上,在DNS中查询网站名称的延迟是感知一个网站性能的重要指标。

为了最大化下载吞吐量,设备供应商和供应商通常会调整他们的设备来容纳大量数据包。当网络准备接收另外一个报文时,网络设备的队列中如果有一个报文,则简单地发送该报文即可。通过这种方式可以保证用户的下载吞吐量。

该技术以延迟的代价来最大化吞吐量。想象一下,当高优先级的报文位于大队列的末尾时,该报文在这个网络上的理论上的延迟可能是100ms,但必须在队列中等待传输。

虽然最大化吞吐量的决定非常成功,但对延迟的影响也是显著的。

斯图尔特·柴郡(Stuart Cheshire)在1990年代中期发出了一个名为愚蠢的延迟的警告,它采用了术语bufferbloat,大约15年后由吉姆·盖蒂(Jim Getty)在他的博客的ACM队列文章bufferbloat:互联网中的黑暗缓冲Bufferbloat FAQ中重点介绍了最大化吞吐量的选择。

在学术、网络和Linux开发社区中,分组交换网络存在的延迟和吞吐量之间的关系是众所周知的。 Linux流量控制核心数据结构可以追溯到1990年代,并且一直在不断开发和扩展,并增加了新的调度器和功能。

相关文章: