.Net Core with 微服务 - 分布式事务 - 可靠消息最终一致性
前面我们讲了分布式事务的2PC、3PC , TCC 的原理。这些事务其实都在尽力的模拟数据库的事务,我们可以简单的认为他们是一个同步行的事务。特别是 2PC,3PC 他们完全利用数据库的事务能力,在一阶段开始事务后不进提交会严重影响应用程序的并发性能。TCC 一阶段虽然不会阻塞数据库,但是它同样是在尽力追求同时成功同时失败的一致性要求。但是在很多时候,我们的应用程序的核心业务为了追求更高的性能、更高的可用性,可以允许在一段时间内的数据不一致性,只需要在最终时刻数据是一致就可以了。基于以上场景我们可以采用基于可靠消息服务的最终一致性分布式事务处理方案。
总体架构

可靠消息最终一致性的架构上分3个部分:
- 主动方:主动发起事务的一方,一般是指分布式事务中最先开始执行的那个服务,也是核心业务服务
- 可靠消息服务:可靠消息服务由关系型数据库、消息队列MQ等组件组成,利用关系型数据库的强一致性特性来持久化消息的状态,利用MQ来保证消息的可靠投递及消费
- 被动方:被动方订阅MQ的消息,当收到MQ的消息后执行对应的业务
以上是比较粗狂的结构图,下面我们来详细分析一下这个事务的执行过程。
流程

该方案总体流程上可分为以下步骤:
- 主动方在真正的业务开始前先向可靠消息服务发送一个“待确认”的消息
- 可靠消息服务收到待确认消息后持久化消息到数据库
- 如果以上操作成功则主动方开始真正的业务,如果失败则直接放弃执行业务
- 如果业务执行成功则发送“确认”消息给可靠消息服务,如果执行失败则发送“取消”给可靠消息服务。
- 如果可靠消息服务收到“确认”消息则更新数据库里的消息记录的状态为“待发送”,如果收到的消息为“取消”则更新消息状态为“已取消”
- 如果上一步更新的数据库为“待发送”,那么会开始往MQ投递消息,并且更改数据库里的消息记录的状态为“已发送”
- 上一步往MQ投递消息成功后,MQ会给被动方推送消息。
- 被动方收到消息后开始处理业务
- 如果业务处理成功,则被动方对MQ进行ACK回复,则这条消息会从MQ内移除掉
- 如果业务处理成功,则发送“已完成”消息给可靠消息服务
- 可靠消息服务收到“已完成”消息后更新数据库消息记录未“已完成”
异常处理
以上我们描述的是一套在理想情况下执行的逻辑。但是分布式系统由于网络的存在,网络的不可靠性会导致我们消息的传递没办法100%成功。我们的可靠消息服务跟主动方、被动方之间的交互也是分布式的,这就需要我们在流程上有很多补偿的机制。以下我们来讨论一些异常情况:
- 如果步骤1发送“待确认”消息失败,主动方业务不会执行,直接放弃事务,不会有影响
- 如果步骤1发送“待确认”消息成功,并且可靠消息已经更新“待确认”成功,但是由于网络问题,比如超时,主动方得到的结果是失败,主动方会放弃执行事务,标记为“已取消”。这个时候就会出现可靠消息服务跟主动方的状态出现不一致的情况。
为解决这个问题,我需要主动方提供一个事务状态查询接口,可靠消息服务这边则启动一个定时任务,定时去查这些长时间处于待确认的事务,然后通过主动方的接口确认这些事务是已执行,还是已取消。 - 如果步骤4,主动方发送“确认”消息失败,可靠消息服务会通过定时任务通过主动方的查询接口去确认状态。
- 步骤6,投递消息给MQ跟更新状态为“已发送”,是这个流程中至关重要的一步。理想的流程是整个2个操作同时成功同时失败,保持强一致性。网上很多文章都会说“为了保证发送MQ消息跟更新消息状态同时成功同时失败,需要把这个2个步骤写同一个本地事务中”。这是完全不靠谱的,数据库跟MQ本就是2个独立的服务,如果通过一个本地的事务就能保证一致性,那么我们现在讨论的分布式事务毫无意义,直接写在一个本地事务里不就完了么。
写在本地事务内只能尽可能的保证数据库更新跟MQ投递消息同时成功,但是并不能保证100%一致。
以下我们来分2种情况分析失败的情况:
事务开始
1. send to mq
2. database update
事务结束
先发送 MQ 消息,可能 MQ 消息发送成功,但是database由于某些原因更新失败了。数据库可以回滚,但是通常的MQ没有回滚能力。
事务开始
1. database update
2. send to mq
事务结束
先更新数据库,再发送 MQ 消息,同样会有问题。就算1,2都执行成功了,但是事务是需要提交的,数据库有可能在提交阶段失败,数据库是可以回滚,但是 MQ 的消息已经发出去了,它并没有回滚的能力。
为了解决这个问题,我们同样需要补偿机制。在可靠消息服务一侧开启定时任务,定时去查询那些长期处于“待发送\已发送”的事务,再次对 MQ 进行投递消息。这个机制有可能造成被动方重复收到 MQ 的消息,这就需要被动方处理业务的时候要进行幂等处理。
总结
通过以上我们详细介绍了可靠消息最终一致性事务解决方案的总体结构跟执行的流程,以及对异常情况的一些补偿方法,总体流程上还是比较清晰简单的。但是可靠消息最终一致性方案在使用上也是具有比较强的局限性,因为它的异步特性跟有可能出现的高延时性不适合处理一些敏感业务。比如它适合处理消费新增积分场景,但是不合适处理积分兑换礼品的场景。因为如果积分扣减延迟了,那么用户就可能兑换超出本身积分多的多的礼品。所以我们选择分布式事务的时候还需根据场景来进行选择。
好了讲了这么多分布式事务的原理,下一期我们使用 .NET 真正的实现一个分布式事务,敬请期待。
.Net Core with 微服务 - 什么是微服务
.Net Core with 微服务 - 架构图
.Net Core with 微服务 - Ocelot 网关
.Net Core with 微服务 - Consul 注册中心
.Net Core with 微服务 - Seq 日志聚合
.Net Core with 微服务 - Elastic APM
.Net Core with 微服务 - Consul 配置中心
.Net Core with 微服务 - Polly 熔断降级
.Net Core with 微服务 - 分布式事务 - 2PC、3PC
.Net Core with 微服务 - 分布式事务 - TCC
关注我的公众号一起玩转技术

.Net Core with 微服务 - 分布式事务 - 可靠消息最终一致性的更多相关文章
- 使用kafka消息队列解决分布式事务(可靠消息最终一致性方案-本地消息服务)
微服务框架Spring Cloud介绍 Part1: 使用事件和消息队列实现分布式事务 本文转自:http://skaka.me/blog/2016/04/21/springcloud1/ 不同于单一 ...
- .Net Core with 微服务 - 分布式事务 - TCC
上一次我们讲解了分布式事务的 2PC.3PC .那么这次我们来理一下 TCC 事务.本次还是讲解 TCC 的原理跟 .NET 其实没有关系. TCC Try 准备阶段,尝试执行业务 Confirm 完 ...
- .Net Core with 微服务 - 分布式事务 - 2PC、3PC
最近比较忙,好久没更新了.这次我们来聊一聊分布式事务. 在微服务体系下,我们的应用被分割成多个服务,每个服务都配置一个数据库.如果我们的服务划分的不够完美,那么为了完成业务会出现非常多的跨库事务.即使 ...
- Spring Cloud Alibaba | 微服务分布式事务之Seata
Spring Cloud Alibaba | 微服务分布式事务之Seata 本篇实战所使用Spring有关版本: SpringBoot:2.1.7.RELEASE Spring Cloud:Green ...
- 微服务分布式事务之LCN、TCC
在亿级流量架构之分布式事务解决方案对比中, 已经简单阐明了从本机事务到分布式事务的演变过程, 文章的最后简单说明了TCC事务, 这儿将会深入了解TCC事务是原理, 以及理论支持, 最后会用Demo举例 ...
- .Net Core with 微服务 - 使用 AgileDT 快速实现基于可靠消息的分布式事务
前面对于分布式事务也讲了好几篇了(可靠消息最终一致性 分布式事务 - TCC 分布式事务 - 2PC.3PC),但是还没有实战过.那么本篇我们就来演示下如何在 .NET 环境下实现一个基于可靠消息的分 ...
- .NET Core with 微服务 - 什么是微服务
微服务是这几年最流行的架构,说起架构不提微服务都不好意思跟人家打招呼.最近想要再梳理一下关于微服务的知识,并且结合本人的一些实践经验来做一些总结与分享.前面会分享一些概念性的东西,后面也会使用.net ...
- spring cloud+dotnet core搭建微服务架构:服务发现(二)
前言 上篇文章实际上只讲了服务治理中的服务注册,服务与服务之间如何调用呢?传统的方式,服务A调用服务B,那么服务A访问的是服务B的负载均衡地址,通过负载均衡来指向到服务B的真实地址,上篇文章已经说了这 ...
- spring cloud+dotnet core搭建微服务架构:配置中心续(五)
前言 上一章最后讲了,更新配置以后需要重启客户端才能生效,这在实际的场景中是不可取的.由于目前Steeltoe配置的重载只能由客户端发起,没有实现处理程序侦听服务器更改事件,所以还没办法实现彻底实现这 ...
随机推荐
- ACM学习笔记:二叉堆
title : 堆 date : 2021-8-3 tags : ACM,数据结构 什么是堆 堆是一棵具有特定性质的二叉树,堆的基本要求是堆中所有结点的值必须大于等于(或小于等于)其孩子结点的值,这也 ...
- SpringBoot开发七-开发注册功能
需求介绍-开发注册功能 首先访问注册页面-点击顶部的链接,打开注册页面 提交注册数据 通过表单提交数据 服务端验证账号是否存在,邮箱是否已经注册 服务端发送激活邮件 激活注册账号 点击邮件中的链接,访 ...
- 谈谈Linux字符设备驱动的实现
@ 目录 字符设备驱动基础 申请设备号 创建设备节点 在驱动中实现操作方法 文件IO调用驱动中的操作 应用程序与驱动的数据交互 内核驱动如何控制外设 控制LED的简单驱动实例 驱动程序的改进 框架复盘 ...
- SQL 练习28
查询平均成绩大于等于 85 的所有学生的学号.姓名和平均成绩 SELECT Student.SId,Student.Sname,平均成绩 FROM Student , (SELECT sid,AVG( ...
- GitNote基于git的个人云笔记
优点 可以存储到git服务(如github,giteee)中的能看到历史版本的git记事本工具. git 是一个很棒的工具,GitNote 支持 git 的全部特性,并且不依赖本地 Git 环境. 你 ...
- 利用maven构建一个spring mvc的helloworld实例
刚开始学习maven和spring mvc,学的云里雾里的 这里提供一个hello world实例,记录自己的学习之路 首先看maven官网的介绍 Apache Maven is a software ...
- ConcurrentDictionary 并发字典
线程安全 Dictionary 本身是不支持线程安全的 线程的字典--ConcurrentDictionary 线程安全实现 写安全 以往线程安全我们通过Lock实现 比如通过lock一个全局的obj ...
- HttpClient 4.3教程-前言
前言 Http协议应该是互联网中最重要的协议.持续增长的web服务.可联网的家用电器等都在继承并拓展着Http协议,向着浏览器之外的方向发展. 虽然jdk中的java.net包中提供了一些基本的方法, ...
- 02.SpringMVC之初体验
1.创建Maven WEB项目 2.导入springmvc的jar包 <dependencies> <dependency> <groupId>org.spring ...
- 在localStorage中存储对象数组并读取
频繁ajax请求导致页面响应变慢. 于是考虑将数据存储在window.storage中,这样只请求一次ajax,而不需要频繁请求. 鉴于localstorage中只能存储字符串,所以我们要借助于JSO ...