利用RabbitMQ消息TTL、队列TTL、DLX 实现延时队列

本文介绍了如何利用RabbitMQ的TTL和DLX特性实现延时队列,详细阐述了场景需求、实现逻辑,并通过实际案例验证了其功能。文中探讨了消息变为死信的条件,展示了配置示例,以及队列和消息TTL的交互影响。
摘要由CSDN通过智能技术生成

概念

TTL(Time To Live) 生存时间(消息有效期等)
DLX (Dead letter exchange)死信交换机

场景

  1. X秒之后查询某订单支付结果
  2. B服务更新数据时发出MQ,A服务消费该MQ查询B服务数据并听不到自己数据库,B服务使用主从分离,主从存在延时,A、B服务数据可能不一致
  3. X分钟后重试请求

实现逻辑

RabbitMQ原生不支持延迟队列,但是RabbitMQ的消息可以有生存时间(TTL),并且可以在消息生存时间结束之后可以转送到指定的DLX中,这样就可以将一个exchange作为中转(并不去消费,仅仅只是保存),消费者创建队列,来消费DLX中的消息,就可以通过TTL以及DLX来实现延时消费

关于死信

官方文档:死信相关描述
截取的相关简介
在这里插入图片描述
消息转换为死信

  1. 消息被消费者显式地调用方法reject、nack 并且不再requeue。
  2. 消息生存时间结束
  3. 消息队列中消息数量到达上限

这里最好的一点就是Exchange 并不需要特殊设置,都是普通的exchange,只需要队列做一些设置即可

看图说话

消息流转过程

图中示例delayExchange以direct类型来举例子,deadExchange以fanout为例,其余类型也支持此流程。service1、service2 产生消息,并且发送到delayExchange中,通过delayQueue来缓冲并且完成消息生存时间过期,再转到deadExchange中,之后客户端通过deadQueue来消费消息

实现

  1. 创建delayExchange
    新增delayExchange

  2. 创建deadExchange
    创建deadExchange

  3. 创建delayQueue
    在这里插入图片描述
    在这里插入图片描述
    需要注意的的式delayQueue的参数设置
    x-dead-letter-exchange 值为接收死信的exchange
    x-message-ttl 值为消息生存时间 单位ms

  4. 创建deadQueue
    创建deadQueue

  5. 绑定
    deadExchange绑定关系
    delayExchange绑定关系
    deadQueue 绑定到 deadExchenge
    delayQueue1 和 delayQueue2 绑定到 delayExchange 分别设置routingKey 为 routingKey1 和 routingKey2

验证

流程验证:
  1. 在delayExchange发送聊天消息分别使用routingKey1和routingKey2
    发送消息
    发送消息
  2. 最终deadQueue中收到两条消息
    在这里插入图片描述
  3. 获取消息详情
    在这里插入图片描述

在这里插入图片描述
结论: 消息可以正常流转

延迟功能验证:

生产者测试代码:

import com.test.CommonTest;
import org.junit.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 测试延时队列消息发送
 *
 * @author hsf
 * @since
 */
public class DelayQueueTest extends CommonTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void sendMQ() {
        rabbitTemplate.convertAndSend("delayExchange", "routingKey1", "routingKey1,time = " + System.currentTimeMillis());
        rabbitTemplate.convertAndSend("delayExchange", "routingKey2", "routingKey2,time = " + System.currentTimeMillis());
    }
}

消费者测试代码:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.context.annotation.Configuration;

import java.nio.charset.StandardCharsets;

/**
 * 延时消息消费
 *
 * @author hsf
 * @since
 **/
@Configuration
public class DelayMsgListener {

    private static final Logger logger = LoggerFactory.getLogger(DelayMsgListener.class);

    @RabbitListener(containerFactory = "rabbitMQListenerFactory", queues = "deadQueue")
    public void handle(Message message) {
        String msg = new String(message.getBody(), StandardCharsets.UTF_8);
        logger.info("msg = {}, receive timestamp = {} ", msg, System.currentTimeMillis());
    }

}

测试输出:
在这里插入图片描述
routingKey1 : 1614330056785 - 1614330055356 = 1429
routingKey2 : 1614330057809 - 1614330055682 = 2127

结论:算上网络时间以及代码运行时间,延时功能有效,此处验证效果不明显,假如想要看到明显效果可以增大队列的TTL

扩展

队列和消息同时设置TTL

rabbitMQ消息本身有TTL队列也有TTL相互关系是什么样子的
新增一个queue,routingKey3 TTL设置为10000ms(10s,这样效果明显)
消费者代码不变
生产者代码:

import com.test.CommonTest;
import org.junit.Test;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 测试延时队列消息发送
 *
 * @author hsf
 * @since
 */
public class DelayQueueTest extends CommonTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void sendMQ() {
        MessageProperties messageProperties = new MessageProperties();
        //手动设置为1s
        messageProperties.setExpiration("1000");
        Message message = new Message(("routingKey3,time =" + System.currentTimeMillis()).getBytes(), messageProperties);
        rabbitTemplate.send("delayExchange", "routingKey3", message);
        //手动设置为15s
        messageProperties.setExpiration("15000");
        message = new Message(("routingKey3,time =" + System.currentTimeMillis()).getBytes(), messageProperties);
        rabbitTemplate.send("delayExchange", "routingKey3", message);
    }
}

结果:
结果
两个消息
1614332275367 - 1614332273759 = 1608
1614332284370 - 1614332274296 = 10074
多次测试之后,第一个趋近于1s 第二个趋近于10s
结论:
当队列和消息同时设置TTL时以短的为准

消息设置TTL队列不设置

直接使用deadQueue测试,直接发送消息到deadExchange中
消费者停止消费
生产者修改代码

import com.test.CommonTest;
import org.junit.Test;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 测试延时队列消息发送
 *
 * @author hsf
 * @since
 */
public class DelayQueueTest extends CommonTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void sendMQ() {
        MessageProperties messageProperties = new MessageProperties();
        //手动设置为20s
        messageProperties.setExpiration("20000");
        Message message = new Message(("消息TTL测试,time =" + System.currentTimeMillis()).getBytes(), messageProperties);
        rabbitTemplate.send("deadExchange", null, message);
        //手动设置为30s
        messageProperties.setExpiration("30000");
        message = new Message(("消息TTL测试,time =" + System.currentTimeMillis()).getBytes(), messageProperties);
        rabbitTemplate.send("deadExchange", null, message);
    }
}


deadQueue变化过程:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
消息随着生存时间逐个过期

结束

延时队列可以通过队列TLL和消息TLL实现,根据需要选择即可,rabbitMQ原生支持
另有方法使用rabbitMQ插件来实现延时队列,这里没有装插件,装了插件再来测试、验证

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值