七、延迟队列
这篇文章详细介绍了如何利用RabbitMQ构建延时队列系统,并提供了多种实现方案和技术细节:
延时队列的基本概念:解释了延迟处理场景下延迟队列的作用和必要性。
RabbitMQ中的TTL机制:描述了如何通过消息属性或队列属性设置延迟时间,并通过交换机实现动态 TTL 管理。
配置与集成:展示了在Spring Boot中集成RabbitMQ的过程,包括依赖注入、配置文件编写以及消息生产者和消费者代码示例。
优化策略:探讨了单条消息长时间未处理的问题,并介绍了使用插件解决的方法。
插件应用:通过安装特定插件实现了自定义交换机和延迟投递机制,解决了单条消息长时间未处理的问题。
总结:强调了 RabbitMQ 在可靠性和高可用性方面的优势,并指出有其他替代方案可供选择。
文章通过代码示例和插件应用展示了从基础到高级的延时队列构建过程。
文章目录
-
一、延时队列的概念
-
二、延时队列的应用场景
-
三、在RabbitMQ中设置消息持久化时间
-
- 3.1 消息如何设置持久化时间
-
- 3.2 队列如何配置持久化时间
-
- 3.3 消息与队列持久化时间有何不同
-
四、整合Spring Boot
-
4.1 构建Spring Boot工程环境
- 4.2 引入相关依赖项
- 4.3 配置项目相关参数设置
- 4.4 配置 Swagger 配置类路径
第五章 队列 TTL
- 六、延时队列优化
-
- 6.1 代码架构图
- 6.2 配置文件类代码
- 6.3 消息生成者代码
七、Rabbitmq 插件实现延迟队列
* 7.1 延时队列插件的配置与安装
* 7.1.1 在Linux环境下进行配置
* 7.1.2 针对Docker容器环境的操作
* 7.2 代码架构图
* 7.3 配置文件类代码
* 7.4 消息生产者代码
* 7.5 消息消费者代码
- 八、总结
一、延迟队列概念
该技术采用延迟机制实现资源分配管理,并通过智能调度算法优化任务执行效率
二、延迟队列使用场景
- 若订单在十分钟内未完成支付,则会自动取消。
- 对于新设店铺而言,在没有商品上架前十个自然日将收到系统提醒信息。
- 用户注册成功后,在未登录三天内将触发短信通知。
- 发起退款申请后,在未得到及时处理的情况下将通知相关运营人员介入处理。
- 在预定会议开始前十分钟需向所有参会人员发出会议开始的通知。
这些场景都具有一个共同特点:都需要在某个特定事件发生后或前的时间节点内完成某项任务。例如,在订单生成事件发生后十分钟内检查该订单的支付状态,并对尚未支付的订单进行关闭处理。看起来像是采用定时任务机制,在每个时段内持续监控数据,并每隔一秒检查一次相关信息。从而提取并处理这些数据即可完成任务。对于类似'账单一周内未支付则自动结算'的需求,则可以通过每天晚上运行一次定时任务来检查所有未支付的账单。然而,在面对如此庞大的数据量时(例如活动期间的数据量可能达到百万甚至千万级别),单纯依靠每秒轮询的方式来处理显然效率低下。

三、RabbitMQ 中的 TTL
TTL 其实是什么意思呢?在 RabbitMQ 中,TTL 是一个用于指定消息或队列属性的关键参数,它代表的是单条消息或该队列中所有消息的最大存活时长。换句话说,当一条消息被发布到 RabbitMQ 系统中时,系统会记录下这条消息的有效存续时间(通常以毫秒为单位)。具体而言,若消息或该队列中的成员配置了 TTL 属性,则表示系统将在指定时间内自动丢弃这些未被处理的消息内容。需要注意的是,当同一消息既设置了自身的 TTL 属性又加入了具有特定 TTL 设置的队列进行处理时,系统将优先采用这两个 TTL 值中的最小值作为新的 TTL 时间限制。此外,这种机制还可以通过两种不同的方式来实现:一种是直接为特定的消息实例分配独立的 TTL 时间限制,另一种则是通过修改相关的队列配置参数来控制全局的消息存活时长。
3.1 消息设置 TTL
一种方式便是针对每条消息设置 TTL

