前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >撸明白分布式事务(四)

撸明白分布式事务(四)

作者头像
全栈程序员站长
发布2022-09-23 10:51:34
1880
发布2022-09-23 10:51:34
举报

大家好,又见面了,我是你们的朋友全栈君。

前言

在分布式系统中,消息队列在服务端的架构中的地位非常重要,主要解决异步处理、系统解耦、流量削峰等场景。多个系统之间如果同步通信很容易造成阻塞,同时会将这些系统会耦合在一起。因此,引入了消息队列,一方面解决了同步通信机制造成的阻塞,另一方面通过消息队列进行业务解耦。简单的服务间调用引入mq如下图所示

撸明白分布式事务(四)
撸明白分布式事务(四)

可靠事件模式

可靠事件模式,通过引入可靠的消息队列,只要保证当前的可靠事件投递并且消息队列确保事件传递至少一次,那么订阅这个事件的消费者保证事件能够在自己的业务内被消费即可。这里,请读者思考,是否只要引入了消息队列就可以解决问题了呢?事实上,只是引入消息队列并不能保证其最终的一致性,因为分布式部署环境下都是基于网络进行通信,而网络通信过程中,上下游可能因为各种原因而导致消息丢失。

其一,主业务服务发送消息时可能因为消息队列无法使用而发生失败。对于这种情况,我们可以让主业务服务(生产者)发送消息,再进行业务调用来确保。一般的做法是,主业务服务将要发送的消息持久化到本地数据库,设置标志状态为“待发送”状态,然后把消息发送给消息队列,消息队列收到消息后,也把消息持久化到其存储服务中,但并不是立即向从业务服务(消费者)投递消息,而是先向主业务服务(生产者)返回消息队列的响应结果,然后主业务服务判断响应结果执行之后的业务处理。如果响应失败,则放弃之后的业务处理,设置本地的持久化消息标志状态为“结束”状态。否则,执行后续的业务处理,设置本地的持久化消息标志状态为“已发送”状态。

代码语言:javascript
复制
public void doServer(){
    // 发送消息
    send();
    // 执行业务
    exec();
    // 更新消息状态
    updateMsg();
}

正反向消息机制

此外,消息队列发生消息后,也可能从业务服务(消费者)宕机而无法消费。绝大多数消息中间件对于这种情况,例如 RabbitMQ、RocketMQ 等引入了 ACK 机制。注意的是,默认的情况下,采用自动应答,这种方式中消息队列会发送消息后立即从消息队列中删除该消息。所以,为了确保消息的可靠投递,我们通过手动 ACK 方式,如果从业务服务(消费者)因宕机等原因没有发送 ACK,消息队列会将消息重新发送,保证消息的可靠性。从业务服务处理完相关业务后通过手动 ACK 通知消息队列,消息队列才从消息队列中删除该持久化消息。那么,消息队列如果一直重试失败而无法投递,就会出现消息主动丢弃的情况,我们需要如何解决呢?聪明的读者可能已经发现,我们在上个步骤中,主业务服务已经将要发送的消息持久化到本地数据库。因此,从业务服务消费成功后,它也会向消息队列发送一个通知消息,此时它是一个消息的生产者。主业务服务(消费者)接收到消息后,最终把本地的持久化消息标志状态为“完成”状态。说到这里,读者应该可以理解到我们使用“正反向消息机制”确保了消息队列可靠事件投递。当然,补偿机制也是必不可少的。定时任务会从数据库扫描在一定时间内未完成的消息并重新投递。

撸明白分布式事务(四)
撸明白分布式事务(四)

注意的是,因为从业务服务可能收到消息处理超时或者服务宕机,以及网络等原因导致而消息队列收不到消息的处理结果,因此可靠事件投递并且消息队列确保事件传递至少一次。这里,从业务服务(消费者)需要保证幂等性。如果从业务服务(消费者)没有保证接口的幂等性,将会导致重复提交等异常场景。此外,我们也可以独立消息服务,将消息服务独立部署,根据不同的业务场景共用该消息服务,降低重复开发服务的成本。

了解了“可靠事件模式”的方法论后,现在我们来看一个真实的案例来加深理解。首先,当用户发起退款后,自动化退款服务会收到一个退款的事件消息,此时,如果这笔退款符合自动化退款策略的话,自动化退款服务会先写入本地数据库持久化这笔退款快照,紧接着,发送一条执行退款的消息投递到给消息队列,消息队列接受到消息后返回响应成功结果,那么自动化退款服务就可以执行后续的业务逻辑。与此同时,消息队列异步地把消息投递给退款基础服务,然后退款基础服务执行自己业务相关的逻辑,执行失败与否由退款基础服务自我保证,如果执行成功则发送一条执行退款成功消息投递到给消息队列。最后,定时任务会从数据库扫描在一定时间内未完成的消息并重新投递。这里,需要注意的是,自动化退款服务持久化的退款快照可以理解为需要确保投递成功的消息,由“正反向消息机制”和“定时任务”确保其成功投递。此外,真正的退款出账逻辑在退款基础服务来保证,因此它要保证幂等性,及出账逻辑的收敛。当出现执行失败的状态并且超过重试次数时,就说明这个任务永久失败了,需要开发人员进行手工介入与排查问题。

撸明白分布式事务(四)
撸明白分布式事务(四)

总结一下,引入了消息队列并不能保证可靠事件投递,换句话说,由于网络等各种原因而导致消息丢失不能保证其最终的一致性,因此,我们需要通过“正反向消息机制”确保了消息队列可靠事件投递,并且使用补偿机制尽可能在一定时间内未完成的消息并重新投递。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/172036.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 可靠事件模式
  • 正反向消息机制
相关产品与服务
消息队列 CMQ 版
消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档