SpringBoot整合RabbitMQ,定時消息

本文基於spring-boot-starter-amqp依賴實現

RabbitMQ實現定時消息有兩種方式

  1. 基於消息TTL與死信交換
  2. 基於插件 RabbitMQ Delayed Message Plugin

基於消息TTL與死信交換

死信交換: 可以爲隊列設置一個死信exchange和routingKey,當隊列上產生死信時,死信會被投遞到設置好的exchange及對應的routingKey,說白點就是可以爲每個隊列設置一個"垃圾桶",產生死信就扔到垃圾桶裏,只不過垃圾桶也是一個正常的隊列.

如何會產生死信(怎樣會被扔到垃圾桶)

  1. 當消息被拒絕,並且拒絕後沒有重新進入隊列
  2. 由於消息TTL(剩餘生存時間),過期的消息
  3. 超過隊列長度而被刪除的消息

思路:

  1. 爲消息設置過期時間

  2. 把消息投遞到一個沒有消費者的隊列,ttl-queue 使消息成爲死信

  3. 爲ttl-queue設置死信交換,dead-letter-queue

  4. 爲dead-letter-queue添加消費者,監聽消息

先聲明那個沒有消費者的隊列,ttl-queue,併爲它指定"垃圾桶"(死信交換)

 

@Configuration
public class TtlConfig {

    @Bean("myTtlExchange")
    public Exchange ttlExchange() {
        return new DirectExchange("mine-ttl-exchange");
    }

    @Bean("myTtlQueue")
    public Queue ttlQueue() {
        //設置在這個隊列上的私信交換(垃圾桶)->
        // mine-dead-letter-exchange交換機,
        // 並且routingKey爲mine.dead.letter
        Map<String, Object> args = new HashMap<>(4);
        args.put("x-dead-letter-exchange", "mine-dead-letter-exchange");
        args.put("x-dead-letter-routing-key", "mine.dead.letter");

        return new Queue("mine-ttl-queue", true, false, false, args);
    }

    @Bean
    @DependsOn({"myTtlExchange", "myTtlQueue"})
    public Binding ttlBinding(Queue myTtlQueue, Exchange myTtlExchange) {
        return BindingBuilder.bind(myTtlQueue).to(myTtlExchange).with("mine.ttl").noargs();
    }
}

監聽上面設置好的"垃圾桶"

 

@Component
@RabbitListener(bindings = @QueueBinding(
        value = @Queue("mine-dead-letter-queue"),
        exchange = @Exchange("mine-dead-letter-exchange"),
        key = "mine.dead.letter" ))
public class DeadLetterListener {

    @RabbitHandler
    public void onMessage(String msg) {
        System.out.println(msg);
        System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:ss:mm")));
    }
}

爲消息設置過期時間

 

/**
 * 每條消息單獨設置過期時間
 */
public void send() {
    System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:ss:mm")));
    rabbitTemplate.convertAndSend("mine-ttl-exchange", "mine.ttl", "this is a message ",message -> {
        //設置10秒過期
        message.getMessageProperties().setExpiration("10000");
        return message;
    });
}

每條消息都設置過期時間,很靈活,若是固定的過期時間,可以在隊列上設置, 爲隊列設置 x-message-ttl 那在這個隊列上的每條消息的過期時間都爲 x-message-ttl 的值,只需要稍微修改一下ttl-queue

 

......
    
    @Bean("myTtlQueue")
    public Queue ttlQueue() {
        //設置在這個隊列上的私信的去處->
        // mine-dead-letter-exchange交換機,
        // 並且routingKey爲mine.dead.letter
        Map<String, Object> args = new HashMap<>(4);
        args.put("x-dead-letter-exchange", "mine-dead-letter-exchange");
        args.put("x-dead-letter-routing-key", "mine.dead.letter");

        //過期時間由隊列統一設置
        //注意不是 x-expires,x-expires爲隊列存活時間,
        // x-message-ttl爲隊列內的消息存活時間
        //注意更改隊列/交換機設置需要刪除原有的
        args.put("x-message-ttl", 20000);
        return new Queue("mine-ttl-queue", true, false, false, args);
    }

.....
    
   /**這樣的話也不用爲每條消息設置過期時間了
     * 消息過期時間由隊列統一決定
     */
    public void send1() {
    System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:ss:mm")));
    rabbitTemplate.convertAndSend("mine-ttl-exchange", "mine.ttl", "this is a message ");
}

基於插件

插件名稱 rabbitmq_delayed_message_exchange

插件下載 (注意版本哦)
https://github.com/rabbitmq/rabbitmq-delayed-message-exchange

解壓後把插件放在RabbitMQ的plugin目錄下,默認路徑

lunix : /usr/lib/rabbitmq/lib/rabbitmq_server-version/plugins/

windows : C:\Program Files\RabbitMQ\rabbitmq_server-version\plugins\

啓用插件

 

# 查看插件是否在列表裏
rabbitmq-plugins list
# 啓用插件
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
# 啓用後再執行
rabbitmq-plugins list

如下,插件前有 [E*] 表示啓用了

 

......
[E*] rabbitmq_delayed_message_exchange 20171201-3.7.x
[  ] rabbitmq_event_exchange           3.7.15
[  ] rabbitmq_federation               3.7.15
[  ] rabbitmq_federation_management    3.7.15
[  ] rabbitmq_jms_topic_exchange       3.7.15
......

使用

  1. 申明一個延時消息的exchange 即:

    • 交換機類型爲 x-delayed-message
    • 爲交換機設置參數 "x-delayed-type" ,值爲四個交換機類型(direct ,fanout ,topic ,headers )
    • 其餘使用與普通交換機無差別

 

@Component
@RabbitListener(bindings = @QueueBinding(
        key = "mine.delay",
        value = @Queue("mine-delay-queue"),
        exchange = @Exchange( value = "mine-delay-exchange",
                              type = "x-delayed-message",
                              arguments = @Argument(name = "x-delayed-type",
                                                    value = ExchangeTypes.DIRECT))))
public class DelayListener {

    @RabbitHandler
    public void onMessage(String msg) {
        System.out.println(msg);
        System.out.println(LocalDateTime.now()
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    }

}
  1. 生產者發送消息時,設置延時的時間 x-delay

 

@Autowired
private RabbitTemplate rabbitTemplate; 

public void send() {
        System.out.println(LocalDateTime.now()
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        
        rabbitTemplate.convertAndSend("mine-delay-exchange", "mine.delay", "this is a message ",
                message -> {
                    MessageProperties messageProperties = message.getMessageProperties();
                    //單位,毫秒
                    messageProperties.setDelay(10000);
                    return message;
                });
    }

轉載請註明出處

系列文章



作者:zolvces
鏈接:https://www.jianshu.com/p/31ed96971818
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

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