3.2 队列设置TTL
第一种是在创建队列的时候设置队列的“x-message-ttl”属性

3.3 两者的区别
- 当设置了一个队列的 TTL 属性时, 如果消息超时就会被丢弃(如果配置了死信队列的话),而另外一种方式则是, 消息即使超时也不会立即被丢弃, 因为消息是否超时将在即将发送给消费者之前进行判断 。如果当前队列存在严重的积压情况, 那么已经超时的消息也可能存活一段时间;此外还需要注意的是, 如果没有设置 TTL, 表示消息永远不会超时;如果将 TTL 设置为 0, 则表示只有当此时可以直接将该消息投递给消费者的时候, 消息才会被保留;否则该消息将会被丢弃。
- 在上一小节中我们介绍了死信队列以及 TTL 这一概念, 现在我们已经通过 RabbitMQ 实现了延时队列所需的两大要素——即队列的 Ttl 属性以及相关的配置机制。接下来只需要将它们进行结合并添加一些必要的细节就能完成延时队列的设计工作了。思考一下的话, 延时队列正是希望调节消息延迟多久后再由处理方进行处理的那个机制,Ttl 则正好能让消息在延迟多久之后变成死信的消息并进入死信队列中。这样一来只需要让消费者持续消费死信队列中的消息就可以了。
四、整合springboot
4.1 idea创建sringboot工程

