查看原文
其他

只有IT人才能读懂的《西游记》

刘超 51CTO技术栈 2019-03-28

作为一部被重播数千次的电视剧,86版《西游记》毫无疑问是一部经典中的经典!对于绝大多数人来说都是童年最深的记忆。


本文作者以西游记为背景,为大家讲述一个有关计算机网络协议的故事。


我佛造经传极乐


话说我佛如来为度化天下苍生,有三藏真经,可劝人为善。


就如图中所示,真经所藏之处,在于云端。佛祖所管辖之下,有四个区域 Region,称为四大部洲,一是东胜神洲,二是南赡部洲,三是西牛贺洲,四是北俱卢洲。


我佛所在西牛贺洲,是主站点。

在每个区域 Region,为保证真经永固,设置多个藏经楼,称为可用区(Available Zone)。


每个藏经楼里面是一排一排的柜子,称为机柜,里面有一排一排的格子,称为服务器,经文就摆放在格子中。

在藏经楼中,柜子根据经文分门别类的组织起来,由不同的神仙进行管理,管理一个柜子的经文的神仙,访问这里面经文的钥匙就在他手里,称为接入层神仙(接入层交换机)。


多个接入层神仙被一组汇聚层神仙(汇聚层交换机)管着,多个汇聚层的神仙被一组核心层神仙(核心交换机)管着。


神仙体系组织严格,层次分明,不同的接入层神仙交换经文,要通过汇聚层神仙同意,不同的汇聚层神仙交换经文,需要核心层神仙同意。

经文的看守要万无一失,因而每一层都是分组看护,互相监督,互相备份,称为堆叠。


虽说每个柜子里面放满了经文,为了防止经文被偷听偷看,经文的内容是被仙术封装在一个虚拟的私密空间里面。


虽然有人可能会偷到物质的经文,但是没有仙术打开这个私密空间,看到的经文如同空白的一样。这个虚拟的私密空间称为 VPC


要解读经文,需要使用每一格中一个不起眼的法宝,就是称为 Openvswitch 的虚拟交换机,顾名思义就是起到经文在虚拟私密空间和物理空间之间的转换作用。

Openvswitch 如何转换呢?使用的是一种称为 VXLAN 的封装技术,但是必须要事先知道芝麻开门的 ID,也即 VXLAN ID,才能看到经文的真正内容。


在虚拟的空间中,放着真正可以解读的真经。

真经有法一藏,谈天;论一藏,说地;经一藏,度鬼;三藏共计三十五部,该一万五千一百四十四卷,乃是修真之径,正善之门。


看来已经前中后台分离,分为基础服务层,组合服务层,Controller 层,共三十五个模块,一万五千多个服务,真是微服务架构啊。


如何能够不要迷失在这个一万五千卷经文中,也是很有挑战的事情,需要一个索引和指南,这就是常说的 RPC 框架和服务注册与发现中心。


为了方便诸多僧侣前来取经,灵山脚下会有一个统一的入口地址,这里有一个神仙,称为金顶大仙,专门来接应取经人的。

由于前来取经的人很多,同时经文也很多,所以金顶大仙多起到负载均衡的作用,将不同的取经人引领到不同的藏经楼,访问不同的经文。


金顶大仙所在的灵山脚下,是一个世界知名的地址,称为外网 IP 地址,这个地址是全球可定位的,所有的取经人都先到这个地方。


金顶大仙通过 NAT 规则,将外网 IP 地址,变成藏经楼的私有 IP 地址,例如 2 号藏经楼三楼,4 号藏经楼五楼等。


在灵山藏经楼里面,是通过私有 IP 地址定位的。真经已经准备好,就差东土取经人了。


观音奉旨上长安


可是佛祖愁啊,是这样说的:我待要送上东土,叵耐那方众生愚蠢,毁谤真言,不识我法门之要旨,怠慢了瑜迦之正宗。


怎么得一个有法力的,去东土寻一个善信,教他苦历千山,远经万水,到我处求取真经,永传东土,劝他众生,却乃是个山大的福缘,海深的善庆、谁肯去走一遭来?


真经就在灵山,可是东土之人愚钝,不知道灵山咋办呢?要一个法力无边的人告诉他们呀。而且最好能够告诉全世界,灵山这里有真经。


好在有观音菩萨,道:“弟子不才,愿上东土寻一个取经人来也。”,观音菩萨有什么法力呢?当然是 BGP 协议了。


刚才那张图画的是一个可用区的情况,对于多个可用区的情况,我们可以隐去计算节点的情况,将外网访问区域放大。

外网 IP 是放在虚拟网关的外网网口上的,这个 IP 如何让全世界知道呢?在核心交换外面是安全设备,然后就是边界路由器。


边界路由器会和多个运营商连接,从而每个运营商都能够访问到这个网站。边界路由器可以通过 BGP 协议,将自己数据中心里面的外网 IP 向外广播,也就是告诉全世界,如果要访问这些外网 IP,都来我这里。


每个运营商也有很多的路由器、很多的点,于是就可以将如何到达这些 IP 地址的路由信息,广播到全国乃至全世界。


