Spring Boot集成Redisson实现延迟队列

 更新时间:2025年08月07日 10:41:34   作者:涛哥是个大帅比  
本文详细介绍了电商支付场景中利用Redisson的RDelayedQueue实现订单的延迟关闭功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

项目场景:

在电商、支付等领域,往往会有这样的场景,用户下单后放弃支付了,那这笔订单会在指定的时间段后进行关闭操作,细心的你一定发现了像某宝、某东都有这样的逻辑,而且时间很准确,误差在1s内;那他们是怎么实现的呢?

一般实现的方法有几种:使用 redisson、rocketmq、rabbitmq等消息队列的延时投递功能。

解决方案:

一般项目集成redis的比较多,所以我这篇文章就说下redisson延迟队列,如果使用rocketmq或rabbitmq需要额外集成中间件,比较麻烦一点。

1.集成redisson

maven依赖

<dependency>
	<groupId>org.redisson</groupId>
	<artifactId>redisson-spring-boot-starter</artifactId>
	<version>3.21.1</version>
</dependency>

yml配置,单节点配置可以兼容redis的配置方式

# redis配置
spring:
  redis:
    database: 0
    host: 127.0.0.1
    password: redis@pass
    port: 6001

 更详细的配置参考:Spring Boot整合Redisson的两种方式

2.配置多线程

因为延迟队列可能会多个任务同时执行,所以需要多线程处理。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@EnableAsync
public class ExecutorConfig {
    /**
     * 异步任务自定义线程池
     */
    @Bean(name = "taskExecutor")
    public ThreadPoolTaskExecutor asyncServiceExecutor() {
    	ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(50);
        //配置最大线程数
        executor.setMaxPoolSize(500);
        //配置队列大小
        executor.setQueueCapacity(300);
        //允许线程空闲时间
        executor.setKeepAliveSeconds(60);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix("taskExecutor-");
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //调用shutdown()方法时等待所有的任务完成后再关闭
        executor.setWaitForTasksToCompleteOnShutdown(true);
        //等待所有任务完成后的最大等待时间
		executor.setAwaitTerminationSeconds(60);
        return executor;
    }
}

3.具体业务

比如消息通知、关闭订单等 ,这里加上了@Async注解,可以异步执行

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.Date;

@Service
public class AsyncService {

	@Async
	public void executeQueue(Object value) {
		System.out.println();
		System.out.println("当前线程:"+Thread.currentThread().getName());
		System.out.println("执行任务:"+value);

		//打印时间方便查看
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		System.out.println("执行任务的时间:"+sdf.format(new Date()));
		//自己的业务逻辑,可以根据id发送通知消息等
		//......
	}
}

4.延迟队列(关键代码)

这里包括添加延迟队列,和消费延迟队列,@PostConstruct注解的意思是服务启动加载一次,参考

Spring Boot项目启动时执行指定的方法
Spring Boot中多个PostConstruct注解执行顺序控制

import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Service
public class TestService {

	@Resource
	private AsyncService asyncService;
	@Resource
	private ThreadPoolTaskExecutor executor;
	@Autowired
	private RedissonClient redissonClient;

