Spring Boot(十四)RabbitMQ延遲佇列

王磊的部落格發表於2019-01-19

一、前言

延遲佇列的使用場景:1.未按時支付的訂單,30分鐘過期之後取消訂單;2.給活躍度比較低的使用者間隔N天之後推送訊息,提高活躍度;3.過1分鐘給新註冊會員的使用者,傳送註冊郵件等。

實現延遲佇列的方式有兩種:

  1. 通過訊息過期後進入死信交換器,再由交換器轉發到延遲消費佇列,實現延遲功能;
  2. 使用rabbitmq-delayed-message-exchange外掛實現延遲功能;

注意: 延遲外掛rabbitmq-delayed-message-exchange是在RabbitMQ 3.5.7及以上的版本才支援的,依賴Erlang/OPT 18.0及以上執行環境。

由於使用死信交換器相對曲折,本文重點介紹第二種方式,使用rabbitmq-delayed-message-exchange外掛完成延遲佇列的功能。

二、安裝延遲外掛

1.1 下載外掛

開啟官網下載:www.rabbitmq.com/community-p…

選擇相應的對應的版本“3.7.x”點選下載。

注意: 下載的是.zip的安裝包,下載完之後需要手動解壓。

1.2 安裝外掛

拷貝外掛到Docker:

docker cp D:\rabbitmq_delayed_message_exchange-20171201-3.7.x.ez rabbit:/plugins

RabbitMQ在Docker的安裝,請參照本系列的上一篇文章:www.apigo.cn/2018/09/11/…

1.3 啟動外掛

進入docker內部:

docker exec -it rabbit /bin/bash

開啟外掛:

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

查詢安裝的所有外掛:

rabbitmq-plugins list

安裝正常,效果如下圖:

Spring Boot(十四)RabbitMQ延遲佇列

重啟RabbitMQ,使外掛生效

docker restart rabbit

三、程式碼實現

3.1 配置佇列

import com.example.rabbitmq.mq.DirectConfig;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DelayedConfig {
    final static String QUEUE_NAME = "delayed.goods.order";
    final static String EXCHANGE_NAME = "delayedec";
    @Bean
    public Queue queue() {
        return new Queue(DelayedConfig.QUEUE_NAME);
    }

    // 配置預設的交換機
    @Bean
    CustomExchange customExchange() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        //引數二為型別:必須是x-delayed-message
        return new CustomExchange(DelayedConfig.EXCHANGE_NAME, "x-delayed-message", true, false, args);
    }
    // 繫結佇列到交換器
    @Bean
    Binding binding(Queue queue, CustomExchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with(DelayedConfig.QUEUE_NAME).noargs();
    }
}
複製程式碼

3.2 傳送訊息

import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;

@Component
public class DelayedSender {
    @Autowired
    private AmqpTemplate rabbitTemplate;

    public void send(String msg) {
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("傳送時間:" + sf.format(new Date()));

        rabbitTemplate.convertAndSend(DelayedConfig.EXCHANGE_NAME, DelayedConfig.QUEUE_NAME, msg, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setHeader("x-delay", 3000);
                return message;
            }
        });
    }
}
複製程式碼

3.3 消費訊息

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;

@Component
@RabbitListener(queues = "delayed.goods.order")
public class DelayedReceiver {
    @RabbitHandler
    public void process(String msg) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("接收時間:" + sdf.format(new Date()));
        System.out.println("訊息內容:" + msg);
    }
}
複製程式碼

3.4 測試佇列

import com.example.rabbitmq.RabbitmqApplication;
import com.example.rabbitmq.mq.delayed.DelayedSender;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.text.SimpleDateFormat;
import java.util.Date;

@RunWith(SpringRunner.class)
@SpringBootTest
public class DelayedTest {

    @Autowired
    private DelayedSender sender;

    @Test
    public void Test() throws InterruptedException {
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
        sender.send("Hi Admin.");
        Thread.sleep(5 * 1000); //等待接收程式執行之後,再退出測試
    }
}
複製程式碼

執行結果如下:

傳送時間:2018-09-11 20:47:51
接收時間:2018-09-11 20:47:54
訊息內容:Hi Admin.
複製程式碼

完整程式碼訪問我的GitHub:github.com/vipstone/sp…

四、總結

到此為止我們已經使用“rabbitmq-delayed-message-exchange”外掛實現了延遲功能,但是需要注意的一點是,如果使用命令“rabbitmq-plugins disable rabbitmq_delayed_message_exchange”禁用了延遲外掛,那麼所有未傳送的延遲訊息都將丟失。

相關文章