厉害吧,这是我佛如来告诉观音菩萨的:“这一去。要踏看路道,不许在霄汉中行,须是要半云半雾;目过山水,谨记程途远近之数,叮咛那取经人。“


就是说你去东土的路上,经过了哪些道路,要记住路径,要记住远近,才能告诉取经人这一路应该怎么走。


玄奘秉诚建大会


当观音菩萨来到东土大唐,正看到玄奘法师坐在高台上,带领众人诵经,念一会《受生度亡经》,谈一会《安邦天宝篆》,又宣一会《劝修功卷》。


菩萨近前来,叫道:“那和尚,你只会谈小乘教法,可会谈大乘么?”玄奘闻言,心中大喜,翻身跳下台来,对菩萨起手道:“老师父,弟子失瞻,多罪。见前的盖众僧人,都讲的是小乘教法,却不知大乘教法如何。”


菩萨道:“你这小乘教法,度不得亡者超升,只可浑俗和光而已。我有大乘佛法三藏,能超亡者升天,能度难人脱苦,能修无量寿身,能作无来无去。”

你看,在西方极乐净土,我佛已经有了更牛的佛经,遥远的东方,还在读本土的僧人早期从西方传过来的经。


这种模式,称为 CDN。

我们部署应用的时候,一般会把静态资源保存在两个地方,一个是 Nginx 后面的 varnish 缓存里面,一般是静态页面。


对于比较大的、不经常更新的静态图片,会保存在对象存储里面。这两个地方的静态资源都会配置 CDN,将资源下发到边缘节点。


最初佛祖传经,都是口口相传,经文都会记在高僧大德的心里,随着高僧云游天下,随着庙宇遍布天下,佛经从而遍布天下。这就相当于将佛经缓存在边缘节点。


配置了 CDN 之后,权威 DNS 服务器上,会为静态资源设置一个 CNAME 别名,指向另外一个域名 cdn.com,返回给本地 DNS 服务器。


当本地 DNS 服务器拿到这个新的域名时,需要继续解析这个新的域名。这个时候,再访问的时候就不是原来的权威 DNS 服务器了,而是 cdn.com 的权威 DNS 服务器。这是 CDN 自己的权威 DNS 服务器。


在这个服务器上,还是会设置一个 CNAME,指向另外一个域名,也即 CDN 网络的全局负载均衡器。


本地 DNS 服务器去请求 CDN 的全局负载均衡器解析域名,全局负载均衡器会为用户选择一台合适的缓存服务器提供服务,将 IP 返回给客户端,客户端去访问这个边缘节点,下载资源。缓存服务器响应用户请求,将用户所需内容传送到用户终端。


如果这台缓存服务器上并没有用户想要的内容,那么这台服务器就要向它的上一级缓存服务器请求内容,直至追溯到网站的源服务器,将内容拉到本地。


CDN 的全局负载均衡策略,就相当于当僧人们想读佛经的时候,不必要都去西天,而是可以就近去问,周围有没有庙宇,然后向庙宇的师傅去请教佛经。


然而缓存的佛经当然是比不上西天取到的经文更新,所以东土由于离西天较远,缓存的还是小乘佛教,要读大乘佛教,就要去西天取经,称为回源。


观音显像化金蝉


观音菩萨打算度化玄奘法师,回源去西天取经。


可是怎么去呢,地址在哪里呢?玄奘法师只听说西天,不知道具体的地址,这就要问观音菩萨了。

这个时候,大家才知道,西天在灵山大雷音寺,距此十万八千里。这个过程称为 DNS 解析。


当在手机上面打开一个 App 的时候,首先要做的事情就是解析这个网站的域名。


在手机运营商所在的互联网区域里,有一个本地的 DNS,手机会向这个 DNS 请求解析 DNS。当这个 DNS 本地有缓存,则直接返回。


如果没有缓存,本地 DNS 才需要递归地从根 DNS 服务器,查到 .com 的顶级域名服务器,最终查到权威 DNS 服务器。


如果你使用云平台的时候,配置了智能 DNS 和全局负载均衡,在权威 DNS 服务中,一般是通过配置 CNAME 的方式,我们可以起一个别名,例如 vip.yourcomany.com。


然后告诉本地 DNS 服务器,让它请求 GSLB 解析这个域名,GSLB 就可以在解析这个域名的过程中,通过自己的策略实现负载均衡。


GSLB 通过查看请求它的本地 DNS 服务器所在的运营商和地址,就知道用户所在的运营商和地址,然后在距离用户位置比较近的 Region 里面,将三个本地负载均衡的公网 IP 地址,返回给本地 DNS 服务器。


本地 DNS 解析器将结果缓存后,返回给客户端。对于手机 App 来说,可以绕过刚才的传统 DNS 解析机制,直接只要 HTTP DNS 服务,通过直接调用 HTTP DNS 服务器,得到这三个本地负载均衡的公网 IP 地址。


这个公网 IP 地址,就是金顶大仙所在的位置。其实这个时候,金顶大仙已经在等待了。


