消息队列常见问题

本文探讨了使用消息队列的原因,包括解耦、异步处理和流量削峰,同时也揭示了其带来的代价如可用性和系统复杂性的提升。对比了ActiveMQ、RabbitMQ、RocketMQ和Kafka,并介绍了如何保证高可用、消息顺序以及处理消息积压和过期的方法。

一、为什么要使用消息队列?

主要三点原因:解耦,异步,削峰;

1、解耦;

如果服务A和服务B耦合,服务B出了问题,会导致服务A访问服务B时失败;
如果使用消息队列,可以返回给用户成功,先把消息持久化,等服务B恢复后,就可以正常消费了;

2、异步:

将消息写入消息队列,非必要的业务逻辑以异步的方式运行不影响士流程业务

3、削峰;

消费者按照数据库能处理的并发量,从消息队列中拉取消息;
比如秒杀活动,一般会因为流量过大,从而导致流量暴增,应用挂掉;
这个时候加上消息队列,服务器接收到用户的请求后,首先写入消息队列,如果消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面;


二、消息队列带来的代价(缺点)

1、系统可用性降低,

如果消息队列挂了,可能影响业务系统的可用性;

2、系统复杂性增加,

引入消息队列,要多考虑很多方面的问题;比如:一致性问题,如何保证消息不被重复消费,如何保证消息不丢失等;

三、常见的消息队列对比

ActiveMQ,主从架构,社区成熟,但性能比较差,版本迭代慢,不推荐使用;
RabbitMQ,主从架构,性能优秀,延时低,达到微秒级,其他产品都是ms级,但基于erlang开发,很难做源码级别的研究和定制,不推荐使用;
RocketMQ,分布式架构,阿里出品,基于Java开发,适合做二次开发和改造;
Kafka,分布式架构,优势在于专为超高吞吐量的实时日志采集、实时数据同步、实时数据计算等场景来设计;
在大数据领域的实时计算以及日志采集被大规模使用;
综上所述,中小型公司,技术实力较为一般,用RabbitMQ是不错的选择;
大型公司,基础架构研发能力较强,用RocketMQ是不错的选择;
如果是大数据领域的实时计算、日志采集等场景,用Kafka是业内标准的;

四、如何保证消息队列的高可用

Kafka: partition和replica机制

Kafka基本架构是多个broker组成,每个broker是一个节点;创建一个topic可以划分为多个 partition,每个partition可以存在于不同的broker上,每个 partition就放一部分数据,这就是天然的分布式消息队列;
意思是,一个topic的数据,是分散放在多个机器上的,每个机器就放一部分数据;
Kafka0.8以后,提供了HA机制,就是replica副本机制,每个 partition的数据都会同步到其他机器上,形成自己的多个replica副本;
然后所有replica会选举一个 leader出来,生产和消费都跟这个leader打交道,其他replica就是follower;
写的时候,leader会负责把数据同步到所有follower上去,读的时候直接读leader;

五、如何保证消息顺序?

1、kafka的topic是无序的,但是一个topic包含多个partition,每个partition内部是有序的;
只要保证生产者写消息时,按照一定的规则写到同一个
partition,不同的消费者读不同的partition消息,就能保证生产和消费者消息的顺序;
2、消费者使用异步重试机制;

在处理消息时,先判断该数据唯一性id(比如订单号)在重试表有没有数据,如果有,则直接把当前消息保存到重试表。如果没有,则进行业务处理,如果出现异常,将该消息保存到重试表;比如订单系统发了“下单”、“支付”、“完成”三条消息;
下单消息由于网络原因处理失败了,后面两条消息是无法入库的,因为只有下单消息的数据才是完整的数据,其他类型的消息只会更新状态;
首先,考虑异步重试机制,如果同步重试,会严重影响消息消费者的消费速度,降低它的吞吐量;然后,考虑消费顺序,必须下单处理完成后,支付才处理,所以处理支付消息时,根据唯一id(订单号)去重试表查,如果有,则将自身也放入重试表,等待重试表进行顺序处理;

六、如何解决消息积压

1、消息体过大

虽说kafka号称支持百万级的 TPS,但从producer发送消息到 broker需要一次网络10,broker写数据到磁盘需要一次磁盘10(写操作),consumer从
broker获取消息先经过一次磁盘 IO (读操作),再经过一次网络10;
一次简单的消息从生产到消费过程,需要经过2次网络10和2次磁盘10,如果消息体过大,势必会增加10的耗时,进而影响 kafka生产和消费的速度;
消费者速度太慢,就会出现消息积压;
而且消息体过大还会浪费服务器的磁盘空间;

解决方案:

消息队列里的消息体,尽量只包含必要信息,非必要信息让消费者自己去查;

2、路由规则不合理

为了保证顺序,按照唯一id去路由 partition,可能会导致某些 partition消息太多,消费者消费不过来,而某些partition消息太少,消费者空闲的情况,

解决方案:

