zoukankan      html  css  js  c++  java
  • [转] 聊聊业务系统中投递消息到mq的几种方式

    背景

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

    • 下单成功之后送积分的操作,我们使用mq来实现
    • 下单成功之后,投递一条消息到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中;如果上面有失败的情况,则调用消息服务取消消息的发送

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

    系统中增加了一个消息服务,下单操作依赖于该服务,业务对改服务依赖性比较高,当消息服务不可用时,整个业务将不可用。
    若step6失败,消息将处于待发送状态,此时业务方需要提供一个回查接口(通过bus_id查询),验证业务是否执行成功;消息服务需新增一个定时任务,对于状态为待发送状态的消息做补偿处理,检查一下业务是否处理成功;从而确定消息是投递还是取消发送
    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中;如果上面有失败的情况,则调用消息服务取消消息的发送

    若step6失败,消息将处于待发送状态,此时业务方需要提供一个回查接口(通过bus_msg_id查询),验证业务是否执行成功;消息服务需新增一个定时任务,对于状态为待发送状态的消息做补偿处理,检查一下业务是否处理成功;从而确定消息是投递还是取消发送

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

    file

    总结

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

    MQ系列整个内容,我们将讨论:

    【转载自:原文地址

  • 相关阅读:
    RHEL简单管理SELINUX
    CentOS配置samba服务
    CentOS中配置NFS服务
    CentOS里route命令详解
    Linux 进程管理之四大名捕
    编辑器之神-VIM
    纠结的链接——ln、ln -s、fs.symlink、require
    History(历史)命令用法 15 例
    备份MySQL数据库
    MySQL 资源大全
  • 原文地址:https://www.cnblogs.com/eedc/p/12851309.html
Copyright © 2011-2022 走看看