重学SpringBoot3-集成Redis(八)之限时任务(延迟队列)

news2024/12/22 19:30:58

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》
期待您的点赞👍收藏⭐评论✍

重学SpringBoot3-集成Redis(八)之限时任务(延迟队列)

  • 1. 延迟任务的场景
  • 2. Redis Sorted Set基本原理
  • 3. 使用 Redis Sorted Set 实现延迟队列
    • 3.1. 引入依赖
    • 3.2. 配置 Redis
    • 3.3. 延迟队列的任务存储和处理
      • 3.3.1 任务存储
      • 3.3.2 任务处理
    • 3.4. 测试效果
      • 3.4.1 测试添加任务
      • 3.4.2 存储格式
  • 4. 优化建议
    • 4.1. 任务幂等性
    • 4.2. 持久化
    • 4.3. 高并发处理
  • 5. 总结

在分布式系统中,延迟任务(或限时任务)是一种常见的需求,通常用于实现延迟执行、定时处理或消息超时等场景。Redis 作为高性能的内存数据库,具备非常灵活的 Sorted Set(有序集合) 数据结构,可以很容易地实现延迟队列,满足限时任务的需求。

在本篇文章中,我们将介绍如何通过 RedisSpring Boot 3 来实现 限时任务(也称为延迟任务或延迟队列),让你能够轻松管理任务的延时执行。

1. 延迟任务的场景

延迟任务的应用场景非常广泛,包括但不限于以下场景:

  1. 订单超时取消:用户下单后未支付,超过一定时间自动取消订单。
  2. 消息超时重发:当消息发送失败,可以延迟重试。
  3. 定时提醒:比如发送通知或定时邮件。

这些场景都需要任务在一段时间后自动执行,因此我们需要一种灵活、高效的解决方案来处理这类限时任务。

2. Redis Sorted Set基本原理

Redis Sorted Set(有序集合)是一种数据结构,它将元素存储在一个有序的集合中,每个元素都有一个唯一的分数(score)与之关联。Sorted Set 的基本原理如下:

  1. 元素和分数:每个元素都有一个唯一的分数与之关联,分数可以是整数或浮点数。
  2. 有序集合:元素按照分数的大小顺序存储在集合中,分数越小的元素越靠近集合的头部。
  3. 唯一性:集合中每个元素的分数必须是唯一的,如果两个元素的分数相同,则后一个元素会覆盖前一个元素。
  4. 插入和删除:元素可以通过 ZADD 命令插入到集合中,通过 ZREM 命令删除元素。
  5. 范围查询:可以通过 ZRANGE 命令查询集合中某个范围内的元素,范围可以是分数范围或索引范围,ZRANGEBYSCORE 可以根据 score 范围查找元素。
  6. 分数更新:可以通过 ZINCRBY 命令更新元素的分数。

Sorted Set 的底层实现使用了跳跃表(Skip List)数据结构,跳跃表是一种高效的有序数据结构,它可以在 O(log n) 的时间复杂度内进行插入、删除和查找操作。

3. 使用 Redis Sorted Set 实现延迟队列

在实现延迟任务时,我们可以将任务的执行时间作为 Sorted Set 中的 score,然后按时间顺序处理任务,确保在指定时间执行。

3.1. 引入依赖

首先,在 pom.xml 中引入 Redis 相关依赖,相关配置请参考重学SpringBoot3-集成Redis(一)基本使用:

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

3.2. 配置 Redis

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

spring:
  data:
    redis:
      host: 1.94.26.81
      port: 6379            # Redis 端口
      password: redis123456 # 如果有密码可以在这里配置
      lettuce:
        pool:
          max-active: 100    # 最大并发连接数
          max-idle: 50       # 最大空闲连接数
          min-idle: 10       # 最小空闲连接数

3.3. 延迟队列的任务存储和处理

接下来,我们通过 Redis 的 Sorted Set 来存储任务,并定时检查任务是否到期。

3.3.1 任务存储

每当有新的任务需要延迟执行时,我们将其加入到 Redis 的 Sorted Set 中,score 为该任务的执行时间戳。

package com.coderjia.boot310redis.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * @author CoderJia
 * @create 2024/10/7 下午 05:10
 * @Description
 **/
@Service
public class DelayedTaskService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final String DELAYED_QUEUE_KEY = "delayedQueue";

    // 添加任务到延迟队列
    public void addTaskToQueue(String taskId, long delayInSeconds) {
        long executeTime = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(delayInSeconds);
        redisTemplate.opsForZSet().add(DELAYED_QUEUE_KEY, taskId, executeTime);
        System.out.println("Added task " + taskId + " to the queue, will be executed in " + delayInSeconds + " seconds.");
    }
}

3.3.2 任务处理

为了定期检查是否有任务到期,我们使用 Spring 的 @Scheduled 注解创建一个定时任务,定时从 Redis 中获取即将到期的任务并执行。

package com.coderjia.boot310redis.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.Set;

/**
 * @author CoderJia
 * @create 2024/10/7 下午 05:10
 * @Description
 **/
@Slf4j
@Service
public class DelayedTaskProcessor {

    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final String DELAYED_QUEUE_KEY = "delayedQueue";

    // 定时任务,检查是否有任务到期
    @Scheduled(fixedRate = 5000) // 每隔5秒执行一次
    public void processDelayedTasks() {
        long currentTime = System.currentTimeMillis();
        Set<String> tasks = redisTemplate.opsForZSet().rangeByScore(DELAYED_QUEUE_KEY, 0, currentTime);

        if (tasks != null && !tasks.isEmpty()) {
            for (String taskId : tasks) {
                // 执行任务
                executeTask(taskId);
                // 从队列中移除已执行的任务
                redisTemplate.opsForZSet().remove(DELAYED_QUEUE_KEY, taskId);
                log.info("Task " + taskId + " is executed.");
            }
        }
    }

    // 模拟任务执行
    private void executeTask(String taskId) {
        // 执行任务
        log.info("Task " + taskId + " is executing...");
    }
}

在这个示例中:

  • addTaskToQueue(String taskId, long delayInSeconds) 方法将任务加入到 Redis 的 Sorted Set 中,延迟 delayInSeconds 秒执行。
  • @Scheduled(fixedRate = 5000) 每隔 1 秒扫描一次 Redis,查找是否有任务的执行时间已到期。如果有,则执行该任务,并从队列中移除。

3.4. 测试效果

在你的业务逻辑中调用上面创建的 addTaskToQueue 方法添加任务到延迟队列中去。

package com.coderjia.boot310redis.demos.web;

import com.coderjia.boot310redis.service.DelayedTaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author CoderJia
 * @create 2024/10/7 下午 05:18
 * @Description
 **/
@Slf4j
@RestController
public class DelayedTaskController {

    @Autowired
    private DelayedTaskService delayedTaskService;

    @GetMapping("/addDelayedTask")
    public String addDelayedTask(@RequestParam("taskId") String taskId, @RequestParam("delay") Long delay) {
        log.info("Adding task to the queue, taskId:{}, delay:{}", taskId, delay);
        delayedTaskService.addTaskToQueue(taskId, delay);
        return "Added task success";
    }
}

3.4.1 测试添加任务

执行以下请求添加 3 条任务到延迟队列中,

http://localhost:8080/addDelayedTask?taskId=3&delay=20

添加任务

任务执行

新增2条任务,先添加任务的后执行,测试效果。

先添加任务的后执行

3.4.2 存储格式

存储格式

4. 优化建议

4.1. 任务幂等性

在分布式环境中,任务可能会被多个节点同时执行。确保任务的幂等性非常重要,可以通过 Redisson 分布式锁来保证同一时刻只有一个节点在执行任务。

        // 获取分布式锁
        RLock lock = redissonClient.getLock("scheduledTaskLock");

        try {
            // 尝试获取锁,最多等待 1 秒,锁定时间 10 秒
            if (lock.tryLock(1, 10, TimeUnit.SECONDS)) {
                try {
                    // 执行任务
                    System.out.println("Executing distributed scheduled task...");
                } finally {
                    lock.unlock(); // 释放锁
                }
            } else {
                System.out.println("Another instance is executing the task...");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

4.2. 持久化

延迟队列可以与持久化存储结合起来,确保任务在 Redis 失败或重启时不会丢失。可以使用 Redis 持久化功能或将任务信息存储在数据库中。

4.3. 高并发处理

对于大量延迟任务,可以通过增加 Redis 集群的规模或使用更高效的数据结构来提升处理性能。

5. 总结

通过 Redis Sorted SetSpring Boot 3,我们可以轻松实现限时任务的调度。Redis 的高性能和有序集合特性为我们提供了实现延迟队列的基础,而 Spring Boot 的定时任务调度则帮助我们定期处理这些任务。

在实际场景中,限时任务的应用非常广泛,比如订单超时处理、消息重发等场景,借助 Redis 我们可以有效管理这些延迟任务并确保系统的高效运行。


希望这篇文章能够帮助你更好地理解如何使用 Spring Boot 3 与 Redis 实现延迟队列。如果你在项目中遇到了相关问题,欢迎在评论区分享你的问题与经验。

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

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

相关文章

粗糙表面的仿真和处理软件

首款基于粗糙表面的仿真和处理软件&#xff0c;该软件具有三种方法&#xff0c;主要是二维数字滤波法&#xff0c;相位频谱法和共轭梯度法。可以分别仿真具有高斯和非高斯分布的粗糙表面&#xff0c;其中非高斯表面利用Johnson转换系统进行变换给定偏度和峰度。对生成的粗糙表面…

Mysql高级篇(下)——数据库备份与恢复

Mysql高级篇&#xff08;下&#xff09;——数据库备份与恢复 一、物理备份与逻辑备份1、物理备份2、逻辑备份3、对比4、总结 二、mysqldump实现逻辑备份1、mysqldump 常用选项2、mysqldump 逻辑备份语法&#xff08;1&#xff09;备份一个数据库&#xff08;2&#xff09;备份…

linux自动挂载tf卡

本人使用的是armbian系统&#xff0c;ssh工具使用的是finalshell&#xff0c;挂载的是一张64G TF卡。 1.查看系统所检测到的磁盘&#xff0c;这里的 sda1检测到的硬盘但是没有被挂载 lsblk //查看信息 2.在根目录新建一个目录tfcard用于挂载硬盘&#xff0c;命令如下&#xf…

【万字长文】Word2Vec计算详解(一)

【万字长文】Word2Vec计算详解&#xff08;一&#xff09; 写在前面 本文用于记录本人学习NLP过程中&#xff0c;学习Word2Vec部分时的详细过程&#xff0c;本文与本人写的其他文章一样&#xff0c;旨在给出Word2Vec模型中的详细计算过程&#xff0c;包括每个模块的计算过程&a…

电商选品/跟卖| 亚马逊商品类爬取

电商跟卖,最重要是了解哪些商品可以卖, 哪些商品不能卖, 为了更好了解商品信息,我们会经常爬取商品类目的信息. 需求 亚马逊类目信息链接爬虫 打开亚马逊类目信息地址 https://www.amazon.com/gp/new-releases/automotive/refzg_bsnr_nav_automotive_0 一直递归下去&#x…

云原生(四十七) | PHP软件安装部署

文章目录 PHP软件安装部署 一、PHP软件部署步骤 二、安装与配置PHP PHP软件安装部署 一、PHP软件部署步骤 第一步&#xff1a;安装 EPEL 仓库 与 Remi仓库 第二步&#xff1a;启用 Remi 仓库 第三步&#xff1a;安装 PHP、PHP-FPM 第四步&#xff1a;启动并开机启用 PH…

10.8 sql语句查询(未知的)

1.查询结果去重 关键字:distinct (放在查询的后面) AC: select distinct university from user_profile 2.查询结果限制返回行数 关键字:limit AC: select device_id from user_profile limit 0,2 3.将查询后的列重新命名 关键字:as AC: select device_id as user_infos…

wildcard使用教程,解决绝大多数普通人的海外支付难题

许多人可能已经注意到,国外的一些先进AI工具对国内用户并不开放。而想要使用这些工具,我们通常会面临两个主要障碍:一是网络访问的限制,二是支付问题。网络问题很容易解决&#xff0c;难的是如何解决在国内充值海外软件。 今天给大家推荐一个工具——wildcard&#xff0c;用它…

【CSS in Depth 2 精译_046】7.1 CSS 响应式设计中的移动端优先设计原则(下)

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一章 层叠、优先级与继承&#xff08;已完结&#xff09; 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位&#xff08;已完结&#xff09; 2.1 相对…

StoryMaker: Towards Holistic Consistent Characters in Text-to-image Generation

https://arxiv.org/pdf/2409.12576v1https://github.com/RedAIGC/StoryMaker 问题引入 针对的是文生图的模型&#xff0c;现在已经有方法可以实现指定人物id的情况下进行生成&#xff0c;但是还没有办法保持包括服装、发型等整体&#xff0c;本文主要解决这个问题&#xff1b…

时间卷积网络(TCN)原理+代码详解

目录 一、TCN原理1.1 因果卷积&#xff08;Causal Convolution&#xff09;1.2 扩张卷积&#xff08;Dilated Convolution&#xff09; 二、代码实现2.1 Chomp1d 模块2.2 TemporalBlock 模块2.3 TemporalConvNet 模块2.4 完整代码示例 参考文献 在理解 TCN 的原理之前&#xff…

GIS后端工程师岗位职责、技术要求和常见面试题

GIS 后端工程师负责设计、开发与维护地理信息系统的后端服务&#xff0c;包括数据存储、处理、分析以及与前端的交互接口等&#xff0c;以实现高效的地理数据管理和功能支持。 GIS 后端工程师岗位职责 一、系统设计与开发 参与地理信息系统&#xff08;GIS&#xff09;项目的…

安装 Petalinux

资料准备 ubuntu 22.04: 运行内存8G 存储空间500G Petalinux&#xff1a;2024.1 安装流程 安装依赖 sudo apt-get update sudo apt-get upgrade sudo apt-get install iproute2 sudo apt-get install gawk sudo apt-get install build-essential sudo apt-ge…

7.3 物联网平台-Thingsboard使用教程

物联网平台-Thingsboard使用教程 目录概述需求&#xff1a; 设计思路实现思路分析 免费下载参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result,wait for chang…

如何使用ssm实现基于web技术的税务门户网站的实现+vue

TOC ssm820基于web技术的税务门户网站的实现vue 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff…

基于matlab的语音信号处理

摘要 利用所学习的数字信号处理知识&#xff0c;设计了一个有趣的音效处理系统&#xff0c;首先设计了几种不同的滤波器对声音进行滤波处理&#xff0c;分析了时域和频域的变化&#xff0c;比较了经过滤波处理后的声音与原来的声音有何变化。同时设计实现了语音的倒放&#xf…

从0开始linux(9)——进程(1)进程管理

欢迎来到博主的专栏&#xff1a;从0开始linux 博主ID&#xff1a;代码小豪 文章目录 查看进程进程管理PID与PPIDfork函数 在上一篇中我们了解到&#xff1a;当运行程序时&#xff0c;操作系统会将磁盘中的二进制文件读取到内存当中&#xff0c;程序运行到结束的过程称为进程&am…

【C++ 11】auto 自动类型推导

文章目录 【 1. 基本用法 】【 2. auto 的 应用 】2.0 auto 的限制2.1 简单实例2.2 auto 与指针、引用、const2.4 auto 定义迭代器2.5 auto 用于泛型编程 问题背景 在 C11 之前的版本&#xff08;C98 和 C 03&#xff09;中&#xff0c;定义变量或者声明变量之前都必须指明它的…

目标检测YOLO实战应用案例100讲-【目标检测】YOLOV11

目录 前言 算法原理 YOLO发展历程 什么是 YOLO11 YOLOv11 的主要特点 YOLO各版本概览 核心优势: YOLOv11改进方向 YOLOv11功能介绍 YOLOv11关键创新 YOLOv11 指标展示 YOLOV11实验 环境设置 准备数据集 训练模型 验证模型 应用领域 一、智慧交通与自动驾…

【Linux实践】实验八:Shell程序的创建及变量

文章目录 实验八&#xff1a;Shell程序的创建及变量实验目的&#xff1a;实验内容&#xff1a;操作步骤&#xff1a;1. 查看环境变量2. 定义变量AK3. 定义变量AM并比较4. 创建Shell程序 实验八&#xff1a;Shell程序的创建及变量 实验目的&#xff1a; 掌握Shell程序的创建过…