加载中...
avatar
文章
42
标签
25
分类
21
首页
Java
Spring全家桶
  • Spring
  • SpringBoot
  • SpringCloud
JVM
源码
  • Mybatis
  • HashMap
归档
其他
  • 互联网电子书汇总
  • JAVA八股文指南
  • 历史
  • 相册
关于
Logo码农StormlingMQ系列(四)| RabbitMQ 死信队列和延迟队列
搜索
首页
Java
Spring全家桶
  • Spring
  • SpringBoot
  • SpringCloud
JVM
源码
  • Mybatis
  • HashMap
归档
其他
  • 互联网电子书汇总
  • JAVA八股文指南
  • 历史
  • 相册
关于

MQ系列(四)| RabbitMQ 死信队列和延迟队列

发表于2024-11-24|更新于2024-12-22|消息队列架构
|总字数:2.6k|阅读时长:10分钟|浏览量:

死信队列

死信是什么

死信:无法被消费的消息。由于特定的原因导致队列中的某些消息无法被消费,这些消息没有后续的处理,就会变成死信。当消息在队列中无法被正常消费时,会被发送到死信队列中。

死信来源

消息 TTL
队列达到最大长度
消息拒签(basicNack 或 basicReject)且重入队列为false(requeue=false)

死信架构

image-20241211220211008

消息TTL

名称 交换机 路由键 类型 特征 参数
普通交换机 normal_exchange zhangsan direct / /
普通队列 normal_queue zhangsan / TTL DLX DLK x-dead-letter-exchange:dead_exchange
x-dead-letter-routing-key:lisi
x-message-ttl: 10 * 1000
死信交换机 dead_exchange lisi direct / /
死信队列 dead_queue lisi / / /

生产者发送消息10条消息到队列 normal-queue,消费者C1关闭不去消费,消息存活时间10s到后,10条消息进去死信队列 dead-queue,

image-20241211220303413

消费端C2开启,开始消费死信队列里的消息

image-20241211222528449

消息被拒

核心代码:在接收消息的地方

//消息拒签 requeue 设置 false 表示不重入队
channel.basicReject(deliveryTag,false)
//消息不确认,multiple=false 不批量 requeue=false 不重入队
//channel.basicNack(deliveryTag,false,false)

发送10条消息到队列

image-20241211225554204

消费端C2拒绝一条消息

image-20241211230004126

队列大小最大长度

将 x-message-ttl 属性改成 x-max-length : 6,重新启动,发送10个消息,死信队列接收到4个消息

名称 交换机 路由键 类型 特征 参数
普通队列 normal_queue zhangsan / TTL DLX DLK x-dead-letter-exchange:dead_exchange
x-dead-letter-routing-key:lisi
x-max-length: 6

image-20241211223706816

延迟队列

概念

延迟队列,队列**内部是有序**的,最重要的特性就体现在它的延时属性上,延时队列中的元素是希望 在指定时间到了**以后或之前取出和处**理,简单来说,延时队列就是用来存放需要**在指定时间被处理**的元素的队列。

场景

  1. 订单在十分钟之内未支付则自动取消
  2. 新创建的店铺,如果在十天内都没有上传过商品,则自动发送消息提醒。
  3. 用户注册成功后,如果三天内没有登陆则进行短信提醒。
  4. 用户发起退款,如果三天内没有得到处理则通知相关运营人员。
  5. 预定会议后,需要在预定的时间点前十分钟通知各个与会人员参加会议

分析场景特点:X 事件发生之后之前 X 时间完成 X 任务

定时轮询:可能可以解决问题,但如果短期数据量很多,活动期间甚至百万千万的数据,轮询就不能解决了

  • ☑️账单一周账单未结算的自动结算

  • ❌订单十分钟内未支付则关闭

RabbitMQ TTL

最大存活时间 `TTL: Time to Live`,`RabbitMQ` 中**消息**或**队列**的属性,表面消息或队列的**最大存活时间**

如果一条消息设置了 `TTL` 属性或者进入了设置`TTL` 属性的队列,那么这 条消息如果在`TTL` 设置的时间内没有被消费,则会成为"死信"。如果同时配置了队列的TTL 和消息的 `TTL`,那么较小的那个值将会被使用,有两种方式设置 `TTL`。

消息 TTL

另一种方式便是针对每条消息设置TTL

image-20241212115827412

队列 TTL

第一种是在创建队列的时候设置队列的 【x-message-ttl】属性

image-20241211231446238

