计算机网络协议——墙都不服,就服你系列
DDB:Distributed date base,分布式数据库
RDS:Relational Datebase Service,关系型数据库服务
CDN:Content Delivery Network,内容分发网络
VPC:Virtual Private Cloud,虚拟私有云。
云端数据中心
Subnet:Subnet 子网。网络的一部分。它是物理上独立的网络段,与网络其它部分共享网络地址,并用子网号区分。
可用区(Available Zone):稳定性,可扩展性
可用区:机柜=1:n
机柜:服务器=1:n
接入层交换机:访问服务器权限的管理
汇聚层交换机:
核心交换机:
组织严格,层次分明
每一层都是分组看护,互相监督,互相备份,称为堆叠。
VPC:Virtual Private Cloud,虚拟私有云
防止信息被盗
Openvswitch的虚拟交换机:信息转换的作用。
信息在虚拟私密空间和物理空间之间的转换作用
Openvswitch如何转换呢?使用的是一种称为VXLAN的封装技术,但是必须要事先知道芝麻开门的ID,也即VXLAN ID,才能看到信息的真正内容。
在虚拟的空间中,放着真正可以解读的信息。
微服务架构:
前中后台分离,分为基础服务层,组合服务层,Controller层。
N个模块,N*M个服务
RPC框架、服务注册与发现中心:
索引和指南
在大量的信息中,查询需要的信息
负载均衡:
nginx
统一的入口地址:获取信息的入口
信息多,信息请求多=》负载均衡,将准确的信息给需要的人
所在的网络:
是一个世界知名的地址,称为外网IP地址,这个地址是全球可定位的
通过NAT规则,将外网IP地址,变成内网的私有IP地址
BGP协议:Border Gateway Protocol,边界网关协议
对于多个可用区的情况,我们可以隐去计算节点的情况,将外网访问区域放大
外网IP是放在虚拟网关的外网网口上的,这个IP如何让全世界知道呢?
在核心交换外面是安全设备,然后就是边界路由器。
边界路由器会和多个运营商连接,从而每个运营商都能够访问到这个网站。
边界路由器可以通过BGP协议,将自己数据中心里面的外网IP向外广播,也就是告诉全世界,如果要访问这些外网IP,都来我这里。
每个运营商也有很多的路由器、很多的点,于是就可以将如何到达这些IP地址的路由信息,广播到全国乃至全世界。
CDN模式:Content Delivery Network,内容分发网络
是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率
关键技术主要有内容存储和分发技术
部署应用的时候,一般会把静态资源保存在两个地方(这两个地方的静态资源都会配置CDN,将资源下发到边缘节点)
一个是nginx后面的varnish缓存里面,一般是静态页面
对于比较大的、不经常更新的静态图片,会保存在对象存储里面
配置了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解析机制,直接只要HTTPDNS服务,
通过直接调用HTTPDNS服务器,得到这三个本地负载均衡的公网IP地址。
客户端证书:证明客户端的合法性
客户端对服务端发起请求:
应用层请求
应用层之下的TCP:防止公网的丢包
请求是应用层的,发起的是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层发送的就是一个请求。
HTTP的报文大概分为三大部分。
第一部分是请求行:
URL就是 www.geektime.com/purchaseOrder ,
版本为HTTP 1.1。
请求的类型叫作POST,它需要主动告诉服务端一些信息,而非获取。需要告诉服务端什么呢?一般会放在正文里面。正文可以有各种各样的格式,常见的格式是JSON。
第二部分是请求的首部:
首部是key value,通过冒号分隔
Content-Type是指正文的格式。例如,我们进行POST的请求,如果正文是JSON,那么我们就应该将这个值设置为JSON。
第三部分才是请求的正文实体。
这里是一个JSON字符串,里面通过文本的形式描述了,要买一个课程,作者是谁,多少钱。
1 2 3 4 5 6 7 8 9 10 11 12 13 | POST / purchaseOrder HTTP / 1.1 Host: www.geektime.com Content - Type : application / json; charset = utf - 8 Content - Length: nnn { "order" : { "date" : "2018-07-01" , "className" : "趣谈网络协议" , "Author" : "刘超" , "price" : "68" } } |
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地址。
在手机运营商的网络里面,网络状况是比较好的。
NAT:Network Address Translation,网络地址转换
到达边界路由器=>开始NAT=>使用公网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地址)。路过宝地,借宿一晚,明日启行,请问接下来该怎么走啊?(中转,询问路由)
在解决第一个问题的时候,每个城关已经通过菩萨的法术,和邻近的城关进行沟通,知道了下面的信息。
路由表:根据这个表格,可以告诉唐僧怎么走
SLB: Server Load Balancing,服务器负载均衡
出了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
在虚拟网关节点的外网网口上,会有一个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。
在虚拟路由节点上,也会有OVS,将网络包封装在VXLAN隧道里面,VXLAN ID就是给你的租户创建VPC的时候分配的。
VXLAN ID就是VPC虚拟空间的ID,
OVS就是那个能够封装和解开私密空间的法宝。
例如:在第一部分,我们 说佛经是存放在一个虚拟空间里面的,要打开这个虚拟空间,解读经文,需要一个芝麻开门的ID。接引佛祖会给玄奘法师一个ID。
包的格式为:
-
- 外层源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层
RPC协议:
在电商服务里面,往往在组合服务层会有一个专门管理下单的服务,
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的格式进行解压缩。
然后将请求分发到线程中进行处理,在线程中,会调用下单的业务逻辑。
懂得内情的注册中心ZK:
容错:服务器请求出错
找到门路:懂得服务方的规矩
.proto文件
下单的业务逻辑比较复杂,往往要调用基础服务层里面的库存服务、优惠券服务等,将多个服务调用完毕,才算下单成功。
下单服务调用库存服务和优惠券服务,也是通过Dubbo的框架,通过注册中心拿到库存服务和优惠券服务的列表,然后选一个调用。
调用的时候,统一使用Hessian2进行序列化,使用Netty进行传输,底层如果跨物理机,仍然需要通过VXLAN的封装和解封装。
以库存为例子的时候,讲述过幂等的接口实现的问题。
因为如果扣减库存,仅仅是谁调用谁减一。这样存在的问题是,如果扣减库存因为一次调用失败,而多次调用,这里指的不是TCP多次重试,而是应用层调用的多次重试,就会存在库存扣减多次的情况。
这里常用的方法是,使用乐观锁(Compare and Set,简称CAS)。
CAS要考虑三个方面,当前的库存数、预期原来的库存数和版本,以及新的库存数。
在操作之前,查询出原来的库存数和版本,真正扣减库存的时候,判断如果当前库存的值与预期原值和版本相匹配,则将库存值更新为新值,否则不做任何操作。
这是一种基于状态而非基于动作的设计,符合REST的架构设计原则。这样的设计有利于高并发场景。
当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。
最终,当下单更新到分布式数据库中之后,整个下单过程才算真正告一段落。
当然,这个下单调用要返回一个结果:我们下单成功啦!!!!!!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· 面试官:你是如何进行SQL调优的?