启发:从MNS事务消息谈分布式事务

启发:从MNS事务消息谈分布式事务

事务消息本质上解决的问题是业务系统与消息系统之间的事务问题(跨系统分布式事务),其基本原理即两阶段提交以及最终一致性保障。最近看了下阿里云mns事务消息的实现原理,介绍的蛮简洁透彻的,对了解分布式事务实现原理挺有帮助,在阅读本文前推荐大家先仔细阅读下阿里云"mns事务消息"一文。

事务消息


背景描述

有时候我们需要实现本地操作和消息发送的事务一致性功能。即:消息发送成功,则本地操作成功;反之,如果消息发送失败,本地操作失败(成功也需要rollback)。保证不出现操作成功但消息发送失败;或者操作失败但消息发送成功的情况;
另外,消费端,我们也希望消息一定被成功处理一次,不会因为消息端程序崩溃而导致消息没有成功处理,进而需要人工重置消费进度。

解决方案

利用消息服务MNS的延迟消息功能来实现。

准备工作

创建两个队列:

  • 1.事务消息队列
    消息的有效期小于消息延迟时间。即如果生产者不主动修改(提交)消息可见时间,消息对消费者不可见;
  • 2.操作日志队列
    记录事务消息的操作记录信息。消息延迟时间为事务操作超时时间。日志队列中的消息确认(删除)后将对消费者不可见。
    <!-- more -->

具体步骤

  • 1.发送一条事务准备消息到事务消息队列;
  • 2.写操作日志信息到操作日志队列,日志中包含步骤1消息的消息句柄;
  • 3.执行本地事务操作;
  • 4.如果步骤3成功,提交消息(消息对消费者可见);反之,回滚消息;
  • 5.确认步骤2中的操作日志(删除该日志消息);
  • 6.步骤4后,消费者可以接收到事务消息;
  • 7.消费者处理消息;
  • 8.消费者确认删除消息;
    如下图:

异常分析:

生产者异常(例如:进程重启):

A.读取操作日志队列超时未确认日志
B.检查事务结果
C.如果检查得到事务已经成功,则提交消息(重复提交无副作用,同一句柄的消息只能成功提交一次)
D.确认操作日志

消费者异常(例如:进程重启):

消息服务提供至少保证消费一次的特性,只要步骤8不成功,消息在一段时间后可以继续可见,被当前消费者或者其他消费者处理。

消息服务不可达(例如:断网)

消息发送和接收处理状态以及操作日志都在消息服务端,消息服务本身具备高可靠和高可用的特点,所以只要网络恢复,事务可以继续,能保证只要生产者:操作成功,则消费者一定能够拿到消息并处理成功;或操作失败, 则消费者收不到消息的最终一致性。

原文地址

在mns消息模型中两阶段提交的体现是:

  • 1.在执行事务前先preSendMessage:其背后的原理是创建一个delay message,但是这个delay message的delaytime > lifetime, 基于这个前提在得到确切的commit/rollback操作前,这个消息对于接受者是永远不可见的;
  • 2.本地事务结束后commit/rollback message:如果本地事务提交成功,需要将之前提交的delay message设置为消费者可见(底层实现应该与将delay变为0类似);对应的如果本地事务提交失败,需要将之前的delay message删除;

这个过程需要注意到,我们务必保证在preSendMessage没得到最终确认之前不被消费者获取到,因此需要将发送的lifetime小于delaytime。

看到这里也许你有疑问,为什么要将过程切分成两阶段提交?我们先假设如果采用一次提交的策略,很显然这次提交的切入点只能存在于①本地事务开始之前②本地事务中③本地事务结束之后,那么先看这三个切入点各自存在什么问题。

  • ①本地事务开始之前提交消息:在本地事务未完成之前,消息的消费者读取到了message,如果消费者后续的服务调用中存在对该次本地事务提交有依赖,那必然导致数据不一致问题;如果本地事务的执行结果是失败的,却通知了消费者,很显然会导致不可预期的数据错误。
  • ②本地事务中:在本地事务中提交消息同样会存在①中的问题,即便sendmessage是在本地事务的最后执行,因为事务的提交和消息被接受到的时序是无法保证的;
  • ③本地事务结束之后:不同于①②两个提交点,本地事务完成之后我们能够明确的知道本地事务的执行结果,因此能够确保事务提交(回滚)与消息被接受是有序的;然而如果消息没有被成功发送消费者接受不到消息,而本地事务却得到了正确执行,这就导致了数据不一致问题,并且如果没有操作日志,这个问题将变得难以追溯;

