彻底搞透分布式一致性( 七 )

< 3; i++) {Message msg = new Message("TopicTest", tags[i], ("Hello RocketMQ " + i).getBytes(StandardCharsets.UTF_8));// 在消息发送时传递给事务监听器的参数SendResult sendResult = producer.sendMessageInTransaction(msg, null);System.out.printf("%s%n", sendResult);}// 关闭生产者producer.shutdown();}}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
单看代码很难理解,简单画了张图,具体如下:
彻底搞透分布式一致性

文章插图
图片
其核心部分就是 TransactionListener 实现 , 其他部分与正常的消息发送基本一致,TransactionListener 主要完成:
  • 执行本地事务,也就是业务操作;
  • 执行结果检测,通过反查业务数据,决定消息的后续处理策略;
为了使用事务消息 , 我们不得不在TransactionListener中编写进行大量的适配逻辑,增加研发成本,同时由于逻辑被拆分到多处,也增加了代码的理解成本 。
事务消息存在一定的问题:
  • 与 MQ 实现强相关,并不是每个 MQ 实现都对事务消息提供支持;
  • API 比较晦涩,存在一定的学习成本,同时需要对业务逻辑拆分到 Listener 中,增加理解成本;
有没有实用性强、使用简单的方案,那可以使用 事务消息表 方案 。
3.3.2.2. 事务消息表事务消息表方案是一种常用的保证消息发送与业务操作一致性的方法 。该方案基于数据库事务和消息队列,将消息发送和业务操作放入同一个事务中 , 并将业务操作和消息发送的状态记录在数据库的消息表中,以实现消息的可靠性和幂等性 。
如下图所示:
彻底搞透分布式一致性

文章插图
图片
核心流程如下:
  • 应用程序开启一个数据库事务,并在事务中执行业务操作和消息发送;
  • 在事务中,将业务操作和消息发送的状态记录到消息表中;
  • 如果业务操作执行成功 , 并且消息发送成功,提交事务,否则回滚事务;
  • 定时扫描消息表 , 并根据消息状态重新发送未被确认的消息 。如果消息发送成功,更新消息状态;否则根据重试次数更新消息状态或者丢弃消息;
通过事务消息表方案,可以保证消息的可靠性和幂等性 。即使在消息发送失败或应用程序崩溃的情况下 , 也可以通过重新发送消息将业务操作和消息发送的状态同步 。同时,该方案可以避免消息重复发送和漏发的情况 。
作为一种通用解决方案,lego 对其进行支持 , 可参考 reliable-message 模块 。
4. 业务补偿不管在设计时使用哪种方案,都是在尽力降低不一致出现的概率,但可怕的是不一致问题终究会发生 。
是不是有些奇怪,做了这么多还是无法从根源上彻底解决一致性问题,在实际工作中就是这样: