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