一、rabbitMQ的基础要点回顾
1.使用场景
1)解耦:使用消息队列避免模块间的直接调用。将所需共享的数据放在消息队列中,对于新增的业务模块,只要对该类消息感兴趣就可以订阅该消息,对原有系统无影响,降低了各个模块的耦合度,提供系统的扩展性。
2)异步:消息队列提供了异步处理机制,在很多时候应用不需要立即处理消息,允许应用把一些消息放入中间件,在之后需要的时候慢慢处理。
3)削峰:高峰期的消息可以被挤压下来,在随后的时间里进行平滑处理,不至于让系统短时间内过载而崩溃。
2.基本概念
二、MQ工作过程
1.如何工作
2.集群模式
如下所示,所有操作要定位到master队列进行
三、rabbitMQ的特性分析
1.特性分析
消息路由 | 支持 | 不同交换器支持不同的消息路由 |
消息有序 | 不支持 | 消费失败,放回队列重新消费将导致消息无序 |
消息时序 | 非常好 | 支持过期时间、延期时间设定 |
容错处理 | 非常好 | 交付重试和死信队列 |
伸缩 | 一般 | 受限于只有一个master queue |
持久化 | 不太好 | 消费过的消息会被立即删除 |
消息回溯 | 不支持 | 消息不支持永久保存 |
高吞吐 | 中等 | 请求的执行最后都是在master queue,单机性能达不到十万 |
2.技术选型
指标\选择 | Kafka | RocketMQ | RabbitMQ |
单机吞吐量 | 17.3w/s | 11.6w/s | 2.6w/s(持久化) |
开发语言 | Scala/Java | Java | Erlang |
主要维护者 | Apache | Alibaba | Mozilla/Spring |
订阅形式 | 基于topic,按照topic进行正则匹配的发布订阅模式 | 基于topic/messageTag,按照消息类型和属性进行正则匹配的发布订阅模式 | 提供了4种:direct、topic、headers和fanout(广播模式) |
持久化 | 支持大量堆积 | 支持大量堆积 | 支持少量堆积 |
顺序消息 | 支持 | 支持 | 不支持 |
集群方式 | 无状态集群 | 多对Master-Slave | 简单集群,复制模式 |
性能稳定性 | 较差 | 一般 | 好 |
四、rabbitMQ实践中可能遇到的问题
1.消息处理过程
1)生产者将消息投递到队列:如果消息设置了持久化属性,将会被写入磁盘,落盘成功后,重启不会导致消息丢失。
2)RabbitMQ将消息传递给消费者:队列收到的消息将以轮询的分发方式发送给消费者。自动应答模式下会立即将消息标记为删除,手动应答模式下。
3)消费者处理消息:手动应答模式下在消息处理之后告诉RabbitMQ可以把该消息删除了。
消费者可以通过设置预取计数值来调整通道上允许的未确认消息的最大数量。
2.顺序消费问题
1)一个队列有多个消费者消费:例如不同订单号的消息消费完成时间不同导致顺序丢失,此时我们可以创建多个队列,每个消费者只消费一个队列,生产者把订单号相同的消息投递到同一个队列(最简单的就是两边都采用除余法),这样同一个订单号的消息就只会被同一个消费者顺序消费。
2)消费者程序内部进行了多线程消费:无法保证哪个线程先执行完,导致顺序错乱。针对这种情况,线程可以不直接消费消息而是把同一个订单号的消息放入同一个内存队列,然后线程再从内存队列中取出消息进行消费。
3)生产者到MQ的过程中出现了错乱:消息在投递时由于网络延迟或者错误重试导致顺序错乱。针对这种情况需要在消费时做一个乱序处理,比如对同一个订单进行插入、修改、删除操作分别标记消息ID为1、2、3,将消息按照消息ID大小从小到大进行排序,每消费一个消息就将消息ID记录在缓存中,如果遇到消息ID更小的消息就丢弃。
3.消息丢失问题
1)生产者在将消息发送到MQ的过程中数据丢失:传输过程因为网络等原因造成的数据丢失可以采用以下解决办法:
①RabbitMQ提供的事务功能,如果消息没有被MQ接收到,生产者会报错并回滚事务尝试重新发送,缺点是同步阻塞操作会大幅降低性能。
②发送方确认机制,只要消息到达MQ就会触发confirmCallback,表示服务器成功接收到信息。如果交换机没能成功将消息投递到队列,就会触发returnCallback。
2)断电导致消息丢失:开启持久化可以将消息持久化到磁盘,如果同时开启了发送方确认则写入成功后才会回复发送方ack,如果没有收到ack回调,也会重发消息。
3)消费者服务在消费时宕机:针对这种情况,可以开启MQ的消费端确认机制。首先关闭MQ的自动ack,在确保消息处理完成之后再手动ack,如果服务宕机或者程序异常,消息会重新入队。
4.重复消费问题
重复消费的原因:MQ回复的确认消息因为宕机或者网络原因未被队列收到,这时消息将会被再次分发。
解决办法(选择其一即可):
1)消费者的业务逻辑接口设计成幂等的,比如在缓存中查询是否已消费过此消息。
2)将消息记录在数据库,每次查询该防重表。
3)使用唯一键,保证不会插入多条重复数据。
5.消息积压问题
1)消费者宕机:检测MQ队列消息积压
2)生产者投递频率超出消费者处理能力:可以限制生产者的发送或者上线更多消费者