请教一个面试题: MQ 顺序消费时出错,怎么处理?

172 天前
 dumbbell5kg

大致意思就是:MQ 顺序消费时,消息 2 依赖消息 1 ,消息 3 依赖消息 2 ,如果消息 2 出错了,后续还有几十万条消息,应该怎么处理?

下面是面试官的原话:

4860 次点击
所在节点    程序员
37 条回复
prime2015
172 天前
这种是需要对方付咨询费的
younger027
172 天前
@loveaeen 哈哈哈 转人工实在是没绷住
Scarb
172 天前
我觉得这种就不该用顺序消息,而是用事务。因为他这样顺序消费实际上只允许一个消费节点同步执行这些步骤,用消息来处理没什么意义。如果一定想用消息作为服务间通信,那就发一条消息,然后收到消息的服务用事务来执行这些步骤。

用顺序消息的情况下怎么都不好。
1. 重试——如果这个消息一直消费不成功一直重试,那会阻塞后面的业务消息消费
2. 找个表存——如果 2 执行失败,3 实际上不应该执行。如果只把 2 存起来,执行 3 ,那执行结果不是你期望的。如果 2 和 3 都要存起来,那消费 3 的时候怎么去判断 2 失败了并且 3 依赖 2 呢?
woodfizky
172 天前
这就是他们实际业务遇到的问题吧哈哈,面试官自己头疼怎么解决呢拿来问你了。

关键是消息队列还要考虑消息消费的顺序和依赖性,这就把问题复杂化了。
怎么判断消息前后是否还有消息需要依赖?或者怎么做能够把依赖体现在消息内?
这也是设计需要考虑的。

如果非要这么干,那我个人倾向于,这些消息识别到后,用别的资源去处理,别干扰正常消息的消费。
比如消费正常队列的时候,判断出来某些消息是有依赖顺序的,那就再推到专门的特殊队列里,或者干脆就直接按需存数据库。
等最后一条消息推送进来,特殊队列的消费端程序能够判断没有后续依赖了,再整个链条一起拎出来处理。
但是这样对延时有影响。不过看面试官的说法,这个等了两天才发现,也是有点离谱,应该要求不高。
hikarugo
172 天前
搁这跟我玩循环引用呢,是想让设计 gc 是吧

消息队列首先是一个队列,队列能保证什么?顺序呗,把其他的任务分配到队列上增加复杂性就违反了 KISS
事务:那我走?
bk201
172 天前
我的想法是第一个所有依赖的消息走一个 tag ,消费失败了一个后续全部进数据库或者 retry 队列里做重试。第二个改掉消息顺序依赖的方式,对相关的顺序操作走同一个消息做顺序执行,消息只做触发。
yvyvyv
172 天前
认同#24 说的 "他们实际业务遇到这个问题了",可能是屎山要炸,还没想好怎么解决,直接将问题抛给面试的。
三个消息需要有序执行,这本来就应该用同步单线程做的事放到一个事务中,这样也能避免多个消费者导致错序。
也可以
合并成一个消息,被消费者拿到后将消息交给逻辑 1 执行,执行后通过事件或者当前消费者服务器的 queue 交到 2 逻辑,同上交到 3 ,同时这个消息需要一个唯一标识,出错记录 db 中。如果 1 出错了也不会发送事件到 2-3 。人工也好,搞个 soc 预警也好。监控下错误日志即时排查呗
ckdxc
172 天前
@Scarb
@woodfizky
我也没明白他那个依赖是整个队列都有顺序的依赖, 还是就 局部一些消息有依赖
msg3 依赖 msg2, msg2 依赖 msg1, msg4 不依赖 msg3 了, 这样就类似分片了, 只需要做好分片管理, 接收分片 1 的时候创建分片任务(存内存, 或者数据库), 分片处理失败, 就回调一个失败回去呗, 让生产方重传整个分片, 或者通知到用户业务失败了

