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

news/2024/7/21 23:39:25/文章来源:https://blog.csdn.net/qq_42914528/article/details/139260326
效率工具
  • 推荐一个程序员的常用工具网站,效率加倍嘎嘎好用:程序员常用工具
云服务器
  • 云服务器限时免费领:轻量服务器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.luyixian.cn/news_show_1053106.aspx

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

相关文章

外卖系统源码解读:校园外卖APP开发全攻略

外卖系统源码解读&#xff1a;校园外卖APP开发全攻略 今天&#xff0c;小编将深入解读外卖系统的源码&#xff0c;详细介绍如何开发一款功能齐全的校园外卖APP&#xff0c;帮助开发者快速上手&#xff0c;打造出高质量的外卖应用。 一、需求分析 应具备以下基本功能&#xff…

vue中的$nextTick和过渡与动画

一.vue中的$nextTick 简述与用法&#xff1a;这是一个生命周期钩子 1.语法&#xff1a;this.$nextTick(回调函数) 2.作用&#xff1a;在下一次DOM更新结束后执行其指定的回调 3.什么时候用&#xff1a;当修改数据后&#xff0c;要基于更新后的新dom进行某些操作时&#xff0c;…

学习笔记之——2D Gaussian Splatting(2DGS)

3DGS在辐射场重建中取得了巨大的成就&#xff0c;实现高质量的新视图合成和快速渲染。最近新出了3DGS的升级版本&#xff0c;2DGS。写下本博文记录本人学习及测试2DGS的过程&#xff0c;本博文仅为本人学习记录用~ Project WebsiteGithub CodeOriginal paper 原理解读 由于3D…

汽车短视频怎么拍?成都科成博通文化传媒公司

汽车短视频怎么拍&#xff1f; 随着短视频平台的兴起&#xff0c;汽车爱好者们纷纷将自己的汽车生活、驾驶体验以及车辆评测等内容以短视频的形式呈现给大众。汽车短视频不仅满足了观众对汽车文化的好奇心&#xff0c;也为汽车品牌和汽车相关行业提供了宣传和推广的新渠道。那…

【机器学习】机器学习基础概念与初步探索

❀机器学习 &#x1f4d2;1. 引言&#x1f4d2;2. 机器学习概述&#x1f4d2;3. 机器学习基础概念&#x1f389;2.1 机器学习的分类&#x1f389;2.2 数据预处理&#x1f308;数据清洗与整合&#x1f308; 特征选择和特征工程&#x1f308;数据标准化与归一化 &#x1f4d2;4. …

Mesa Gallium框架入门初探

Mesa Gallium框架入门初探 MESA Gallium框架 MESA源码里面有2套架构&#xff0c;现在驱动主要基于Gallium架构。 这里我们重点来看看Gallium架构: 经典架构 Gallium架构 Gallium展开 Gallium中主要包含下面几块&#xff1a; Auxiliary模块&#xff1a;一些公共函数或者辅助…

菜鸟的JavaSE学习之旅5

这是一个目录 面向对象类和对象类对象类的定义一个对象的内存成员变量和局部变量定义位置不同作用范围不同默认值不同内存位置不同生命周期不同 关键字this关键字 构造方法标准的类 面向对象 当需要实现一个功能的时候&#xff0c;不关心具体的步骤&#xff0c;而是找一个具有…

Python考试复习--day4

1.三角函数计算 import math aeval(input()) beval(input()) x(-bpow(2*a*math.sin(math.pi/3)*math.cos(math.pi/3),0.5))/(2*a) print(x) math库 2.分段函数B import math xeval(input()) if -6<x<0:yabs(x)5 elif 0<x<3:ymath.factorial(x) elif 3<x<6:y…

JMM 理解

JMM&#xff1a; Java Memory Model (JAVA内存模型) 【JMM】 允许编译器和缓存以数据在处理器特定的缓存(或寄存器)和主存之间的移动次序拥有重要的特权。 非程序员使用了volatitle 或 synchronized明确请求了某些可见性的保证。 从几个方面学习 1、什么是JMM&#xff1f; 答…

