用双十一的故事串起碎片的网络协议

1 部署一个高可用高并发的电商网站

首先,需要有个电商平台,假设特别大,这个平台应该部署在哪呢?假如使用公有云,公有云有多个位置,我们把主站点放在华东。
用双十一的故事串起碎片的网络协议
为了每个点都能雨露均沾,也为了高可用性,往往需要有多个机房,形成多个可用区。由于咱们的应用是在两个区的,所以假如任何一个可用区挂了,都不会受影响。
我们来回想数据中心那一节,每个可用区里都有一片片的机柜,每个机柜上都有一排排的服务器,每个机柜都有一个接入交换机,有一个汇聚交换机将多个机柜连在一起。
这些服务器里面部署的都是计算节点,每台上面都有Open vSwitch 创建的虚拟机交换机,将来这台机器上创建的虚拟机,都会连接到Open vSwitch上。
用双十一的故事串起碎片的网络协议
你在云计算的界面上创建了一个VPC(虚拟私有网络),指定一个IP端,这样以后你部署的所有应用都会在这个虚拟网络里,使用你分配的这个IP段。为了不同的VPC相互隔离,每个VPC都会被分配一个VXLAN的ID。尽管不同用户的虚拟机有可能在同一个物理机上,但是不同的VPC压根是不通的。
由于有两个可用区,在这个VPC里面,要为每个可用区分配一个Subnet,也就是在大的网段里分配两个小的网段,当两个可用区里面网段不同的时候,就可以配置路由策略,访问另外一个可用区,走某一条路由了。

接下来,应该创建数据持久层。大部分云平台都会提供PaaS服务,也就是说,不需要你自己搭建数据库,而是采用直接提供数据库的服务,并且单机房的主备切换都是默认做好的,数据库也是在虚拟机里的,只不过从界面上看,你看不到数据库所在的虚拟机而已。
云平台会给每个Subnet的数据库实例分配一个域名。创建数据库实例的时候,需要制定可用区和Subnet,这样创建出来的数据库实例就可以通过这个Subnet的私网IP进行访问。
为了分库分表实现高并发的读写,在创建的多个数据库实例之上,会创建一个分布式数据库的实例,也需要制定可用区和Subnet,还会为分布式数据库分配一个私网IP和域名。
对于数据库这种高可用性比较高的,需要进行跨机房高可用,因而两个可用区都要部署一套,但是只能有一个是主,一个是备,云平台往往会根据提供的数据库同步工具,将应用的数据库同步给备数据库集群。
接下来是创建缓存集群。云平台也提供PaaS服务,也需要每个可用区和Subnet创建一套,由于读写性能要求高,一般不要求跨可用区读写。
在往上层就是不熟咱们自己的写的程序了。基础服务层、组合服务层、Controller层,以及Nginx层,API网关等等。这些都是部署在虚拟机里面的。她们之间通过RPC互相调用,需要到注册中心进行注册。
它们之间的网络通信是虚拟机与虚拟机之间的。如果是同一台物理机,则那台物理机上的OVS就能转发过去;如果是不同的物理机,这台物理机的OVS和另外一台物理机的OVS中间有一个VXLAN 的隧道,将请求转发过去。
再往外就是负载均衡了,负载均衡也是云平台提供的PaaS服务,也是属于某个VPC的,部署在虚拟虚拟机里面的,但是负载均衡有个外网的IP,这个外网地址就是在网关节点的外网网口上的。在网关节点上,会有NAT规则,将外网的IP转换为VPC里面私网的IP地址,通过这些私网IP地址,访问到的虚拟机上的负载均衡节点,然后通过负载均衡节点转发到API网关的节点。
网关节点的外网网口是带公网IP地址的,里面有一个虚拟网关转发模块,还会有一个OVS,将私网IP地址放到VXLAN隧道里面,转发到虚拟机上,从而实现外网和虚拟机网络之间的互通。
不同的可用区之间,通过核心交换机连在一起,核心交换机之外是边界路由器。
在华北、华东、华南同样也部署一套,每个地区都创建了VPC,这就通过有一种机制将VPC连接在一起。云平台一般会提供硬件的VPC互联的方式,当然也可以使用软件互联的方式,也就是使用v*n网关,通过IPsec v*n 将不同地区的不同VPC通过v*n连接起来。
对于不同地区和不同运营商的用户,我们希望他能够就近访问到网站,而且当一嗯嗯点出了故障之后,我们希望能够在不同的地区之间切换,这就需要有只能DNS,这个也是云平台提供的。对于一些静态资源,可以保护在对象存储里面,通过CDN下发到边缘节点,这样客户端就能尽快加载出来。

