聊聊业务系统中投递消息到mq的几种方式

背景

电商中有这样的一个场景:

  1. 下单成功之后送积分的操作,我们使用mq来实现
  2. 下单成功之后,投递一条消息到mq,积分系统消费消息,给用户增加积分

我们主要讨论一下,下单及投递消息到mq的操作,如何实现?每种方式优缺点?

方式一

step1:start transaction
step2:生成订单
step3:投递消息到mq
step4:commit transaction

这种方式是将发送消息放在了事务提交之前,可能存在的问题:

step3发生异常

导致step4失败,下单失败,直接影响到下单业务

step4发生异常,其他step成功

下单失败,消息投递成功,给用户增加了积分

方式二

我们将发送消息放到事务之后进行:
step1:start transaction
step2:生成订单
step3:commit transaction
step4:投递消息到mq

可能会出现的问题:
step4发生异常,其他step成功

导致下单成功,投递消息失败,用户未增加积分

上面两种是比较常见的做法,也是最容易出错的。

方式三

step1:start transaction
step2:生成订单
step3:本地库中插入一条需要发送消息的记录t_msg_record
step3:commit transaction
step5:新增一个定时器,轮询t_msg_record,将待发送的记录投递到mq中

这种方式借助了数据库的事务,业务和消息记录作为了一个原子操作,业务成功之后,消息日志必定是存在的。解决了前两种方式遇到的问题。如果我们的业务系统比较单一,可以采用这种方式。

对于微服务化的情况,上面这种方式不是太好,每个服务都需要上面的操作;也不利于扩展。

方式四

增加一个消息服务消息库,负责消息的落库、将消息发送投递到mq。

step1:start transaction
step2:生成订单
step3:当前事务库插入一条日志:生成一个唯一的业务id(bus_id),将bus_id和订单关联起来保存到当前事务所在的库中
step4:调用消息服务:携带bus_id,将消息先落地入库,此时消息的状态为待发送状态,返回消息id(msg_id)
step5:commit transaction
step6:如果上面都成功,调用消息服务,将消息投递到mq中;如果上面有失败的情况,则调用消息服务取消消息的发送

能想到上面这种方式,已经算是有很大进步了,我们继续分析一下可能存在的问题:

  1. 系统中增加了一个消息服务,下单操作依赖于该服务,业务对改服务依赖性比较高,当消息服务不可用时,整个业务将不可用。
  2. 若step6失败,消息将处于待发送状态,此时业务方需要提供一个会查接口(通过bus_id查询),验证业务是否执行成功;消息服务需新增一个定时任务,对于状态为待发送状态的消息做补偿处理,检查一下业务是否处理成功;从而确定消息是投递还是取消发送
  3. step4依赖于消息服务,如果消息服务性能不佳,会导致当前业务的事务提交时间延长,容易产生死锁,并导致并发性能降低。我们通常是比较忌讳在事务中做远程调用处理的,远程调用的性能和时间往往不可控,会导致当前事务变为一个大事务,从而引发其他故障。

方式五

在以上方式中,我们继续改进,进而出现了更好的一种方式:

step1:生成一个全局唯一业务消息id(bus_msg_id),调用消息服务,携带bus_msg_id,将消息先落地入库,此时消息的状态为待发送状态,返回消息id(msg_id)
step2:start transaction
step3:生成订单
step4:当前事务库插入一条日志(将step3中的业务和bus_msg_id关联起来)
step5:commit transaction
step6:分2中情况:如果上面都成功,调用消息服务,将消息投递到mq中;如果上面有失败的情况,则调用消息服务取消消息的发送

方式五和方式四对比,比较好的一个地方:将调用消息服务,消息落地操作,放在了事务之外进行,这点小的改进其实算是一个非常好的优化。

总结

  1. 若我们的系统系统比较小比较单一简单,建议采用方式三
  2. 若我们的系统采用微服务的方式,建议使用方式五
  3. 你们的系统中如何发送消息的,大家可以留言,我们一起讨论,一起进步。

mq系列整个内容

  1. 聊聊mq的使用场景
  2. 聊聊业务系统中投递消息到mq的几种方式
  3. 如何确保投递消息一定成功?
  4. 聊聊消息消费的几种方式
  5. 如何确保消息至少消费一次
  6. 如何保证消息消费的幂等性

路人甲Java,只生产干货,公众号:javacode2018,喜欢的关注一下。

原文地址:https://www.cnblogs.com/itsoku123/p/10813423.html

时间: 2024-08-05 04:50:02

聊聊业务系统中投递消息到mq的几种方式的相关文章

Windows系统中监控文件复制操作的几种方式