这个时候,李世民突然开始说话了,曰:“谁肯领朕旨意,上西天拜佛求经?“ 并愿意买下观音手中的两件宝物,“锦澜袈裟”一领,“九环锡杖”一根,佛祖说过:”若有取经人坚心来此,穿我的袈裟,免堕轮回;持我的锡枚,不遭毒害。“

玄奘法师回答:“贫僧不才,愿效犬马之劳,与陛下求取真经,祈保我王江山永固。”

这个时候,菩萨说了:“西天路远,更多虎豹妖魔。只怕有去无回,难保身命。”


玄奘道:“我已发了弘誓大愿,不取真经,永堕沉沦地狱。“

其实这里的对话是很有意思的,玄奘法师回复李世民的和回复观音菩萨的不同。


这个时候,李世民作为世俗的君王,已经想求取真经了,也就是东土大唐作为客户端,要发起对于服务端的请求了。但是玄奘法师知道,唐王李世民去取经,求的是江山永固。


所以李世民的请求是应用层的,发起的是 HTTP 的协议,在 HTTP 的请求正文中,怕是写的“江山永固”四个字。


而玄奘法师回复观音菩萨的时候,说的就不同了,是一种对于真经和佛法本身的坚持。


所以玄奘法师是 TCP 层的,TCP 是面向连接的,TCP 是靠谱的协议,但是这不能说明它面临的网络环境好。


从 IP 层面来讲,如果网络状况的确那么差,是没有任何可靠性保证的,而作为 IP 的上一层 TCP 也无能为力,唯一能做的就是更加努力,不断重传,通过各种算法保证。


也就是说,对于 TCP 来讲,IP 层你丢不丢包,我管不着,但是我在我的层面上,会努力保证可靠性。


这一点在流沙河有了验证。观音菩萨度化沙悟净的时候,沙悟净说:“菩萨,我在此间吃人无数,向来有几次取经人来,都被我吃了。凡吃的人头,抛落流沙,竟沉水底(这个水,鹅毛也不能浮),惟有九个取经人的骷髅,浮在水面,再不能沉。我以为异物,将索儿穿在一处,闲时拿来顽耍,这去,但恐取经人不得到此,却不是反误了我的前程也?”


菩萨日:“岂有不到之理?你可将骷髅地挂在头顶下,等候取经入,自有用处。”

所以沙悟净脖子上这九个骷髅,是唐三藏的前九辈子,一旦吃了,就不断的重试。


为了能够实现重试,实现 TCP 的可靠性,客户端和服务器需要建立连接。

HTTPS 协议是基于 TCP 协议的,因而要先建立 TCP 的连接。在这个例子中,TCP 的连接是从手机上的 App 和负载均衡器 SLB 之间的。也就是唐僧和金顶大仙之间,到了金顶大仙,就不怕了,会指引到佛祖那里的。


尽管中间要经过很多的路由器和交换机,但是 TCP 的连接是端到端的。


TCP 这一层和更上层的 HTTPS 无法看到中间的包的过程。尽管建立连接的时候,所有的包都逃不过在这些路由器和交换机之间的转发,转发的细节我们放到那个下单请求的发送过程中详细解读,这里只看端到端的行为。


对于 TCP 连接来讲,需要通过三次握手建立连接,为了维护这个连接,双方都需要在 TCP 层维护一个连接的状态机。


一开始,客户端和服务端都处于 CLOSED 状态。服务端先是主动监听某个端口,处于 LISTEN 状态。


然后客户端主动发起连接 SYN,之后处于 SYN-SENT 状态。服务端收到发起的连接,返回 SYN,并且 ACK 客户端的 SYN,之后处于 SYN-RCVD 状态。


客户端收到服务端发送的 SYN 和 ACK 之后,发送 ACK 的 ACK,之后处于 ESTABLISHED 状态。


这是因为,它一发一收成功了。服务端收到 ACK 的 ACK 之后,处于 ESTABLISHED 状态,因为它的一发一收也成功了。


当 TCP 层的连接建立完毕之后,接下来轮到 HTTPS 层建立连接了,在 HTTPS 的交换过程中,TCP 层始终处于 ESTABLISHED。


对于 HTTPS,客户端会发送 Client Hello 消息到服务器,用明文传输 TLS 版本信息、加密套件候选列表、压缩算法候选列表等信息。另外,还会有一个随机数,在协商对称密钥的时候使用。


然后,服务器会返回 Server Hello 消息,告诉客户端,服务器选择使用的协议版本、加密套件、压缩算法等。这也有一个随机数,用于后续的密钥协商。


然后,服务器会给你一个服务器端的证书,然后说:“Server Hello Done,我这里就这些信息了。”


客户端当然不相信这个证书,于是你从自己信任的 CA 仓库中,拿 CA 的证书里面的公钥去解密电商网站的证书。


如果能够成功,则说明电商网站是可信的。这个过程中,你可能会不断往上追溯 CA、CA 的 CA、CA 的 CA 的 CA,反正直到一个授信的 CA,就可以了。


