MQ 聊聊消息队列那些事( 二 )


消息队列要注意的问题问题一:可用性MQ作为整个整个分布式架构的重要部件,如果MQ服务不可用,那整个系统都挂了 。因此,MQ必须要支持集群 。当下主流的MQ中间件都能够不同程度的支持集群,实现了MQ服务的高可用 。
问题二:消息丢失消息丢失有可能发生在生产者丢失消息、MQ本身丢失消息、消费者丢失消息3个方面 。

  • 生产者丢失消息生产者丢失消息一般是在发送消息的时候出现异常(譬如网络异常),导致MQ无法接收到消息 。这个问题可以采用本地消息表+回调通知+定时任务的方式解决 。?就以系统A发送消息,系统B消费消息为例,具体解决方案如下:1、系统A执行本地事务业务逻辑,并且往本地消息表插入一条数据(代表准备要发送的消息),消息状态为“未发送” 。本地事务成功,提交保存本地数据,失败则回滚 。2、本地事务成功后,发送消息给MQ 。3、MQ接收到消息后,回调通知系统A,系统A把本地消息表对应的消息记录状态变为“已发送” 。4、定时任务轮询本地消息表,超过一定时间状态为“未发送”的消息重新发送给MQ 。5、定时任务处理超过一定次数一直发送不成功的消息告警,人工介入 。?
  • MQ丢失消息消息成功发送到MQ,是先放到内存里的,如果还没来得及给消费者消费消息,MQ服务就挂了,就会丢失消息 。这个问题一方面可以做集群,但集群的数据同步也需要一定时间,如果在同步数据之前就MQ服务挂了,消息也会丢失 。还有一个方法就是MQ接收到消息的同时,把消息数据持久化到磁盘,这样,MQ服务恢复的时候就可以从磁盘获取数据重新给消费者消费 。可能有人会问,那消息还没来得及持久化到磁盘MQ服务就挂了咋办?如果是这样,就可以用到前面说到的本地消息表,把本地消息表里的数据重新发一遍 。?
  • 消费者丢失消息消费者从MQ拉取消息,还没来得及处理消息,消费者服务器挂了 。此时,可能造成消费者丢失消息 。这种情况,可以让消费者处理完消息时给MQ一个确认消息来解决 。如果MQ没有收到确认消息,就会有重试的机制,最终确保消息给到消费者消费 。当然了,如果重试超过一定次数,就应该告警,人工介入 。
问题三:重复消费因为在网络延迟的情况下,消息重复发送的问题不可避免的发生 。譬如,生产者发送消息的时候使用了重试机制,发送消息后由于网络原因没有收到MQ的确认信息,然后又去重新发送了一次消息 。但其实MQ已经接到了消息,并返回了响应,只是因为网络原因导致生产者没有收到MQ的确认信息 。这种情况下,生产者的消息重试机制就会继续就这个消息重新发送,从而导致同一条消息多次发送,这样消费者也会重复消费这条消息 。当然,这只是列举了一种情况,实际上还有其他情况会导致消息被重复消费 。
解决重复消费的关键就是在消费者端引入幂等性机制 。什么是幂等性机制呢?我们可以把它理解成,假如一个接口被重复调用,依然可以保证数据的准确性 。举个例子,比如每条消息都会有一条唯一的id,消费者处理完这个消息会存储这个id,如果处理消息之前能找到这个id,就说明这条消息已经处理过了,就不做处理并且返回给MQ一个确认信息 。
消息队列中间件为什么要用消息队列中间件?自己写不行吗?我们之所以要用中间件,是因为这些中间件已经解决了很多消息队列常见的问题(高可用、消息丢失、重复消费......),而且各种中间件都有各自的特性,已经做得非常成熟了,你确定你写的有这些中间件好用吗?
目前在市面上比较主流的MQ中间件主要有,Kafka、ActiveMQ、RabbitMQ、RocketMQ 等这几种 。网上找来这几个中间件的对比,如下表:

经验总结扩展阅读