前言

最早了解到交换机,还是初高中那会,家里使用机顶盒来使用网络电视业务,虽然当时家里的电视本身就可以联网......重点就是,电视边上只有一个宽带接口,但是需要使用宽带的却又两个设备(路由器 + 机顶盒),当时电信师傅是从包里掏出一个交换机(我之所以知道是交换机是因为上边标着交换机的字样,当然现在我在补全计算机网络知识的时候知道了还有一种不再使用的叫做集线器的东西)然后将宽带线,连接机顶盒和连接路由器的线材都接在交换器上。接下来,路由器和机顶盒都可以上网了还留下了一个多余的LAN口。

当时的我的理解就是,交换器是一个聚合器,可以将两个不同的宽带 “焊接” 为一条宽带,后来自己给家里组网我也是采用这种想法的,当然在近期尝试学习计算机网络的时候,我对于交换机的机制有了一定的了解,这篇文章主要用作记录用途,理解计算机时理解网络的重要基石,十分重要。

正文

交换机的基本模型

在了解交换器原理之前,我们首先需要看看交换器的正常状态 :交换机上有多个LAN口,而每一个LAN口都接上一个网线,网线的另一端可以是电脑,路由器,宽带,或者是另一个交换器。其实按照我比较喜欢的逻辑是尝试去分析为什么要设计交换器这种东西,但是想要了解这一部分估计是需要正式阅读《计算机网络》那本大黑书,现在我还不打算读,我现在读的是更加简单,但是书有点老的《网络是如何连接的》。所以这里就省略“为什么要设计交换机”这部分描述了。

好的,现在我们想想,连接在交换机上的两台设备如何进行通信?假设现在是两个计算机连接到了同一个交换机上,假设我们什么都不知道,想到的最简单的方法一定是 “连接在LAN1上的计算机告诉交换机:我需要向连接在LAN2的设备发送信息。” 对吧最简单,最直白。但是这就要求一件事情 - 发送信息的计算机需要知道另一台计算机连接在了哪个LAN口,这样设计是当然可以的,而且超级简单,问题是一旦这样设计,一旦需要更换计算机连接的LAN口,就需要手动在发起通信的设置模块中重新设置,这对于简单,不经常变化的网络而言当然没有问题,但是这种手动更新LAN口的方式完全不适合大规模网络,而且易用性相对较差。

为了摆脱手动设置发送到哪个LAN口,可以尝试摆烂,我们的先人早就是摆烂专业户了 - 直接将信息发送到所有的LAN口,让LAN连接的计算机自己处理自己需要的信息。但是这种模式显然不够安全,而且效率也很低,随便一条信息,就同时需要转发到所有LAN口,假如有一个交换器同时有5个LAN口,ABCDE,A向B发送信息,C向D发送信息,那么因为群发的特性,E就需要同时接受两个消息,了解计算机的人都知道,这个时候的通用处理就是排队。关键是一个两个LAN口的交流导致了其他无关的LAN口也保持负载状态,LAN口一多,通信效率一定会大打折扣,其实这就是集线器 - 现在已经弃用了,这里暂时不提,后续时机成熟后我会深入讲解。

现在我们重新思考这一逻辑,“连接在LAN1上的计算机告诉交换机:我需要向连接在LAN2的设备发送信息。” —— 我们是不是忘记了什么,我们通信的本质就是计算机A与计算机B的通信,LAN1 和 LAN2 本来就是两个计算机连接在交换器的端口,而我们之所以如此在意这一点,是因为在逻辑上一定是少不了确认计算机B到底连接在交换器的哪个LAN口上的,因为信号是必须依赖物理链路传输。想要从计算机A将信息传输到计算机B,必须经过连接AB的网线。这里是A向B发送信息,至少也要知道B所连接的LAN口。但是为什么确认计算机连接在哪个LAN口需要我们人工肉眼查看?能不能这样做,计算机B连接上交换机的时候,计算机B告诉交换机,我现在连接到你了,你登记一下,交换器一看,计算机B连接到了LAN2,于是在内存中记录:计算机B 入住 LAN2 房间。这个时候 A来到交换机面前,说我是来找计算机B的,你看看他在哪里,一查记录,计算机B连接到了LAN2 ,于是领着计算机A 的信件到 LAN2 。

现在我们已经得到了解决问题的基本模式了,我们将尝试优化部分细节,计算机A, 计算机B,计算机C一定是需要有不同的标识的,因为交换机依靠这个标识来查询不同的计算机连接在哪个LAN口。这个表示最简单的办法就是设置为计算机名字,但是这样做,当设备变多的时候,想有一个好名字可不容易。其次就是可能会重名,重名才是关键问题,一旦重名的设备接入到同一个交换机,就会引发问题,交换机中的记录是应该覆盖,还是应该单开一个,但是连接到另一个LAN口?发送信息的时候同时发送到两者?想要确保不重名,最好还是弄一串数字,这个数字由同一个机构统一分配,确保不重复。

