目录
SpringCloud的组件分类来讲的,主要包括:
-
分布式事务
-
注册中心
-
远程调用
-
服务保护
等几个方面
1.分布式事务
分布式事务,就是指不是在单个服务或单个数据库架构下,产生的事务,例如:
-
跨数据源的分布式事务
-
跨服务的分布式事务
-
综合情况
我们之前解决分布式事务问题是直接使用Seata框架的AT模式,但是解决分布式事务问题的方案远不止这一种。
1.1.CAP定理
解决分布式事务问题,需要一些分布式系统的基础知识作为理论指导,首先就是CAP定理。
1998年,加州大学的计算机科学家 Eric Brewer 提出,分布式系统有三个指标:
-
Consistency(一致性)
-
Availability(可用性)
-
Partition tolerance (分区容错性)
它们的第一个字母分别是 C
、A
、P
。Eric Brewer认为任何分布式系统架构方案都不可能同时满足这3个目标,这个结论就叫做 CAP 定理。
我们只可能实现CA,CP,AP模式。
1.1.1.一致性
Consistency
(一致性):用户访问分布式系统中的任意节点,得到的数据必须一致。
比如现在包含两个节点,其中的初始数据是一致的,当我们修改了一方的数据之后,我们要是的两边的数据相同,就只能进行数据同步。
1.1.2.可用性
Availability (可用性):用户访问分布式系统时,读或写操作总能成功。
只能读不能写,或者只能写不能读,或者两者都不能执行,就说明系统弱可用或不可用。
1.1.3.分区容错
Partition
,就是分区,就是当分布式系统节点之间出现网络故障导致节点之间无法通信的情况,
Tolerance
,就是容错,即便是系统出现网络分区,整个系统也要持续对外提供服务。
1.1.4.矛盾
在分布式系统中,网络不能100%保证畅通,也就是说网络分区的情况一定会存在。而我们的系统必须要持续运行,对外提供服务。所以分区容错性(P
)是硬性指标,所有分布式系统都要满足。而在设计分布式系统时要取舍的就是一致性(C
)和可用性(A
)了。
假如现在出现了网络分区,如图:
由于网络故障,当我们把数据写入node01时,可以与node02完成数据同步,但是无法同步给node03。现在有两种选择:
-
允许用户任意读写,保证可用性。但由于node03无法完成同步,就会出现数据不一致的情况。满足AP
-
不允许用户写,可以读,直到网络恢复,分区消失。这样就确保了一致性,但牺牲了可用性。满足CP
可见,在分布式系统中,A(可用性)
和C(一致性)
之间只能满足一个。因为,分区的原因,我们只能满足上面两个个性之中的一个。1.就是牺牲一致性,让两个分区度可以读写,这样也就使得两个分区数据不一致的情况,也就是取消了一致性。2.牺牲可用性,满足一致性,也就是使得两个分区不能进行写操作,只能进行读操作,当数据不再被改变时,也就满足了数据的一致性。
1.2.BASE理论
既然分布式系统要遵循CAP定理,那么问题来了,我到底是该牺牲一致性还是可用性呢?如果牺牲了一致性,出现数据不一致该怎么处理?
人们在总结系统设计经验时,最终得到了一些心得:
-
Basically Available (基本可用):分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。
-
Soft State(软状态):在一定时间内,允许出现中间状态,比如临时的不一致状态。
-
Eventually Consistent(最终一致性):虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致。
以上就是BASE理论。
简单来说,BASE理论就是一种取舍的方案,不再追求完美,而是最终达成目标。因此解决分布式事务的思想也是这样,有两个方向:
-
AP思想:各个子事务分别执行和提交,无需锁定数据。允许出现结果不一致,然后采用弥补措施恢复,实现最终一致即可。例如
AT
模式就是如此 -
CP思想:各个子事务执行后不要提交,而是等待彼此结果,然后同时提交或回滚。在这个过程中锁定资源,不允许其它人访问,数据处于不可用状态,但能保证一致性。例如
XA
模式
1.3.AT模式的脏写问题
我们先回顾一下AT模式的流程,AT模式也分为两个阶段:
第一阶段是记录数据快照,执行并提交事务:
第二阶段根据阶段一的结果来判断:
-
如果每一个分支事务都成功,则事务已经结束(因为阶段一已经提交),因此删除阶段一的快照即可
-
如果有任意分支事务失败,则需要根据快照恢复到更新前数据。然后删除快照
-
这种模式在大多数情况下(99%)并不会有什么问题,不过在极端情况下,特别是多线程并发访问AT模式的分布式事务时,有可能出现脏写问题,如图:
解决思路就是引入了全局锁的概念。在释放DB锁之前,先拿到全局锁。避免同一时刻有另外一个事务来操作当前数据。
Seata AT 模式 | Apache Seatahttps://seata.apache.org/zh-cn/docs/dev/mode/at-mode/
2.注册中心
本章主要学习Nacos中的一些特性和原理,以及与Eureka的功能对比。
2.1.环境隔离
企业实际开发中,往往会搭建多个运行环境,例如:
-
开发环境
-
测试环境
-
预发布环境
-
生产环境
这些不同环境之间的服务和数据之间需要隔离。
还有的企业中,会开发多个项目,共享nacos集群。此时,这些项目之间也需要把服务和数据隔离。
因此,Nacos提供了基于namespace
的环境隔离功能。具体的隔离层次如图所示:
说明:
-
Nacos中可以配置多个
namespace
,相互之间完全隔离。默认的namespace
名为public
-
namespace
下还可以继续分组,也就是group ,相互隔离。 默认的group是DEFAULT_GROUP
-
group
之下就是服务和配置了
2.2.分级模型
在一些大型应用中,同一个服务可以部署很多实例。而这些实例可能分布在全国各地的不同机房。由于存在地域差异,网络传输的速度会有很大不同,因此在做服务治理时需要区分不同机房的实例。
例如item-service,我们可以部署3个实例:
-
127.0.0.1:8081
-
127.0.0.1:8082
-
127.0.0.1:8083
假如这些实例分布在不同机房,例如:
-
127.0.0.1:8081,在上海机房
-
127.0.0.1:8082,在上海机房
-
127.0.0.1:8083,在杭州机房
Nacos中提供了集群(cluster
)的概念,来对应不同机房。也就是说,一个服务(service
)下可以有很多集群(cluster
),而一个集群(cluster
)中下又可以包含很多实例(instance
)。
因此,结合我们上一节学习的namespace
命名空间的知识,任何一个微服务的实例在注册到Nacos时,都会生成以下几个信息,用来确认当前实例的身份,从外到内依次是:
-
namespace:命名空间
-
group:分组
-
service:服务名
-
cluster:集群
-
instance:实例,包含ip和端口
这就是nacos中的服务分级模型。
在Nacos内部会有一个服务实例的注册表,是基于Map实现的,其结构与分级模型的对应关系如下:
2.3.Eureka
Eureka是Netflix公司开源的一个服务注册中心组件,早期版本的SpringCloud都是使用Eureka作为注册中心。由于Eureka和Nacos的starter中提供的功能都是基于SpringCloudCommon规范,因此两者使用起来差别不大。Eureka服务端要自己创建项目,但是Nacos服务端不需要自己创建项目,只需要上传至虚拟机就好。
2.4.Eureka和Nacos对比
Eureka和Nacos都能起到注册中心的作用,用法基本类似。但还是有一些区别的,例如:
-
Nacos支持配置管理,而Eureka则不支持。
而且服务注册发现上也有区别,我们来做一个实验:
我们停止user-service
服务,然后观察Eureka控制台,你会发现很长一段时间过去后,Eureka服务依然没有察觉user-service
的异常状态。
这与Eureka的健康检测机制有关。在Eureka中,健康检测的原理如下:
-
微服务启动时注册信息到Eureka,这点与Nacos一致。
-
微服务每隔30秒向Eureka发送心跳请求,报告自己的健康状态。Nacos中默认是5秒一次。
-
Eureka如果90秒未收到心跳,则认为服务疑似故障,可能被剔除。Nacos中则是15秒超时,30秒剔除。
-
Eureka如果发现超过85%比例的服务都心跳异常,会认为是自己的网络异常,暂停剔除服务的功能。
-
Eureka每隔60秒执行一次服务检测和清理任务;Nacos是每隔5秒执行一次。
综上,你会发现Eureka是尽量不剔除服务,避免“误杀”,宁可放过一千,也不错杀一个。这就导致当服务真的出现故障时,迟迟不会被剔除,给服务的调用者带来困扰。
不仅如此,当Eureka发现服务宕机并从服务列表中剔除以后,并不会将服务列表的变更消息推送给所有微服务。而是等待微服务自己来拉取时发现服务列表的变化。而微服务每隔30秒才会去Eureka更新一次服务列表,进一步推迟了服务宕机时被发现的时间。
而Nacos中微服务除了自己定时去Nacos中拉取服务列表以外,Nacos还会在服务列表变更时主动推送最新的服务列表给所有的订阅者。
综上,Eureka和Nacos的相似点有:
-
都支持服务注册发现功能
-
都有基于心跳的健康监测功能
-
都支持集群,集群间数据同步默认是AP模式,即最全高可用性
Eureka和Nacos的区别有:
-
Eureka的心跳是30秒一次,Nacos则是5秒一次
-
Eureka如果90秒未收到心跳,则认为服务疑似故障,可能被剔除。Nacos中则是15秒超时,30秒剔除。
-
Eureka每隔60秒执行一次服务检测和清理任务;Nacos是每隔5秒执行一次。
-
Eureka只能等微服务自己每隔30秒更新一次服务列表;Nacos即有定时更新,也有在服务变更时的广播推送
-
Eureka仅有注册中心功能,而Nacos同时支持注册中心、配置管理
-
Eureka和Nacos都支持集群,而且默认都是AP模式
3.远程调用
我们知道微服务间远程调用都是有OpenFeign帮我们完成的,甚至帮我们实现了服务列表之间的负载均衡。但具体负载均衡的规则是什么呢?何时做的负载均衡呢?
接下来我们一起来分析一下。
3.1.负载均衡原理
在SpringCloud的早期版本中,负载均衡都是有Netflix公司开源的Ribbon组件来实现的,甚至Ribbon被直接集成到了Eureka-client和Nacos-Discovery中。
但是自SpringCloud2020版本开始,已经弃用Ribbon,改用Spring自己开源的Spring Cloud LoadBalancer了,我们使用的OpenFeign的也已经与其整合。
接下来我们就通过源码分析,来看看OpenFeign底层是如何实现负载均衡功能的。