用Redisson的延迟队列RDelayedQueue处理延迟任务或者定时任务
什么是Redisson
Redisson在基于NIO的Netty框架上,充分的利用了Redis键值数据库提供的一系列优势,在Java实用工具包中常用接口的基础上,为使用者提供了一系列具有分布式特性的常用工具类。
什么是RDelayedQueue
获取RDelayedQueue:
public <V> RDelayedQueue<V> getDelayedQueue(RQueue<V> destinationQueue) { if (destinationQueue == null) { throw new NullPointerException(); } return new RedissonDelayedQueue<V>(queueTransferService, destinationQueue.getCodec(), commandExecutor, destinationQueue.getName()); }
RDelayedQueue是一种延迟队列接口,Redisson自带一个叫‘RedissonDelayedQueue’的实现类是他的子类,用于实现延迟任务。注意根据上面源码可以看到用Redisson获取RDelayedQueue时需要传一个实现RQueue队列的参数,而Redisson又可以创建另一个队列叫‘RedissonBlockingDeque’,它就是实现RBlockingDeque接口的阻塞队列,而RBlockingDeque又是RQueue的子类。
获取:RBlockingDeque:
public <V> RBlockingDeque<V> getBlockingDeque(String name) { return new RedissonBlockingDeque<V>(commandExecutor, name, this); }
RDelayedQueue的使用方法
1、项目中要配置Redis,然后根据Redis配置Redisson;
#Redis配置 # Redis数据库索引(默认为0) # Redis服务器地址 # Redis服务器连接端口 # Redis服务器连接密码(默认为空) # 链接超时时间 单位 ms(毫秒) # 连接池最大连接数(使用负值表示没有限制) 默认 8 # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 # 连接池中的最大空闲连接 默认 8 # 连接池中的最小空闲连接 默认 0 redis: database: 0 host: 127.0.0.1 port: 6379 connect-timeout: 3000 password: lettuce: pool: max-active: 8 max-wait: -1 max-idle: 8 min-idle: 0
RedissonConfig:
@Configuration public class RedissonConfig { @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private String port; @Value("${spring.redis.database}") private int database; @Bean public RedissonClient getRedisson() { Config config = new Config(); String address = "redis://" + host + ":" + port; config.useSingleServer().setAddress(address).setDatabase(database); return Redisson.create(config); } }
2、通过Redisson获取阻塞队列(RBlockingDeque)的子类:RedissonBlockingDeque,并通过它获取延迟队列(RDelayedQueue)的子类:RedissonDelayedQueue;
RBlockingDeque<User> blockingDeque = redissonClient.getBlockingDeque("demoName"); RDelayedQueue<User> delayedQueue = redissonClient.getDelayedQueue(blockingDeque); delayedQueue.offer(user, 5, TimeUnit.SECONDS); //记录设置任务的时间 System.out.println(user.getUserId() + "添加时间是:" + LocalDateTime.now()); //开启新线程执行任务,不阻塞主线程 new Thread(new Runnable() { @Override public void run() { while (true) { User user2 = blockingDeque.poll(); if (user2 != null) { System.out.println(user2); System.out.println(user2.getUserId() + "执行时间是:" + LocalDateTime.now()); } } } }).start();
3、让RedissonDelayedQueue的‘offer’方法添加延迟任务对象,然后通过RedissonBlockingDeque的‘poll’或者‘take’方法获取延迟任务对象然后进行后续操作。
队列take()和poll()的区别:
take():返回队列的头元素,并把它从队列中删除,如果队列为空时则阻塞线程直到有新的元素添加进来并返回;
poll():返回队列的头元素,并把它从队列中删除,如果队列头元素为空则返回null但是不阻塞线程;
使用项目Demo
任务需求:新增一个对象User,并让他5s以后介绍自己(toString方法打印信息)。
项目结构:
pom依赖:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.code</groupId> <artifactId>MyRDelayedQueue</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.5.4</version> </dependency> <!-- Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.5.4</version> </dependency> <!-- Redisson --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.20.0</version> </dependency> </dependencies> </project>
application.yml文件:
#Redis配置 # Redis数据库索引(默认为0) # Redis服务器地址 # Redis服务器连接端口 # Redis服务器连接密码(默认为空) # 链接超时时间 单位 ms(毫秒) # 连接池最大连接数(使用负值表示没有限制) 默认 8 # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 # 连接池中的最大空闲连接 默认 8 # 连接池中的最小空闲连接 默认 0 spring: redis: database: 0 host: 127.0.0.1 port: 6379 connect-timeout: 3000 password: lettuce: pool: max-active: 8 max-wait: -1 max-idle: 8 min-idle: 0 server: port: 8080
启动类:
@SpringBootApplication public class MyRDQueueApplication { public static void main(String[] args) { SpringApplication.run(MyRDQueueApplication.class, args); } }
Redisson配置文件:
@Configuration public class RedissonConfig { @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private String port; @Value("${spring.redis.database}") private int database; @Bean public RedissonClient getRedisson() { Config config = new Config(); String address = "redis://" + host + ":" + port; config.useSingleServer().setAddress(address).setDatabase(database); return Redisson.create(config); } }
User:
public class User { private String userId; private String userName; private String password; public User() { } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } //介绍自己 public String Speak() { return "我的信息是:{" + "userId='" + userId + '\'' + ", userName='" + userName + '\'' + ", password='" + password + '\'' + '}'; } }
controller:
@RestController public class IndexController { @Autowired public QueueService queueService; @RequestMapping("/queue") public String addUser(){ return queueService.addUser(); } }
service:
@Service public class QueueService { @Autowired public RedissonClient redissonClient; public String addUser() { //新增User User user = new User(); user.setUserId("123456"); user.setUserName("queueTask"); user.setPassword("666666"); //5秒后让新增的User讲话 RBlockingDeque<User> blockingDeque = redissonClient.getBlockingDeque("speak"); RDelayedQueue<User> delayedQueue = redissonClient.getDelayedQueue(blockingDeque); delayedQueue.offer(user, 5, TimeUnit.SECONDS); //指定日期格式 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); //打印添加任务时间 System.out.println("添加时间:" + formatter.format(LocalDateTime.now())); new Thread(new Runnable() { @Override public void run() { while (true) { User task = null; try { task = blockingDeque.take(); System.out.println(task.Speak()); //打印执行任务时间 System.out.println("执行时间:" + formatter.format(LocalDateTime.now())); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); return user.getUserId(); } }
结果测试:
启动项目后请求路径:http://localhost:8080/queue
后台打印:
添加时间:2024-06-05 13:42:07 我的信息是:{userId='123456', userName='queueTask', password='666666'} 执行时间:2024-06-05 13:42:12
结果:执行任务时间比添加时间推迟5秒。