2 大声告诉全世界,可以到我这里买东西

当电商应用搭建完毕之后,接下来需要将如何访问到这个电商网站广播给全网。刚才那张图画的是一个可用区的情况,对于多个可用区的情况,我们可以隐去计算节点的情况,将外网访问区域扩大。
外网IP是放在虚拟网关的外网网口上的,这个IP如何让全世界知道呢?当然是通过BGP路由协议了。
每个可用区都有自己的汇聚交换机,如果机器数目比较多,可以直接用核心交换机,每个Region也有自己的核心交换区域。
在核心交换机外边是安全设备,然后就是边界路由器。边界路由器会和多个运营商连接,从而每个运营商都能够访问到这个网站。边界路由器可以通过BGP协议,将自己的数据中心里面的外网IP向外广播,也就是告诉全世界,如果要访问这些IP,都来我这里。
每个运营商也有很多的路由器,很多的点,于是就可以将如何到达这些IP地址的路由信息,广播到全国乃至全世界。

3 打开手机来上网,域名解析得地址

这个时候,不但你的网站IP地址全知道了,你打的广告可能大家也都看到了,于是又客户下载App来买东西了。
用双十一的故事串起碎片的网络协议
客户的手机开机后,在附近寻找基站eNodeB,发送请求,申请上网。基站将请求发给MME,MME对手机进行认证和授权,还会请求HSS看有没有钱,看看是在哪里上网。当MME通过了手机的认证之后,开始建立隧道,建设的数据通路分两段路,其实是两个隧道。一段是从eNodeB到SGW,第二段是从SGW到PGW,在PGW之外,就是互联网。PGW会为手机分配一个IP地址,手机上网都是带着这个IP地址的。
当在手机上面打开一个APP的时候,首先要做的事情就是解析这个网站的域名。
在手机运营商所在的互联网里,有一个本地DNS,手机会向这个DNS请求解析DNS,这个DNS本地有缓存,则直接返回,如果没有缓存,本地DNS需要递归的从根DNS服务器,查到.com的顶级域名服务器,最终查到权威DNS。
在你使用云平台的时候,配置了智能DNS和全局负载均衡,在权威DNS服务中,一般是通过配置CNAME的方式,我们可以起一个别名,例如,vip.yourcompany.com,然后告诉本地DNS服务器,让它请求GSLB解析这个域名,GSLB就可以在解析这个域名的过程中,通过自己的策略实现负载均衡。
GSLB通过查看请求它的本地DNS服务器所在的运营商和地址,就知道用户所在的运营商和地址,然后将距离用户位置比较近的Region里面,三个负载均衡SLB的公网IP地址,返回给本地DNS服务器。本地DNS解析器将结果缓存后,返回给客户端。
对于手机APP来说,可以绕过刚才传统的DNS解析机制,直接只要HTTPDNS服务,通过直接调用本地HTTPDNS服务器,得到这三个SLB公网的IP地址。

4 购物之前看图片,静态资源CDN

