V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
7911364440
V2EX  ›  程序员

问个分布式事务的问题

  •  
  •   7911364440 · 2023-05-24 14:31:28 +08:00 · 2047 次点击
    这是一个创建于 547 天前的主题,其中的信息可能已经有所发展或是发生改变。

    业务流程大概是这样,从 RocketMQ 消费消息,将收到的数据转发给多个数据源(可能是 MySQL 、Oracle 、Kafka 等)。
    问题是如何保证一条消息最终成功发送给所有客户端?假设写 MySQL 成功了,发送 Kafka 时失败了,要怎么重发

    目前想到的方案:

    1. 将发送失败的消息保存到本地数据库,同时记录目标客户端,再通过定时任务重发。
    2. 跟 1 类似,将失败的消息发送到消息队列中的某个特定的 topic ,客户端消费这个 topic 实现重发。
    23 条回复    2023-05-25 11:43:31 +08:00
    lhcnic
        1
    lhcnic  
       2023-05-24 14:42:17 +08:00
    RocketMQ 不是自带消费重试的吗?
    sujin190
        2
    sujin190  
       2023-05-24 14:48:51 +08:00
    或许可以更粗暴点,从 RocketMQ 收到消息通过新的交换机再次发送 RocketMQ 各个不同的数据源队列去,然后各数据源各自消费者,反正不成功消息不会从队列消息,自动就有重试
    wangpugod2003
        3
    wangpugod2003  
       2023-05-24 14:49:58 +08:00
    你这个需求,是保证整个流程,做到消息的 exactly once 处理.
    首先需要确认发出去了,每个模块收到了回 ack, 没有就 retry 该模块; 这样保证 > 1; 注意 retry 多次后如果没收到,再到定时任务中设置一段时间后重发,如果再没收到可以放到 dead message queue 中人工处理;
    然后每个对端都要 idempotent ,确保收到的消息(maybe > 1)但是只处理 1 次。一般是需要一个 transaction ID 。
    xhinliang
        4
    xhinliang  
       2023-05-24 14:50:08 +08:00
    第一种方法,用事务消息

    第二种,用 MySQL 记录下所有的客户端的发送状态
    1. 在准备发送前,写入 MySQL ,初始化所有客户端的写入状态为 INIT
    2. 给每一个客户端发送,发送成功则修改 MySQL 对应客户端的写入状态为 SUCC ,失败则修改为 FAILED
    3. 异步进程扫描所有状态为 INIT 或者 FAILED 的记录,重发
    xuanbg
        5
    xuanbg  
       2023-05-24 15:05:33 +08:00
    消息的消费者如果消费不成功,就把接收到的消息发送到延时队列。延时队列绑定正常的队列,这样过期时间到了就好自动转移到正常的队列里面,就能再次消费。如此循环不息,总有成功的时候。
    7911364440
        6
    7911364440  
    OP
       2023-05-24 15:34:58 +08:00
    @lhcnic 问题是需要保证所有客户端都成功接收。如果写 MySQL 成功了,发送 Kafka 时失败了,只需要给 Kafka 重发,不能给所有客户端都重发一遍,会有重复数据。
    7911364440
        7
    7911364440  
    OP
       2023-05-24 15:39:25 +08:00
    @sujin190 逻辑上应该是可行的,实现起来也很简单,但是会导致一条消息会出现在多个 topic 中,客户端越多冗余的数据就越多,太占用空间了,这个业务每天上千万的数据量,还有图片之类的数据,每条消息都很大。
    wu00
        8
    wu00  
       2023-05-24 15:40:03 +08:00
    如果你不能改变数据来源(RocketMQ)
    建议在[RocketMQ] 到 [写入多个数据源] 之间加一层 MQ 或者数据库,让整个业务变成多个独立的事务单元
    比如:接收到 RocketMQ 的消息,写入到 MQ ,有几个数据源写几条消息,再加一个消费端专门消费 MQ 中的消息分别写入各自的数据源。
    ConfusedBiscuit
        9
    ConfusedBiscuit  
       2023-05-24 16:04:12 +08:00
    分布式事务,自己想方案容易绕晕,可以找一些成熟的方案,比如 TCC 模型就比较简单易懂。收到 RocketMQ 消息后,自己按照 TCC 模型封装每个下游( MySQL 、Oracle 之类的比较简单,Kafka 之类的可能需要考虑用一些新特性)
    dqzcwxb
        10
    dqzcwxb  
       2023-05-24 16:17:56 +08:00
    你这个思路没有问题,但是注意的是要保证重发时的幂等
    Dream95
        11
    Dream95  
       2023-05-24 16:19:27 +08:00
    @7911364440 RocketMQ 本来就会存在重复消费问题,需要在消费者端去重的
    Dream95
        12
    Dream95  
       2023-05-24 16:20:01 +08:00
    @7911364440 可以用不同的消费组实现
    notwaste
        13
    notwaste  
       2023-05-24 16:21:24 +08:00
    做好做好监控,然后人工肉偿
    silypie
        14
    silypie  
       2023-05-24 16:25:33 +08:00
    一般都是自动重试,多次失败后人工处理吧
    silypie
        15
    silypie  
       2023-05-24 16:26:07 +08:00
    保证最终一致就行
    coderxy
        16
    coderxy  
       2023-05-24 16:26:51 +08:00
    要想实现分布式事务,最基本的一个点就是下游要自己保证幂等, 否则无从谈起。
    jorneyr
        17
    jorneyr  
       2023-05-24 16:49:40 +08:00
    这个是数据一致性吧,不算分布式事务,直接多次轮询进行补偿把没完成的给完成。
    如果是分布式事务,A 成功,B 失败,需要把 A 的操作给回滚。
    LeeSeoung
        18
    LeeSeoung  
       2023-05-24 16:53:52 +08:00
    有个极端情况要考虑哈,比如你发给某个消费端超时了,你认为是失败的,但是实际消费端是成功的,这个时候你做重试,要保证消费端的幂等。
    lvxiaomao
        19
    lvxiaomao  
       2023-05-24 16:56:06 +08:00
    感觉是没有问题的, 只要消费短幂等就好
    xyjincan
        20
    xyjincan  
       2023-05-25 08:48:35 +08:00
    kafka 好呀,不同类型客户消费,使用不同分组就行,共享一个消息队列
    documentzhangx66
        21
    documentzhangx66  
       2023-05-25 09:52:07 +08:00
    最省事的办法,是反向操作。

    需要全局一致性的数据,全部放在关系型数据库里处理,此数据库用于全局专门处理这类数据。这种设计设计,性能、备份、HA 都很好做。
    xiaohundun
        22
    xiaohundun  
       2023-05-25 10:35:19 +08:00
    是不是可以参考 seata 的 TCC 模式?
    candafromcn
        23
    candafromcn  
       2023-05-25 11:43:31 +08:00
    RocketMQ 不能像 kafka 一样分组消费吗,每一个需要同步的数据源认为是一个消费者,保证每个消费者 at least once 的消费就行了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   991 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 21:13 · PVG 05:13 · LAX 13:13 · JFK 16:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.