前言
随着互联网的发展,对于后端服务的架构也需要随之进行改变。从传统的单体架构到服务化架构再到微服务架构,架构在不断的演变,在解决问题的同同时也带来了新的问题。在微服务架构下,服务之间的调用通过网络带宽进行数据的交流,在这个背景下由于网络的不稳定加上微服务间是自行管理自己的数据,所以会出现不一致的问题。本文针对分布式系统下微服务间的不一致性进行分析,并提供解决方案。
该内容是阅读《分布式服务架构--原理、设计与实战》的读后总结笔记
一、什么是分布式系统的一致性
为了满足需求,让后端服务更好的为用户提供服务,这时候就需要将我们的服务进行拆分,拆分又分为水平拆分和垂直拆分。
- 水平拆分:由于一个节点无法满足性能需求,于是将一个节点扩展为多个具有一致功能的节点,这些节点共同分担任务
- 垂直拆分:一个节点处理的功能逻辑比较多,于是将这个功能逻辑进行拆分,多个节点每个节点完成这个功能逻辑的一部分。这样就是的每个节点的功能职责单一、简单
由于将一个系统拆分了多个系统,我们要使得多个系统之间的信息、工作进度、状态一致并协调有序的工作。这就是分布式系统饿一致性问题。
1.1 分布式系统一致性问题的案例
- 转账问题:A转账给B,A扣款成功,但是B账户加钱失败
- 同步调用超时问题:A服务调用B服务,但是由于网络超时A接受到超时错误,但是此时B并不知道超时,还继续处理任务,就导致A和B的不一致性
- 掉单:在分布式系统中,两个系统协调处理一个流程,分别为对方的上下游,如果一个系统中存在一个请求(通常是指订单)但是另外一个系统中不存在,则会导致掉单
- 缓存和数据库的不一致、本地缓存和分布式缓存以及数据库状态不一致
二、酸碱平衡理论
什么是酸碱平衡理论,这个理论是如何解决分布式环境下的不一致性问题的
2.1 ‘酸’理论 ACID
ACID主要是通过强一致性来解决不一致性的问题
- A:原子性
- C:一致性
- I:隔离性
- D:持久性
强一致性是指服务会从一个明确的状态变化到另外一个明确的状态,不存在中间的临时状态。
2.2 CAP (帽子理论)
- C:一致性。在分布式系统中所有数据备份,在同一时刻具有同样的值,所有节点在同一时刻读到的数据都应该是最新的数据副本
- A:可用性。在任何故障模型下,服务器都会在有限的时间内处理完成并进行响应
- P:分区容忍性。尽管网络上有部分消息丢失,但是系统仍然可以继续工作
CAP原理证明,任何分布式系统只可同时满足以上两点,无法三者兼顾
2.3 BASE理论(碱)
BASE模型包含如下三个元素
- BA:基本可用
- S:soft软状态,状态可以在一段时间内不同步
- E:状态最终达到一致即可
BASE理论是相对于ACID理论的,它通过牺牲强一致性来达到可用性和最终一致性
三、分布式一致性协议
3.1 两阶段提交协议
两阶段提交协议是将分布式事务分为两个阶段,一个阶段是准备阶段,第二个阶段是提交阶段
其中协调者为资源管理器,参与者为资源管理器。
两阶段提交协议的缺点:
- 阻塞:对于每一个操作都需要收到明确的响应才会进行下一步,否则阻塞,占用的资源一直被锁定
- 单点故障:如果协调者宕机,参与者没有协调者指挥则会一直阻塞
- 脑裂:协调者发送事务给参与者,有的参与者接受到事务并执行,有的参与者没有接收到则没有执行,这就导致不一致情况的发生
3.2 三阶段提交协议
三阶段提交和两阶段提交的区别在于三阶段提交增加了一个询问步骤,这样可以防止参与者本来服务就不可用导致阻塞的问题,然后准备阶段增加了超时,如果参与者超时则默认成功,并提交事务。较于两阶段提交三阶段提交不容易阻塞。
3.3 TCC协议
TCC协议将事务分为三个阶段try、confirm、cancel。其实类似于两阶段提交协议,但是增加了一个cancel操作,在提交失败之后提供一个cancel的回滚操作。但是当cancel命令发送的时候如果有的节点接收到命令,有的节点没有接收到命令,这种情况导致的不一致性问题,TCC协议会先尝试进行修复,如果修复不了则最终需要人工修复。
四、一致性问题的解决方案
通过上面的分析,分布式环境下的一致性问题我们可以通过一致性协议来实现强一致性,或者是使用酸碱理论来实现最终一致性。要实现强一致性,则需要使用到两阶段提交、三阶段提交和TCC。实现最终一致性有一些非常有效、简单的模式,下面就介绍这些模式及其应用场景。
- 查询模式:每一个服务都应该有一个查询接口用来向外输出操作执行的状态,服务的调用者可以通过查询接口得知服务操作执行的状态
- 补偿模式:当服务之间的调用发生超时的时候,由于调用方并不知道执行方是否接收到请求并执行了操作,所以需要调用查询模式提供的查询接口,如果状态不一致,则根据执行方的状态来选择是继续完成为完成的操作还是将已经完成的操作进行回滚,来达到最终一致性
- 异步确保模式:通过异步通知的方式来达到最终一致,这种方式解决了高峰时期系统的压力。比如A调用B,请求之后立刻返回,B将任务执行成功之后通过异步回调的方式通知A任务已执行完成。如果通知为任务执行失败,则A调用B的回滚接口
- 定时校对模式:不适用异步确保模式或者异步通知的情况也有可能出现异步调用超时的情况,可以使用定时任务定期扫描数据库,发现处于异常状态的数据则进行处理
- 可靠消息模式:在同步调用的时候容易出现调用超时,导致分布式环境下的不一致性的问题。通过可靠消息的方式充分解耦客户端和服务端,同时需要保证消息发送的可靠性和消息消费的可靠性
五、超时处理模式
分布式环境下的不一致性主要的原因就是网络超时导致服务之间的状态不能相互通知,处理好网络超时异常发生时的数据,然后再结合上面的实现最终一致性的模式,就可以达到我们分布式系统的最终一致性。
网络调用方式分为同步调用、异步调用以及基于消息队列的异步调用。下面就通过分析不同情况下的网络调用超时,以及超时之后的解决方案。
5.1 同步调用超时
同步调用的情况下,请求者和执行者是通过定义接口的响应报文来通信的,接口的返回通常分为如下两种情况:
- 两种状态:成功、失败
- 三种状态:成功、失败、处理中
5.1.1 两种状态下的同步调用超时
第一种情况就是客户调用服务器的时候发生了超时,这是时候由于不知道服务1是否已经接收到请求并已经在处理请求了,则可以调用服务1提供的查询接口,查询执行的状态,如果查询到执行状态为成功则继续在执行下面的操作,如果查询的结果是执行失败则需要重新请求。重新请求的情况需要注意,也有可能查询请求到达时第一个请求还没有到达,这种情况下重试就导致多次请求,这时就需要保证服务1的幂等性。
这个超时发生在服务1调用服务2的情况,这种情况由于服务1对外的接口只有成功或者失败,则这里必须使用快速失败策略,同时调用服务2的冲正接口,服务2的冲正接口判断开始时是否接收到请求并在接收到请求后回滚。
5.1.1 三种状态的同步调用超时
这种情况跟两种状态的时候一样,需要调用服务1的查询接口,根据响应的状态来判断后面需要执行的操作。
服务1调用服务2的时候超时,这时候两种状态和三种状态的就不一样,三种状态的时候可以给客户段返回一个处理中的状态,然后通过定时任务去补偿数据。
5.2 异步调用超时
5.2.1 发送同步请求超时
这种情况的超时需要通过服务1的查询接口来补齐状态,确保服务1已经接收到请求
5.2.2 异步调用内部超时
由于这里使用的是异步任务的方式,所以对于响应时间没有要求,服务1需要调用服务2的查询接口查询状态,然后进行补偿。
5.2.3 第三种情况就是在异步通知的时候超时,这种情况可以使用重试的方式
5.3 消息队列超时
这点就需要去研究消息队列如何保证消息的可靠性了。
总结
本文通过说明什么是分布式环境下的一致性,已经分布式环境下的酸碱理论说明达到分布式环境的一致性需要根据自己的需求是需要酸-强一致性还是碱-最终一致性,这样酸碱结合。然后阐述了分布式环境下不一致情况的解决方:查询法、补偿法等。以及在分布式环境下超时调用问题是产生不一致问题的主要原因,以及在调用链中产生了超时之后如何处理来保证一致性。最终是推荐使用三种状态的方法,保存一个中间状态来达到最终的一致性。