Spring Boot 中使用 Redis 和 Lua 脚本实现一个延时队列

效率工具
  • 推荐一个程序员的常用工具网站,效率加倍嘎嘎好用:程序员常用工具
云服务器
  • 云服务器限时免费领:轻量服务器2核4G
  • 腾讯云:2核2G4M云服务器新老同享99元/年,续费同价
  • 阿里云:2核2G3M的ECS服务器只需99元/年,续费同价

在分布式系统中,延时队列是一种常见的需求,它允许我们将任务延迟一段时间后再执行。常见的应用场景包括订单超时处理、短信发送延迟、缓存失效处理等。本文将介绍如何在Spring Boot项目中,结合Redis和Lua脚本实现一个高效的延时队列。

一、项目准备

1.1 引入依赖

在Spring Boot项目中,我们需要引入以下依赖:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency>
</dependencies>

1.2 配置Redis

application.yml中配置Redis连接信息:

spring:redis:host: localhostport: 6379password: lettuce:pool:max-active: 8max-idle: 8min-idle: 0

1.3 创建Redis配置类

创建一个配置类来配置RedisTemplate:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(new StringRedisSerializer());return template;}
}

二、实现延时队列

延时队列的核心思想是使用Redis的有序集合(Sorted Set)来存储任务,每个任务关联一个延时时间。当时间到达时,通过Lua脚本将任务从有序集合中移到处理队列中。

2.1 创建任务发布接口

首先,我们创建一个接口来发布延时任务:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;import java.time.Instant;@Service
public class DelayQueueService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;private static final String DELAY_QUEUE_KEY = "delay_queue";public void addTask(String taskId, long delayInSeconds) {long score = Instant.now().getEpochSecond() + delayInSeconds;redisTemplate.opsForZSet().add(DELAY_QUEUE_KEY, taskId, score);}
}

2.2 Lua脚本处理任务

Lua脚本用于从有序集合中取出到期的任务,并将其移到处理队列中:

local delayQueueKey = KEYS[1]
local readyQueueKey = KEYS[2]
local currentTime = tonumber(ARGV[1])
local tasks = redis.call('ZRANGEBYSCORE', delayQueueKey, 0, currentTime)if next(tasks) ~= nil thenfor _, task in ipairs(tasks) doredis.call('ZREM', delayQueueKey, task)redis.call('LPUSH', readyQueueKey, task)end
endreturn tasks

2.3 创建任务处理接口