客户想要在购物网站买一件东西的时候,一般是先去详情页看看图片,是不是想买的那一款。
用双十一的故事串起碎片的网络协议
我们再部署电商应用的时候,一般会把静态资源保存在两个地方,一个是接入层nginx后面的varnish缓存里面,一般是静态页面;对于比较大的,不经常更新的静态图片,会保存在对象存储里面。这两个地方的静态资源都会配置CDN,将资源下发到边缘节点。
配置了CDN之后,权威DNS服务器上,会为一个静态资源设置一个CNAME别名,执行另外一个域名CDN.com,返回给本地的DNS服务器。
当本地DNS服务器拿到这个新的域名时,需要继续解析新的域名。这个时候,再访问的时候就不是原来的权威DNS服务器了,而是cdn.com的权威服务器了。这是CDN自己的权威服务器。
在这个服务器上,还是会设置一个CNAME,指向另外一个域名,也即CDN网络的全局负载均衡器。
本地DNS服务器去请求CDN的全局负载均衡器解析域名,全局负载均衡会为用户选择一台合适的缓存服务器提供服务,将IP返回给客户端,客户端去访问这个边缘节点,下载资源,缓存服务器相应用户请求,将用户所需内容传送到用户终端。
如果这台缓存服务器上没有这个用户想要的内容,那么这台服务器就要向它的上一级缓存服务器请求内容,直至追溯到网站的源服务器,将内容拉到本地。

5 看上宝贝点下单 双方开始建连接

用双十一的故事串起碎片的网络协议
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之后,处于ESTABLISTHED状态,因为它一发一收成功了。
当TCP层的链接建立完毕后,接下来轮到HTTPS层建立连接了,在HTTPS的交换过程中,TCP层始终处于ESTABLISHED。
对于HTTPS,客户端会发送Client Hello 消息到服务器,用明文传输TLS版本信息,加密套件候选列表,压缩算法候选列表等信息。另外,还会有一个随机数,在协商对称秘钥的时候使用。
然后服务器会返回一个Service Hello消息,告诉客户端,服务器选择使用的协议版本,加密套件,压缩算法等,这也有一个随机数,用户后续的秘钥协商。
然后,服务器会给一个服务器端的证书,然后说:”Service Hello Done,我这里就这些信息了。”
客户端当然不相信这个证书,于是从自己信任的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消息试试。
当双方握手结束之后,就可以通过对称秘钥进行加密传输了。

6 发送下单请求网络包,西行需要出网关

当客户端和服务端之间建立了连接之后,接下来就要发送下单请求的网络包了。
在用户层发送的是HTTP网络包,因为服务端提供的是RESTFUL API,因而HTTP层发送的就是一个请求。
用双十一的故事串起碎片的网络协议
HTTP报文大概分为三大部分。第一部分请求行,第二部分请求的首部,第三部分才是请求的正文实体。
在请求行中,URL是 www.geektime.com/purchaseOrder ,HTTP版本为1.1
请求的类型叫POST,它需要主动告诉服务端一些信息,而非获取,需要告诉服务端什么呢?一般会放在正文里,正文里有各种格式,常见是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头,发送出去,一个完整的网络包的格式是
用双十一的故事串起碎片的网络协议

7 一座座城池一道道关,流量拥塞与重传

对于手机来讲,默认的网关在PGW上,在移动网络里面,从手机到SGW,到PGW是有一条隧道的。在这条隧道里面,会将上面的这个包作为隧道的乘客协议放在里面,外面SGW和PGW在核心网机房的IP地址。网络包知道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网关,就从核心网到达了互联网。在网络世界里,每个运营商的网络成为自治系统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,将目标地址设置为C2的MAC地址,达到C2后,找到下一跳是云平台的边界路由器,于是将目标MAC设置为边界路由器的MAC地址。
    这一路,都是只换MAC,不换目标IP地址。
    在云平台的边界路由器,会将下单的包转发进来,经过核心交换、汇聚交换、到达外网网关节点的SLB的公网IP地址。
    可以看到,手机到SLB的公网IP,是一个端到端的链接,链接过程中,发了很多包,所有的这些包,无论是TCP三次握手,还是HTTP的秘钥交换,都是要走如此复杂的过程到达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需要实现幂等。
    当ACK过的包交给应用层之后,TCP的缓存就空了出来,就会导致途中的大三角,也即接收方能容纳的总缓存,整体顺时针滑动,小的三角形,也即接收方告知发送方的窗口总大小,也即还没有完全确认收到的缓存大小,如果把这些填满了,就不能再发了,因为没确认收到,所以一个都不能扔。