其实观音菩萨手里的锡杖和袈裟,就相当于佛祖颁发的证书,保证西行路上的安全,玄奘法师这个网络包别被别人吃了,或者篡改。

就像误入小雷音一集中,白眉老佛想吃了唐僧肉,自己披上袈裟,西天取经,求得正果。


当然,一开始观音菩萨拿出锡杖和袈裟这个证书的时候,大家也不相信,所以需要观音菩萨现出真身,作为 CA,证明给客户端,唐王李世民和玄奘法师才下拜。

证书验证完毕之后,觉得这个服务端是可信的,于是客户端计算产生随机数字 Pre-master,发送 Client Key Exchange,用证书中的公钥加密,再发送给服务器,服务器可以通过私钥解密出来。


接下来,无论是客户端还是服务器,都有了三个随机数,分别是:自己的、对端的,以及刚生成的 Pre-Master 随机数。通过这三个随机数,可以在客户端和服务器产生相同的对称密钥。


有了对称密钥,客户端就可以说:“Change Cipher Spec,咱们以后都采用协商的通信密钥和加密算法进行加密通信了。”


然后客户端发送一个 Encrypted Handshake Message,将已经商定好的参数等,采用协商密钥进行加密,发送给服务器用于数据与握手验证。


同样,服务器也可以发送 Change Cipher Spec,说:“没问题,咱们以后都采用协商的通信密钥和加密算法进行加密通信了”,并且也发送 Encrypted Handshake Message 的消息试试。


当双方握手结束之后,就可以通过对称密钥进行加密传输了。


唐王素酒送三藏


玄奘这个网络包要发出了。太宗设朝,聚集文武,要去送行。李世民送给玄奘三个东西。


上一节说了太宗是应用层,关注保大唐江山永固,玄奘是 TCP 层,要通过坚定的意志到达西天。


李世民给的第一个东西是通关文牒,这个是 IP 层的,将来要通过这个文牒通过一个个城关。


第二个东西是紫金钵盂,这个用于玄奘法师到了某个城市里面化斋,同时打听路的时候使用,这个是一个 MAC 层的。


第三个东西是白马一匹,作为远程脚力,这个是物理层的。


最后,太宗敬了玄奘一杯素酒,言道:宁恋本乡一捻土,莫爱他乡万两金。三藏方悟捻土之意,复谢恩饮尽,辞谢出关而去。


当客户端和服务端之间建立了连接后,接下来就要发送下单请求的网络包了。


在用户层发送的是 HTTP 的网络包,因为服务端提供的是 RESTful API,因而 HTTP 层发送的就是一个请求。

POST /purchaseOrder HTTP/1.1
Host: www.xxxxxx.com
Content-Type: application/json; charset=utf-8
Content-Length: nnn

{
 "order": {
  "date""2018-07-01",
  "className""趣谈网络协议",
  "Author""刘超",
  "price""68"
 }
}


HTTP 的报文大概分为三大部分。第一部分是请求行,第二部分是请求的首部,第三部分才是请求的正文实体。


在请求行中,URL 就是 www.xxxxxx.com/purchaseOrder ,版本为 HTTP 1.1。


请求的类型叫作 POST,它需要主动告诉服务端一些信息,而非获取。需要告诉服务端什么呢?一般会放在正文里面。正文可以有各种各样的格式,常见的格式是 JSON。


请求行下面就是我们的首部字段。首部是 key value,通过冒号分隔。


Content-Type 是指正文的格式。例如,我们进行 POST 的请求,如果正文是 JSON,那么我们就应该将这个值设置为 JSON。


接下来是正文,这里是一个 JSON 字符串,里面通过文本的形式描述了,要买一个课程,作者是谁,多少钱。


这样,HTTP 请求的报文格式就拼凑好了。接下来浏览器或者移动 App 会把它交给下一层传输层。


怎么交给传输层呢?也是用 Socket 进行程序设计。如果用的是浏览器,这些程序不需要你自己写,有人已经帮你写好了;如果在移动 App 里面,一般会用一个 HTTP 的客户端工具来发送,并且帮你封装好。


HTTP 协议是基于 TCP 协议的,所以它使用面向连接的方式发送请求,通过 Stream 二进制流的方式传给对方。当然,到了 TCP 层,它会把二进制流变成一个的报文段发送给服务器。


在 TCP 头里面,会有源端口号和目标端口号,目标端口号一般是服务端监听的端口号,源端口号在手机端,往往是随机分配一个端口号。这个端口号在客户端和服务端用于区分请求和返回,发给那个应用。


在 IP 头里面,都需要加上自己的地址(即源地址)和它想要去的地方(即目标地址)。


当一个手机上线的时候,PGW 会给这个手机分配一个 IP 地址,这就是源地址,而目标地址则是云平台的负载均衡器的外网 IP 地址。


在 IP 层,客户端需要查看目标地址和自己是否是在同一个局域网,计算是否是同一个网段,往往需要通过 CIDR 子网掩码来计算。


对于这个下单场景,目标 IP 和源 IP 不会在同一个网段,因而需要发送到默认的网关。一般通过 DHCP 分配 IP 地址的时候,也会同时配置默认网关的 IP 地址。


