大致意思就是:MQ 顺序消费时,消息 2 依赖消息 1 ,消息 3 依赖消息 2 ,如果消息 2 出错了,后续还有几十万条消息,应该怎么处理?
下面是面试官的原话:
1
poltao 1 天前
根据他的回答来看用的不还是消息队列本身的机制嘛(加上业务消费出错直接报异常),没搞明白他问这个问题的意义是什么,这个面试题不好
|
2
blackstack 1 天前 ![]() 感觉他这个设计是有问题的,如果队列同时存在 N 个消费者,他又要怎么去控制消息消费的前后顺序??
整个题目看起来是在一个错误的设计上,把一个问题复杂化,再寻找一个解决的方法来纠正整个错误。 |
![]() |
3
wangtian2020 1 天前
事务回滚不行吗
|
![]() |
4
meshell 1 天前
和二楼的想法一样。
|
5
blackstack 1 天前
如果他每次依赖的消息数量是固定的,比如固定 3 个,那消费者每次取 3 条消息,取出后就进行消息确认,再对消息内的数据进行处理。
如果在处理的过程出现异常,将这三条存在异常的消息发到另外一个专门处理异常的队列,由另外一个消费者来处理这些异常数据。 如果他连每次依赖的消息数量都不固定,我还是认为这个设计是存在问题的。 |
![]() |
6
lyxxxh2 1 天前
[laravel - 任务链]( https://learnku.com/docs/laravel/10.x/queues/14873#f62992)
框架自带的队列,一般都有任务链吧。 |
![]() |
7
SeaTac 1 天前 via iPhone
别的不说 光是这个“发现出问题是两天前了”
没有 alarm 么 在我眼里能拖两天的问题说明不是啥严重的问题 |
![]() |
8
SeaTac 1 天前 via iPhone
另外 mq 自带 dlq 吧 把 message 放进去呗
|
9
loveaeen 1 天前
tag 按照依赖进行消息区分,同类型打到一个分区里,减少队列堆积数。
然后要不就发到死信做二次消费,还报错就转人工吧。 |
![]() |
10
ytmsdy 1 天前
同意二楼的观点,这个消息队列设计的就是有问题的。为什么要设计一个相互依赖的消息队列?
为什么不在设计队列的时候,把一整个消费动作都整理成一个?反正一个出错了,整个事务都是要混滚的。 |
![]() |
12
barnetime 1 天前
你不如问他 怎么保证消息的顺序性, 发送的时候时 123, 到 mq 变成了 213 (
|
13
StephenCurryII 1 天前 ![]() 有些面试官问的就是他们开发中遇到的问题,但可能本身就是设计缺陷,却要应聘者找答案给他解决,这种解决,每个人的方式都不一样,我能给你解决鸡毛啊。
|
![]() |
15
me1onsoda 1 天前
不理解为什么会 hang 住,默认已经实现按序消费,1 消费了,消费 2 出错发回队列,后续的消息也重新发回队列,还是顺序消费的
|
![]() |
16
abc950309 1 天前
主流 MQ 都有顺序保证机制的,比如 Kafka 的 Message Key 。如果消费侧配置的没问题,就可以确保同一 Message Key 下的消息消费是有序的。
|
17
z1829909 1 天前 via Android
他这里设计有点拧巴,让消息之间有逻辑上的依赖可以,但是不代表有依赖就要等前置项消费完。
可以接收到一个消息找个地方暂存,然后这条消息就 ack ,当最后一个消息到,校验下是否收齐了,然后 merge 。 |
18
z1829909 1 天前 via Android ![]() 你的方案是对的,几十万消息根本就是特别低的量级,找个表存一下。
换句话说,如果他们的量只有几十万,直接同步做算了,为啥要整成异步,结果还在用同步的思想硬整异步的逻辑。 |
![]() |
19
bronyakaka 1 天前
1 、重试
2 、出错的消息标记为失败并入库,后续手动处理 3 、如果是强依赖,那根本不能消费处理后面的消息,直接告警算了。弱依赖就先消费存着呗 |
![]() |
20
siweipancc 1 天前 via iPhone
架构错误,为什么这么设计?
|
21
prime2015 1 天前
这种是需要对方付咨询费的
|
![]() |
22
younger027 1 天前
@loveaeen 哈哈哈 转人工实在是没绷住
|
23
Scarb 1 天前
我觉得这种就不该用顺序消息,而是用事务。因为他这样顺序消费实际上只允许一个消费节点同步执行这些步骤,用消息来处理没什么意义。如果一定想用消息作为服务间通信,那就发一条消息,然后收到消息的服务用事务来执行这些步骤。
用顺序消息的情况下怎么都不好。 1. 重试——如果这个消息一直消费不成功一直重试,那会阻塞后面的业务消息消费 2. 找个表存——如果 2 执行失败,3 实际上不应该执行。如果只把 2 存起来,执行 3 ,那执行结果不是你期望的。如果 2 和 3 都要存起来,那消费 3 的时候怎么去判断 2 失败了并且 3 依赖 2 呢? |
![]() |
24
woodfizky 1 天前 ![]() 这就是他们实际业务遇到的问题吧哈哈,面试官自己头疼怎么解决呢拿来问你了。
关键是消息队列还要考虑消息消费的顺序和依赖性,这就把问题复杂化了。 怎么判断消息前后是否还有消息需要依赖?或者怎么做能够把依赖体现在消息内? 这也是设计需要考虑的。 如果非要这么干,那我个人倾向于,这些消息识别到后,用别的资源去处理,别干扰正常消息的消费。 比如消费正常队列的时候,判断出来某些消息是有依赖顺序的,那就再推到专门的特殊队列里,或者干脆就直接按需存数据库。 等最后一条消息推送进来,特殊队列的消费端程序能够判断没有后续依赖了,再整个链条一起拎出来处理。 但是这样对延时有影响。不过看面试官的说法,这个等了两天才发现,也是有点离谱,应该要求不高。 |
![]() |
25
fyxtc 1 天前
搁这跟我玩循环引用呢,是想让设计 gc 是吧
消息队列首先是一个队列,队列能保证什么?顺序呗,把其他的任务分配到队列上增加复杂性就违反了 KISS 事务:那我走? |
![]() |
26
bk201 1 天前
我的想法是第一个所有依赖的消息走一个 tag ,消费失败了一个后续全部进数据库或者 retry 队列里做重试。第二个改掉消息顺序依赖的方式,对相关的顺序操作走同一个消息做顺序执行,消息只做触发。
|
27
yvyvyv 1 天前
认同#24 说的 "他们实际业务遇到这个问题了",可能是屎山要炸,还没想好怎么解决,直接将问题抛给面试的。
三个消息需要有序执行,这本来就应该用同步单线程做的事放到一个事务中,这样也能避免多个消费者导致错序。 也可以 合并成一个消息,被消费者拿到后将消息交给逻辑 1 执行,执行后通过事件或者当前消费者服务器的 queue 交到 2 逻辑,同上交到 3 ,同时这个消息需要一个唯一标识,出错记录 db 中。如果 1 出错了也不会发送事件到 2-3 。人工也好,搞个 soc 预警也好。监控下错误日志即时排查呗 |
![]() |
28
ckdxc 23 小时 32 分钟前
@Scarb
@woodfizky 我也没明白他那个依赖是整个队列都有顺序的依赖, 还是就 局部一些消息有依赖 msg3 依赖 msg2, msg2 依赖 msg1, msg4 不依赖 msg3 了, 这样就类似分片了, 只需要做好分片管理, 接收分片 1 的时候创建分片任务(存内存, 或者数据库), 分片处理失败, 就回调一个失败回去呗, 让生产方重传整个分片, 或者通知到用户业务失败了 如果真的是整个队列都是一条一条的有依赖关系, 那也只能是存起来, 有专门的程序处理, 或者人工处理 突然想起来, 我这里也有个, 数据的增量同步, 中间有失败的, 真的就是阻塞后面的增量数据了, 不过有个全同步机制, 可以按数据表级别, 同步到对端, 有一个环境, 没人管, 积压了 100 多 G 数据在增量同步表里面, 不过是积压在生产者这边 既然依赖关系这么强, 建议每条消息都增加处理标识, 要么生产者拉取一下结果, 失败了就不发 msg3 了, 要么消费者自己调回调, 具体看业务, 可能有些业务, 能定时核对, 保证正确, 有些就必须得人工介入处理 |
![]() |
29
qxmqh 23 小时 32 分钟前
这很明显设计上有问题,我估计就是屎山,可能也不是他设计的,但是他遇到了,想问你,保不齐就要爆炸了。很大可能即使你入职了,第一个解决的问题就是这个问题。
|
![]() |
30
liprais 23 小时 30 分钟前
这面试官自己都没搞懂就在这装了,别理他就行了,真做系统的哪有这么搞的
|
31
X2S2 23 小时 26 分钟前
同意 27 楼的
我猜测他的场景可能是消息重放,比如物流状态的变更。我怀疑是不是同一家面试的 如果出现异常,将 异常数据及后面依赖的消息 直接或者指定重试次数后报警并写入数据库(可以把一组数据的标识 和 msgid ,消息内容等写入)。 例如出问题的是 1 ,将 1 写入异常数据库,如果消费 2 的时候,判断异常数据库中是否有 1 ,有就先写入,不做消费。 写入的数据,人工再处理。 # 如果使用的是 rocketmq 的顺序消息 1 、顺序发送 理论上在集群环境,生产者不唯一,那么发送到 broke 队列里的消息顺序可能是乱序的。 基于他的场景,应该 1 ,2 ,3 这种顺序大概率存在时间差,可能不用考虑乱序。 如果要保证顺序发送,可能采用的是记录日志,然后使用定时任务或者 timer 来发送,并且分布式锁保证任务只有一个节点执行,保证发送者的唯一进而保证消息发送的顺序性。 2 、顺序消费 rocketmq 客户端通过 申请 broke 锁保证一个消费者拉取消息、通过对消费队列加锁保证一个线程可以做消费。 如果出异常,因为顺序消息重试次数默认-1 即一只重试,所以会阻塞队列。这种情况主动报警,并记入数据库,后续如果还有依赖的消息,直接标记为异常,同样记入数据库。 |
33
sampeng 23 小时 23 分钟前
这就是死区队列的用法啊。mq 不自带,aws 的 sqs 是可以开启死区队列。处理消息的时候发现有问题放入死区队列。另外有消费者单独处理死区队列按业务逻辑进行处理就好了,即不会堵后面的,后面的发现前置任务错误直接快速失败也和 redis 等东西配合也可以快速做到。mq 也可以做到,就是比较麻烦而且原子处理没研究过是不是要一些特殊处理,大概逻辑就是这样。
|
34
strivezheng 6 小时 18 分钟前
加一个 redis 缓存池,所有的消息都要入池。如果 2 失败了,当消费到 3 时,根据依赖关系向前溯源(在缓存池中查数据),把关联的 123 都重新投递到消息队列进行消费。这样既保证了消息不会丢失,也能不阻塞
|