MQ常见问题总结

RabbitMQ常见问题链接:
面试常见问题总结

选型:常用MQ各自的特点

这决定了技术选型,业务特点决定侧重点,从而决定在该侧重点方面取胜的MQ被选择
多维度比较主流MQ
在这里插入图片描述

比如:
是否支撑消息堆积方面,kafka,RocketMQ优于RabbitMQ
通信协议反方面,RabbitMQ可能优选
考虑吞吐量:RocketMQ
考虑大数据:Kafka
面向日志:Kafka

问题分类

高可用考虑幂等性、如何避免消息丢失(消息是否接收是一方面、消息背后的操作是否执行是另一方面)
高性能/高并发:考虑重复接收消息时多线程的并发处理(避免多个线程处理的是一批数据)。因为监听queue的也是线程组,所以也必然有多线程处理具有同一业务含义消息(重复发送造成)的情形
特殊需求:顺序性

参考文章:
RabbitMQ如何保证消息的幂等性、可靠性、顺序性

如何保证高可用

单机版本与微服务版本防止消息丢失

消息存在内存中,就会面临丢失的情境,核心要素在于落盘/消息持久化。无论是单机场景落在本地磁盘还是微服务之间落在DB。
在这里插入图片描述

单机版本,交换机、队列、消息都支持持久化到磁盘,可以通过原始API设置持久化,比如设置durable参数就可以启动队列的持久化。

微服务,需要考虑发送消息之前先持久化到DB中。
还需要考虑中间件产品的高可用,防止存储过程中的宕机,持久化未完成造成问题。

启用持久化必然导致性能下降,所以考虑性能与安全之间的取舍。

保证高可用的核心

全面考虑的话需要考虑三个状态。
比较全量的机制是:ACK实时确认消息 + MQ消息重试(不可能无限制重试,重试N次移除) + 定时任务兜底检测(处理重试也没有成功的消息)
例如:领域事件入库,触发MQ发送消息,消费者接受到消息并且完成后续业务处理才会删除库中的领域事件。如果未删除,就会有定时任务扫库,然后触发新的MQ消息发送。
定时任务发送MQ消息需要消费者处考虑幂等性,防止业务出现问题。

三个状态:
消息是否成功到达交换机(生产者成功发送)、消费者是否成功从队列中取到(消费者成功接收)、消息背后的业务操作是否成功执行

数据中存入三个状态
在这里插入图片描述

一个demo

发送消息过程中消息安全的保证:
首先在生成消息之后存入消息数据库,服务方法中实现一个回调方法
exchanger提供了确认机制,开启之后调用你的回调方法告知你是否拿到消息,该回调方法中要执行SQL完成消息成功发送状态的修改
在这里插入图片描述

消费者成功接收时执行SQL,修改消息状态为MQ成功发送/消费者成功接收

消费者成功消费之后会发送ACK,删除MQ中的消息。
消费失败的时候不能删除消息,执行重试,重试次数到顶,也会删除MQ消息。

MQ默认自动应答,消费者接收到就直接删除消息,所以此时要取消自动应答。需要手动应答,告知消费成功还是失败。成功删除,失败时设置重试机制等补救。
重试机制:消费者告知失败时queue可以再次发送给你一份
或者告知队列删后续处理方式,删除或者是重新发送
告知成功的代码:
在这里插入图片描述

告知失败的代码:
在这里插入图片描述

RocketMQ:内置了消息的三种状态,无需用户自己去维护
在这里插入图片描述

如何看待消息堆积

解决思路
首先我们得知道为什么会产生消息堆积?
1、消息发送的速率远远大于消息消费的速率。
2、消费者出现了问题,导致无法消费。

从生产者角度处理
一般我们的系统容量或者处理能力都是规划好的,出现消息堆积的情况,大部分是由于流量暴增引起,这个时候可以考虑控制生产者的速率,对前端机器流量进行限速限流。