8 从数据中心进网关,公网NAT成私网

包从手机端经历千难万险,终于到了SLB的公网IP所在的公网端口,由于匹配上了MAC地址和IP地址,因而将网络包收进来。
用双十一的故事串起碎片的网络协议
在虚拟网关的节点的外网网口上,会有一个NAT规则,将公网IP转为VPC里面的私网IP地址,这个私网IP地址就是SLB的HAProxy所在的虚拟机私网IP地址。
当然为了承载比较大的吞吐量,虚拟网关节点会有多个,物理网络会将流量分发到不同的虚拟网关节点。同样HAProxy也会是一个大的集群,虚拟网关会选择某个负载均衡节点,将某个请求分发给它,负载均衡之后是Controller 层,也是部署在虚拟机里面的。
当网络包里面的目标IP编程私有IP之后,虚拟路由会查找路由规则,将网络包从下方的私网网口发出来。这个时候包的格式为

  • 源MAC:网关MAC;
  • 目标MAC:HAProxy虚拟机的MAC;
  • 源IP:UE的公网IP;
  • 目标IP:HAProxy虚拟机的私网IP。

9 进入隧道打标签,RPC远程调用下单

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

  • 外层源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请求,内容是下单购买一个课程。

10 下单扣减库存优惠券,数据入库,返回成功

下单时一个复杂的过程,因而在组合服务层有一个专门管理下单的服务,Controller会通过RPC调用这个组合服务层。
假如我们使用的是Dubbo, 则Controller层需要读取注册中心,将下单服务的进程列表拿出来,选出一个来调用。Dubbo中默认的RPC协议是Hessian2。Hessian2将下单的远程调用序列化为二进制进行传输。
Netty是一个非阻塞的基于时间的网络传输框架。Controller层和下单服务之间,使用了Netty网络传输框架。有了Netty,就不用自己编写复杂的异步Socket程序了。Netty使用的方式,就是IO多路复用,有事通知的方式。
Netty还是工作在Socket这个一层的,发送的网络包还是基于TCP的。在TCP的下层,还是需要封装上IP头和MAC头。如果跨物理机访问,还是需要封装在VXLAN隧道里面,底层的这些封装,Netty都不感知,只需要做好异步通信就好了。
在Netty的服务端,就是下单服务中,收到请求后,先用Hessian2的格式进行解压缩,然后将请求分发到线程里进行处理,在线程中,会调用下单的业务。
下单的业务逻辑比较负载,也是通过Dubbo从注册中心拿服务列表,然后选一个调用。
调用的时候,统一用Hessian2序列化,使用Netty进行网络传输,如果跨物理机,仍然需要VXLAN的封装和解封装。
以库存为例,幂等接口的实现问题。因为如果扣减库存,仅仅是谁调用谁减一,存在的问题是如果扣库存因为一次调用失败,而多次调用,这里指的不是TCP多次重试,而是应用层调用的多次重试,就会存在扣减多次的情况,这里经常用乐观锁的方式,CAS要考虑到当前的库存数,预期原来的库存数和版本,以及新的库存数,在操作之前,查询出原来的库存数和版本,真正扣减库存的时候,判断如果库存的值和预期原值,和版本相匹配,则更新为新值,否则不做操作。
最终,下单更新到分布式数据库中之后,整个下单过程才算是真正高一段落。