博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
分布式系统中的消息队列传递
阅读量:4113 次
发布时间:2019-05-25

本文共 2550 字,大约阅读时间需要 8 分钟。

1.消息队列

在分布式系统架构中,消息队列的核心职责是为不同的应用系统提供异步通信服务,通常涉及以下三个重要角色:

• 消息发布者,发送消息的应用系统,负责创建消息对象并通过网络发布到消息Broker,发布的过程一般是同步的。
• 消息Broker,异步消息的“代理人”,负责接收并持久化消息,保证将消息投递到指定的消息订阅者应用系统。
• 消息订阅者,订阅消息的应用系统,负责消费消息Broker投递过来的消息。

在这里插入图片描述

异步消息队列
在这里插入图片描述

  1. “账单服务” 处理 “账单查询Case” 的耗时由 60 ms 缩减至 13 ms, 提高了服务的吞吐量。
  2. “账单服务” 和 “风险控制服务”、“短信通知服务” 完全解耦,如果在业务演进过程中,增加了新的下游服务,“账单服务” 完全无需变更。
  3. 当 “风险控制服务” 和 “短信通知服务” 不可用时,不会导致 “账单服务” 不可用,
    通过以上 “账单查询Case” 的设计方案,可以阐明引入消息队列给分布式应用架构带来的三大核心优势。

如果需求是 “变更Case”,要实现的基本业务逻辑如下:

  1. 写入数据库,变更指定账户的账单记录。
  2. 记录用户检索行为,为风险控制提供数据积累。
  3. 发送短信到用户手机,通知用户其账户变更金额。
    与 “查询Case” 的区别在于数据库操作是写入,而不再是检索。二者的主要区别是 “数据库检索” 不涉及数据库事务,而 “数据库写入” 一定会涉及到数据库事务,按照之前的引入消息队列设计思路,“变更Case” 的设计方案如下:
    在这里插入图片描述

“变更Case” 消息队列设计方案存在一个严重问题: “变更” 关联的数据库变更事务提交成功后,如果 “发布账单变更消息” 发送失败(例如网络异常或者消息队列服务不可用),则会导致 “记录用户行为” 和 “短信通知用户” 后续动作失败,无法完成风险控制数据积累,用户也无法及时获取到账户变更信息。

为了解决这个严重问题,初步考虑先发布 “变更” 消息,再做数据库变更的设计方案。但还是无法解决 “消息发布” 和 “数据库事务” 可能不一致性的严重问题,如果消息已发布成功过了,数据库变更事务回滚了,就会导致用户的账单没有变更,但用户却收到了账户变更短信,存在一致性漏洞的 “变更Case” 消息队列设计方案如下所示
在这里插入图片描述

2.事务型消息接收与投递流程

事务型消息是否被投递与发送端系统本地数据库事务保持一致,如果本地数据库事务提交则消息会被投递给订阅端;如果本地数据库事务回滚,则直接丢弃消息不投递给订阅端系统。

  • 事务型消息设计方案
    为了解决 “消息发布” 和 “数据库事务” 不一致性的严重问题,消息队列需要提供一种特殊类型的消息:消息队列收到消息后不会立刻投递消息到消息订阅者,而是根据消息发布者应用的数据库事务状态决定消息是否投递。如果数据库事务提交,则消息投递到订阅者;反之则不投递。此类消息被命名为 “事务型消息”。具体设计方案如下:
    在这里插入图片描述

事务型消息回查——两阶段消息流程

事务型消息流程的第一阶段是消息发布端发送消息到可靠消息组件,第二阶段是消息发布端发送提交或者回滚指令到可靠消息组件,可靠消息组件根据此指令决定是否投递消息到订阅端系统。当第二阶段指令出现异常时,可靠消息组件在一定时间后主动回查消息发送端系统,确认对应的事务型消息是否投递。
按照 “事务型消息设计方案E” 的时序图,消息发布者和消息队列之间增加了一个 “二阶段” 消息,用来标明对应事务型消息的 “事务状态”,消息队列根据 “二阶段” 消息决定是否投递消息到下游消息订阅者。应用 “事务型消息”,“账单变更Case” 的可行解决方案如下所示:
在这里插入图片描述
至于依据数据库事务提交/回滚状态决定事务型 “二阶段” 消息的发送,可以通过Spring Framework提供的事务模板同步器自动感知消息发布者本地事务状态,相关接口是:
按照 “账单变更Case” 消息队列-事务型消息设计方案 ,可以满足“账单服务数据库变更”与“异步消息是否投递到订阅者应用”的事务一致性需求。结合Spring Framework的事务模板工具类伪代码如下:

transactionTemplate.execute(new TransactionCallback() {
@Override public Object doInTransaction(TransactionStatus status) {
try {
messageQueueSDK.publishTransactionMessage(message); dbService.doUpdateOperation(); } catch (Exception e) {
status.setRollbackOnly(); } return null; }});

至此,消息队列 “事务型消息” 的设计方案和实现原理基本阐明清楚了,还遗留两个可以深究的关键点:

  • 为什么消息发布方法需要在本地数据库事务方法之前?
  • 如果消息队列收不到事务型消息的二阶段“提交 or 回滚” 消息,如何处理?
    在这里插入图片描述
    在分布式系统架构中,消息队列提供 “事务型消息” 特性是必不可少的,主要注意三个核心点:
  1. 消息队列事务型消息基于 “二阶段” 消息实现。
  2. 事务型消息是否投递与消息发布者本地事务状态保持一致。
  3. 事务型消息状态回查是保证了 “事务型消息” 的严谨性。

3.消息传递的推拉模式思考

• 推模式push:由消息中间件主动发消息给消费者

• 拉模式pull:消费者主动从消息中间件拉取消息
• 比较:采用push模式,可以尽可能快的把消息发给消费者,但是如果消费者处理一条消息能力较弱(处理时间长),消息中间件会不断的发消息给消费者,到时消费者的缓存区溢出;采用pull模式,可能会增加消息的延迟。
• 消息订阅者收到的消息不保证有序,即收到消息的顺序与发布者发送消息的顺序可能会不一致
• 消息投递策略为至少一次,即对于同一条消息消息订阅者可能收到多次,要求订阅者保证幂等特性

转载地址:http://sbrsi.baihongyu.com/

你可能感兴趣的文章
Servlet的生命周期
查看>>
JAVA八大经典书籍,你看过几本?
查看>>
《读书笔记》—–书单推荐
查看>>
【设计模式】—-(2)工厂方法模式(创建型)
查看>>
有return的情况下try catch finally的执行顺序(最有说服力的总结)
查看>>
String s1 = new String("abc"); String s2 = ("abc");
查看>>
JAVA数据类型
查看>>
Xshell 4 入门
查看>>
SoapUI-入门
查看>>
Oracle -常用命令
查看>>
JAVA技术简称
查看>>
ORACLE模糊查询优化浅谈
查看>>
2016——个人年度总结
查看>>
2017——新的开始,加油!
查看>>
【Python】学习笔记——-6.2、使用第三方模块
查看>>
【Python】学习笔记——-7.0、面向对象编程
查看>>
【Python】学习笔记——-7.1、类和实例
查看>>
【Python】学习笔记——-7.2、访问限制
查看>>
【Python】学习笔记——-7.3、继承和多态
查看>>
【Python】学习笔记——-7.4、获取对象信息
查看>>