从消费者角度处理

  1. 消费者设计时采用线程池的机制
    如果并发一高,这个时候消息产生的速度会很快,但是由于我们处理消息的能力(数据库或者其他东西)有限,无法一下子处理这么多消息。这个时候就会出现消息堆积。而消费者消费的速度跟不上。
    对于RabbitMQ,内部有这样的机制:设置两个参数
    1、concurrentConsumers
    2、prefetchCount
    concurrentConsumers设置的是对每个listener在初始化的时候设置的并发消费者的个数,在业务繁忙时将会多开线程。
    prefetchCount是每次一次性从broker里面取的待消费的消息的个数。prefetchCount是BlockingQueueConsumer内部维护的一个阻塞队列LinkedBlockingQueue的大小,其作用就是如果某个消费者队列阻塞,就无法接收新的消息,该消息会发送到其它未阻塞的消费者。
    实际上就是线程池的思路,可以使用多线程提升consumer的并发处理能力,设置缓冲区blockingQueue存储暂时无法处理的消息。
  2. 临时扩容,增加消费者组服务实例

异步处理

  1. 死信队列或者落入日志文件中,定时任务重复执行保证异步消费完毕
  2. MQ需要支持大量消息积压,一定程度上拿MQ做缓冲

系统架构上考虑:生产者限流 + 消费者线程池 + 临时扩容机制 + 兜底逻辑
MQ的设计限制,导致的消费者数是没法动态拓展的,这个时候可以考虑将原先队列进行拆分,比如新建一个topic 分担一部分消息,这个方式需要对系统的上下游都要进行调整,在实际操作难度可能比较高,处理起来可能也比较耗时,如果在事前有做好这个设计那事发后就能很好进行调整。

幂等性设计思路

MQ消息幂等实现思路
消息幂等性,其实就是保证一个消息背后的业务操作不会因为被消费者重复接收处理多次而导致出现业务问题,执行多次与执行一次的效果相同。

问题产生根源:ACK未完成
1,网络问题造成
当broker接收到消息会发送ACK给生产者,当消费者消费完消息之后,通常会发送一个ack应答确认信息给broker。这中间有可能因为网络中断等原因,导致生产者/broker未能收到确认消息,由此这条消息将会被重复发送进行消费。
2,大批量数据处理导致的超时
比如消费者需要处理大批量数据,超时没有发送ACK导致重复发送消息。

MQ幂等性实现demo
通常通过唯一性保证来避免消费重复消费:
幂等令牌的方式:

  1. 消息全局ID或者写个唯一标识(如时间戳、UUID等) :每次消费消息之前根据消息id去查库判断该消息是否已消费过,如果已经消费过,则不处理这条消息,否则正常消费消息,并且进行DB update操作。(消息全局ID作为数据库表的主键,防止重复)
    MQ中针对3种可能丢失的场景,设置三种状态,可以一一检查当前消息的状态。
  2. 利用Redis的setnx 命令:给消息分配一个全局ID,只要消费过该消息,将 < id,message>以K-V键值对形式写入redis,消费者开始消费前,先去redis中查询有没消费记录即可。

业务手段:

  1. 不使用唯一message ID但是通过在执行业务操作时取最新结果做状态对比来区分是否处理过,这就与业务强相关。

其他方式:

  1. 借助唯一索引。入库时统一数据入库通过duplicate key error报错进行拦截。

从生产者侧与消费者侧处理消息重复:
生产者侧避免消息重复:由MQ提供API判定,setKeys,通过key不重复保证。生产者重发的消息只会在set中记录一次。
消费者侧只能靠唯一性的那些思路来保证幂等性。

消息唯一性标志生成方式

https://www.cnblogs.com/jpfss/p/11590376.html
消息队列的唯一ID生成:

  1. UUID
  2. 数据库自增ID的方式
  3. 如果面对某些不能用自增ID的方式,可以使用redis的自增方式
  4. 雪花算法

保证消息有序消费

核心就是在内存中维护消息的先后顺序:
https://blog.csdn.net/zw791029369/article/details/109561457?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161492657116780264030865%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=161492657116780264030865&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-1-109561457.first_rank_v2_pc_rank_v29&utm_term=MQ%E6%B6%88%E6%81%AF%E5%B9%82%E7%AD%89%E6%80%A7%E6%9C%BA%E5%88%B6
在这里插入图片描述

其他

push与pull模式

pull模式代码实现
两种模式各种的优点

MQ负载均衡代码的实现

https://xiaojkql.github.io/2021/03/28/rocketMQ/我理解的RocketMQ—消费者负载均衡的实现/

负载均衡引起的同一条消息在某个时刻出现在不同的消费者中

https://jaskey.github.io/blog/2020/11/26/rocketmq-consumer-allocate/

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值