但是客户端不会直接使用默认网关的 IP 地址,而是发送 ARP 协议,来获取网关的 MAC 地址,然后将网关 MAC 作为目标 MAC,自己的 MAC 作为源 MAC,放入 MAC 头,发送出去。

一个完整的网络包的格式是这样的:

接下来,网络包就正式发出了。如果你是用手机打开 App,下单购物发送网络包,一般通过手机运营商的网络。

客户的手机开机以后,在附近寻找基站 eNodeB,发送请求,申请上网。基站将请求发给 MME,MME 对手机进行认证和鉴权,还会请求 HSS 看有没有钱,看看是在哪里上网。


当 MME 通过了手机的认证之后,开始建立隧道,建设的数据通路分两段路,其实是两个隧道。一段是从 eNodeB 到 SGW,第二段是从 SGW 到 PGW,在 PGW 之外,就是互联网。


PGW 会为手机分配一个 IP 地址,手机上网都是带着这个 IP 地址的。


对于手机来讲,默认的网关在 PGW 上。在移动网络里面,从手机到 SGW,到 PGW 是有一条隧道的。


在这条隧道里面,会将上面的这个包作为隧道的乘客协议放在里面,外面 SGW 和 PGW 在核心网机房的 IP 地址。网络包直到 PGW(PGW 是隧道的另一端)才将里面的包解出来,转发到外部网络。


所以,从手机发送出来的时候,网络包的结构为:

  • 源 MAC:手机也即 UE 的 MAC。

  • 目标 MAC:网关 PGW 上面的隧道端点的 MAC。

  • 源 IP:UE 的 IP 地址。

  • 目标 IP:SLB 的公网 IP 地址。


进入隧道之后,要封装外层的网络地址,因而网络包的格式为:

  • 外层源 MAC:E-NodeB 的 MAC。

  • 外层目标 MAC:SGW 的 MAC。

  • 外层源 IP:E-NodeB 的 IP。

  • 外层目标 IP:SGW 的 IP。

  • 内层源 MAC:手机也即 UE 的 MAC。

  • 内层目标 MAC:网关 PGW 上面的隧道端点的 MAC。

  • 内层源 IP:UE 的 IP 地址。

  • 内层目标 IP:SLB 的公网 IP 地址。


当隧道在 SGW 的时候,切换了一个隧道,为从 SGW 到 PGW 的隧道,因而网络包的格式为:

  • 外层源 MAC:SGW 的 MAC。

  • 外层目标 MAC:PGW 的 MAC。

  • 外层源 IP:SGW 的 IP。

  • 外层目标 IP:PGW 的 IP。

  • 内层源 MAC:手机也即 UE 的 MAC。

  • 内层目标 MAC:网关 PGW 上面的隧道端点的 MAC。

  • 内层源 IP:UE 的 IP 地址。

  • 内层目标 IP:SLB 的公网 IP 地址。


在 PGW 的隧道端点将包解出来,转发出去的时候,一般在 PGW 出外部网络的路由器上,会部署 NAT 服务,将手机的 IP 地址转换为公网 IP 地址,当请求返回的时候,再 NAT 回来。


因而在 PGW 之后,相当于做了一次欧洲十国游型的转发,网络包的格式为:

  • 源 MAC:PGW 出口的 MAC。

  • 目标 MAC:NAT 网关的 MAC。

  • 源 IP:UE 的 IP 地址。

  • 目标 IP:SLB 的公网 IP 地址。


在 NAT 网关,相当于做了一次玄奘西游型的转发,网络包的格式变成:

  • 源 MAC:NAT 网关的 MAC。

  • 目标 MAC:A2 路由器的 MAC。

  • 源 IP:UE 的公网 IP 地址。

  • 目标 IP:SLB 的公网 IP 地址。


在手机运营商的网络里面,网络状况是比较好的。对于玄奘法师,在大唐国境之内,还是比较平安的。


原文说:师徒们行了数日,到了巩州城。早有巩州合属官吏人等,迎接入城中。安歇一夜,次早出城前去。


一路饥餐渴饮,夜住晓行,两三日,又至河州卫。早有镇边的总兵与本处僧道,闻得是钦差御弟法师上西方见佛,无不恭敬,接至里面供给了,着僧纲请往福原寺安歇。


本寺僧人,一一参见,安排晚斋。斋毕,吩咐二从者饱喂马匹,天不明就行。


真的是有接有送。行经半日,只见对面处,有一座大山,真个是高接青霄,崔巍险峻。


此山唤做两界山,东半边属我大唐所管,西半边乃是鞑靼的地界。过了这座山,就不是大唐的土地了。

历经千山与万险


离开大唐的国土,接下来的路应该怎么走呢?


好在此去西天,要经过一个个国家,每个国家有一个个城关,玄奘法师只要到处问路,只要这些城关的守门人知道大概路怎么走,就能一个个国家的走下去,如果遇到国家,还有通关文牒,还能保护玄奘法师在国内的安全。

这里有两个问题要解决,第一个是每个城关的守门人和每个国家,是怎么知道去西天怎么走的。第二个问题是玄奘如何问路,如何走。