”单次提交“遇到的主要问题是:无法保障本地事务与消息被接受到的时序问题(或者说两个分布式事务的时序)以及数据的一致性问题。再回到”两阶段提交“,两阶段提交能解决这两个问题吗?两阶段提交的确认操作是在本地事务完成之后(这个类似于③),因此其能够解决时序问题,但是如果这个确认操作执行的过程中发生了宕机等情况导致确认操作失败,依然会导致数据不一致问题。

在mns事务消息中最终一致性的实现:

mns通过延迟消息机制实现了两阶段提交,其如何保证数据一致性问题呢?一般我们的策略都是通过操作流水来进行补偿以达到数据的最终一致性,同样的mns也是基于这个原理实现。

  • 在preSendMessage之后,mns会在日志队列中记录一条opLog(opLog通过记录preSendMessage的receipthandle来进行关联),并且将这个opLog的delayTime设置为事务的超时时间;
  • 当本地事务执行结束,并且preSendMessage被commit/rollback之后,再将这条opLog删除;
  • 同时存在一个任务监听日志队列,当接收到opLog的消息,检查对应的preSendMessage相关联的本地事务是否执行成功。如果本地事务执行成功,则通过opLog中保存的receipthandle补偿一次对preSendMessage的commit操作,如果checker发现本地事务执行失败,那对应的补偿一次rollback操作;

通过建立对opLog的监听,我们能够确保事务的最终一致性吗?回答这个问题前,我们先看这个问题的本质:最终、一致性。
最终一致性问题的产生是由于发生了一些不可预期的问题,导致一个事务被提交(回滚),但消息没被commit(rollback)。我们通过opLog来追溯那些没有得到最终确认的消息并进行补偿(最终),并且通过检查本地事务的状态来确认这次补偿是commit或者是rollback(一致性)。正是基于这个补偿的策略,mns事务消息解决了"两阶段提交"所遗留的一致性问题,但这个过程中我们需要注意几个细节:

  • 补偿策略执行的时候需要明确知道本地事务的执行结果,因此我们的本地事务中需要记录preSendMessage所关联的本地事务操作结果。我们的做法是本地事务中同时记录下preSendMessage的receipthandle, 当补偿任务执行的时候,会通过opLog关联的receipthandle来检查,如果没有找到相关记录,那认为之前的本地事务被rollback了,否则commit;
  • MNS如何建立了preSendMessage<=>Local Transaction<=>opLog之间的关联关系?最简单的实现肯定是通过preSendMessage的MessageId来实现,不过mns通过preSendMessage的receipthandle来建立了这个关联(ReceiptHandle含义)同时避免了额外的存储;
  • mns的补偿机制建立在对opLog的监听,那么我们怎么确定一个补偿的执行时机是合适的呢?补偿一定要在事务有明确结果之后执行才有意义,那么什么时候能得到明确的事务执行结果?其实我们是无法确切的知道这个时间点的,但我们能够有一个最低期望时间:不管一个事务成功或者失败,它的周期都不能超过事务的超时时间。因此我们在发送opLog时需要设置opLog的delayTime>TransactionTimeout(如何确认transactionTimeout)来保证补偿任务执行的时候本地事务一定执行完成。

从mns事务消息到分布式事务的启发

上面啰嗦的写了一堆,看到这我们不妨对思考下mns事务消息解决的是业务系统(本地事务)与消息中间件之间的事务协同问题,如果是两个业务系统之间的分布式事务如何实现?
好吧,如果坚持看到这,你可能觉得我标题党了...那么我建议你再读一下”mns事务消息“一文。

更多文章请访问我的博客
转载请注明出处

时间: 2024-08-03 21:29:19

启发:从MNS事务消息谈分布式事务的相关文章

Java中JDBC事务与JTA分布式事务总结与区别_java

Java事务的类型有三种:JDBC事务.JTA(Java Transaction API)事务.容器事务.常见的容器事务如Spring事务,容器事务主要是J2EE应用服务器提供的,容器事务大多是基于JTA完成,这是一个基于JNDI的,相当复杂的API实现.所以本文暂不讨论容器事务.本文主要介绍J2EE开发中两个比较基本的事务:JDBC事务和JTA事务. JDBC事务 JDBC的一切行为包括事务是基于一个Connection的,在JDBC中是通过Connection对象进行事务管理.在JDBC中,

浅谈分布式事务

现今互联网界,分布式系统和微服务架构盛行.一个简单操作,在服务端非常可能是由多个服务和数据库实例协同完成的.在一致性要求较高的场景下,多个独立操作之间的一致性问题显得格外棘手. 基于水平扩容能力和成本考虑,传统的强一致的解决方案(e.g.单机事务)纷纷被抛弃.其理论依据就是响当当的CAP原理.往往为了可用性和分区容错性,忍痛放弃强一致支持,转而追求最终一致性. 分布式系统的特性 在分布式系统中,同时满足CAP定律中的一致性 Consistency.可用性 Availability和分区容错性 P