素数判断的奥秘与编程实践

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、素数定义的深入理解 二、非素数的例子与思考 三、素数判断的编程实现 1. 穷举法判断素…

什么是知识中台?为什么企业需要知识中台?

如今市面上的企业数不胜数&#xff0c;企业的任何一个小细节都会产生很大的影响。近几年来一直很热门的知识中台备受企业关注。关于如何高效地管理、整合和运用知识&#xff0c;成为了每一家企业都在重点关注的问题。而知识中台&#xff0c;就是为了解决这一问题而诞生的一个全…

Java整合EasyExcel实战——2 导出复杂表头

详情代码 实体类 Data public class ComplexHeadData {ExcelProperty({"主标题", "字符串标题"})private String string;ExcelProperty({"主标题", "日期标题"})private Date date;ExcelProperty({"主标题", "数字标…

Zigbee +PC上位机 无线控制二维云台开发笔记

今日尝试开发一款简单好学的PC上位机无线控制二维云台的小试验品&#xff1a; 主要开发环境与工具介绍&#xff1a; 单片机 STM32F103C8T6 使用标准库函数编程 Visual Studio 2022软件C# Winform 开发 上位机控制软件 DL_20 无线串口模块 &#xff0b; USB-TTL 模块 实现无线通…

解读:Mint Blockchain 最新路线图,释放 NFT 生态重磅发展计划

作者&#xff1a;Mint Ecosystem 关于 Mint Blockchain&#xff1a;Mint Blockchain 是一个以太坊原生 L2 网络&#xff0c;核心是发展 NFT 生态和产业&#xff0c;促进 NFT 领域的 Mass Adoption 产生。MintCore 团队致力于将 Mint Blockchain 打造成一个围绕服务 NFT 资产的…

spring-boot集成slf4j(二)logback配置详解

一、configuration 根节点&#xff1a;configuration&#xff0c;作为顶级标签&#xff0c; 可以用来配置一些lockback的全局属性&#xff0c;常见的属性如下&#xff1a; &#xff08;1&#xff09;scan“true” &#xff1a;scan是否开启自动扫描&#xff0c;监控配置文件更…

mac安装的VMware虚拟机进行桥接模式配置

1、先进行网络适配器选择&#xff0c;选择桥接模式 2、点击网络适配器 设置... 3、选择WiFi&#xff08;我使用的是WiFi&#xff0c;所以选择这个&#xff09;&#xff0c;注意看右边的信息&#xff1a;IP和子网掩码&#xff0c;后续配置虚拟机的ifcfg-ens文件会用到 4、编辑if…

mipi-csi笔记

数据格式 长包&#xff0c;短包 用DI来判断数据类型 测试帧率&#xff0c;如用1G的示波器 下面的代表这是一张图片,用帧间隙来测试YUV422视频的帧率 fps10hz的外同步

❤ Vscode和Idea都可以使用的-AI插件(官方-百度出的)

❤ Vscode和Idea都可以使用的-AI插件&#xff08;官方-百度出的&#xff09; 最新AI特别火&#xff0c;给大家推荐一下最新出的VScode插件&#xff0c;辅助我们写代码&#xff01; 1、下载地址&#xff1a; > https://comate.baidu.com/zh/shopping?inviteCodefkzlak8f …

B站pink老师CSS学习(一)

文章目录 一、CSS基础选择器1.标签选择器2.类选择器3. id选择器4.通配符选择器 二、字体属性1.字体2.字体大小3.字体粗细4.文字样式5.复合属性 三、文本属性1.文本颜色2.对齐文本3.装饰文本4.文本缩进5.行间距 四、CSS引入方式1. 内部样式表2.行内样式表3.外部样式表 一、CSS基…

CTF流量分析之wireshark使用

01.基本介绍 在CTF比赛中&#xff0c;对于流量包的分析取证是一种十分重要的题型。通常这类题目都是会提供一个包含流量数据的pcap文件&#xff0c;参赛选手通过该文件筛选和过滤其中无关的流量信息&#xff0c;根据关键流量信息找出flag或者相关线索。 pcap流量包的分析通常…