然后就是继续思考,假设标识为MAC1的设备连接到了交换机LAN1,交换机产生一条记录 MAC1 -> LAN1,这条记录应该保持多久?显然,直到 MAC1 标识的设备从LAN1卸下来前,这一条记录都应该保存。当MAC1 表示的设备从 LAN1 卸下来后,交换器发现 LAN1的连接断开,主动删除内存中的记录。这是最理想的状态,但是实际上,现代的交换机虽然有MAC 表的逻辑,但是修改 内存中记录的时机却有所不同……,还有就是,假如我们的计算机A ,也就是MAC1标识的计算机,尝试去联系计算机C(标识为MAC3),而MAC3并没有连接到交换器,所以交换器内部也没有MAC3 ->LANn 的记录,这个时候如何处理,到底是直接丢弃,还是广播到所有的LAN口?这两个操作我们还没做详细指定,并且第二条好像怎样都可以(实际上两者中有一个更好)。

交换机的递归模型

在交换机的基本模型上,我们解决了大致的问题,但是却遗留了少部分看起来都可以的行为设定没有处理,这些细节我们现在并不知道如何处理,我们可以暂时放在一边,因为至少就在一个交换机下,这些细节问题无关紧要。

我们现在重新审视一下交换机,交换机是一个方形盒子,里边有很多LAN口的凹槽(或者通俗点叫做插座),可以插入LAN口的网线。我们前边的逻辑可以理解为LAN口的网线是设备的延伸,因为我们本质上需要的是设备间的信息传递,而LAN口网线只是为了让设备连接到交换机的必备品,所以我们可以将其视为设备的延伸,现在问题来了,前边我们将 “电脑 + LAN 网线” 当作一个设备,这个设备从整体上看,留下一个LAN的 “插头”,交换器本身连接上了一个LAN网线,从整体上看,不也是满足 “设备”的外观吗,唯一的不同就是交换器上还有一些多余的LAN口,可以连接其他不同的 “设备”。如此,就构成了交换机的递归模型,交换器不仅仅可以连接作为设备的电脑等,还可以连接交换机本身。

graph TD
A[A交换机] <---> B[B交换机]
C[设备a] --> A[A交换机]
D[设备b] --> A[A交换机]
E[设备c] --> B[B交换机]
F[设备d] --> B[B交换机]

如果两个交换机连接在一起,很明显,要么这个交换机有一个控制系统,这个控制系统携带一个MAC,可以作为一个设备,要么就是想要将数据转发到这个交换机上的其他设备。前者我们的逻辑已经很好理解,当交换机A连接上另一个交换机B时,交换机A自动将自己的MAC告知交换机B,随后交换机B更新MAC表,之后连接上那个交换机的设备cd就可以访问交换器A的控制端。

但是如果我们是希望能够让交换机A连接的设备ab可以被交换机B连接的设备cd所访问,这个时候我们需要意识到,这个时候的交换机不需要也不应该具有MAC,因为他此时并不是一个设备而是信息的一个中转点,如果这个设备真的具有MAC,那么一定是他连接的所有MAC的全体。这里有点抽象,简单表示就是MAC表上一个LAN,可以和多个MAC绑定。

现在我们需要思考一个不容易察觉的问题,我们的MAC表是否有必要完整记录所有可以连接的设备?也就是除了记录直接连接到LAN口的设备,还从连接到这个交换机的另一台交换机的内存中,将他的MAC表中的设备一同更新到自己的MAC表中,需要注意的是,假如我们确实这样做,我们必须同步更新所有交换机设备的变化,否则一旦设备断联或者发生变更,交换机MAC中就会有不可连接的记录,或者查无此人的错误,并更严重的是只要物理连接不断开,交换机中的MAC记录和LAN的绑定就不能销毁,即使这些查询根本不活跃,否则,由于就没有完整记录所有可以连接的设备,违背前提条件。为了满足我们的假设,我们需要付出巨大代价,设备变更的时候全链路修改,以及不活跃的连接依旧保持在MAC表中,降低查询事件。虽然这样的代价,在今天似乎已经无所谓了。但是仍然会带来高昂的设备成本。【我们今天已经有一些大型数据中心为了降低延迟,直接在所有交换机都维护一个完整的MAC表了,但是在既往硬件水准没有跟上的时候采用的是另一种方式】

