服务系统数据的一致性:可靠事件

简介:

640?wx_fmt=png&wxfrom=5&wx_lazy=1


微服务架构解决了很多问题,但是同时引入了很多问题。设计到系统,其中绕不开的就是数据一致性,从本地事务,到后来的分布式事务,都能够有效的保证数据一致性。但是在微服务架构中,这两种方式都不是最好的选择。


场景:话费充值业务


1.用户进入便民中心进入话费充值 页面,输入电话号码、选择面值;

2.购买话费充值商品,有库存限制则判断库存,生成充值购买订单;

3.选择对应的支付方式(银联、支付宝、微信)进行支付操作;

4.支付成功后,近实时话费到账即可显示账户可用余额;


此业务流程看似不是很复杂对吧,是虚拟业务线的流程,和实体最大的差别在物流发货流程,当消费者点击购买按钮时,交易后台会进行库存检查、下单、减库存、更新订单状态等一连串的服务调用,每一个操作对应一个独立服务,不同服务一般会有独立的数据库,因此会产生分布式事务的问题。

 

电商社区——话费充值中心,核心业务流程为:


640?wx_fmt=png&wxfrom=5&wx_lazy=1


1、使用本地事务和分布式事务保证一致性


在传统的应用中,最简单、最直接、最普遍的会使用一个关系型数据库,通过关系型数据库的事务保证数据的一致性。这种事务有四个基本要素ACID

 

A(Atomicity,原子性)整个事务中的所有操作,要么全部完成,要么全部失败,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。


C(Consistency,一致性)一个事务可以封装状态改变(除非它是一个只读的)。事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少。


I(Isolation,隔离性)隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。


D(Durability,持久性)在事务完成以后,该事务对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。


在传统的本地事务中,为了保证数据一致性,我们只需要先开始一个事务,然后进行新增、修改、删除等操作,然后提交事务,如果发生异常就回滚。


640?wx_fmt=png&wxfrom=5&wx_lazy=1

随着组织规模不断扩大、业务量不断增加,单机应用已不足以支撑庞大的业务量和数据量。这个需要对应用和数据进行拆分。就出现了需要同时访问多个数据库的情况。这个时候就需要分布式事务来保证数据一致性,也就是常说的两阶段提交协议(2PC,Two Phase Commitment Protocol)。


在这个协议中,最关键的点就是,多个数据库的活动,均由一个事务协调器的组件来控制。具体的分为5个步骤:


1. 应用程序调用事务协调器中的提交方法


2. 事务协调器将联络事务中涉及的每个数据库,并通知它们准备提交事务(这是第一阶段的开始)


3. 接收到准备提交事务通知后,数据库必须确保能在被要求提交事务时提交事务,或在被要求回滚事务时回滚事务。

如果数据库无法准备事务,它会以一个否定响应来回应事务协调器。


4. 事务协调器收集来自各数据库的所有响应。


5. 在第二阶段,事务协调器将事务的结果通知给每个数据库。

如果任一数据库做出否定响应,则事务协调器会将一个回滚命令发送给事务中涉及的所有数据库。如果数据库都做出肯定响应,则事务协调器会指示所有的资源管理器提交事务。一旦通知数据库提交,此后的事务就不能失败了。通过以肯定的方式响应第一阶段,每个资源管理器均已确保,如果以后通知它提交事务,则事务不会失败。

640?wx_fmt=png&wxfrom=5&wx_lazy=1

在传统的系统架构中,通常使用的是数据库来作为资源管理器,数据的一致性通过事务来保证,即使实在分布式事务中,也能够利用数据库的事务来实现数据一致性。


但是在微服务架构中,数据访问变得复杂。


通常情况下,数据都是各个微服务私有的,只能通过API的方式访问数据。


这种方式可以实现微服务之间的松耦合,使彼此独立的微服务更容易的进行扩展。