	/**
	 * 添加延迟任务
	 */
	public void addQueue() {
		//获取延迟队列
		RBlockingQueue<Object> blockingQueue = redissonClient.getBlockingQueue("delayedQueue");
		RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingQueue);
		for (int i = 1; i <= 10; i++) {
			long delayTime = 5+i; //延迟时间(秒)
//			long delayTime = 5; //这里时间统一,可以测试并发执行
			delayedQueue.offer("延迟任务"+i, delayTime, TimeUnit.SECONDS);
		}
		//打印时间方便查看
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		System.out.println("添加任务的时间:"+sdf.format(new Date()));
	}

	/**
	 *	服务启动时加载,开始消费延迟队列
	 */
	@PostConstruct
	public void consumer() {
		System.out.println("服务启动时加载>>>>>>");
		//获取延迟队列
		RBlockingQueue<Object> delayedQueue = redissonClient.getBlockingQueue("delayedQueue");

		//启用一个线程来消费这个延迟队列
		executor.execute(() ->{
			while (true){
				try {
//					System.out.println("while中的线程:"+Thread.currentThread().getName());
					//获取延迟队列中的任务
					Object value = delayedQueue.poll();
					if(value == null){
						//如果没有任务就休眠1秒,休眠时间根据业务自己定义
						Thread.sleep(1000);	//这里休眠时间越短,误差就越小
						continue;
					}
					//异步处理延迟队列中的消息
					asyncService.executeQueue(value);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}
}

5.测试接口

import com.test.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {

	@Autowired
	private TestService testService;

	/*
	 * 添加延迟任务
	 */
	@GetMapping(value = "/addQueue")
	public String addQueue() {
		testService.addQueue();
		return "success";
	}

}

6.测试结果

总结:

  1. Redisson的的RDelayedQueue是基于Redis实现的,而Redis本身并不保证数据的持久性。如果Redis服务器宕机,那么所有在RDelayedQueue中的数据都会丢失。因此,我们需要在应用层面进行持久化设计,例如定期将RDelayedQueue中的数据持久化到数据库。
  2. 在设计延迟任务时,我们应该根据实际需求来合理设置延迟时间,避免设置过长的延迟时间导致内存占用过高。

到此这篇关于Spring Boot集成Redisson实现延迟队列的文章就介绍到这了,更多相关SpringBoot Redisson延迟队列内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MyBatis-Plus整合金仓数据库KingbaseES的实战指南

    MyBatis-Plus整合金仓数据库KingbaseES的实战指南

    随着数字中国建设的深入推进,国产数据库在关键业务系统中扮演着越来越重要的角色,本文将通过一个电商系统的实战案例,深入探讨MyBatis-Plus整合KingbaseES的详细步骤吧
    2025-10-10
  • Java中synchronized关键字的使用和原理详解

    Java中synchronized关键字的使用和原理详解

    这篇文章主要介绍了Java中synchronized关键字的使用和原理详解,JVM 是通过进入、退出 对象监视器(Monitor)来实现对方法、同步块的同步的,而对象监视器的本质依赖于底层操作系统的互斥锁实现,需要的朋友可以参考下
    2023-09-09
  • java 三元操作符用法说明

    java 三元操作符用法说明

    这篇文章主要介绍了java 三元操作符用法说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • eclipse启动tomcat无法访问的解决方法

    eclipse启动tomcat无法访问的解决方法

    这篇文章介绍了eclipse启动tomcat无法访问的解决方法,有需要的朋友可以参考一下
    2013-10-10
  • intellij idea 将模块打jar包的步骤详解

    intellij idea 将模块打jar包的步骤详解

    这篇文章主要介绍了intellij idea 将模块打jar包的步骤,本文图文并茂给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-09-09
  • java 删除数组元素与删除重复数组元素的代码

    java 删除数组元素与删除重复数组元素的代码

    在java中删除数组元素与过滤重复数组元素我们都会需要去遍历数组然后根据我们设置的值或方法进行去除数组
    2013-10-10
  • 浅谈Spring的两种配置容器

    浅谈Spring的两种配置容器

    这篇文章主要介绍了浅谈Spring的两种配置容器,介绍了其实现以及简单的实例,具有一定参考价值,需要的朋友可以了解下。
    2017-10-10
  • Java easyexcel使用教程之导出篇

    Java easyexcel使用教程之导出篇

    EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单,节省内存著称,下面这篇文章主要给大家介绍了关于Java easyexcel使用教程之导出篇的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-06-06
  • java 中JFinal getModel方法和数据库使用出现问题解决办法

    java 中JFinal getModel方法和数据库使用出现问题解决办法

    这篇文章主要介绍了java 中JFinal getModel方法和数据库使用出现问题解决办法的相关资料,需要的朋友可以参考下
    2017-04-04
  • Spring Boot 安全 API 构建之加密解密功能的实践记录

    Spring Boot 安全 API 构建之加密解密功能的实践记录

    本文详述了如何在SpringBoot3.3环境中实施API加密的最佳实践,包括选择合适的加密算法,密钥管理,数据加密,防止加密漏洞,安全日志记录,测试和监控等方面,同时,文章也对RSA非对称加密和AES对称加密的实现步骤进行了详细的解析
    2024-10-10

最新评论