前言
最近接触了多线程和MQ等性能相关的内容,来写写MQ在实际开发中容易碰到的问题及其解决方法。这同时也是面试中常见的提问。
提示:以下是本篇文章正文内容,下面案例可供参考
一、消息堆积
1. 消息堆积的产生与影响
消息堆积的产生主要在于两方面,其一为消费的太慢或消费方出现异常,其二为生产方生产地太快。总的来说就是生产消费速度不匹配造成的
一旦出现了消息堆积,我们最直接的影响就是新消息无法入队,随之而来的就是等待时长过长,用户等待烦躁,降低了用户体验。
2. 消息堆积的解决方案
对症下药,既然是产销速度不匹配,就针对二者做优化
- 增加消费者或对消费者做多线程处理(这同时也会带来一些问题,比如可能造成消息的无序排列)
- 提高消费者性能(做算法优化或缩短线程休眠时间等)
二、消息丢失
1. 情景
- 生产方丢失
- MQ中丢失
- 消费方丢失
2.解决方案
针对上述丢失情形,开发者或RabbitMQ都能对它们给出解决方案
- 生产方丢失解决方案: RabbitMQ提供了发送方确认机制【普通确认、批量确认、异步监听确认】,失败后会重发消息
- 丢列中丢失解决方案:生产环境中,网络或服务器存在宕机,重启的情况。RabbitMQ提供了持久化交换机的方案应对。与spring boot整合时也是默认持久化的
- 消费方丢失解决方案:消费方接收到MQ提供的数据时,若设置为自动回复,MQ会自动删除消息,此时若消费方宕机,数据就会丢失。因此应该设置为手动回复MQ的方式
三、有序消费
1.情景
- 在
work queue
模式下,只有一个队列,但存在多个消费者。多个消费者线程的竞争会导致数据乱序。 - 在
简单队列
模式下,同样的多个消费者线程也会导致数据乱序
2.解决方案
- 使用多个队列,对消息的id值做
hash
。再对队列数取模(hash结果%队列数
),将结果相同的消息压入同一个队列中去,这就保证了一个队列中有且仅有一个消费者 - 在MQ队列后的Java代码中(消费方),再为每一个线程加一个内存队列,根据消息的id求hash值,然后把相同的结果压入同一个内存队列
四、重复消费
1.情景
因消息重发机制会出现消息重复消费的情况
2.解决方案
- 幂等操作,同一个操作执行N次,但结果不变
- 若实际业务中用不了幂等,则保存消息id到数据库(Redis),每次消费前查看消息是否已被消费过