消息队列RabbitMQ - 深入了解高级特性

483 阅读6分钟

消息队列在日常开发中其实运用的非常广泛,之前也发表过一下关于MQ的一些文章,有需要了解学习的同学可以阅览一下,希望能为你带来帮助:

初探RabbitMQ系列 - 了解消息中间件的工作原理和使用

🎈本篇文章主要会设计会涉及以下内容:

  • MQ消息的重试机制以及重试规则的配置
  • 什么是延时消息?
  • MQ延时消息的两种实现方式
  • 死信Exchange

一、🎈消息重试机制

消息队列RabbitMQ版服务端有默认的消息重试机制,不支持您在Consumer客户端重新配置消息重试机制和关闭消息重试机制。消息队列RabbitMQ版服务端默认的消息重试机制如下:

  • 如果您没有开启Consumer客户端消费消息,就不会触发消息重试。
  • 如果您开启了Consumer客户端消费消息,消费失败,即Consumer客户端一分钟内没有应答消息,则触发消息重试。

重试规则

  • 重试期间,任何一次消费成功,即Consumer客户端应答消息,则立即停止消息重试。
  • 重试时间间隔为60秒。
  • 重试最多16次。超过16次,则停止重试。您可以选择:
    • 丢弃消息:如果您没有为重试失败的消息所在的Queue配置死信Exchange,则消息重试失败后被丢弃。
    • 将消息发送至死信Exchange:如果您为重试失败的消息所在的Queue配置了死信Exchange,则消息重试失败后被发送到死信Exchange,并根据RoutingKey和Binding Key被路由至目标Queue。目标Queue中的消息支持查询和导出。
Spring:
  # 消息队列配置
  rabbitmq:
    host: 8.129.42.30                  #rabbitmq的连接地址
    port: 5672                         #rabbitmq的连接amqp端口号
    virtual-host: /                    #rabbitmq的虚拟host
    username: sdjy                     #rabbitmq的用户名
    password: sdjy@rabbitmq2021        #rabbitmq的密码
    listener:
      simple:
        acknowledge-mode: manual       # 确认消息-手动确认
        retry:
          enabled: true                # 开启重试
          max-attempts: 3              # 重试次数,默认为3次
          initial-interval: 5000       # 重试间隔时间(单位毫秒)
          max-interval: 1200000        # 重试最大时间间隔(单位毫秒)、
          multiplier: 2                # 应用于前一重试间隔的乘法器
    publisher-confirm-type: correlated # 发布确认属性配置;新版本已弃用publisher-confirms
    template:
      mandatory: true                  #配合发布确认

二、🌈延时消息

如果您希望消息被投递后延迟一段时间被消费者消费,您可以使用消息队列RabbitMQ版的延时消息。消息队列RabbitMQ版原生支持延时消息,使用方式比开源RabbitMQ更简单。

什么是延时消息

延时消息是指在指定时间段之后才被消费者消费的消息。

💡延时队列应用场景

  • 在电商交易中超时未支付自动关闭订单,如已完成支付则忽略,(定时关单)。
  • 用户注册后,三天内未登陆,自动短信提醒。
  • 用户发起退货退款,3天内未得到处理则自动通知相关运营人员。
  • 预定会议,会议前10分钟自动提醒各位参会人员。
  • 上课提醒,学员会在开课前N分钟收到上课提醒的消息推送。

🎉方案对比

消息队列RabbitMQ版和开源RabbitMQ支持的延时消息实现方案对比如下:

项目开源RabbitMQ消息队列RabbitMQ版
死信Exchange+Queue的消息存活时间✔️✔️
死信Exchange+消息的消息存活时间✔️✔️
开源延时消息插件方案✔️✔️
原生延时消息方案(推荐)✔️

1️⃣原生延时消息方案

消息队列RabbitMQ版通过对消息设置delay来实现延时效果。消息队列RabbitMQ版原生延时消息的流转过程如下:

  1. 生产者向Exchange发布设置了delay的消息。
  2. Exchange将消息路由至Queue。
  3. 在设置的delay时间到期后,消费者才能从Queue消费消息。

注意 消息队列RabbitMQ版原生延时消息,则不要为Queue设置消息存活时间。如果您为Queue设置消息存活时间,则会导致延时消息失效。