http://blog.sina.com.cn/s/blog_4596beaa0100lp4y.html 1. ICopyHook 作用: 监视文件夹和打印机移动,删除, 重命名, 复制操作. 可以得到源和目标文件名. 可以控制拒绝操作. 缺点: 不能对文件进行控制. 只对Shell文件操作有效, 对原生Api MoveFile, CopyFile之类的操作无效. 用法: 从ICopyHook派生一个COM对象, 重载CopyCallbackA和CopyCallbackW, 然后把COM注册到H

现代IM系统中的消息系统架构

前言IM全称是『Instant Messaging』,中文名是即时通讯.在这个高度信息化的移动互联网时代,生活中IM类产品已经成为必备品,比较有名的如钉钉.微信.QQ等以IM为核心功能的产品.当然目前微信已经成长为一个生态型产品,但其核心功能还是IM.还有一些非以IM系统为核心的应用,最典型的如一些在线游戏.社交应用,IM也是其重要的功能模块.可以说,IM系统已经是任何一个带有社交属性的应用需要具备的基础功能,网络上对于这类系统的设计与实现的讨论也越来越多. IM系统在互联网初期即存在,其基础技

实现业务系统中的用户权限管理--实现篇

在设计篇中,我们已经为大家阐述了有关权限管理系统的数据库设计,在本篇中,我们将重点放在其实现代码部分.为了让你能够更直接更有效的看到全部动作的代码,我们使用"动作分解列表"的方式来陈述每个动作以及相关资源. 实现权限管理功能的动作 动作分解 动作名 相关表名 操作集类型 (S,U,I,D,SQL) 表单 模组 字符资源 是否分页? 返回提示? 权限检测 权限初始化安装 setup 无 无 无 setup setupok 否 否 否 显示添加管理组界面 addnewgroup 无 无 a

实现业务系统中的用户权限管理--设计篇

B/S系统中的权限比C/S中的更显的重要,C/S系统由于具有特殊的client,所以訪问用户的权限检測能够通过client实现或通过client+server检測实现,而B/S中,浏览器是每一台计算机都已具备的,假设不建立一个完整的权限检測,那么一个"非法用户"非常可能就能通过浏览器轻易訪问到B/S系统中的全部功能.因此B/S业务系统都须要有一个或多个权限系统来实现訪问权限检測,让经过授权的用户能够正常合法的使用已授权功能,而对那些未经授权的"非法用户"将会将他们彻

[转]实现业务系统中的用户权限管理--设计篇

  实现业务系统中的用户权限管理--设计篇 B/S系统中的权限比C/S中的更显的重要,C/S系统因为具有特殊的客户端,所以访问用户的权限检测可以通过客户端实现或通过客户端+服务器检测实现,而B/S中,浏览器是每一台计算机都已具备的,如果不建立一个完整的权限检测,那么一个“非法用户”很可能就能通过浏览器轻易访问到B/S系统中的所有功能.因此B/S业务系统都需要有一个或多个权限系统来实现访问权限检测,让经过授权的用户可以正常合法的使用已授权功能,而对那些未经授权的“非法用户”将会将他们彻底的“拒之门

现代IM系统中的消息系统架构 - 架构篇

https://mp.weixin.qq.com/s/sAlI8MCIKUSj5KbcT3W-Lw 现代IM系统中的消息系统架构 - 架构篇 原创: 木洛 云栖社区 1周前 原文地址:https://www.cnblogs.com/yuanjiangw/p/10786256.html

C#中关于增强类功能的几种方式

C#中关于增强类功能的几种方式 本文主要讲解如何利用C#语言自身的特性来对一个类的功能进行丰富与增强,便于拓展现有项目的一些功能. 拓展方法 扩展方法被定义为静态方法,通过实例方法语法进行调用.方法的第一个参数指定该方法作用于哪个类型,并且该参数以 this 修饰符为前缀.仅当使用 using 指令将命名空间显式导入到源代码中之后,扩展方法才可使用. namespace Extensions { public static class StringExtension { public stati

Struts2中获取HttpServletRequest,HttpSession等的几种方式

转自:http://www.kaifajie.cn/struts/8944.html package com.log; import java.io.IOException; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import o

WPF中使用文件浏览对话框的几种方式

原文:WPF中使用文件浏览对话框的几种方式 WPF本身并没有为我们提供文件浏览的控件, 也不能直接使用Forms中的控件,而文件浏览对话框又是我们最常用的控件之一. 下面是我实现的方式 方式1: 使用win32控件OpenFileDialog ? 1 2 3 4 5 6 7 Microsoft.Win32.OpenFileDialog ofd = new Microsoft.Win32.OpenFileDialog(); ofd.DefaultExt = ".xml"; ofd.Fil