整合SpringBoot

架构:队列TTL

创建两个队列 QA 和 QB,两者队列 TTL 分别设置为 10S 和 40S,然后在创建一个交换机 X 和死信交 换机 Y,它们的类型都是direct,创建一个死信队列 QD,它们的绑定关系

image-20241211233441601

依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-amqp</artifactId>
 </dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

配置

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672 
spring.rabbitmq.username=guest 
spring.rabbitmq.password=guest

配置类:TtlQueueConfig

@Configuration
public class TtlQueueConfig {
    //普通交换机和队列
    public static final String X_EXCHANGE = "X";
    public static final String QUEUE_A = "QA";
    public static final String QUEUE_B = "QB";
    //普通路由键
    public static final String XA_ROUTING_KEY = "XA";
    public static final String XB_ROUTING_KEY = "XB";
    //死信交换机和队列
    public static final String Y_DEAD_LETTER_EXCHANGE = "Y";
    public static final String DEAD_LETTER_QUEUE = "QD";
    //死信路由键
    public static final String Y_DEAD_LETTER_ROUTING_KEY = "YD";

    //声明普通交换机 X
    @Bean("xExchange")
    public DirectExchange xExchange() {
        return new DirectExchange(X_EXCHANGE);
    }

    //声明死信交换机 Y
    @Bean("yExchange")
    public DirectExchange yExchange() {
        return new DirectExchange(Y_DEAD_LETTER_EXCHANGE);
    }

    //声明普通队列 A 绑定到对应死信交换机 Y 上
    @Bean("queueA")
    public Queue queueA() {
        Map<String, Object> args = new HashMap<>(3);
        args.put("x-dead-letter-exchange", Y_DEAD_LETTER_EXCHANGE);
        args.put("x-dead-letter-routing-key", Y_DEAD_LETTER_ROUTING_KEY);
        args.put("x-message-ttl", 10000);
        return QueueBuilder.durable(QUEUE_A).withArguments(args).build();
    }

    //声明普通队列 B ttl 40s 并绑定到对应死信交换机Y
    @Bean("queueB")
    public Queue queueB() {
        Map<String, Object> args = new HashMap<>(3);
        args.put("x-dead-letter-exchange", Y_DEAD_LETTER_EXCHANGE);
        args.put("x-dead-letter-routing-key", Y_DEAD_LETTER_ROUTING_KEY);
        args.put("x-message-ttl", 40000);
        return QueueBuilder.durable(QUEUE_B).withArguments(args).build();
    }

    //声明死信队列QD
    @Bean("queueD")
    public Queue queueD() {
        return QueueBuilder.durable(DEAD_LETTER_QUEUE).build();
    }

    //声明普通队列 A 绑定到 普通交换机 X
    @Bean
    public Binding queueBindingX(@Qualifier("queueA") Queue queueA,
                                 @Qualifier("xExchange") DirectExchange xExchange) {
        return BindingBuilder.bind(queueA).to(xExchange).with(XA_ROUTING_KEY);
    }

    //声明普通队列 B 绑定到 普通交换机 X
    @Bean
    public Binding queueBBindingX(@Qualifier("queueB") Queue queueB,
                                  @Qualifier("xExchange") DirectExchange xExchange) {
        return BindingBuilder.bind(queueB).to(xExchange).with(XB_ROUTING_KEY);
    }

    @Bean
    public Binding deadLetterBindingY(@Qualifier("queueD") Queue queueD,
                                  @Qualifier("yExchange") DirectExchange yExchange) {
        return BindingBuilder.bind(queueD).to(yExchange).with(Y_DEAD_LETTER_ROUTING_KEY);
    }


}

生产者:SendMsgController

@Slf4j
@RestController
@RequestMapping("/ttl")
public class SendMsgController {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @GetMapping("sendMsg/{msg}")
    public void sendMsg(@PathVariable String msg) {
        log.info("当前时间:{},发送一条消息给两个TTL队列:{}", new Date(), msg);
        rabbitTemplate.convertAndSend("X", "XA", msg);
        rabbitTemplate.convertAndSend("X", "XB", msg);
    }
}

消费者:DeadLetterQueueConsumer

@Slf4j
@Component
public class DeadLetterQueueConsumer {
    @RabbitListener(queues = {"QD"})
    public void receiveD(Message message, Channel channel) {
        String msg = new String(message.getBody());
        log.info("当前时间:{},收到死信队列信息:{}", new Date(), msg);
    }
}