我们先第一个问题,这个观音菩萨从西天来东土的时候,已经通过一种法术告诉这些国家和城关了。


菩萨的法术主要分两种情况,一种情况是在一个国家内部如何走,另一种情况在国家之间,在野外如何走的问题。


在一个国家内部,菩萨主要遵循最短路径原则,就是走得路越少越好,道路越短越好。


但是国家之间,菩萨不但要考虑远近的问题,还要考虑政策的问题。例如有的国家路近,但是路过的国家看不惯僧人,见了僧人就抓。例如灭法国,连光头都要抓。这样的情况即便路近,也最好绕远点走。


菩萨的法术是什么呢?咱们在大学里面学习计算机网络与数据结构的时候,知道求最短路径常用的有两种方法:

  • Bellman-Ford 算法

  • Dijkstra 算法


在计算机网络中基本也是用这两种方法计算的。距离矢量路由(distance vector routing),它是基于 Bellman-Ford 算法的。链路状态路由(link state routing),基于 Dijkstra 算法。


最常用的两种路由协议:

  • OSPF(Open Shortest Path First,开放式最短路径优先)就是这样一个基于链路状态路由协议,广泛应用在数据中心中的协议,称为内部网关协议(Interior Gateway Protocol,简称 IGP)。

  • BGP 协议使用的算法是路径矢量路由协议(path-vector protocol)。它是距离矢量路由协议的升级版,称为外网路由协议(Border Gateway Protocol,简称 BGP)。


路由协议是城关之间相互沟通到哪里应该怎么走的协议。

第二个问题,也就是玄奘如何问路,如何走。这就是 IP 协议。


这就要靠通关文牒了,里面写着贫僧来自东土大唐(就是源 IP 地址),欲往西天拜佛求经(指的是目标 IP 地址)。路过宝地,借宿一晚,明日启行,请问接下来该怎么走啊?

在解决第一个问题的时候,每个城关已经通过菩萨的法术,和邻近的城关进行沟通,知道了下面的信息。

这个叫路由表,根据这个表格,可以告诉唐僧怎么走。接下来我们看完整故事。

出了 NAT 网关,就从核心网到达了互联网。在网络世界,每一个运营商的网络成为自治系统 AS。每个自治系统都有边界路由器,通过它和外面的世界建立联系。


对于云平台来讲,它可以被称为 Multihomed AS,有多个连接连到其他的 AS,但是大多拒绝帮其他的 AS 传输包。例如一些大公司的网络。


对于运营商来说,它可以被称为 Transit AS,有多个连接连到其他的 AS,并且可以帮助其他的 AS 传输包,比如主干网。


如何从出口的运营商到达云平台的边界路由器?在路由器之间需要通过 BGP 协议实现,BGP 又分为两类,eBGP 和 iBGP。自治系统间,边界路由器之间使用 eBGP 广播路由。内部网络也需要访问其他的自治系统。


边界路由器如何将 BGP 学习到的路由导入到内部网络呢?通过运行 iBGP,使内部的路由器能够找到到达外网目的地最好的边界路由器。


网站的 SLB 的公网 IP 地址早已经通过云平台的边界路由器,让全网都知道了。


于是这个下单的网络包选择了下一跳是 A2,也即将 A2 的 MAC 地址放在目标 MAC 地址中。


到达 A2 之后,从路由表中找到下一跳是路由器 C1,于是将目标 MAC 换成 C1 的 MAC 地址。


到达 C1 之后,找到下一跳是 C2,将目标 MAC 地址设置为 C2 的 MAC。到达 C2 后,找到下一跳是云平台的边界路由器,于是将目标 MAC 设置为边界路由器的 MAC 地址。


你会发现,这一路,都是只换 MAC,不换目标 IP 地址。这就是所谓下一跳的概念。


在云平台的边界路由器,会将下单的包转发进来,经过核心交换,汇聚交换,到达外网网关节点上的 SLB 的公网 IP 地址。


我们可以看到,手机到 SLB 的公网 IP,是一个端到端的连接,连接的过程发送了很多包。


所有这些包,无论是 TCP 三次握手,还是 HTTPS 的密钥交换,都是要走如此复杂的过程到达 SLB 的,当然每个包走的路径不一定一致。


当网络包走在这个复杂的道路上,很可能一不小心就丢了,怎么办?这就需要借助 TCP 的机制重新发送。


既然 TCP 要对包进行重传,就需要维护一个 Sequence Number,看哪些包到了,哪些没到,哪些需要重传,传输的速度应该控制到多少,这就是 TCP 的滑动窗口协议。

整个 TCP 的发送,一开始会协商一个 Sequence Number,从这个 Sequence Number 开始,每个包都有编号。


滑动窗口将接收方的网络包分成四个部分:

  • 已经接收,已经 ACK,已经交给应用层的包。

  • 已经接收,已经 ACK,未发送给应用层。

  • 已经接收,尚未发送 ACK。

  • 未接收,尚有空闲的缓存区域。


