(转)蚂蚁金服:消息队列事务型消息原理浅析

原文地址:https://yq.aliyun.com/articles/596022
摘要: 在金融级分布式架构的领域内,消息队列是普遍被应用的异步通信产品,本文主要分为以下几个小结,循序渐进的对消息队列产品事务型消息设计原理进行分析和阐述: 消息队列简介 消息队列应用实例 事务型消息设计方案 事务型消息总结 消息队列简介 在分布式系统架构中,消息队列的核心职责是为不同的应用系统提供异步通信服务,通常涉及以下三个重要角色: 消息发布者,发送消息的应用系统,负责创建消息对象并通过网络发布到消息 Broker,发布的过程一般是同步的。

在金融级分布式架构的领域内,消息队列是普遍被应用的异步通信产品,本文主要分为以下几个小结,循序渐进的对消息队列产品事务型消息设计原理进行分析和阐述:

  1. 消息队列简介

  2. 消息队列应用实例

  3. 事务型消息设计方案

  4. 事务型消息总结

消息队列简介

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

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

在分布式系统架构中,引入消息队列带来的三大核心优势如下:

  • 提高核心链路吞吐量

  • 降低应用系统之间的耦合度

  • 增强整体服务的高可用能力

消息队列应用实例

分析和设计一个典型的支付应用业务逻辑,以 “账单查询 Case” 为例,基本业务逻辑如下:

  • 检索数据库,获取指定账户的账单记录。
  • 记录用户的检索行为,为风险控制提供数据积累。
  • 发送短信到用户手机,通知用户其账单被查询事件。

依赖 “同步 RPC” 的设计方案 A 如下所示:

“账单检索 Case” 同步 RPC 设计方案 A

依赖 “异步消息队列” 的设计方案 B 如下所示:


“账单检索 Case” 消息队列设计方案 B

对比以上 A, B 两个设计方案,引入消息队列的设计方案 B 具有如下优势:

  • “账单服务” 处理 “账单查询 Case” 的耗时由 60 ms 缩减至 13 ms, 提高了服务的吞吐量。
  • “账单服务” 和 “风险控制服务”、“短信通知服务” 完全解耦,如果在业务演进过程中,增加了新的下游服务,“账单服务” 完全无需变更。
    当 “风险控制服务” 和 “短信通知服务” 不可用时,不会导致 “账单服务” 不可用。

通过以上 “账单查询 Case” 的设计方案,可以阐明引入消息队列给分布式应用架构带来的三大核心优势。

下面继续分析和设计 “账单变更 Case”,基本业务逻辑如下:

  • 写入数据库,变更指定账户的账单记录。
  • 记录用户检索行为,为风险控制提供数据积累。
  • 发送短信到用户手机,通知用户其账户变更金额。

与 “账单查询 Case” 的区别在于数据库操作是写入,而不再是检索。二者的主要区别是 “数据库检索” 不涉及数据库事务,而 “数据库写入” 一定会涉及到数据库事务,按照之前的引入消息队列设计思路,“账单变更 Case” 的设计方案 C 如下:

“账单变更 Case” 消息队列设计方案 C

“账单变更 Case” 消息队列设计方案 C 存在以下严重问题:

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

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

“账单变更 Case” 消息队列设计方案 D

至此,可以梳理一下完美解决 “账单变更 Case” 需要解决的关键点:

  • 必须满足“一致性”要求,即账单服务数据库变更事务提交成功,风险控制服务和短信通知服务收到“账单变更”消息;账单服务数据库变更事务回滚,风险控制服务和短信通知服务不会收到“账单变更”消息。

  • “账单变更”消息发布失败,尽量避免导致数据库变更事务的回滚。

事务性消息设计方案

为了解决以上描述的两个需求,消息队列需要提供一种特殊类型的消息:消息队列收到消息后不会立刻投递消息到消息订阅者,而是根据消息发布者应用的数据库事务状态决定消息是否投递。如果数据库事务提交,则消息投递到订阅者;反之则不投递。此类消息被命名为 “事务型消息”。具体设计方案如下:

事务型消息设计方案 E

按照 “事务型消息设计方案 E” 的时序图,消息发布者和消息队列之间增加了一个 “二阶段” 消息,用来标明对应事务型消息的 “事务状态”,消息队列根据 “二阶段” 消息决定是否投递消息到下游消息订阅者。应用 “事务型消息”,“账单变更 Case” 的可行解决方案如下所示:


账单变更 Case” 消息队列-事务型消息设计方案 F

按照 “账单变更 Case” 消息队列-事务型消息设计方案 F,可以满足“账单服务数据库变更”与“异步消息是否投递到订阅者应用”的事务一致性需求。结合 Spring Framework 的事务模板工具类伪代码如下:

transactionTemplate.execute(new transactionCallback(){

@override

public Object doInTransaction(TransationStatus status){

try{

messageQueueSDK.publishTransactionMessage(message);

dbService.doUpdateOperation();

}catch (Exception e){

status.seRollbackOnly();

return null;

}

};

至于依据数据库事务提交/回滚状态决定事务型 “二阶段” 消息的发送,可以通过 Spring Framework 提供的事务模板同步器自动感知消息发布者本地事务状态,相关接口是:

org.springframework.transaction.support

TransactionSynchronizationManager.registerSyncronization(TransactionSyncronization)

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

  1. 为什么消息发布方法需要在本地数据库事务方法之前?

  2. 如果消息队列收不到事务型消息的二阶段“提交 or 回滚” 消息,如何处理?

针对第一个关键点,假定方法执行时序是先执行本地数据库事务方法,之后发布 “事务型” 消息,那么消息发布失败会导致消息发布者本地事务回滚,这明显是不符合预期的,因为数据库事务回滚的成本比较消息发布失败是高昂的。

针对第二个关键点,在分布式网络架构中是可能出现的,比如网络异常、消息队列服务短时间不可用等。这也是消息队列提供严谨的 “事务型消息” 特性必须要解决的问题,如果消息队列没有收到 “提交 or 回滚” 回滚消息,则无法决定是否投递消息到消息订阅者,因此,严谨的 “事务型消息” 设计方案需要有一个异常场景,命名为 “事务型消息状态回查”,具体设计方案如下:

事务型消息回查设计方案G

需要明确的是,“事务型消息状态回查” 只在 “提交 or 回滚消息” 失败的场景下被触发,属于异常路径。

事务性消息总结

在分布式系统架构中,尤其是金融级业务应用的解决方案设计中,消息队列提供 “事务型消息” 特性是必不可少的,“数据一致性” 是金融级分布式架构的基本要求,本文通过实例逐步说明消息队列产品支持 “事务型消息” 的必要性、设计方案和原理,定义了明确的消息队列事务型消息的核心原理:

  • 消息队列事务型消息基于 “二阶段” 消息实现。

  • 事务型消息是否投递与消息发布者本地事务状态保持一致。

  • 事务型消息状态回查是保证了 “事务型消息” 的严谨性。

原文发布时间为:2018-05-22

本文来自云栖社区合作伙伴“中生代技术”,了解相关信息可以关注“中生代技术”。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容

  • 消息队列设计精要 消息队列已经逐渐成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终...
    meng_philip123阅读 1,511评论 1 25
  • “ 消息队列已经逐渐成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列...
    落羽成霜丶阅读 3,982评论 1 41
  • 前天我不是写了一篇关于《开始写吧——非虚构文学创作》的书评吗?当时写它时我才看了两章,就夸它好。 今天看完全书我才...
    妮娜读书阅读 984评论 2 21
  • 前言 作为一名后端狗,也有一个全栈梦。前端工程师第一步,CSS的基础得打好。本文仅用于记录上周学习CSS的过程,文...
    LNAmp阅读 399评论 0 2
  • Vuder说说 兴证资管的网站总算更新了第二版,凡事自己跟的痛苦和错误也是完全经历才能体会的。近期金融市场也有了很...
    vuder阅读 354评论 0 0