热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

分布式事务(六)之可靠消息最终一致性

消息发送一致性:是指产生消息的业务动作与消息发送的一致。也就是说,如果业务操作成功,那么由这个业务操作所产生的消息一定要成功投递出去(一般是发送到kafka、rocketmq、ra

消息发送一致性:是指产生消息的业务动作与消息发送的一致。也就是说,如果业务操作成功,那么由这个业务操作所产生的消息一定要成功投递出去(一般是发送到kafka、rocketmq、rabbitmq等消息中间件中),否则就丢消息。

可靠消息最终一致性

发送消息不可靠性

既然提到了可靠消息的最终一致性,那么说明现有的消息发送逻辑存在不可靠性,我们用下面的几种情况来演示消息的不可靠性。

  • 先进行数据库操作,再发送消息:

    public void test1(){
    //1 数据库操作
    //2 发送MQ消息
    }
    

    这种情况下无法保证数据库操作与发送消息的一致性,因为可能数据库操作成功,发送消息失败

  • 先发送消息,再操作数据库:

    public void test1(){
    //1 发送MQ消息
    //2 数据库操作
    }
    

    这种情况下无法保证数据库操作与发送消息的一致性,因为可能发送消息成功,数据库操作失败。

  • 在数据库事务中,先发送消息,后操作数据库:

    @Transactional
    public void test1(){
    //1 发送MQ消息
    //2 数据库操作
    }
    

    这里使用spring 的@Transactional注解,方法里面的操作都在一个事务中。同样无法保证一致性,因为发送消息成功了,数据库操作失败的情况下,数据库操作是回滚了,但是MQ消息没法进行回滚。

  • 在数据库事务中,先操作数据库,后发送消息:

    @Transactional
    public void test1(){
    //1 数据库操作
    //2 发送MQ消息
    }
    

    这种情况下,貌似没有问题,如果发送MQ消息失败,抛出异常,事务一定会回滚(加上了@Transactional注解后,spring方法抛出异常后,会自动进行回滚)。
    这只是一个假象,因为发送MQ消息可能事实上已经成功,如果是响应超时导致的异常。这个时候,数据库操作依然回滚,但是MQ消息实际上已经发送成功,导致不一致。

  • 使用JTA事务管理器:

    前面通过spring的@Transactional注解加在方法上,来开启事务。其实有一个条件没有明确的说出来,就是我们配置的事务管理器是DataSourceTransactionManager。

    事实上,Spring还提供了另外一个分布式事务管理器JtaTransactionManager。这个是使用XA两阶段提交来保证事务的一致性。当然前提是,你的消息中间件是实现了JMS规范中事务消息相关API(回顾前面我们介绍JTA规范时,提到DB、MQ都只是资源管理器RM,对于事务管理器来说,二者是等价的)。

    因此如果你满足了2个条件:1、使用JtaTransactionManager 2、DB、MQ分别实现了JDBC、JMS规范中规定的RM应该实现的两阶段提交的API,就可以保证消息发送的一致性。

    DB作为RM,一般都是支持两阶段提交的。不过,一些MQ中间件并不支持,所以你要找到支持两阶段提交的MQ中间件。另外,JtaTransactionManager只是一个代理,你需要提供一个真实的事务管理器(TM)实现。如前面提到了atomikos公司,就有这样的产品。

    但是笔者依然不建议,这样做。因为XA两阶段提交性能低,我们使用消息中间件就是为了异步解耦,这种情况,虽然保证了一致性,但是响应时间却大大增加,系统可用性降低。

可靠发送消息的解决方案

有两种方法可以实现可靠消息发送:基于MQ的事务消息和本地事务表。

基于MQ的事务消息

以RocketMQ的事务消息为例,如下图所示,消息的可靠发送由发送端 Producer进行保证(消费端无需考虑),可靠发送消息的步骤如下:

  1. 发送一个事务消息,这个时候,RocketMQ将消息状态标记为Prepared,注意此时这条消息消费者是无法消费到的;
  2. 执行业务代码逻辑,可能是一个本地数据库事务操作;
  3. 确认发送消息,这个时候,RocketMQ将消息状态标记为可消费,这个时候消费者,才能真正的保证消费到这条数据。

如果确认消息发送失败了怎么办?RocketMQ会定期扫描消息集群中的事务消息,如果发现了Prepared消息,它会向消息发送端(生产者)确认。RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。

如果消费失败怎么办?阿里提供给我们的解决方法是:人工解决。

RocketMQ

本地事务表

并不是所有的mq都支持事务消息。也就是消息一旦发送到消息队列中,消费者立马就可以消费到。此时可以使用独立消息服务、或者本地事务表。

本地事务

可以看到,其实就是将消息先发送到一个我们自己编写的一个"独立消息服务"应用中,刚开始处于prepare状态,业务逻辑处理成功后,确认发送消息,这个时候"独立消息服务"才会真正的把消息发送给消息队列。消费者消费成功后,ack时,除了对消息队列进行ack(图中没有画出),对于独立消息服务也要进行ack,"独立消息服务"一般是把这条消息删除。而定时扫描prepare状态的消息,向消息发送端(生产者)确认的工作也由独立消息服务来完成。

对于"本地事务表",其实和"独立消息服务"的作用类似,只不过"独立消息服务"是需要独立部署的,而"本地事务表"是将"独立消息服务"的功能内嵌到应用中。

我是御狐神,欢迎大家关注我的微信公众号:wzm2zsd

qrcode_for_gh_83670e17bbd7_344-2021-09-04-10-55-16

参考文档

柔性事务:可靠消息最终一致性

本文最先发布至微信公众号,版权所有,禁止转载!


推荐阅读
  • 2021最新总结网易/腾讯/CVTE/字节面经分享(附答案解析)
    本文分享作者在2021年面试网易、腾讯、CVTE和字节等大型互联网企业的经历和问题,包括稳定性设计、数据库优化、分布式锁的设计等内容。同时提供了大厂最新面试真题笔记,并附带答案解析。 ... [详细]
  • 云原生应用最佳开发实践之十二原则(12factor)
    目录简介一、基准代码二、依赖三、配置四、后端配置五、构建、发布、运行六、进程七、端口绑定八、并发九、易处理十、开发与线上环境等价十一、日志十二、进程管理当 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • 本文介绍了OpenStack的逻辑概念以及其构成简介,包括了软件开源项目、基础设施资源管理平台、三大核心组件等内容。同时还介绍了Horizon(UI模块)等相关信息。 ... [详细]
  • 分享css中提升优先级属性!important的用法总结
    web前端|css教程css!importantweb前端-css教程本文分享css中提升优先级属性!important的用法总结微信门店展示源码,vscode如何管理站点,ubu ... [详细]
  • ElasticSerach初探第一篇认识ES+环境搭建+简单MySQL数据同步+SpringBoot整合ES
    一、认识ElasticSearch是一个基于Lucene的开源搜索引擎,通过简单的RESTfulAPI来隐藏Lucene的复杂性。全文搜索,分析系统&# ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • 企业数据应用挑战及元数据管理的重要性
    本文主要介绍了企业在日常经营管理过程中面临的数据应用挑战,包括数据找不到、数据读不懂、数据不可信等问题。针对这些挑战,通过元数据管理可以实现数据的可见、可懂、可用,帮助业务快速获取所需数据。文章提出了“灵魂”三问——元数据是什么、有什么用、又该怎么管,强调了元数据管理在企业数据治理中的基础和前提作用。 ... [详细]
  • Activiti7流程定义开发笔记
    本文介绍了Activiti7流程定义的开发笔记,包括流程定义的概念、使用activiti-explorer和activiti-eclipse-designer进行建模的方式,以及生成流程图的方法。还介绍了流程定义部署的概念和步骤,包括将bpmn和png文件添加部署到activiti数据库中的方法,以及使用ZIP包进行部署的方式。同时还提到了activiti.cfg.xml文件的作用。 ... [详细]
  • 一次上线事故,30岁+的程序员踩坑经验之谈
    本文主要介绍了一位30岁+的程序员在一次上线事故中踩坑的经验之谈。文章提到了在双十一活动期间,作为一个在线医疗项目,他们进行了优惠折扣活动的升级改造。然而,在上线前的最后一天,由于大量数据请求,导致部分接口出现问题。作者通过部署两台opentsdb来解决问题,但读数据的opentsdb仍然经常假死。作者只能查询最近24小时的数据。这次事故给他带来了很多教训和经验。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • 无处不在,详解iOS集成第三方登录(SSO授权登录<无需密码>)
    1.前言 不多说,第三登录无处不在!必备技能,今天以新浪微博为例。这是上次写的iOS第三方社交分享:http:www.cnblogs.comqingchep3727559.html ... [详细]
  • 浅析对象 VO、DTO、DO、PO 概念
    作者|CatQi链接|cnblogs.comqixuejiap4390086.html前言由于此订阅号换了个皮肤,导致用户接受文章不及时。读者可以打开订阅号「Web项 ... [详细]
  • RabbitMq之发布确认高级部分1.为什么会需要发布确认高级部分?在生产环境中由于一些不明原因,导致rabbitmq重启,在RabbitMQ重启期间生产者消息投递失败,导致消息丢 ... [详细]
author-avatar
木扎尔特2502918527
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有