我们创建一个接口来处理到期的任务:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;import java.time.Instant;
import java.util.List;@Service
public class TaskProcessor {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Autowiredprivate JedisPool jedisPool;private static final String DELAY_QUEUE_KEY = "delay_queue";private static final String READY_QUEUE_KEY = "ready_queue";private static final String LUA_SCRIPT = "local delayQueueKey = KEYS[1] " +"local readyQueueKey = KEYS[2] " +"local currentTime = tonumber(ARGV[1]) " +"local tasks = redis.call('ZRANGEBYSCORE', delayQueueKey, 0, currentTime) " +"if next(tasks) ~= nil then " +"    for _, task in ipairs(tasks) do " +"        redis.call('ZREM', delayQueueKey, task) " +"        redis.call('LPUSH', readyQueueKey, task) " +"    end " +"end " +"return tasks ";@Scheduled(fixedRate = 1000)public void processTasks() {try (Jedis jedis = jedisPool.getResource()) {List<String> tasks = (List<String>) jedis.eval(LUA_SCRIPT, 2, DELAY_QUEUE_KEY, READY_QUEUE_KEY, String.valueOf(Instant.now().getEpochSecond()));for (String task : tasks) {// 处理任务System.out.println("Processing task: " + task);}}}
}

在这个实现中,processTasks 方法每秒执行一次,通过Lua脚本检查并处理到期的任务。

三、测试与验证

3.1 添加测试任务

在控制器中添加接口来发布延时任务:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class DelayQueueController {@Autowiredprivate DelayQueueService delayQueueService;@PostMapping("/addTask")public String addTask(@RequestParam String taskId, @RequestParam long delayInSeconds) {delayQueueService.addTask(taskId, delayInSeconds);return "Task added";}
}

3.2 验证任务处理

启动Spring Boot应用程序,通过HTTP请求添加任务:

curl -X POST "http://localhost:8080/addTask?taskId=task1&delayInSeconds=10"
curl -X POST "http://localhost:8080/addTask?taskId=task2&delayInSeconds=20"

检查控制台输出,确认任务在指定的延迟时间后被正确处理:

Processing task: task1
Processing task: task2

四、总结

通过本文,我们学习了如何在Spring Boot项目中使用Redis和Lua脚本实现延时队列。通过Redis的有序集合存储任务和Lua脚本处理到期任务,可以实现高效的延时任务处理机制。结合Spring Boot的定时任务调度,能够方便地实现任务的定期检查和处理。这种方法不仅简单高效,还能很好地扩展和维护,是实现延时队列的一个优秀方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/842879.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

仿冒、钓鱼、入侵……警惕邮件安全这些“坑”

为了保证用户对电子邮箱系统的安全使用&#xff0c;保证个人的隐私和财产的安全&#xff0c;我们呼吁每个人都要加强自己的网络安全意识&#xff0c;在对电子邮件进行处理的时候&#xff0c;要对钓鱼邮件进行认真的识别&#xff0c;同时还需要设定一个客户的密码来保证你的邮箱…

【Unity实战】Mirror/UNET中SyncVar和SyncList需要注意的点

SyncVar和SyncList在Unity开发中喜闻乐见&#xff0c;常用于脚本中字段的同步。 但也时常会出现修改了但是没同步的问题。 故本人根据过往踩的坑进行了以下总结&#xff1a; 1. 尽量不要用它进行类的同步 在Unity中&#xff0c;[SyncVar] 特性通常用于同步Unity网络游戏中基…

新旅程:类与对象的魔法课堂

&#x1f389;&#x1f389;&#x1f389;欢迎莅临我的博客空间&#xff0c;我是池央&#xff0c;一个对C和数据结构怀有无限热忱的探索者。&#x1f64c; &#x1f338;&#x1f338;&#x1f338;这里是我分享C/C编程、数据结构应用的乐园✨ &#x1f388;&#x1f388;&…

html+CSS部分基础运用7

项目1 设计简易灯箱画廊 1.实验所需素材 在trees文件夹中提供一个MP3文件和18个JPG文件&#xff0c;设计页面时可以使用。 2.编程实现简易灯箱画廊&#xff0c;鼠标单击任一个图像超链接&#xff0c;在底部浮动框架中显示大图像&#xff0c;效果如图4-1所示的页面。 图4-1 简…

如果jupyter notebook不能实现网页自动跳转,参考下面的链接

一招搞定Jupyter-notebook命令行打开之后不能自动跳转浏览器_一招搞定jupter notebook命令行打开之后-CSDN博客

使用大模型做应用的一些问题

使用了一段时间的大模型应用&#xff0c;遇到一些问题&#xff0c;分享给大家。 使用大模型的基本情况 使用了下面三种大模型&#xff1a; 百度 ERNIE-3 kimi 大模型 chatGPT3.5 使用的大模型应用架构&#xff1a; langchainlangchain RAGlangchain Agentvector 数据…

Echarts图表库推荐以及使用Echarts实现饼图端头弧形效果

推荐Echarts图表库官方链接http://www.ppchart.com/#/ 下面是一段实现饼图端头弧形效果的Echarts代码 虽然有了上面的图表库&#xff0c;里面案例也挺多&#xff0c;但是就是没找到我想要的这种效果&#xff0c;索性就手写了一个 下面代码可以直接去我上面的图标库运行看效果…

书籍学习|基于SprinBoot+vue的书籍学习平台(源码+数据库+文档)

书籍学习平台 目录 基于SprinBootvue的书籍学习平台 一、前言 二、系统设计 三、系统功能设计 1平台功能模块 2后台功能模块 5.2.1管理员功能模块 5.2.2用户功能模块 5.2.3作者功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 …

工程机械比例阀电流采集方案——IPEhub2与IPEmotion APP

自从国家实施一带一路和新基建计划以来&#xff0c;工程机械的需求量呈现出快速增长的趋势。而关于工程机械&#xff0c;其比例阀的控制问题不容忽视。比例阀是一种新型的液压控制装置——在普通压力阀、流量阀和方向阀上&#xff0c;用比例电磁铁替代原有的控制部分&#xff0…

如何使用Cloudways搭建WordPress网站

如今&#xff0c;搭建网站已经变得非常简单&#xff0c;这主要得益于开源的CMS建站系统的兴起。即使是不懂编程的人也能轻松搭建自己的网站&#xff0c;这些CMS系统提供了丰富的主题模板和插件&#xff0c;使用户可以通过简单的拖放和配置操作来建立自己的网站。 WordPress是目…

[前端] 空值合并运算符(??)原理(笔记)

以如下代码为例 const avatar computed(() > props.user.avatar ?? fallbackAvatar)该运算符的工作原理是&#xff0c;如果左侧的表达式props.user.avatar的值为null或undefined&#xff0c;那么它会返回右侧的fallbackAvatar。然而&#xff0c;如果props.user.avatar的…

大语言模型实战——搭建纯本地迷你版RAG

1. 概念 RAG&#xff08;Retrieval Augmented Generation&#xff09;检索增强生成&#xff0c;它结合了搜索技术和大语言模型的提示词功能&#xff0c;以搜索算法找到的信息作为背景上下文&#xff0c;来辅助大语言模型&#xff08;Large Language Model, LLM&#xff09;生成…

Oracle数据库操作问题汇总

一、简介 Oracle Database&#xff0c;又名Oracle RDBMS&#xff0c;或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是世界上流行的关系数据库管理系统&#xff0c;系统可移植性好、使用方便、功能强&…

基于SpringBoot+Vue在线动漫信息平台设计和实现(源码+LW+部署讲解)

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; &#x1f339;推荐一个人…

python 密码生成器

要用 Python 创建一个密码生成器,可以使用 Python 的内置库来生成随机字符。以下是一个简单的密码生成器示例,您可以根据需要进行修改和扩展。 import random import stringdef generate_password(length=12):"""生成一个指定长度的密码"""# …

Ubuntu20.04安装VINS_Mono 和 VINS_Fusion

文章目录 一、问题描述二、依赖环境1. Eigen 安装2. glog 安装3. gflags 安装4. ceres 安装 三、VINS-Mono 安装1. git 下载并安装2. OpenCV 版本冲突3. 运行 四、VINS—Fusion 安装1. git 下载并安装2. OpenCV 版本冲突3. 运行 五、日常bug1. 动静态库链接冲突 一、问题描述 …

今日好料推荐(Altium Designer + 仿真器驱动)

今日好料推荐&#xff08;Altium Designer 仿真器驱动&#xff09; 参考资料在文末获取&#xff0c;关注我&#xff0c;获取优质资源。 Altium Designer Altium Designer 是一种高度集成的电子设计自动化 (EDA) 软件工具&#xff0c;广泛应用于电子电路和印刷电路板 (PCB) …

操作系统实验--终极逃课方法

找到图片里的这个路径下的文件 &#xff0c;结合当前题目名称&#xff0c;把文件内容全部删除&#xff0c;改为print print的内容为下图左下角的预期输出的内容

Java---Cloneable接口---浅克隆和深克隆

在Java中&#xff0c;我们如何实现一个对象的克隆呢&#xff1f; 在Java中实现对象的克隆&#xff0c;我们要用到Cloneable接口。克隆也分为浅克隆和深克隆。 1.实现浅克隆 1.重写clone方法 当我们想直接通过前面已经建立好的对象来调用Object类中的clone方法时&#xff0c;…

解决mybatis/mybatis plus报错:Invalid bound statement (not found) 的方法汇总

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)问题&#xff0c;即在mybatis中dao接口与mapper配置文件在做映射绑定的时候接口与xml不匹配&#xff0c;要么是找不到&#xff0c;要么是找到了却匹配不到。 我的问题是项目没有把最新的方法x…