但是带来的一个问题就是,不清楚各自底层的数据存储(不一定是关系型数据库),无法通过统一的事务协调器来完成数据一致性。

 

简单的说,传统的本地事务或分布式事务不适合微服务架构。


2、服务架构中的最终一致性


在分布式系统架构中有一个CAP理论:任何分布式系统只可同时满足一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)中的两点,没法三者兼顾。


对于分布式系统来说,分区容错性是基本要求,否则就失去了价值。因此,就只能在可用性和一致性之间做出选择。如果选择提供一致性需要付出在满足一致性之前阻塞其他并发访问的代价。


这可能持续一个不确定的时间,尤其是在系统已经表现出高延迟时或者网络故障导致失去连接时。


依据目前的成功经验,可用性一般是更好的选择,但是在服务和数据库之间维护数据一致性是非常根本的需求,微服务架构中选择满足最终一致性,最终一致性是指系统中的所有数据副本经过一段时间后,最终能够达到一致的状态,这里所说的一段时间,也要是用户可接受范围内的一段时间。


从一致性的本质来看,就是在一个业务逻辑中包含的所有服务要么都成功,要么都失败。那我们又该如何选择方向,来保证成功还是保证失败呢? 


就以话费充值为例,我们采用可靠事件的模式来实现最终一致性。


2.1 可靠事件模式


可靠事件模式属于事件驱动架构,当某件重要事情发生时,例如更新一个业务实体,微服务会向消息代理发布一个事件。消息代理会向订阅事件的微服务推送事件,当订阅这些事件的微服务接收此事件时,就可以完成自己的业务,也可能会引发更多的事件发布。


以上面话费充值为例:


1. 订单服务创建一个订单,发布一个“创建订单”事件

640?wx_fmt=png&wxfrom=5&wx_lazy=1

2. 支付服务消费“创建订单”事件,待支付完成后发布一个“支付成功”事件

640?wx_fmt=png&wxfrom=5&wx_lazy=1

3.订单服务消费“支付成功”事件,订单状态更新为待发货。

640?wx_fmt=png&wxfrom=5&wx_lazy=1


从而就实现了完整的业务流程。

这个过程可能导致出现不一致的地方在于:


1.某个服务在更新了业务实体后发布事件却失败


2.虽然服务发布事件成功,但是消息代理未能正确推送事件到订阅的微服务


3.接受事件的微服务重复消费了事件


可靠事件模式在于保证可靠事件投递和避免重复消费。可靠事件投递定义为:每个服务原子性的业务操作和发布事件,消息代理确保事件传递至少一次。避免重复消费要求服务实现幂等性,如支付服务不能因为重复收到事件而多次支付。


3、可靠事件的实现方式


3.1 本地事件表


本地事件表方法将事件和业务数据保存在同一个数据库中,使用一个额外的“事件恢复”服务来恢复事件,由本地事务保证更新业务和发布事件的原子性。考虑到事件恢复可能会有一定的延时,服务在完成本地事务后可立即向消息中心发布一条消息。


1. 微服务在同一个本地事务中记录业务数据和消息数据


2. 微服务实时发布一条事件关联业务服务中,如果事件发布成功立即删除记录的事件,这样能够保证事件投递的实时性。


3. 事件恢复服务定时从事件表中恢复未发布成功的事件,重新发布,重新发布成功后删除记录的事件,这样能够保证事件一定能够被投递。


这样能够很好的解决网络IO异常和服务器宕机的问题,但是业务系统和事件耦合在一起,额外的事件数据操作给数据库带来压力,也成为异步事件机制的一个瓶颈。


3.2 外部事件表


针对本地事件表出现的问题,提出外部事件表方法,将事件持久化到外部的事件系统,事件系统需提供实时事件服务以接收微服务发布的事件,同时事件系统还需要提供事件恢复服务来确认和恢复事件。


1. 业务服务在事务提交前,通过实时事件服务向事件系统请求发送事件,事件系统只记录事件并不真正发送


