SpringBoot中,如何整合RabbitMQ實現延時隊列? 一、介紹 二、實現方式 三、下載插件 四、在SpringBoot整合RabbitMQ 五、測試

一、介紹

  • 1、什麼是延時隊列?
    延時隊列即就是放置在該隊列裏面的消息是不需要立即消費的,而是等待一段時間之後取出消費
  • 2、適用場景
    (1)商城訂單超時未支付,取消訂單
    (2)使用權限到期前十分鐘提醒用戶
    (3)收益項目,投入後一段時間後產生收益

二、實現方式

從以上場景中,我們可以看出,延時隊列的主要功能就是在指定的時間之後做指定的事情,那麼,我們思考有哪些工具我們可以使用?

  • 1、Redis 監聽過期 Key
https://lizhou.blog.csdn.net/article/details/109238083
  • 2、RabbitMQ等實現延時隊列

這也是本<typo id="typo-299" data-origin="片" ignoretag="true">片</typo>文章中要講的知識點,使用 RabbitMQ 實現延時隊列有兩種方式

(1)利用兩個特性: Time To Live(TTL)、Dead Letter Exchanges(DLX)

(2)利用 RabbitMQ 中的插件 x-delay-message

本文主要講解第二種方式,使用插件的方式

三、下載插件

RabbitMQ 實現了一個插件 x-delay-message 來實現延時隊列,我們可以從 這裏 下載到它

https://www.rabbitmq.com/community-plugins.html

選擇 rabbitmq_delayed_message_exchange 插件,如圖所示

選擇 .ez 格式的文件下載,下載後放置 RabbitMQ 的安裝目錄下的 plugins 目錄下,如我的路徑爲

D:\Program Files\RabbitMQ Server\rabbitmq_server-3.7.16\plugins

執行命令

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

安裝插件完成

四、在SpringBoot整合RabbitMQ

1、引入 RabbitMQ 依賴

<!-- rabbitmq消息隊列 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2、配置 RabbitMQ 信息

spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    listener:
      simple:
        # 手動ACK 不開啓自動ACK模式,目的是防止報錯後未正確處理消息丟失 默認 爲 none
        acknowledge-mode: manual

3、RabbitMQ 常量類

package com.asurplus.common.rabbitmq;

/**
 * rabbit常量類
 *
 * @Author Lizhou
 */
public final class RabbitConst {

    /**
     * 交換機
     */
    public static final String DELAY_EXCHANGE = "delay_exchange";

    /**
     * 隊列
     */
    public static final String DELAY_QUEUE = "delay_queue";

    /**
     * 路由
     */
    public static final String DELAY_KEY = "delay_key";

}

4、RabbitMQ 配置類

package com.asurplus.common.rabbitmq;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * rabbitmq配置類
 *
 * @Author Lizhou
 */
@Configuration
public class RabbitConfig {

    /**
     * 延時隊列交換機
     *
     * @return
     */
    @Bean
    public CustomExchange delayExchange() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        return new CustomExchange(RabbitConst.DELAY_EXCHANGE, "x-delayed-message", true, false, args);
    }

    /**
     * 延時隊列
     *
     * @return
     */
    @Bean
    public Queue delayQueue() {
        return new Queue(RabbitConst.DELAY_QUEUE, true);
    }

    /**
     * 給延時隊列綁定交換機
     *
     * @return
     */
    @Bean
    public Binding delayBinding(Queue delayQueue, CustomExchange delayExchange) {
        return BindingBuilder.bind(delayQueue).to(delayExchange).with(RabbitConst.DELAY_KEY).noargs();
    }
}

5、RabbitMQ 生產者

package com.asurplus.common.rabbitmq;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * rabbitMq生產者
 *
 * @Author Lizhou
 */
@Component
@Slf4j
public class RabbitProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 發送消息
     *
     * @param object      發送對象
     * @param millisecond 延時(毫秒)
     */
    public void sendDelayMessage(Object object, long millisecond) {
        this.rabbitTemplate.convertAndSend(
                RabbitConst.DELAY_EXCHANGE,
                RabbitConst.DELAY_KEY,
                object.toString(),
                message -> {
                    message.getMessageProperties().setHeader("x-delay", millisecond);
                    return message;
                }
        );
    }
}

6、RabbitMQ 消費者

package com.asurplus.common.rabbitmq;

import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * activeMq消費者
 *
 * @Author Lizhou
 */
@Component
@Slf4j
public class RabbitConsumer {

    /**
     * 接收消息
     *
     * @param object 監聽的內容
     */
    @RabbitListener(queues = RabbitConst.DELAY_QUEUE)
    public void cfgUserReceiveDealy(Object object, Message message, Channel channel) throws IOException {
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        try {
            log.info("接受消息:{}", object.toString());
        } catch (Exception e) {
            log.error(e.getMessage());
            /**
             * basicRecover方法是進行補發操作,
             * 其中的參數如果爲true是把消息退回到queue但是有可能被其它的consumer(集羣)接收到,
             * 設置爲false是隻補發給當前的consumer
             */
            channel.basicRecover(false);
        }
    }
}

五、測試

package com.asurplus;

import com.asurplus.common.rabbitmq.RabbitProducer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class RabbitmqApplication {

    @Autowired
    private RabbitProducer product;

    @GetMapping("init")
    public void init() {
        String message1 = "這是第一條消息";
        String message2 = "這是第二條消息";
        product.sendDelayMessage(message1, 5000);
        product.sendDelayMessage(message2, 10000);
    }

    public static void main(String[] args) {
        SpringApplication.run(RabbitmqApplication.class, args);
    }

}

通過測試,第一條消息在 5s後接收到,第二條消息在 10s後接收到,說明我們的延時隊列已經成功

作者:Asurplus、
原文鏈接:https://lizhou.blog.csdn.net/article/details/113917675

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章