问个分布式事务的问题

2023-05-24 14:31:28 +08:00
 7911364440

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

目前想到的方案:

  1. 将发送失败的消息保存到本地数据库,同时记录目标客户端,再通过定时任务重发。
  2. 跟 1 类似,将失败的消息发送到消息队列中的某个特定的 topic ,客户端消费这个 topic 实现重发。
1817 次点击
所在节点    程序员
23 条回复
lhcnic
2023-05-24 14:42:17 +08:00
RocketMQ 不是自带消费重试的吗?
sujin190
2023-05-24 14:48:51 +08:00
或许可以更粗暴点,从 RocketMQ 收到消息通过新的交换机再次发送 RocketMQ 各个不同的数据源队列去,然后各数据源各自消费者,反正不成功消息不会从队列消息,自动就有重试
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
2023-05-24 14:50:08 +08:00
第一种方法,用事务消息

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

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/942575

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX