1.2 控制拥塞
从设计第一个网络以来,资源分配和拥塞控制就是网络领域中一直被研究的复杂问题。时至今日,它们依然是研究的活跃领域。这些问题之所以复杂的一个原因是,它们不仅限于分层网络协议中的某一层。资源分配部分在网络中的路由器、交换机和链路中完成,部分在运行在终端主机上的传输层协议中完成。终端主机可以通过特定协议向网络节点传达它们的资源需求,而网络节点可以响应资源的可用性。应用层协议本身也可能包含了缓解拥塞的设计,例如,根据当前网络条件改变视频传输的解析度。所以,这是一个典型的系统性问题,除非你查看了整个系统中所有相关的部分,否则你不可能完全理解它们。
在我们进一步讨论之前,我们应该先澄清我们的术语。当我们谈论资源分配时,我们指的是网络设备试图满足应用程序对网络资源(主要是链路带宽和路由器或交换机中的缓存)的竞争性需求的过程。当然,通常不可能满足所有需求,这意味着一些用户或应用程序得到的网络资源可能少于他们想要的。资源分配问题的一部分是决定何时说不,以及对谁说不。
我们使用术语拥塞控制来描述网络节点(包括终端主机)为预防或应对超载所做的努力。由于拥塞通常对所有人都不利,所以首要任务是减轻拥塞,或者从一开始就预防拥塞。这可以通过仅说服一些主机停止发送数据从而改善所有其他人的情况来实现。然而,拥塞控制机制中更常见的是具备一定的公平性——也就是说,它们试图在所有用户之间分享痛苦,而不是只给少数人造成更大的痛苦。因此,我们可以看到许多拥塞控制机制内置了某种资源分配。
理解流量控制和拥塞控制之间的区别也很重要。流量控制的目的是防止发送端过快发送数据压垮慢的接收端。相比之下,拥塞控制旨在防止一组发送方发送过多的数据从而压垮网络中某个资源不足的点。这两个概念经常被混淆;但是正如我们将看到的,它们也共享一些类似的机制。
考虑到拥塞控制和资源分配可能在各种不同的地方(网络设备,终端主机)和各种不同的网络分层(IP层,传输层,应用层)中实现,最好可以从一个简单的方法开始进行研究,而这正是Jacobson 和 Karels 所做的(尽管他们的解决方案最终内容有很多细节)。
在早期的互联网中,路由器使用的是最基本的资源分配方法:First In First Out(FIFO)和Tail Drop。路由器不感知数据流或应用程序,只是按照到达的顺序接收数据包。当出向链路容量小于到达速率时,数据包将被放入队列,并按照先进先出(FIFO)的规则处理队列中的数据。当队列满时,后到达的数据包将被丢弃(Tail Drop)。尽管历史悠久,但这仍然是今天最常见的队列形式;我们将在后续章节中讨论包括Active Queue Management 在内的其他队列形式。
早期互联网发生拥塞崩溃的原因在于,被丢弃的数据包并不只是被弃置和遗忘。如果端到端的传输协议是TCP(对于大多数互联网流量来说的确也是基于TCP),丢弃的数据包将会被重传。因此,随着拥塞的增加,重传的数据包数量也在增加;换句话说,在发生拥塞时,进入网络的数据包数量会多于用户和应用程序带来的实际负载。更多的数据包会导致更多的丢包,进而导致更多的重传,进而导致更多的数据包进入网络中,以此循环往复。这就是导致崩溃的原因(注,不断的正反馈放大问题,最后导致崩溃)。
在讨论拥塞时,一个有用的术语是 goodput,它与吞吐量(throughput)有所区别,只有实际能传输数据的数据包才会计算在goodput之内。假设一个链路的利用率达到100%,但有60%的数据包是因为之前的丢失而被重新传输,那么你可以说这个链路的 goodput 只有40%。
早期研究者对拥塞控制的一个关键认知是,当TCP遇到拥塞时不应该无脑的重传丢包,而必须同时做出其它响应。TCP需要能检测到拥塞——它可以通过识别数据包丢失来实现检测——随后通过减少发送到网络中的流量来响应拥塞。这种在拥塞发生时,端到端协议和网络之间的交互,构成了大部分当今拥塞控制和拥塞规避方法的基础。我们将在后面的章节深入去看这些方法具体如何工作。
Last updated