好的这是我最开始想到的模式,可能是我活在一个硬件高度发达的现代社会,我首先想到的就是全映射,因为即使是百万条记录在今天的硬件也是能做到纳秒级查询时间的。这也是得益于折腾服务器,留下的经验。

但是在早期硬件资源紧张的时代(如 2000s–2010s),不可能让每台交换机都存百万级 MAC 条目,为了降低内存占用,提升资源使用率,不仅不主动同步另一台交换机上的MAC表,而且即便是已经物理上连接的设备,只要长时间不通信,直接从MAC表删除记录。 - 那个硬件匮乏时代的哲学就是物尽其用,尽可能降低资源暂用,即使需要牺牲一些其他东西。设计出这个的人是很天才的(如果我不知道今天的交换机通信原理,我根本不能做到自然想到这种解决方案,但是反推他的理由还是可以的),因为他认识到,设备与设备之间能否进行通信,是因为他们的物理链路是联通的,而不是因为有MAC表告知通信的路径。因此即使没有MAC表的记录的通信路径,也是完全可以进行通信的,但是没有地图和有地图的唯一区别就是需要不断的尝试。看起来像是茫茫人海找到唯一的那个人(更好的说法叫做通知,因为通知可以不用知道这个人的具体位置,但是查询需要,这里是从通用语义优化一下表述),一定耗时又耗力。

但是 —— 我们刻板印象中的通知一个人耗时又耗力,那是建立在一个人去找另一个人,找到了然后通知,并且毫无条理的找的情况,今天我们遇到的是,交换机的树状链路,当一个交换机找不到的时候,没有办法通知的时候,我就直接不管了,这个任务交给其他的交换机,而假设这些交换机也没找到(无法通知),这些交换机自己就会通知他们连接的其他交换机寻找,这样一来就是每过几微秒,同时查找的人就会按倍数增长,并且由于所有的设备只能物理连接上一台交换机,所以每一台交换机的工作都不会白费,下一台交换机不会通知上一台的已经查询过的设备。因为他已经查询过了,并且自己的MAC表中刚好记录了上一台交换机中的连接的设备的可能新很低,上一台交换机的工作大部分都是独占和有效的。

现在我们在交换机基础模型遗留的问题就得到基本解决方法 : MAC表不存在某一条MAC的记录,这次通知不会废弃,而是会转发到所有的端口,因为这些端口上连接的其他交换器会继续尝试通知。而且交换机上的MAC表并不是我们说的,物理链路不断开就保持记录,而是为了占用最小化,即使物理连接没有断开,只要设备不活跃,也删除那一条记录。

交换机的运行模式

经过了这么多的思考,我们已经可以认识到,交换机的每一个特性都是历史的必然,网络工程师,协调硬件与现实需求做出的最优化妥协,这不是什么天才的设计,而是历史的必然。

今天的交换机的运行模式可以按照如下方式描述:交换机上有多个LAN口,并且交换机自身维护了一个MAC表,MAC表记录了交换机上每一个LAN口可以联通的设备(MAC)。MAC表的更新按照如下模式进行,当设备与交换机的物理连接建立或者断开时,会自动更新MAC表,进行增删操作。同时引入一个老化时间,一个设备如果在老化时间内不进行通信,交换机就会在MAC表中删除那条记录。当交换机与交换机进行连接的时候,由于二者的等效作用,他们各自会对对方发出一致的消息,严谨一点就是:它们会相互发送大量“控制帧”(control frames),这些帧不会更新 MAC 表中的用户设备记录,但会触发关键的网络管理操作。【防止环路(STP)链路聚合(LACP)邻居发现(LLDP/CDP)拓扑同步(TCN)】。

当连接上交换机的设备尝试向另一个设备发送发送帧(数据帧,含源 MAC、目标 MAC),交换机首先检查MAC表中,是否有目标MAC,如果有,直接转发帧到对应LAN口,如果没有找到,转发到除接收端口外的所有 LAN 口,也就是我们说的泛洪,LAN口下既可能是目标设备,也可能是另一个交换机,如果是另一个交换器,则会触发源MAC学习,接受帧的交换机会在他的MAC表中记录源MAC以及传入帧的端口。构成一条新的MAC表记录。如果是目标设备,则目标设备接收到原始帧(源MAC ,目标MAC不随传递过程变化)。目标设备接受到数据帧的时候,可以选择发送响应帧,此时连接目标设备的交换机因为源MAC学习,在MAC表中记录了源MAC的端口(在响应帧中是目标MAC),所以不会再触发泛洪,交换机向指定LAN口转发数据帧的时候,交换机同时也会更新该MAC的老化时间。