深入浅出的 RocketMQ-面试题解析
首先来说一下订阅关系是什么意思。主要讨论的就是在同一个消费者组内的,所订阅的内容不一致。这会导致一个什么问题呢,我们在前几节中讨论过一个重平衡 的问题,就是在同一个消费者组内,消费者的新增和消失之后,都会进行负载均衡的一个操作。将大家订阅的内容,重新分配。
好,如果一个组内的消费者所订阅的内容是不一样的,在重平衡之后,怎样呢。
是不是分给你的 MessageQueue 的内容,有一部分你订阅的能消费。你没有订阅的对你来说就是脏数据,你不去拉取。
就会造成消息丢失。堆积的问题。
所以,这个订阅关系一致性是这样引出来的,所以就要求我们在使用 RocketMQ 的时候记录好,各个消费者之间的订阅关系,组内的订阅关系。当我们消息积压的时候,也不至于,无头苍蝇一样。没有方向。
在我们的 MQ 中既然生产者,nameServer,消费者都能有多个,那么我们的 Broker 也可以。
单主节点就是最基础的配置,当然,这一个挂了,MQ也就彻底停摆了。所以除了一般学习也不建议。
多个主节点来分担一个节点的压力,看着是挺好的,成本低,就可以到达效果,但是八股看的从来都不是正常情况。当一个节点挂了,问题就来了,
**生产者:**发现消息发送失败,就会启动重试,最终会发往另一个 Broker,这一方面还是问题不大。
**消费者:**重点就是消费者这里,如果生产者发送顺序消息的时候,Broker 在中间断开,那么消费者就无法顺利(正常处理)顺序信息。这就是多主的确定,一个挂了,就有消息丢失。这时候有同学问了,生产者发送到两个 Broker 信息呢。(๑ᵕ⌓ᵕ̤),这里就有点本末倒置了,这样也可以,那不就是两个 Broker 正常情况下,都有一般的性能浪费嘛。╮(╯_╰)╭
多主多从,做性能分担,主从节点直接互备信息,看着是挺好的,那主从之间是同步还是异步呢。
异步 还是那个问题,主节点挂掉,就会丢失数据。顺序消息被打断。
同步 这个方法看着不错,但是主节点挂了,之后,过了一会儿又恢复怎么办,消费者继续消费主节点?那消息不就有差异性了嘛。
这是一个比较好的选择,redis 的集群架构也和这个差不多,为了对抗上面的主节点挂掉又恢复所出现的消息差异性问题。
这个架构就是主从同步机制,加从节点投票选举。在主节点挂了之后,从节点就会投票选举新主节点。在之前的主节点恢复之后,默认成为从节点就好。
这个思想在Redis中也是常见的。
这方面其实文字不好理解,同学们还是搜一些其他八股背一下。
kafka 的零拷贝就是在系统文件读取的绝缘层之内移动,不捞出来。
(这块不会,就别说了,越说越乱,像主包这样(๑ᵕ⌓ᵕ̤))
这个就要从三部分说起
生产者:这里就是依靠一个消息确认机制,生产者只需要关心,自己的消息是不是发送给了 Broker 就可以了。当出现网络波动的时候,生产者就会利用重试机制,来应对。
Broker:在接收到生产者消息的时候,我们就先存储到 log 文件中,存储成功了,在给生产者发送 ack 确认机制。
消费者:只有确保消息都处理完成了,才会向 Broker 提交消费点位信息。
多出一种策略,性能就差一分。
消息在生产的时候,生产者为了防止因为网络波动等没有发送到的原因,有重试机制。
但是这防止的是生产者—》Broker 这一步。当 Broker—》生产者这一步网络波动了,生产者是感知不到的,就以为没发送过去,就会开始重试。
所以消息重复的这个问题是无法避免的。
因此我们可以在业务中,发送消息的时候增加业务 id(主键 id),来标记这个消息,消费的时候利用业务 id(主键 id)检测是否已经消费过了。
最后一种比较好的方式就是利用 redis,我们可以编写一个幂等性的注解,然后利用传递的业务 id 来检测是否重复消费。
这个问题要分情况讨论:
开发测试环境:如果在开发测试这种少流量的时候就检测到了消息堆积,我们更要考虑的是业务 bug。因为这个时候都能堆积,就很有可能是业务的问题。
正式环境:在开发环境中测试问题的时候在正式环境中突然堆积,我们这时候要做的应该是尽快解决消息堆积。所以在开发的时候就要保存好 topic,MessageQueue 的数量,防止出现消费盲区(有消费者没事干)。
之后在进行分析,是我们开发时候没想到的问题,开始第三方服务延时,错误的问题,我们没有做兜底策略。
正常运行很久之后:这个时候就要考虑是否是第三方服务所引起的,还是第三方业务引起的。第三方服务嘛就是突然不可用,导致堆积。
第三方业务:有可能和我们的服务产生冲突。比如数据库同时修改,新增范围查询,引起 mysql 的表 长时间处于一个有锁的状态。
当然还是要,特殊情况,特殊分析嘛
比如我们之前的一个业务,就是用户发送的评论,帖子,图片,堆积在敏感信息检测这一步,(不机审,也有人审)。所以我们利用多线程异步编排的方案+自定义线程池拒绝策略来兜底。
拒绝策略:最大线程也满了,就不走机审了,直接放行到人审核(未机审)。
人工可以在消息堆积少的情况下,再让机器审核一遍(防止人工没看出来)。