对于 TCP 层来讲,每一个包都有 ACK。ACK 需要从 SLB 回复到手机端,将上面的那个过程反向来一遍,当然路径不一定一致,可见 ACK 也不是那么轻松的事情。


如果发送方超过一定的时间没有收到 ACK,就会重新发送。只有 TCP 层 ACK 过的包,才会发给应用层,并且只会发送一份,对于下单的场景,应用层是 HTTP 层。


你可能会问了,TCP 老是重复发送,会不会导致一个单下了两遍?是否要求服务端实现幂?


从 TCP 的机制来看,是不会的。只有收不到 ACK 的包才会重复发,发到接收端,在窗口里面只保存一份,所以在同一个 TCP 连接中,不用担心重传导致二次下单。


但是 TCP 连接会因为某种原因断了,例如手机信号不好,这个时候手机把所有的动作重新做一遍,建立一个新的 TCP 连接,在 HTTP 层调用两次 RESTful API。这个时候可能会导致两遍下单的情况,因而 RESTful API 需要实现幂等。


当 ACK 过的包发给应用层之后,TCP 层的缓存就空了出来,这会导致上面图中的大三角,也即接收方能够容纳的总缓存,整体顺时针滑动。


小的三角形,也即接收方告知发送方的窗口总大小,也即还没有完全确认收到的缓存大小,如果把这些填满了,就不能再发了,因为没确认收到,所以一个都不能扔。


功成行满见真如


唐僧经历九九八十一难,终于到达了西天。发现金顶大仙已经在等他们了。

网络包从手机端经历千难万险,终于到了 SLB 的公网 IP 所在的公网网口。由于匹配上了 MAC 地址和 IP 地址,因而将网络包收了进来。


到了西天,唐僧度过最后一条河凌云仙渡的时候,发现滚浪飞流,约有八九里宽阔,四无人迹。


好不容易盼来一条船,还没有底。原来驾船的是接引佛祖,玄奘法师的肉体随着河水飘走,从而脱胎换骨,成就金身。

在虚拟网关节点的外网网口上,会有一个 NAT 规则,将公网 IP 地址转换为 VPC 里面的私网 IP 地址,这个私网 IP 地址就是 SLB 的 HAProxy 所在的虚拟机的私网 IP 地址。


从而网络包也脱胎换骨,实现公网 IP 到私有网络 IP 的转换。

当然为了承载比较大的吞吐量,虚拟网关节点会有多个,物理网络会将流量分发到不同的虚拟网关节点。


同样 HAProxy 也会是一个大的集群,虚拟网关会选择某个负载均衡节点,将某个请求分发给它,负载均衡之后是 Controller 层,也是部署在虚拟机里面的。


当网络包里面的目标 IP 变成私有 IP 地址之后,虚拟路由会查找路由规则,将网络包从下方的私网网口发出来。


这个时候包的格式为:

  • 源 MAC:网关 MAC。

  • 目标 MAC:HAProxy 虚拟机的 MAC。

  • 源 IP:UE 的公网 IP。

  • 目标 IP:HAProxy 虚拟机的私网 IP。


在第一部分,我们说佛经是存放在一个虚拟空间里面的,要打开这个虚拟空间,解读经文,需要一个芝麻开门的 ID。接引佛祖会给玄奘法师一个 ID。


在虚拟路由节点上,也会有 OVS,将网络包封装在 VXLAN 隧道里面,VXLAN ID 就是给你的租户创建 VPC 的时候分配的。


VXLAN ID 就是 VPC 虚拟空间的 ID,OVS 就是那个能够封装和解开私密空间的法宝。


包的格式为:

  • 外层源 MAC:网关物理机 MAC。

  • 外层目标 MAC:物理机 A 的 MAC。

  • 外层源 IP:网关物理机 IP。

  • 外层目标 IP:物理机 A 的 IP。

  • 内层源 MAC:网关 MAC。

  • 内层目标 MAC:HAProxy 虚拟机的 MAC。

  • 内层源 IP:UE 的公网 IP。

  • 内层目标 IP:HAProxy 虚拟机的私网 IP。


在物理机 A 上,OVS 会将包从 VXLAN 隧道里面解出来,发给 HAProxy 所在的虚拟机。


HAProxy 所在的虚拟机发现 MAC 地址匹配,目标 IP 地址匹配,就根据 TCP 端口,将包发给 HAProxy 进程,因为 HAProxy 是在监听这个 TCP 端口的。


因而 HAProxy 就是这个 TCP 连接的服务端,客户端是手机。对于 TCP 的连接状态,滑动窗口等,都是在 HAProxy 上维护的。


在这里 HAProxy 是一个四层负载均衡,也即他只解析到 TCP 层,里面的 HTTP 协议他不关心,就将请求转发给后端的多个 Controller 层的一个。


HAProxy 发出去的网络包就认为 HAProxy 是客户端了,看不到手机端了,网络包格式如下:

  • 源 MAC:HAProxy 所在虚拟机的 MAC。

  • 目标 MAC:Controller 层所在虚拟机的 MAC。

  • 源 IP:HAProxy 所在虚拟机的私网 IP。

  • 目标 IP:Controller 层所在虚拟机的私网 IP。