如何用消息系统避免分布式事务?(转)

http://www.cnblogs.com/LBSer/p/4715395.html 前阵子从支付宝转账1万块钱到余额宝,这是日常生活的一件普通小事,但作为互联网研发人员的职业病,我就思考支付宝扣除1万之后,如果系统挂掉怎么办,这时余额宝账户并没有增加1万,数据就会出现不一致状况了. 上述场景在各个类型的系统中都能找到相似影子,比如在电商系统中,当有用户下单后,除了在订单表插入一条记录外,对应商品表的这个商品数量必须减1吧,怎么保证?!在搜索广告系统中,当用户点击某广告后,除了在点击事件表中增

如何用消息系统避免分布式事务?

前阵子从支付宝转账1万块钱到余额宝,这是日常生活的一件普通小事,但作为互联网研发人员的职业病,我就思考支付宝扣除1万之后,如果系统挂掉怎么办,这时余额宝账户并没有增加1万,数据就会出现不一致状况了. 上述场景在各个类型的系统中都能找到相似影子,比如在电商系统中,当有用户下单后,除了在订单表插入一条记录外,对应商品表的这个商品数量必须减1吧,怎么保证?!在搜索广告系统中,当用户点击某广告后,除了在点击事件表中增加一条记录外,还得去商家账户表中找到这个商家并扣除广告费吧,怎么保证?!等等,相信大家或

谈谈分布式事务之四: 两种事务处理协议OleTx与WS-AT

在年前写一个几篇关于分布式事务的文章,实际上这些都是为了系统介绍WCF事务处理体系而提供的相关的背景和基础知识.今天发最后一篇,介绍分布式事务采用的两种协议,即OleTx和WS-AT,内容比较枯燥,但对于后续对WCF事务处理框架进行深入剖析的系列文章来说,确是不可以缺少的.总的来说,基于WCF的分布式事务采用的是两阶段提交(2PC:Two Phase Commit)协议.具体来说,我们可以选择如下两种事务处理协议实现WCF的分布式式事务,它们按照各自的方式提供了对两阶段提交的实现. OleTx:

GTS for DRDS分布式事务的实现理解

GTS介绍 全局事务服务(Global Transaction Service,简称 GTS)是一款高性能.高可靠.接入简单的分布式事务中间件,用于解决分布式环境下的数据一致性问题. 一个完整的业务往往需要调用多个子业务或服务,随着业务的不断增多,涉及的服务及数据也越来越多,越来越复杂.传统的系统难以支撑,出现了应用和数据库等的分布式系统.分布式系统又带来了数据一致性的问题,从而产生了分布式事务. 分布式事务是指事务发起者.资源管理器.事务协调者及资源分别位于不同的分布式系统的不同节点之上. G

大白话聊聊分布式事务

大白话聊聊分布式事务 什么是分布式事务 简单的来说就是,一个大的操作由两个或者更多的小的操作共同完成.而这些小的操作又分布在不同的网络主机上.这些操作,要么全部成功执行,要么全部不执行. 拿转账的例子来说下什么是分布式事务.张三和李四在不同的城市,存储他们账户信息的服务器也在不同的网络主机上.张三有30元钱,李四有30元钱.张三给李四转账5元就是一个事务.完成这个事务,需要两个操作.首先得从张三账户上扣5元,然后再给李四账户上加5元.事务执行完毕后,必须是两个操作都执行成功,要么都失败. 事务的

关于分布式事务

一.普通事务与分布式事务 1.1 普通事务 普通事务就是一般所说的数据库事务,大家对数据库事务应该都很了解,这里再简单介绍下. 事务是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成.当事务被提交给了DBMS(数据库管理系统),则DBMS(数据库管理系统)需要确保该事务中的所有操作都成功完成且其结果被永久保存在数据库中,如果事务中有的操作没有成功完成,则事务中的所有操作都需要被回滚,回到事务执行前的状态;同时,该事务对数据库或者其他事务的执行无影响,所有的事务都好像在独立的

谈谈分布式事务(Distributed Transaction)[共5篇]

[第1篇] SOA需要怎样的事务控制方式 在一个基于SOA架构的分布式系统体系中,服务(Service)成为了基本的功能提供单元,无论与业务流程无关的基础功能,还是具体的业务逻辑,均实现在相应的服务之中.服务对外提供统一的接口,服务之间采用标准的通信方式进行交互,各个单一的服务精又有效的组合.编排成为一个有机的整体.在这样一个分布式系统中某个活动(Activity)的实现往往需要跨越单个服务的边界,如何协调多个服务之间的关系使之为活动功能的实现服务,涉及到SOA一个重要的课题:服务协作(Serv