如果真的是整个队列都是一条一条的有依赖关系, 那也只能是存起来, 有专门的程序处理, 或者人工处理

突然想起来, 我这里也有个, 数据的增量同步, 中间有失败的, 真的就是阻塞后面的增量数据了,
不过有个全同步机制, 可以按数据表级别, 同步到对端,
有一个环境, 没人管, 积压了 100 多 G 数据在增量同步表里面, 不过是积压在生产者这边

既然依赖关系这么强, 建议每条消息都增加处理标识, 要么生产者拉取一下结果, 失败了就不发 msg3 了, 要么消费者自己调回调, 具体看业务, 可能有些业务, 能定时核对, 保证正确, 有些就必须得人工介入处理
qxmqh
172 天前
这很明显设计上有问题,我估计就是屎山,可能也不是他设计的,但是他遇到了,想问你,保不齐就要爆炸了。很大可能即使你入职了,第一个解决的问题就是这个问题。
F281M6Dh8DXpD1g2
172 天前
这面试官自己都没搞懂就在这装了,别理他就行了,真做系统的哪有这么搞的
X2S2
172 天前
同意 27 楼的

我猜测他的场景可能是消息重放,比如物流状态的变更。我怀疑是不是同一家面试的

如果出现异常,将 异常数据及后面依赖的消息 直接或者指定重试次数后报警并写入数据库(可以把一组数据的标识 和 msgid ,消息内容等写入)。
例如出问题的是 1 ,将 1 写入异常数据库,如果消费 2 的时候,判断异常数据库中是否有 1 ,有就先写入,不做消费。
写入的数据,人工再处理。



# 如果使用的是 rocketmq 的顺序消息
1 、顺序发送
理论上在集群环境,生产者不唯一,那么发送到 broke 队列里的消息顺序可能是乱序的。
基于他的场景,应该 1 ,2 ,3 这种顺序大概率存在时间差,可能不用考虑乱序。
如果要保证顺序发送,可能采用的是记录日志,然后使用定时任务或者 timer 来发送,并且分布式锁保证任务只有一个节点执行,保证发送者的唯一进而保证消息发送的顺序性。

2 、顺序消费
rocketmq 客户端通过 申请 broke 锁保证一个消费者拉取消息、通过对消费队列加锁保证一个线程可以做消费。
如果出异常,因为顺序消息重试次数默认-1 即一只重试,所以会阻塞队列。这种情况主动报警,并记入数据库,后续如果还有依赖的消息,直接标记为异常,同样记入数据库。
julyclyde
172 天前
@liprais
sampeng
172 天前
这就是死区队列的用法啊。mq 不自带,aws 的 sqs 是可以开启死区队列。处理消息的时候发现有问题放入死区队列。另外有消费者单独处理死区队列按业务逻辑进行处理就好了,即不会堵后面的,后面的发现前置任务错误直接快速失败也和 redis 等东西配合也可以快速做到。mq 也可以做到,就是比较麻烦而且原子处理没研究过是不是要一些特殊处理,大概逻辑就是这样。
strivezheng
171 天前
加一个 redis 缓存池,所有的消息都要入池。如果 2 失败了,当消费到 3 时,根据依赖关系向前溯源(在缓存池中查数据),把关联的 123 都重新投递到消息队列进行消费。这样既保证了消息不会丢失,也能不阻塞
ayogo
171 天前
@ytmsdy 他们的那套感觉像是纯粹的在原有数据上进行加减式更新,太奇怪了。对于依赖性强的数据,必须得在中间插一个校验和同步的数据条目,之前写过一个远程屏幕的方案就是用对画面进行分割,然后检查每个区域相比上一帧是否有更新,结果发现一旦出现丢包那么就会出现严重的糊屏,必须定期加同步帧来确保画面没问题。
CoderChan
171 天前
@barnetime 顺序问题 mq 本身有机制
barnetime
171 天前
@CoderChan 如果需要严格顺序, mq 是不能保证的

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

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

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

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

© 2021 V2EX