2. 业务服务在提交后,通过实时事件服务向事件系统确认发送,如果发送事件失败,业务系统通过重试补偿发送事件,事件得到确认后事件系统才真正发布事件到消息代理


3. 业务服务在业务回滚时,通过实时事件向事件系统取消事件


4. 事件系统的事件恢复服务会定期找到未确认发送的事件向业务服务查询状态,根据业务服务返回的状态决定事件是要发布还是取消


该方式将业务系统和事件系统独立解耦,都可以独立伸缩。但是这种方式需要一次额外的发送操作,并且需要发布者提供额外的查询接口,这样就增加了系统实现的复杂性。


另一个需要注意的地方就是,微服务实现数据一致性最好的方式是最终一致性。有些需要考虑的极端情况下,是需要人工接入的,这里就不展开了。


来源:中生代技术

原文链接

相关文章
|
2月前
|
消息中间件 存储 监控
|
9月前
|
消息中间件 存储 Kafka
MQ 学习日志(六) 保证消息的可靠性传输
消息的可靠性传输 简述
72 0
|
10月前
|
消息中间件 存储 缓存
该如何保证消息的可靠性传输
该如何保证消息的可靠性传输
82 0
|
10月前
|
分布式计算 Java
数据可靠性与一致性:分布式事务处理的挑战与解决方案
在分布式系统中,确保数据的可靠性和一致性是一项重要且具有挑战性的任务。随着分布式计算的普及,开发人员面临着处理跨多个节点和服务的分布式事务的问题。本文将介绍分布式事务处理中的挑战,并探讨一些常见的解决方案。
195 0
|
11月前
|
存储 安全 API
强大而可靠的DiskQ:持久化队列的解决方案
DiskQ作为一种持久化队列,提供了一种强大而可靠的解决方案。本文将介绍DiskQ的特点、用途以及如何使用DiskQ来处理数据和任务。
强大而可靠的DiskQ:持久化队列的解决方案
分布式事物如何保证接口请求顺序性?
前言 先抛出一个问题大家思考一下:在分布式系统中,我们如何保证多个请求的顺序性问题,比如有A/B两个系统,系统A在一次订单业务处理中,向B系统发送三次请求,先进行插入订单操作,然后对订单状态进行修改,最后增加用户积分。 但是这三次请求分别落在了不同的机器上,并且插入订单的操作由于一些意外导致延迟,修改订单操作先执行了,但是此时并没有订单信息,也就会出现我们期望之外的结果了。
|
Cloud Native 物联网 Java
一种用于保证多方子系统数据一致性的方法
目前我司的物联网平台是基于云原生架构的,目前主要用来对接第三方弱电子系统,比如海康ISC、大华ICC等。 弱电子系统会提供人员、空间等开放接口,物联网平台通过调用开放平台的增删改接口,将我方数据同步到多个弱电子系统中。 由于这多方系统都是独立的系统,具有独立的事务,当其中某个子系统发生异常后,前面调用的子系统并无感知,于是造成子系统产生**脏数据**,并且导致该类数据无法再次处理成功。
一种用于保证多方子系统数据一致性的方法
|
消息中间件 分布式计算 Kafka
【Flink】(九)状态一致性、端到端的精确一次(ecactly-once)保证1
【Flink】(九)状态一致性、端到端的精确一次(ecactly-once)保证1
232 0
【Flink】(九)状态一致性、端到端的精确一次(ecactly-once)保证1
|
消息中间件 存储 缓存
【Flink】(九)状态一致性、端到端的精确一次(ecactly-once)保证2
【Flink】(九)状态一致性、端到端的精确一次(ecactly-once)保证2
360 0
【Flink】(九)状态一致性、端到端的精确一次(ecactly-once)保证2
|
存储 消息中间件 中间件
可靠消息的最终一致性解决方案
可靠消息的最终一致性解决方案
221 0
可靠消息的最终一致性解决方案

热门文章

最新文章