4.2 添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.15</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.jz</groupId>
<artifactId>springboot-rabbitmq</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-rabbitmq</name>
<description>springboot-rabbitmq</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--RabbitMQ 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</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>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!--RabbitMQ 测试依赖-->
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.7.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
4.3 修改配置文件
# 配置rabbitmq的连接信息
spring:
rabbitmq:
host: 192.168.159.128
port: 5672
username: admin
password: admin
mvc:
pathmatch:
matching-strategy: ant_path_matcher
4.4 添加Swagger配置类
package com.jz.springbootrabbitmq.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/** * @author Gaoyonghao
* @date 2023-09-21 8:44
* @copyright Copyright (c) 2023 Gaoyonghao
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket webApiConfig() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("webApi")
.apiInfo(webApiInfo())
.select()
.build();
}
private ApiInfo webApiInfo() {
return new ApiInfoBuilder()
.title("rabbitmq接口文档")
.description("本文档描述了rabbitmq接口定义")
.version("1.0")
.contact(new Contact("aaa", "aaa", "aaa"))
.build();
}
}
五、队列TTL
5.1 代码架构图
生成两个队列 QA 和 QB,并对它们的 TTL 设置为 10 秒和 40 秒;随后生成交换机 X 和死信交换机 Y(均为 direct 类型);建立一个死信队列 QD,请参考下文详细说明其绑定关系

5.2 配置文件类代码
package com.jz.springbootrabbitmq.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
/** * @author Gaoyonghao
* @date 2023-09-21 8:48
* @copyright Copyright (c) 2023 Gaoyonghao
*/
@Configuration
public class TtlQueueConfig {
private static final String X_EXCHANGE = "X";
private static final String QUEUE_A = "QA";
private static final String QUEUE_B = "QB";
private static final String Y_DEAD_LETTER_EXCHANGE = "Y";
private static final String DEAD_LETTER_QUEUE = "QD";
// 声明xExchange
@Bean
public DirectExchange xExchange() {
return new DirectExchange(X_EXCHANGE);
}
// 声明yExchange
@Bean
public DirectExchange yExchange() {
return new DirectExchange(Y_DEAD_LETTER_EXCHANGE);
}
// 声明队列QA,设置队列ttl为10s
@Bean
public Queue queueA() {
HashMap<String, Object> args = new HashMap<>();
// 声明当前队列绑定的死信交换机
args.put("x-dead-letter-exchange", Y_DEAD_LETTER_EXCHANGE);
// 声明当前队列绑定的死信路由key
args.put("x-dead-letter-routing-key", "YD");
// 声明队列的TTL
args.put("x-message-ttl", 10000);
return QueueBuilder.durable(QUEUE_A).withArguments(args).build();
}
@Bean
public Queue queueB() {
HashMap<String, Object> args = new HashMap<>();
// 声明当前队列绑定的死信交换机
args.put("x-dead-letter-exchange", Y_DEAD_LETTER_EXCHANGE);
// 声明当前队列绑定的死信路由key
args.put("x-dead-letter-routing-key", "YD");
// 声明队列的TTL
args.put("x-message-ttl", 40000);
return QueueBuilder.durable(QUEUE_B).withArguments(args).build();
}
@Bean
public Queue queueD() {
return QueueBuilder.durable(DEAD_LETTER_QUEUE).build();
}
// 声明队列A绑定X交换机
@Bean
public Binding queueABindingX(Queue queueA, DirectExchange xExchange) {
return BindingBuilder.bind(queueA).to(xExchange).with("XA");
}
// 声明队列B绑定X交换机
@Bean
public Binding queueBBindingX(Queue queueB, DirectExchange xExchange) {
return BindingBuilder.bind(queueB).to(xExchange).with("XB");
}
// 声明队列D绑定Y交换机
@Bean
public Binding queueDBindingY(Queue queueD, DirectExchange yExchange) {
return BindingBuilder.bind(queueD).to(yExchange).with("YD");
}
}
5.3 消息生产者代码
package com.jz.springbootrabbitmq.controller;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
/** * @author Gaoyonghao
* @date 2023-09-21 9:18
* @copyright Copyright (c) 2023 Gaoyonghao
*/
@Slf4j
@RestController
@RequestMapping("/ttl")
@Api("rabbitmq消息发送者")
public class SendMsgController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/sendMsg/{message}")
public void sendMsg(@PathVariable String message) {
log.info("当前时间:{},发送一条消息给两个TTL队列:{}", new Date(), message);
rabbitTemplate.convertAndSend("X", "XA", "消息来自ttl为10s的队列:" + message);
rabbitTemplate.convertAndSend("X", "XB", "消息来自ttl为40s的队列:" + message);
}
}
5.4 消息消费者代码
package com.jz.springbootrabbitmq.controller;
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.nio.charset.StandardCharsets;
import java.util.Date;
/** * @author Gaoyonghao
* @date 2023-09-21 9:26
* @copyright Copyright (c) 2023 Gaoyonghao
*/
@Slf4j
@Component
public class DeadLetterQueueConsumer {
@RabbitListener(queues = "QD")
public void receiveD(Message message, Channel channel) {
String msg = new String(message.getBody(), StandardCharsets.UTF_8);
log.info("当前时间:{},收到死信队列消息:{}", new Date(), msg);
}
}
打开一个超链接 http://localhost:8080/ttl/sendMsg/笑一笑

一条信息在10秒后达到死信状态并被系统处理掉,在此期间另一条信息将在40秒后同样达到死信状态并被系统处理掉。通过这种机制就建立了一个延时队列系统。然而这种方式存在明显的局限性即每引入一个新的时间间隔就需要为该间隔配置一个独立的队列资源。目前我们只支持10秒和40秒两种时间间隔的选择如果希望延迟超过一小时就需要配置具有1小时TTL值的长时存活队列了。对于那些需要提前知道即将召开会议的情况比如预定会议并要求提前通知的情形我们可能还需要配置无数个不同TTL值来应对各种可能出现的需求
六、延时队列优化
6.1 代码架构图
在这里新增了一个队列 QC,绑定关系如下,该队列不设置 TTL 时间

6.2 配置文件类代码
package com.jz.springbootrabbitmq.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/** * @author Gaoyonghao
* @date 2023-09-21 10:01
* @copyright Copyright (c) 2023 Gaoyonghao
*/
@Configuration
public class MsgTtlQueueConfig {
private static final String Y_DEAD_LETTER_EXCHANGE = "Y";
private static final String QUEUE_C = "QC";
// 声明队列C
@Bean
public Queue queueC() {
return QueueBuilder
.durable(QUEUE_C)
.deadLetterExchange(Y_DEAD_LETTER_EXCHANGE)
.deadLetterRoutingKey("YD")
.build();
}
// 绑定队列C至交换机X
@Bean
public Binding queueCBindExchangeX(Queue queueC, DirectExchange xExchange) {
return BindingBuilder.bind(queueC).to(xExchange).with("XC");
}
}
6.3 消息生成者代码
package com.jz.springbootrabbitmq.controller;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
/** * @author Gaoyonghao
* @date 2023-09-21 9:18
* @copyright Copyright (c) 2023 Gaoyonghao
*/
@Slf4j
@RestController
@RequestMapping("/ttl")
@Api("rabbitmq消息发送者")
public class SendMsgController {
@Autowired
private RabbitTemplate rabbitTemplate;
@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);
}
}
向服务器发送请求

该方法可能会存在延迟的情况,在最初阶段就已经介绍过一种通过配置 RabbitMQ 消息属性来实现持久化的方式:即在配置时为每个队列指定一个 TTL 值(通常是 3600 秒)。然而需要注意的是,在 RabbitMQ 中,并不是所有的队列都会自动应用这个 TTL 值来处理队列的消息。具体来说,在 RabbitMQ 中只会检查第一个接收到的消息是否超时:如果该条消息已超时,则会将其丢入到死信队列中等待处理;而如果第一个接收到的消息未超时,则其他后续接收到的消息将不会被立即处理(除非它们也被设置为超时状态)。因此,在实际应用中需要特别注意这一特性以确保系统的可靠性和稳定性
七、Rabbitmq 插件实现延迟队列
上文提及其存在的问题是不容忽视的。如果无法实现消息级别的 TTL,并能在预设 TTL 时间内及时删除事件记录,则就不能构建一个通用的有效延时队列。那么我们该如何着手解决这一关键问题呢?接下来我们将深入探讨并提出可行解决方案。
7.1 安装延时队列插件
访问 https://www.rabbitmq.com/community-plugins.html ,获取 rabbitmq_delayed_message_exchange 插件文件 ,然后将解压后的插件文件放置于RabbitMQ软件的插件目录下
7.1.1 linux环境下的安装
切换到 RabbitMQ 的安装目录中的 plugins 子目录,并运行相应的命令以启用 rabbitmq-delayed-message-exchange 插件之后,请重新启动 RabbitMQ 服务
$rabbitmq-plugins enable rabbitmq-delayed-message-exchange

7.1.2 docker容器环境下的安装
- 将插件拷贝至容器中
docker cp rabbitmq_delayed_message_exchange-3.12.0.ez gyh_rabbitmq:/plugins
- 进入容器,并查看插件
docker exec -it gyh_rabbitmq /bin/bash
rabbitmq-plugins list
- 启动插件并重启容器
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
exit # 退出至宿主机系统
docker restart gyh_rabbitmq
完成容器重启后,请您进入RabbitMQWeb管理界面并切换至Exchanges设置页面。您可以在此处新增一个交换点,并确认其类型是否被识别为延迟消息类型,请参考下图

7.2 代码架构图
在当前环境中引入了一个队列实例(延迟队列)和一个自定义交换模块配置文件(delayed.exchange),其关联配置如下:

7.3 配置文件类代码
在我们的自定义交换机设计中引入了一种新型的消息传输模式。这种新类型的的消息传输支持延迟机制下实现的消息投递功能。具体而言,在消息传递完成后不会立即发送至目的队列;相反地,则会将其存储于mnesia(一种分布式数据存储系统)的数据表中。只有当达到指定的时间节点才会将消息发送至目标队列进行处理。
package com.jz.springbootrabbitmq.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
/** * @author Gaoyonghao
* @date 2023-09-21 10:56
* @copyright Copyright (c) 2023 Gaoyonghao
*/
@Configuration
public class DelayedQueueConfig {
private static final String DELAYED_QUEUE_NAME = "delayed.queue";
private static final String DELAYED_EXCHANGE_NAME = "delayed.exchange";
private static final String DELAYED_ROUTING_KEY = "delayed.routingkey";
@Bean
public Queue delayedQueue() {
return QueueBuilder.durable(DELAYED_QUEUE_NAME).build();
}
// 自定义交换机
@Bean
public CustomExchange delayedExchange() {
HashMap<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 bindingDelayQueue(Queue delayedQueue, CustomExchange delayedExchange) {
return BindingBuilder
.bind(delayedQueue)
.to(delayedExchange)
.with(DELAYED_ROUTING_KEY)
.noargs();
}
}
7.4 消息生产者代码
package com.jz.springbootrabbitmq.controller;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
/** * @author Gaoyonghao
* @date 2023-09-21 9:18
* @copyright Copyright (c) 2023 Gaoyonghao
*/
@Slf4j
@RestController
@RequestMapping("/ttl")
@Api("rabbitmq消息发送者")
public class SendMsgController {
private static final String DELAYED_EXCHANGE_NAME = "delayed.exchange";
private static final String DELAYED_ROUTING_KEY = "delayed.routingkey";
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/sendDelayMsg/{message}/{dealTime}")
public void sendDelayMsg(@PathVariable String message, @PathVariable Integer dealTime) {
rabbitTemplate.convertAndSend(DELAYED_EXCHANGE_NAME, DELAYED_ROUTING_KEY, message, correlationData -> {
correlationData.getMessageProperties().setDelay(dealTime);
return correlationData;
});
log.info("当前时间:{}发送一条时长{}毫秒TTL信息给队列delayed.queue:{}", new Date(), dealTime, message);
}
}
7.5 消息消费者代码
package com.jz.springbootrabbitmq.controller;
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.nio.charset.StandardCharsets;
import java.util.Date;
/** * @author Gaoyonghao
* @date 2023-09-21 9:26
* @copyright Copyright (c) 2023 Gaoyonghao
*/
@Slf4j
@Component
public class DeadLetterQueueConsumer {
private static final String DELAYED_QUEUE_NAME = "delayed.queue";
@RabbitListener(queues = DELAYED_QUEUE_NAME)
public void receiveDelayedQueue(Message message, Channel channel) {
String msg = new String(message.getBody(), StandardCharsets.UTF_8);
log.info("当前时间:{},收到延时队列消息:{}", new Date(), msg);
}
}
发起调用:请调用...:发送延迟消息到指定地址

第二个消息被先消费掉了,符合预期
八、总结
在处理延迟需求的场景中,延时队列表现出色。通过RabbitMQ实现延时队列功能,充分体现了该技术的优势所在:确保消息可靠发布和可靠投递的同时,并结合死信机制来保证至少被消费一次,并且能有效避免未被正确处理的消息丢失。此外,基于RabbitMQ集群特性设计的延时系统能够很好地规避单点故障问题,在任何一个节点失效的情况下仍能维持系统的稳定运行。值得提到的是,在处理延迟需求方面存在多种替代方案:例如Java中的DelayQueue框架、Redis的数据结构zset也可用于实现类似功能,并且Quartz任务调度机制以及Kafka的时间轮机制也是常见的选择。这些不同方案各有特点与适用场景