从业务层面出发,改变路由规则;使消息发送更均匀;

3、如果已堆积了十几万消息,只能临时紧急扩容

新建一个topic,partition是原来的10倍,临时建立好原先10倍的queue数量;
然后写一个临时的分发数据的 consumer程序,部署上去,消费积压的数据,直接均匀轮询写入临时建立好的10倍数量的 queue;
接着临时使用10倍的机器来部署 consumer,每一批consumer消费一个临时queue的数据;
这种做法相当于是临时将queue资源和consumer资源扩大10倍,以正常的10倍速度来消费数据;
等快速消费完积压数据之后,恢复原先部署的架构;

七、mq中的消息过期失效了怎么办

可以采取批量重导,就是大量积压的时候,直接将数据写到数据库,等过了高峰期以后将这批数据一点点导出来,重新写入mq中;

<think> 我们参考了多个引用资料,并从中提取关键信息: 引用[1]:RabbitMQ是一个开源的消息代理,基于AMQP协议实现,提供可靠的消息传递机制。 引用[2]:RabbitMQ是由erlang语言开发,基于AMQP协议,用于应用程序之间的通信,在分布式系统中应用广泛。 引用[3]:展示了如何通过RabbitMQ实现延时队列(使用插件x-delay-message)以及消费者监听队列并处理消息的代码示例。 引用[4]:提到了RabbitMQ的重要知识点,包括消息确认机制(ConfirmCallback, ReturnCallback, 消费方手动ack)。 接下来,我们将总结RabbitMQ消息队列的常见知识点: 1. 基本概念:包括什么是消息队列,RabbitMQ的核心组件(生产者、消费者、队列、交换机、绑定等)。 2. 协议:AMQP协议。 3. 消息确认机制:生产者到交换机的确认(ConfirmCallback)、交换机到队列的确认(ReturnCallback)、消费者手动确认(ack)。 4. 延时队列:通过插件实现。 5. 消息持久化:确保消息在服务器重启后不丢失。 6. 工作模式:简单模式、工作队列模式、发布订阅模式、路由模式、主题模式等。 我们将按照这些知识点组织回答。 </think> ### RabbitMQ消息队列常见知识点 #### 1. **核心概念与架构** - **消息队列(MQ)**:遵循FIFO原则的消息存储机制,实现应用间的异步通信[^2]。 - **AMQP协议**:RabbitMQ实现的开放标准协议,定义了消息路由规则[^1][^2]。 - **核心组件**: - **生产者(Producer)**:发送消息的应用程序 - **消费者(Consumer)**:接收消息的应用程序 - **交换机(Exchange)**:接收生产者消息并路由到队列 - **队列(Queue)**:存储消息的缓冲区 - **绑定(Binding)**:连接交换机与队列的规则 #### 2. **消息确认机制(关键可靠性保障)** - **生产者确认**(ConfirmCallback):确保消息到达交换机[^4] ```java // Spring AMQP示例 rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> { if(!ack) log.error("消息未到达交换机: "+cause); }); ``` - **队列确认**(ReturnCallback):确保消息从交换机路由到队列[^4] - **手动ACK**:消费者处理完成后显式确认,防止消息丢失 ```java @RabbitListener(queues="QUEUE_NAME") public void handleMessage(Message message, Channel channel) { try { // 业务处理 channel.basicAck(deliveryTag, false); // 手动确认 } catch (Exception e) { channel.basicNack(deliveryTag, false, true); // 重试 } } ```[^3][^4] #### 3. **高级特性** - **延时队列**(两种实现方式): 1. 死信队列+TTL过期 2. **x-delay-message插件**(官方推荐)[^3] - **消息持久化**: ```java MessageProperties props = new MessageProperties(); props.setDeliveryMode(MessageDeliveryMode.PERSISTENT); // 持久化消息 ``` - **负载均衡**:多个消费者监听同一队列实现工作队列模式 #### 4. **工作模式** | 模式 | 特点 | 适用场景 | |---------------|-------------------------------|-----------------------| | 简单队列 | 1生产者-1队列-1消费者 | 单任务处理 | | 工作队列 | 1队列多消费者竞争消费 | 任务分发 | | 发布订阅 | 扇形交换机广播消息 | 系统通知 | | 路由模式 | Direct交换机键完全匹配 | 分类消息处理 | | 主题模式 | Topic交换机支持通配符匹配 | 复杂路由场景 | #### 5. **运维与监控** - **管理插件**:提供Web控制台(默认端口15672) - **集群模式**:支持磁盘/内存节点混合部署 - **流量控制**:通过`channel.basicQos()`限制未ACK消息数 - **死信队列(DLX)**:处理失败消息的重试或归档 #### 6. **最佳实践** - 始终启用消息持久化(队列+消息双持久化) - 生产环境开启Confirm和Return机制[^4] - 消费者使用手动ACK替代自动确认 - 避免队列无限增长(设置TTL或最大长度) - 使用备用交换机处理无法路由的消息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值