关于java:分布式事务六之可靠消息最终一致性

10次阅读

共计 2183 个字符,预计需要花费 6 分钟才能阅读完成。

音讯发送一致性:是指产生音讯的业务动作与音讯发送的统一。也就是说,如果业务操作胜利,那么由这个业务操作所产生的音讯肯定要胜利投递进来(个别是发送到 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 会依据发送端设置的策略来决定是回滚还是持续发送确认音讯。这样就保障了音讯发送与本地事务同时胜利或同时失败。

如果生产失败怎么办?阿里提供给咱们的解决办法是:人工解决。

本地事务表

并不是所有的 mq 都反对事务音讯。也就是音讯一旦发送到音讯队列中,消费者立马就能够生产到。此时能够应用独立音讯服务、或者本地事务表。

能够看到,其实就是将音讯先发送到一个咱们本人编写的一个 ” 独立音讯服务 ” 利用中,刚开始处于 prepare 状态,业务逻辑解决胜利后,确认发送音讯,这个时候 ” 独立音讯服务 ” 才会真正的把音讯发送给音讯队列。消费者生产胜利后,ack 时,除了对音讯队列进行 ack(图中没有画出),对于独立音讯服务也要进行 ack,” 独立音讯服务 ” 个别是把这条音讯删除。而定时扫描 prepare 状态的音讯,向音讯发送端 (生产者) 确认的工作也由独立音讯服务来实现。

对于 ” 本地事务表 ”,其实和 ” 独立音讯服务 ” 的作用相似,只不过 ” 独立音讯服务 ” 是须要独立部署的,而 ” 本地事务表 ” 是将 ” 独立音讯服务 ” 的性能内嵌到利用中。

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

参考文档

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

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

正文完
 0