发起一个请求 http://localhost:8080/ttl/sendMsg/嘻嘻嘻

image-20241212111944595

第一条消息在 10S 后变成了死信消息,然后被消费者消费掉,第二条消息在 40S 之后变成了死信消息, 然后被消费掉,这样一个延时队列就打造完成了。

优化:动态设置Ttl的队列

在这里新增了一个队列 QC,绑定关系如下,该队列不设置TTL 时间

image-20241212112115951

配置类 MsgTtlQueueConfig

@Component 
public class MsgTtlQueueConfig { 
    public static final String Y_DEAD_LETTER_EXCHANGE = "Y"; 
    public static final String QUEUE_C = "QC"; 
 
    //声明队列 C 死信交换机 
    @Bean("queueC") 
    public Queue queueB(){ 
        Map<String, Object> args = new HashMap<>(3); 
        //声明当前队列绑定的死信交换机 
        args.put("x-dead-letter-exchange", Y_DEAD_LETTER_EXCHANGE); 
        //声明当前队列的死信路由 key 
        args.put("x-dead-letter-routing-key", "YD"); 
        //没有声明 TTL 属性 
        return QueueBuilder.durable(QUEUE_C).withArguments(args).build(); 
    } 
	//声明队列 B 绑定 X 交换机 
    @Bean 
    public Binding queuecBindingX(@Qualifier("queueC") Queue queueC, 
    							  @Qualifier("xExchange") DirectExchange xExchange){ 
   	 	 	return BindingBuilder.bind(queueC).to(xExchange).with("XC"); 
    } 
} 

生产者发送消息

核心代码: correlationData.getMessageProperties().setExpiration(ttlTime);

@GetMapping("sendExpirationMsg/{message}/{ttlTime}") 
public void sendMsg(@PathVariable String message,@PathVariable String ttlTime) { 
    rabbitTemplate.convertAndSend("X", "XC", message, correlationData->{ 	
        correlationData.getMessageProperties().setExpiration(ttlTime); 
        return correlationData; 
	}); 
	log.info("当前时间:{},发送一条时长{}毫秒 TTL 信息给队列 C:{}", new Date(),ttlTime, message); 
} 
# 发起请求 
http://localhost:8080/ttl/sendExpirationMsg/你好 1/20000 
http://localhost:8080/ttl/sendExpirationMsg/你好 2/2000

image-20241212112615811

分析:RabbitMQ 只会检查第一个消息是否过期,如果消息过期则丢到死信队列, 如果第一个消息的延时时长很长,而第二个消息的延时时长很短,第二个消息并不会优先得到执行

安装延时插件

  1. 在官网上下载 https://www.rabbitmq.com/community-plugins.html,下载 rabbitmq_delayed_message_exchange

  2. 将插件复制到RabbitMQ 容器的 /plugins 路径,

    # 复制插件到容器(rabbitmq)的路径(/plugins)
    docker cp D:/rabbitmq_delayed_message_exchange-4.0.2.ez rabbitmq:/plugins
  3. 执行命令

    rabbitmq-plugins enable rabbitmq_delayed_message_exchange

    image-20241212113757789

  4. 插件生效

image-20241212113809910

优化:延时队列插件 delayed

在这里新增了一个队列delayed.queue,一个自定义交换机 delayed.exchange,绑定关系如下:

image-20241212113930549

配置类 DelayedQueueConfig

在我们自定义的交换机中,这是一种新的交换类型,该类型消息支持延迟投递机制 消息传递后并不会立即投递到目标队列中,而是存储在 【**mnesia】(一个分布式数据系统)表中,当达到投递时间时,才投递**到目标队列中。

@Configuration
public class DelayedQueueConfig {
    public static final String DELAYED_EXCHANGE_NAME = "delayed.exchange";
    public static final String DELAYED_QUEUE_NAME = "delayed.queue";
    public static final String DELAYED_ROUTING_KEY = "delayed.routingkey";

    @Bean
    public Queue delayedQueue() {
        return new Queue(DELAYED_QUEUE_NAME);
    }

    // 自定义延迟交换机
    @Bean
    public CustomExchange delayedExchange() {
        Map<String, Object> args = new HashMap<>();
        //自定义交换机的类型
        args.put("x-delayed-type", "direct");
        return new CustomExchange(DELAYED_EXCHANGE_NAME, "x-delayed-message", true, false, args);
    }

    // 绑定延迟队列 到 延迟交换机
    @Bean
    public Binding bindingDelayedQueue(@Qualifier("delayedQueue")Queue delayedQueue,
                                       @Qualifier("delayedExchange") CustomExchange delayedExchange  ) {
        return BindingBuilder.bind(delayedQueue).to(delayedExchange).with(DELAYED_ROUTING_KEY).noargs();
    }
}

消息生产者代码

public static final String DELAYED_EXCHANGE_NAME = "delayed.exchange"; 
public static final String DELAYED_ROUTING_KEY = "delayed.routingkey"; 

 @GetMapping("sendDelayMsg/{msg}/{delayTime}")
 public void sendMsg(@PathVariable String msg, @PathVariable Integer delayTime) {
 	rabbitTemplate.convertAndSend("delayed.exchange", "delayed.routingkey", msg, correlationData -> {
 		correlationData.getMessageProperties().setDelay(delayTime);
 		return correlationData;
 	});
 	log.info("当前时间:{},发送一条延迟{}毫秒信息给队列delayed.queue:{}", new Date(), delayTime, msg);
 }

消息消费者代码:DeadLetterQueueConsumer 添加消费

@RabbitListener(queues = {"delayed.queue"})
public void receiveDelayedQueue(Message message) {
	String msg = new String(message.getBody());
	log.info("当前时间:{},收到延时队列的消息:{}", new Date(), msg);
}
# 发起请求: 
http://localhost:8080/ttl/sendDelayMsg/come on baby1/20000 
http://localhost:8080/ttl/sendDelayMsg/come on baby2/2000

image-20241212114815590

第二个消息被先消费掉了,符合预期

总结

延时队列在需要延时处理的场景下非常有用,使用 `RabbitMQ` 来实现延时队列可以很好的利用 `RabbitMQ` 的特性,如:消息可靠发送、消息可靠投递、死信队列来保障消息至少被消费一次以及未被正 确处理的消息不会被丢弃。另外,通过 `RabbitMQ` 集群的特性,可以很好的解决单点故障问题,不会因为 单个节点挂掉导致延时队列不可用或者消息丢失。 
文章作者: stormling
文章链接: http://www.stormling.top/posts/40901.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 码农Stormling!
消息队列架构
cover of previous post
上一篇
MQ系列(五)| Kafka 快速入门
Kafka 快速入门介绍 参考:Kafka 是什么? 架构一个高性能,高扩展性,高可用,支持持久化的超强消息队列,它就是我们常说的消息队列 KafkaZookeeper 协调管理多个 broker 组成,内部有多个 topic 分类,每个 topic 又分成多个 partition ,每个 partition 有多个副本 replia,不同的partition 会分布在不同 broker 上,提升性能同时,还增加了系统可用性和可扩展性 高性能 对消息进行分类,每个类是一个 topic 单个 topic 的消息可能过多,可将单个队列拆分成多个段,每段就是一个分区 partition ,每个消费者负责一个 partition 高扩展性可将 partition 分部在多台设备,每台设备代表一个 broker 高可用存在一个问题,如果其中一个partition所在的 broker 挂了,那么这部分的消息不久丢失了吗? 可以给partition 多加几个副本 replica,从中分为 Leader 和 Follower,Leader 负责生产者和消费者的读写,Follower...
cover of next post
下一篇
MQ系列(三)| RabbitMQ 消息确认机制
RabbitMQ 消息确认机制 :heavy_exclamation_mark::heavy_exclamation_mark::heavy_exclamation_mark:温馨提示:基于JDK17、SpringBoot 2.1.8.RELEASE 版本,由于RabbitMQ 在 SpringBoot3+ 的配置项有所不同, 所以请严格按照该本版来使用,挖一坑:【后续会出一个SpringBoot3+版本的配置相关教程】 架构 概念保证消息不丢失,可靠抵达,可以使用事务消息,性能下降250倍 为此引入确认机制 生产者确认回调:publisher confirmCallback 生产者退回回调:publisher returnCallback未投递到queue退回模式 消费者确认:consumer ack确认机制 ComfirmCallback【生产者确认回调】 概念:ComfirmCallback是生产者消息确认机制的一部分。当生产者发送消息到 RabbitMQ 的交换器(Exchange)后,RabbitMQ 会返回一个确认消息给生产者,这个确认过程可以通过...
相关推荐
cover
2024-11-30
MQ系列(七)| RocketMQ 为什么性能不如Kafka?
RocketMQ 为什么性能不如 Kafka? RocketMQ 使用的是 mmap 零拷贝技术,而 kafka 使用的是 sendfile (硬件设备技术 SG-DMA,不影响(不占用)CPU工作) mmap 内核缓冲区->映射用户缓冲区->内核缓冲区->网卡sendfile 内核缓冲区-> SG-DMA -> 网卡 在上篇文章《rocketmq 是什么》中,我们了解到 RocketMQ 的架构其实参考了 kafka 的设计思想,同时又在 kafka 的基础上做了一些调整。看起来,RocketMQ 好像各方面都比 kafka 更能打。 但 kafka 却一直没被淘汰,说明 RocketMQ 必然是有着不如 kafka 的地方。是啥呢?性能,严格来说是吞吐量。阿里中间件团队对它们做过压测,同样条件下,kafka 比 RocketMQ 快 **50%**左右。但即使这样,RocketMQ 依然能每秒处理 10w 量级的数据,依旧非常能打。你不能说 RocketMQ 弱,只能说 Kafka 性能太强了。 不过这就很奇怪了,为什么...
cover
2024-11-28
MQ系列(六)| RocketMQ 快速入门
RocketMQ 快速入门 本参考链接:RocketMQ 是什么?原作者:小白debug 前言 作为一个程序员,假设你有 A、B 两个服务,A 服务发出消息后,不想让 B 服务立马处理到。而是要过半小时才让 B 服务处理到,该怎么实现? 这类延迟处理消息的场景非常常见,举个例子,比如我每天早上到公司后都会点个外卖,我希望外卖能在中午送过来,而不是立马送过来,这就需要将外卖消息经过延时后,再投递到商家侧。 延时消息场景那么问题就来了,有没有优雅的解决方案?当然有,没有什么是加一层中间层不能解决的,如果有,那就再加一层。这次我们要加的中间层是消息队列 **RocketMQ**。 RocketMQ 是什么?RocketMQ 是阿里自研的国产消息队列,目前已经是 Apache 的顶级项目。和其他消息队列一样,它接受来自生产者的消息,将消息分类,每一类是一个 topic,消费者根据需要订阅 topic,获取里面的消息。 是不是很像我们上篇文章里提到的消息队 Kafka,那么问题很自然就来了,既然都是消息队列,那它们之间有什么区别呢? RocketMQ 和 Kafka...
cover
2024-11-27
MQ系列(五)| Kafka 快速入门
Kafka 快速入门介绍 参考:Kafka 是什么? 架构一个高性能,高扩展性,高可用,支持持久化的超强消息队列,它就是我们常说的消息队列 KafkaZookeeper 协调管理多个 broker 组成,内部有多个 topic 分类,每个 topic 又分成多个 partition ,每个 partition 有多个副本 replia,不同的partition 会分布在不同 broker 上,提升性能同时,还增加了系统可用性和可扩展性 高性能 对消息进行分类,每个类是一个 topic 单个 topic 的消息可能过多,可将单个队列拆分成多个段,每段就是一个分区 partition ,每个消费者负责一个 partition 高扩展性可将 partition 分部在多台设备,每台设备代表一个 broker 高可用存在一个问题,如果其中一个partition所在的 broker 挂了,那么这部分的消息不久丢失了吗? 可以给partition 多加几个副本 replica,从中分为 Leader 和 Follower,Leader 负责生产者和消费者的读写,Follower...
cover
2024-11-20
MQ系列(三)| RabbitMQ 消息确认机制
RabbitMQ 消息确认机制 :heavy_exclamation_mark::heavy_exclamation_mark::heavy_exclamation_mark:温馨提示:基于JDK17、SpringBoot 2.1.8.RELEASE 版本,由于RabbitMQ 在 SpringBoot3+ 的配置项有所不同, 所以请严格按照该本版来使用,挖一坑:【后续会出一个SpringBoot3+版本的配置相关教程】 架构 概念保证消息不丢失,可靠抵达,可以使用事务消息,性能下降250倍 为此引入确认机制 生产者确认回调:publisher confirmCallback 生产者退回回调:publisher returnCallback未投递到queue退回模式 消费者确认:consumer ack确认机制 ComfirmCallback【生产者确认回调】 概念:ComfirmCallback是生产者消息确认机制的一部分。当生产者发送消息到 RabbitMQ 的交换器(Exchange)后,RabbitMQ 会返回一个确认消息给生产者,这个确认过程可以通过...
cover
2024-11-14
MQ系列(二)| RabbitMQ 整合 SpringBoot
RabbitMQ 整合 SpringBoot概述 大多应用中,可通过消息服务中间件来提升系统异步通信、扩展解耦能力、流量削峰 消息服务中两个重要概念:消息代理(`message broker`)和目的地(`destination`) 当消息发送者发送消息以后,将由消息代理接管,消息代理保证消息传递到指定目的地。 消息队列主要有两种形式的目的地1. 队列(`queue`):点对点消息通信(`point-to-point`) 2. 主题(`topic`):发布(`publish`)/订阅(`subscribe`)消息通信 RabbitMQ 架构图 概念生产者 Producer生产者是消息的发送方,它将消息发送到 RabbitMQ 的交换器中。 ✨消息 Message 消息=消息头+消息体,根据routekey发送到指定的交换机 Exchange 消息头:含有各种属性 routing-key(路由键)、priority(优先级)、delivery-mode(指出该消息可能需要持久性存储)等。 ✨消息代理...
cover
2024-11-10
MQ系列(一)| RabbitMQ 快速入门
RabbitMQ 快速入门 官网:https://www.rabbitmq.com/ 入门教程:https://www.rabbitmq.com/tutorials 最新版本:4.0.2 版本参考:JDK17、Maven Or Gradle 1、简介RabbitMQ是一个可靠且成熟的消息传递和流代理,易于部署在云环境、本地和本地机器上。它目前被全球数百万人使用。 2、为什么使用公司业务场景核心:解耦、异步、削峰 2.1、解耦A系统发数据给到BCD系统,如果E系统需要接入?C系统不需要了?A系统的负责人就需要来回修改接口对接其他系统。 如果使用MQ,A系统产生一条数据,发送到MQ中,那个系统需要数据自己去MQ消费。如果新的系统需要数据,直接从MQ中消费;某个系统不需要数据的话,取消消费这个MQ即可。这样A系统不需要考虑谁发送数据给谁,不需要考虑是否调用成功、失败超时等问题。 总结:通过一个MQ,Pub/Sub发布订阅消息模型,A系统就和其他系统彻底耦合了。 2.2.1、项目应用...

