MQ高级

79 阅读6分钟

MQ高级

消息可靠性

说明:消息由生产者发送到MQ,最终被消费者正常消费挖不出。

RabbitMQ发送与消费消息的模型:

image.png

消息丢失的几种情况:

1.生产者连不上MQ:

2.生产者发送消息未达到交换机

3.MQ宕机,队列中的消息不见了

4.消费者收到消息,还没消费,消费者宕机了。

如何保证消息不丢失:

1.生产者确认机制

  • publisher-confirm

    • 消息成功投递到交换机,返回ack
    • 消息未成功投递到交换机,返回nack,记录消息以及交换机等相关信息到数据库,后期可以编写任务去补偿发送
  • publisher-return

    • 未正确到达队列,返回ack及失败原因,记录消息以及交换机等相关信息到数据库,后期可以编写任务去补偿发送

2.持久化机制

  • 交换机持久化
  • 队列持久化
  • 消息持久化

3.消费者ack机制

  • ack取值

    • none:只要消息到达消费者,消费者直接返回ack给MQ,MQ收到ack,会把队列中的消息删除,消息可能会丢失

    • manual:手动ack

      • 消费成功,调用API给MQ返回ack
      • 消费失败,调用API给MQ返回nack,并且让消息重回队列
    • auto:自动ack。消费消息不出异常,返回ack给MQ。消费消息出异常了,返回nack,把消息重回队列

      • 本地重试

        • 达到最大重试次数(可以配置)后,还是失败,则返回ack,不requeue。MQ会删除队列消息
      • 失败策略

        • RejectAndDontRequeueRecoverer:重试耗尽后,直接reject,丢弃消息。默认就是这种方式
        • ImmediateRequeueMessageRecoverer:重试耗尽后,返回nack,消息重新入队
        • RepublishMessageRecoverer:重试耗尽后,将失败消息投递到指定的交换机
      • 使用 RepublishMessageRecovere

        • 把消息投递到失败的交换机,路由队列。记录日志,将来人工干预

消息重复消费问题

幂等性

说明:多次执行同一个操作,最终的结果是一样的。

对于非幂等性操作,多次消费消息,会造成数据一致性的问题,所以要保证重复消费消息的问题。

解决播放:

1、生产者

  • 在发送消息时,向Redis写入一个消息的唯一ID(UUID),并且把它当作key写入到Redis

2、消费者

  • 消费者先从Redis中获取消息ID,如果存在,则表示有人在消费消息,我什么都不做。如果不存在,表示是该消息是第一次被消费,正常消费,并且向存储Key为消息ID的数据到Redis,同时设置过期时间
  • 消费者在消费消息时,先通过redis的setnx命令设置一个key为消息ID,值为0的操作。预先定义好0表示消息正在消费,1表示消息已经消费完成。

    • 设置成功,表示当前消息没有被消费,消费消息,并且把redis的值改成1,手动ack
    • 设置失败,表示消息正在被消费或者已经消费完成了,获取redis消息key的值,判断是否为1,如果为1,手动ack

消息积压问题

产生的原因:

  • 生产者生产消息的速度 远高于 消费者消费消息的速度?于是就会造成消息积压在MQ

    • 可能是设计问题,需要重新设置生产者与消费者数量匹配,比如有个生产者对应多个消费者

    • 可能是消费者出了问题:

      • 消费者出异常了,需要解决消费者代码
      • 消费者宕机了,需要修复宕机的情况,并且临时开启多个消费者,来以倍速消费积压的消息。当积压的消息消费的差不多的情况,再关闭临时消费者

解决办法:

采用惰性队列:

  • 提升消息的存储能力。
  • 可以把消息持久化到本地,后面再读取慢慢消费

死信交换机

成为死信的几种情况:

  • 消费者使用basic.reject 或 basic.nack 声明消费失败,并且消息的requeue参数设置为false
  • 消息设置了过期时间,或者消息存放的队列设置了过期时间,超时无人消费
  • 要投递的队列消息满了,无法投递

这些就是死信,然后会通过路由规则经过交换机路由到一个队列,这个交换机就叫死信交换机,这个队列就是死信队列

死信交换机:

如果这个包含死信的队列配置了 dead-letter-exchange 属性,指定了一个交换机,那么队列中的死信就会投递到这个交换机中,而这个交换机称为 死信交换机 (Dead Letter Exchange,检查DLX)。

TTL:

超时未消费,消息变成死信的两种情况:

  • 消息所在的队列设置了超时时间
  • 消息本身设置了超时时间

如果两者都设置了,以短的时间为优先

延迟【时】队列:

  • 一种实现 消费者延迟收到消息 的模式
  • 在RabbitMQ中,没有延迟队列的功能。可以使用 TTL + 死信队列 的方式实现延迟队列

应用场景:

  • 延迟发送短信
  • 用户下单,如果用户在15 分钟内未支付,则自动取消订单
  • 预约工作会议,20分钟后自动通知所有参会人员

DelayExchange插件:

因为延迟队列的需求非常多,所以RabbitMQ的官方也推出了一个插件,原生支持延迟队列效果

原理:

  • DelayExchange需要将一个交换机声明为delayed类型。

  • 发送消息到delayExchange时的执行过程

    • 接收消息
    • 判断消息是否具备x-delay属性
    • 如果有x-delay属性,说明是延迟消息,持久化到硬盘,读取x-delay值,作为延迟时间
    • 返回routing not found结果给消息发送者
    • x-delay时间到期后,重新投递消息到指定队列

RabbitMQ如何实现延迟队列:

  • 死信队列 + TTL:由于队列的原因,会先消费队首的消息,如果后续的消息消费的时间比队首的要早,会导致消费无法消费。
  • 装一个Delay插件:没有队列,不会出现无法消费的问题

RabbitMQ与Kafka的区别

Kafka

优点:

  • 吞吐量大
  • 功能强大:处理流媒体

缺点:

  • 时效性差:消息延迟时间比MQ长

特点:

  • 重Topic:收发消息都必须指定Topic
  • 拉模式:由消费者主动去Broker中拉取消息
  • 消息消费完后,不会删除,保留历史消息

RabbitMQ

优点:

  • 时效性强、可靠性高

缺点:

  • 吞吐量比Kafka小

特点:

  • 轻Topic:不同工作模式可以选择是否使用Topic
  • 推模式:由Broker主动把消息推送给消费者
  • 消息阅后即焚