当然这个包发出去之后,还是会被物理机上的 OVS 放入 VXLAN 隧道里面,网络包格式为:

  • 外层源 MAC:物理机 A 的 MAC。

  • 外层目标 MAC:物理机 B 的 MAC。

  • 外层源 IP:物理机 A 的 IP。

  • 外层目标 IP:物理机 B 的 IP。

  • 内层源 MAC:HAProxy 所在虚拟机的 MAC。

  • 内层目标 MAC:Controller 层所在虚拟机的 MAC。

  • 内层源 IP:HAProxy 所在虚拟机的私网 IP。

  • 内层目标 IP:Controller 层所在虚拟机的私网 IP。


在物理机 B 上,OVS 会将包从 VXLAN 隧道里面解出来,发给 Controller 层所在的虚拟机。


Controller 层所在的虚拟机发现 MAC 地址匹配,目标 IP 地址匹配,就根据 TCP 端口,将包发给 Controller 层的进程,因为他是在监听这个 TCP 端口的。


在 HAProxy 和 Controller 层之间,维护一个 TCP 的连接。Controller 层收到包之后,他是关心 HTTP 里面是什么的,于是解开 HTTP 的包,发现是一个 POST 请求,内容是下单购买一个课程。


取得真经成金身


玄奘法师终于到达西天大雷音寺,见到了我佛如来。

佛祖愿意传经给玄奘,于是让玄奘去藏经楼取经文,谁知道西天也有西天的规矩,如果不懂这里的规矩,就很难和管理经文的人沟通,取不到真经。

同理,在电商服务里面,往往在组合服务层会有一个专门管理下单的服务,Controller 层虽然对外暴露的是标准的 RESTful 协议,但是对内会通过 RPC 协议调用这个组合服务层。如果不懂这个协议,就没法通信。


假设我们使用的是 Dubbo,则 Controller 层需要读取注册中心,将下单服务的进程列表拿出来,选出一个来调用。

Dubbo 中默认的 RPC 协议是 Hessian2。Hessian2 将下单的远程调用序列化为二进制进行传输。


Netty 是一个非阻塞的基于事件的网络传输框架。Controller 层和下单服务之间,使用了 Netty 的网络传输框架。


有了 Netty,就不用自己编写复杂的异步 Socket 程序了。Netty 使用的方式,就是咱们讲 Socket 编程的时候,一个项目组支撑多个项目(IO 多路复用,从派人盯着到有事通知)这种方式。


Netty 还是工作在 Socket 这一层的,发送的网络包还是基于 TCP 的。在 TCP 的下层,还是需要封装上 IP 头和 MAC 头。


如果跨物理机通信,还是需要封装的外层的 VXLAN 隧道里面。当然底层的这些封装,Netty 都不感知,它只要做好它的异步通信即可。


在 Netty 的服务端,也即下单服务中,收到请求后,先用 Hessian2 的格式进行解压缩。然后将请求分发到线程中进行处理,在线程中,会调用下单的业务逻辑。


玄奘师徒好在后来碰到了懂得内情的注册中心——弥勒佛,从而回到灵山,还是按照人家的规矩办了,才将无字经文,换成有字经文。

下单的业务逻辑比较复杂,往往要调用基础服务层里面的库存服务、优惠券服务等,将多个服务调用完毕,才算下单成功。


下单服务调用库存服务和优惠券服务,也是通过 Dubbo 的框架,通过注册中心拿到库存服务和优惠券服务的列表,然后选一个调用。


调用的时候,统一使用 Hessian2 进行序列化,使用 Netty 进行传输,底层如果跨物理机,仍然需要通过 VXLAN 的封装和解封装。


咱们以库存为例子的时候,讲述过幂等的接口实现的问题。因为如果扣减库存,仅仅是谁调用谁减一。


这样存在的问题是,如果扣减库存因为一次调用失败,而多次调用,这里指的不是 TCP 多次重试,而是应用层调用的多次重试,就会存在库存扣减多次的情况。


这里常用的方法是,使用乐观锁(Compare and Set,简称 CAS)。CAS 要考虑三个方面,当前的库存数、预期原来的库存数和版本,以及新的库存数。


在操作之前,查询出原来的库存数和版本,真正扣减库存的时候,判断如果当前库存的值与预期原值和版本相匹配,则将库存值更新为新值,否则不做任何操作。


这是一种基于状态而非基于动作的设计,符合 REST 的架构设计原则。这样的设计有利于高并发场景。


当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其他线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。


最终,当下单更新到分布式数据库中之后,整个下单过程才算真正告一段落。

当然,这个下单调用要返回一个结果。我们下单成功啦!!!!!!


作者:刘超

编辑:陶家龙、孙淑娟

来源:转载自刘超的通俗云计算(ID:popsuper1982)微信公众号。

精彩文章推荐:

史上最污技术解读,我竟然秒懂了...

历时3年,美图全面容器化踩过的坑

小心踩雷,一次Java内存泄漏排查实战

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存