在springboot中利用Redis实现延迟队列
文章目录
- 前言
- 一、基本思路
- 二、springboot实现案例
- 三、测试
- 总结
前言
在开发过程中,有很多场景都需要用到延迟队列来解决。目前支持延迟队列的中间件也不少,特别是基于JMS模式下的消息中间件基本上都支持延迟队列。但是有时我们项目规模可能比较小,用不上JMS这些中间件。那么利用Redis也可以实现延迟队列的功能。
一、基本思路
利用Redis来实现延迟队列的主要思路是借助Redis的Sorted Set数据类型来实现。
具体做法是将任务的执行时间作为分数(score),任务的内容作为值(value),将任务按照执行时间排序存储在有序集合中。然后周期性地检查有序集合中的任务,根据当前时间和任务的执行时间来决定是否执行任务。
当需要添加新的延迟任务时,只需将任务的执行时间和内容添加到有序集合中即可。当然,你可能需要一个后台进程或定时任务来不断地检查有序集合,以执行到期的任务。
二、springboot实现案例
根据上面的思路,我们可以直接来写代码,本案例的完整代码点击下载。
首先,确保你的Spring Boot项目中已经配置好了Redis依赖。你可以在pom.xml文件中添加如下依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
然后,创建一个延迟队列管理器(DelayQueueManager)类,用于添加任务到延迟队列和处理到期任务:
package com.test.spring.redisdelayqueue;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;import java.util.Set;
/*** @author code-long* @version 1.0.0* @ClassName DelayQueueManager.java* @description*/
@Component
public class DelayQueueManager {private static final String key = "delayQueue";@Autowiredprivate RedisTemplate<String, String> redisTemplate;public void addToDelayQueue(String value, long delayMillis) {redisTemplate.opsForZSet().add(key, value, System.currentTimeMillis() + delayMillis);}public Set<String> getExpiredItems(long currentTime) {return redisTemplate.opsForZSet().rangeByScore(key, 0, currentTime);}public void removeItems(Set<String> items) {redisTemplate.opsForZSet().remove(key, items.toArray());}
}
接下来,我们利用spring的定时任务创建一个定时任务,用于定期检查延迟队列中的到期任务并执行。
同时我们还需要单独创建一个线程来专门处理逻辑,因为如果在定时任务直接处理逻辑可能会导致定时任务阻塞的现象,在这个线程中我们为了保证队列的顺序性,在使用BlockingDeque来模拟一个队列。当然如果你的队列逻辑处理不需要保持顺序性,完全可以使用多线程来处理任务。
具体实现代码:
package com.test.spring.redisdelayqueue;import lombok.extern.slf4j.Slf4j;
import org.h2.util.DateTimeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.text.DateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Set;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;/*** @author code-long* @version 1.0.0* @ClassName RedisDelayQueueApplication.java* @description*/
@SpringBootApplication(scanBasePackages = "com.test.spring.redisdelayqueue")
@EnableScheduling
@Slf4j
@RestController
public class RedisDelayQueueApplication {@Autowiredprivate DelayQueueManager delayQueueManager;@Scheduled(fixedRate = 1000) // 每秒执行一次public void processDelayQueue() {long currentTime = System.currentTimeMillis();Set<String> expiredItems = delayQueueManager.getExpiredItems(currentTime);// 处理到期任务,这里就可以达到延迟队列的模型//如果在这里直接处理逻辑,会影响到定时任务执行不完全的现象,比如一个任务执行需要2秒,那么就会阻塞JOB的执行,所以我们要另外启动一个线程来专门处理逻辑for (String item : expiredItems) {//将过期数据加入到执行队列DelayQueueInstance.getInstance().receive(item);}// 从延迟队列中移除已处理的任务:这里的删除可以放到线程中逻辑执行完成再删除if(!expiredItems.isEmpty()){delayQueueManager.removeItems(expiredItems);}}//应用启动成功后,就启动线程@EventListenervoid listener(ApplicationReadyEvent event) {DelayQueueInstance.getInstance().start();}//模拟入队操作@RequestMapping("/push")@Transactionalpublic String test1(){//模拟一个30秒的延迟队列delayQueueManager.addToDelayQueue("{这里可以使json数据:30}",30000);delayQueueManager.addToDelayQueue("{这里可以使json数据:10}",10000);System.out.println("["+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) +"]添加数据到队列");return "success";}public static void main(String[] args) {SpringApplication.run(RedisDelayQueueApplication.class, args);}
}
三、测试
我们在浏览器中访问或者使用curl调用接口:
curl http://localhost:8881/push
后台打印结果为:
[2024-03-14 22:28:54]添加数据到队列
[2024-03-14 22:29:05]收到数据---{这里可以使json数据:10}
[2024-03-14 22:29:25]收到数据---{这里可以使json数据:30}
我们可以看到,基本上能实现延迟队列的功能,只是这里有一点小小的瑕疵,任务可能会存在1秒的误差,但是这依赖于我们定时任务的循环时间,如果时间越短,误差的时间也就越短,定时任务间隔时间越长,误差也就越大。但1秒误差在实际的业务过程中已经是可以接受的了,对服务器来说性能也可以接受。
总结
使用Redis实现延迟队列的好处包括简单、高效,并且Redis本身就具有持久化和高可用性的特性,使得延迟队列的实现更加可靠。如果项目没有必要上JMS中间件,那么使用Redis是一个不错的方案。
相关文章:
在springboot中利用Redis实现延迟队列
文章目录 前言一、基本思路二、springboot实现案例三、测试总结 前言 在开发过程中,有很多场景都需要用到延迟队列来解决。目前支持延迟队列的中间件也不少,特别是基于JMS模式下的消息中间件基本上都支持延迟队列。但是有时我们项目规模可能比较小&…...
UpGrow评论:AI能将我的Instagram粉丝数增加10倍吗?
UpGrow Review: Can AI Grow My Instagram Followers 10X? 概述 UpGrow是一款专注于Instagram增长的AI驱动型社交媒体工具。它通过其庞大的300多人的网络,先进的定位功能,实时分析以及卓越的客户服务,帮助用户有机地增长Instagram关注者。…...
申请软著提交的演示视频有什么要求
申请软件著作权时,演示视频是一个重要的材料,主要用于展示软件的功能和操作流程。演示视频的要求可能会根据不同的申请机构和项目有所不同,但一般来说,以下是几个常见的要求: 内容完整性:演示视频需要展示…...
mac【启动elasticsearch报错:can not run elasticsearch as root
mac【启动elasticsearch报错:can not run elasticsearch as root 问题原因 es默认不能用root用户启动,生产环境建议为elasticsearch创建用户。 解决方案 为elaticsearch创建用户并赋予相应权限。 尝试了以下命令创建用户,adduser esh 和u…...
面试算法-65-二叉树的层平均值
题目 给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:[3.00000,14.50000,11.00000] 解释:第 0 层的…...
Linux: boot: latency启动延迟分析
https://elinux.org/images/6/64/Chris-simmonds-boot-time-elce-2017_0.pdf https://www.hcltech.com/sites/default/files/documents/resources/whitepaper/files/an_insight_to_optimize_embedded_linux_boot_time_performance.pdf 无意看到这个启动延迟分析,虽…...
QT界面制作
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);this->setWindowFlag(Qt::FramelessWindowHint);//接收动图QMovie *mv new QMovie(":/pictrue/th.gif…...
进阶二叉树
目录 二叉树 二叉搜索树 二叉搜索树的定义 二叉搜索树的操作 哈夫曼树 哈夫曼树的定义 哈夫曼树的构造 哈夫曼树的性质 平衡二叉树 平衡二叉树的定义: 平衡二叉树的插入调整 1.LL插入/LL旋转 2.RR插入/RR旋转 3.LR插入/LR旋转 4.RL插入/RL旋转 二叉树…...
无人机拦截
配置yolo CUDA报错 nvcc fatal : Unsupported gpu architecture compute_30.(1)查看显卡匹配型号:https://blog.csdn.net/u013308762/article/details/121658823 (2)查看显卡:nvidia-smi -a 》NVIDIA GeF…...
CSDN 编辑器设置图片缩放和居中
CSDN 编辑器设置图片缩放和居中 文章目录 CSDN 编辑器设置图片缩放和居中对齐方式比例缩放 对齐方式 Markdown 编辑器插入图片的代码格式为 CSDN 的 Markdown 编辑器中插入图片,默认都是左对齐,需要设置居中对齐的话,…...
有哪些工具可以替代Gitbook?这篇文章告诉你
你是否曾经在搜索在线文档创建和共享工具时,遇到了Gitbook? Gitbook 是一个相当出色的工具,具有强大的编辑和发布功能,但也有其不足之处,如使用起来有一定的技术要求,入门门槛较高等。如果你正在寻找Gitbook的替代品&…...
小迪安全43WEB 攻防-通用漏洞任意文件下载删除重装敏感读取黑白审计
#知识点: 1、文件操作类安全问题 2、文件下载&删除&读取 3、白盒&黑盒&探针分析 #详细点: 文件读取:基本和文件下载利用类似 文件下载:利用下载获取源码或数据库配置文件及系统敏感文件为后续出思路 …...
大模型提示学习样本量有玄机,自适应调节方法好
引言:探索文本分类中的个性化示例数量 在自然语言处理(NLP)领域,预测模型已经从零开始训练演变为使用标记数据对预训练模型进行微调。这种微调的极端形式涉及到上下文学习(In-Context Learning, ICL)&…...
Redis监控工具
Redis 是一种 NoSQL 数据库系统,以其速度、性能和灵活的数据结构而闻名。Redis 在许多领域都表现出色,包括缓存、会话管理、游戏、排行榜、实时分析、地理空间、叫车、聊天/消息、媒体流和发布/订阅应用程序。Redis 数据集完全存储在内存中,这…...
低代码表单设计器为企业数字转型强劲赋能!
想要实现数字化转型,创造流程化办公,让企业在信息高速发展的社会中抢占更多市场份额,进一步提升市场竞争力,就需要借助专业的软件平台提高效率。低代码开发平台拥有易操作、灵活、可视化的发展优势,作为一种新型的应用…...
【C#】Conventions(惯例)最佳实践和准则
在C#中,Conventions(惯例)是指编写代码时的一套最佳实践和准则。这些惯例旨在提高代码的可读性、一致性和可维护性。虽然这些惯例不是语言的强制规则,但遵循它们可以使你的代码更加清晰和专业。 以下是一些常见的C#编码惯例: 命名约定: 使用有意义的、描述性的名称。类名和公…...
vue3中使用cesium
vue3中使用cesium Cesium是一个开源的JavaScript库,专门用于创建3D地球和2D地图的Web应用程序。它提供了丰富的功能和工具,使得开发人员能够轻松地构建出高质量的地理空间可视化应用。 1. 安装cesium包 npm install cesium2. 复制node_modules中的Ces…...
arduino ide 开发esp8266注意事项
1.引脚序列号必须是常量来定义,否则会无限重启。 #define p2 2 const int Pin2p2; pinMode(Pin2, OUTPUT); 2.关于wifi的模式,ap,sta,apsta三种模式的初始化必须放在void set_up(){}这个函数里,不能额外搞个自定义函数…...
RTC协议与算法基础 - RTP/RTCP
首先,需要说明下,webrtc的核心音视频传输是通过RTP/RTCP协议实现的,源码位于src/modules/rtp_rtcp目录下: 下面让我们对相关的内容基础进行简要分析与说明: 一、TCP与UDP协议 1.1、TCP协议 TCP为了实现数据传输的可…...
c语言游戏实战(8):飞机大作战
前言: 飞机大作战游戏是一种非常受欢迎的射击类游戏,玩家需要控制一架战斗机在屏幕上移动,击落敌机以获得分数。本游戏使用C语言编写,旨在帮助初学者了解游戏开发的基本概念和技巧。 在开始编写代码之前,我们需要先了…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
Windows电脑能装鸿蒙吗_Windows电脑体验鸿蒙电脑操作系统教程
鸿蒙电脑版操作系统来了,很多小伙伴想体验鸿蒙电脑版操作系统,可惜,鸿蒙系统并不支持你正在使用的传统的电脑来安装。不过可以通过可以使用华为官方提供的虚拟机,来体验大家心心念念的鸿蒙系统啦!注意:虚拟…...
DeepSeek越强,Kimi越慌?
被DeepSeek吊打的Kimi,还有多少人在用? 去年,月之暗面创始人杨植麟别提有多风光了。90后清华学霸,国产大模型六小虎之一,手握十几亿美金的融资。旗下的AI助手Kimi烧钱如流水,单月光是投流就花费2个亿。 疯…...
React父子组件通信:Props怎么用?如何从父组件向子组件传递数据?
系列回顾: 在上一篇《React核心概念:State是什么?》中,我们学习了如何使用useState让一个组件拥有自己的内部数据(State),并通过一个计数器案例,实现了组件的自我更新。这很棒&#…...
JUC并发编程(二)Monitor/自旋/轻量级/锁膨胀/wait/notify/锁消除
目录 一 基础 1 概念 2 卖票问题 3 转账问题 二 锁机制与优化策略 0 Monitor 1 轻量级锁 2 锁膨胀 3 自旋 4 偏向锁 5 锁消除 6 wait /notify 7 sleep与wait的对比 8 join原理 一 基础 1 概念 临界区 一段代码块内如果存在对共享资源的多线程读写操作…...