2️⃣原生延时消息最佳实践

  • 生产者客户端 消息队列RabbitMQ版原生延时消息的使用方式非常简单。您只需要在生产者客户端发布消息时,通过delay为消息设置一个延时时间。

    发布延时消息的Java示例代码如下:


Map<String, Object> headers = new HashMap<>();
    headers.put("delay", "xx");
    AMQP.BasicProperties props = new AMQP.BasicProperties.Builder().messageId(UUID.randomUUID().toString()).headers(headers).build();
  • 消费者客户端

    为保证延时消息时效性,建议您在消费消息时使用push模式的basic.consume方法,而不要使用pull模式的basic.get方法。因为消息队列RabbitMQ版的消息是分布式存储的,如果您使用pull模式的basic.get方法获取消息,并不能保证正好从存储的节点获取消息。

开源延时消息插件方案

为了减少与开源RabbitMQ的差别,消息队列RabbitMQ版也基于原生的延时消息支持使用开源插件式的方式来使用延时消息,并免去插件的安装。具体使用流程如下:

  1. 声明x-delayed-message类型的Exchange,并填写该Exchange的扩展参数x-delayed-type以指定Exchange的路由类型。示例如下:


Map<String, Object> args = new HashMap<String, Object>();
    args.put("x-delayed-type", "direct");
    channel.exchangeDeclare("ExchangeName", "x-delayed-message", true, false, args);
参数说明如下:

| 参数               | 说明                                                                                                                                                                                         |
| ----------------- | ------------------------------------------------- |
| x-delayed-type    | Exchange的类型,指定路由规则。取值说明如下:direct、fanout、topic、headers、x-jms-queue、x-jms-topic                                           |
| ExchangeName      | Exchange的名称。****说明** 请确保声明的Exchange已存在。 |
|x-delayed-message | 指定Exchange类型,以支持投递延时消息。                  |
  1. 发送延时消息。在消息的Header属性中增加一个键为x-delay,值为毫秒数的键值对,并且指定发送的目标Exchange为上一步已声明的Exchange。示例如下:

byte[] messageBodyBytes = "delayed payload".getBytes("UTF-8");
    Map<String, Object> headers = new HashMap<String, Object>();
    headers.put("x-delay", 5000);
    AMQP.BasicProperties.Builder props = new AMQP.BasicProperties.Builder().headers(headers);
    channel.basicPublish("ExchangeName", "", props.build(), messageBodyBytes);

该示例中,消息到达Exchange后,会在5000毫秒后投递到对应的Queue。

三、🍿死信队列Exchange

消息队列RabbitMQ版死信Exchange适用于处理被消费者否定应答或重试失败的消息。下面介绍死信Exchange的核心概念、路由流程、配置方式、注意事项和更多信息。

核心概念

  • 死信Exchange 用于路由死信消息的Exchange。死信Exchange会根据Binding Key、死信Routing Key、Header属性将死信消息投递至死信Queue。死信Exchange可以是任何一种常见类型的Exchange,例如Direct Exchange。

  • 死信Routing Key 死信消息的路由规则。如果不设置死信消息的Routing Key,则死信消息的Routing Key默认为消息本身的Routing Key。

  • 死信消息 被重新发送到死信Exchange的消息。消息变成死信消息的可能原因如下:

  1. requeue参数被设置为false,消费者使用basic.rejectbasic.nack否定应答(NACK)消息。
  2. 消息重试超过16次,消息重试失败。
  • 死信队列 死信Exchange绑定的Queue,用于存储死信消息。

消息路由的整个过程

死信Exchange路由死信消息的流程大概梳理如下:

  1. 生产者将消息发送到交换机Exchange
  2. 有交换机Exchange将消息路由到队列Queue
  3. 消费者从队列里拉取消息
  4. 当消费者消费失败后消息重试次数超过16次依然没有成功消费时,或者消费者没有消费消息而直接否定应答,消息变成死信消息。
  5. 队列会根据x-dead-letter-exchange将死信消息发送到死信Exchange,并根据x-dead-letter-routing-key为死信消息设置死信Routing Key
  6. 死信Exchange会将死信消息路由到死信队列中去。

image.png

以上即是本篇文章的全部内容,欢迎收藏⭐️+关注👨‍🎓+留言📃!!!