评论
ValineGitalk
avatar
stormling
文章
42
标签
25
分类
21
Follow Me
公告
欢迎大家来到Stormling博客
目录
  1. 1. 死信队列
    1. 1.1. 死信是什么
    2. 1.2. 死信来源
    3. 1.3. 死信架构
      1. 1.3.1. 消息TTL
      2. 1.3.2. 消息被拒
      3. 1.3.3. 队列大小最大长度
  2. 2. 延迟队列
    1. 2.1. 概念
    2. 2.2. 场景
      1. 2.2.1. 分析场景特点:X 事件发生之后之前 X 时间完成 X 任务
    3. 2.3. RabbitMQ TTL
      1. 2.3.1. 消息 TTL
      2. 2.3.2. 队列 TTL
    4. 2.4. 整合SpringBoot
      1. 2.4.1. 架构:队列TTL
        1. 2.4.1.1. 依赖
        2. 2.4.1.2. 配置
        3. 2.4.1.3. 配置类:TtlQueueConfig
        4. 2.4.1.4. 生产者:SendMsgController
        5. 2.4.1.5. 消费者:DeadLetterQueueConsumer
      2. 2.4.2. 优化:动态设置Ttl的队列
        1. 2.4.2.1. 配置类 MsgTtlQueueConfig
        2. 2.4.2.2. 生产者发送消息
      3. 2.4.3. 安装延时插件
      4. 2.4.4. 优化:延时队列插件 delayed
        1. 2.4.4.1. 配置类 DelayedQueueConfig
        2. 2.4.4.2. 消息生产者代码
        3. 2.4.4.3. 消息消费者代码:DeadLetterQueueConsumer 添加消费
    5. 2.5. 总结
最新文章
面向八股文面试专场
面向八股文面试专场2025-01-22
【每日早报】-2025-01-21 - 星期二
【每日早报】-2025-01-21 - 星期二2025-01-21
规则引擎 Drools 8+ 快速入门
规则引擎 Drools 8+ 快速入门2024-12-11
数据库系列(二) | Mybatis Plus 3.0+快速入门
数据库系列(二) | Mybatis Plus 3.0+快速入门2024-12-09
分布式系列(二) | Redisson分布式锁
分布式系列(二) | Redisson分布式锁2024-12-05
©2019 - 2025 By stormling
码农Stormling程序员,关注公众号【码农Stormling】回复【面试】获取最全面试pdf
搜索
数据加载中