Redisson分布式调度执行任务
在使用Redisson中的RScheduledExecutorService时候,任务中需要调用Spring Bean,但是报错NullPointException(空指针)
Redisson中RScheduledExecutorService的使用
问题
在使用Redisson中的RScheduledExecutorService时候,任务中需要调用Spring Bean,但是报错NullPointException(空指针)

java.lang.reflect.InvocationTargetException: null
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_301]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_301]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_301]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_301]
at org.redisson.executor.RedissonExecutorRemoteService.invokeMethod(RedissonExecutorRemoteService.java:107) ~[redisson-3.17.7.jar:3.17.7]
at org.redisson.RedissonRemoteService.lambda$executeMethod$11(RedissonRemoteService.java:434) [redisson-3.17.7.jar:3.17.7]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[na:1.8.0_301]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_301]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_301]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_301]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.82.Final.jar:4.1.82.Final]
at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_301]
Caused by: java.lang.NullPointerException: null
at org.wl.test.core.TaskScheduler.run(TaskScheduler.java:24) ~[classes/:na]
at org.redisson.executor.TasksRunnerService.executeRunnable(TasksRunnerService.java:342) ~[redisson-3.17.7.jar:3.17.7]
at org.redisson.executor.TasksRunnerService.scheduleAtFixedRate(TasksRunnerService.java:127) ~[redisson-3.17.7.jar:3.17.7]
... 12 common frames omitted
原因
Redisson的线程池中执行任务时,没有注入Spring Bean
解决方法
于是自己创建了一个项目,一步一步尝试
1. 引入依赖
注意我使用的redisson版本是3.17.7
需要注意Spring Boot与redisson版本的兼容性可参考连接:redisson-spring-boot-starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.17</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-extra</artifactId>
<version>5.8.22</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.17.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.7.17</version>
<scope>test</scope>
</dependency>
2. redis配置文件
配置文件参考链接:redisson-spring-boot-starter
application.yml
spring:
redis:
cluster:
nodes:
- 127.0.0.1:6380
- 127.0.0.1:6381
- 127.0.0.1:6382
- 127.0.0.1:6383
- 127.0.0.1:6384
- 127.0.0.1:6385
# 数据库索引
database: 0
redisson:
file: classpath:redisson.yml
redisson.yml
clusterServersConfig:
idleConnectionTimeout: 10000
connectTimeout: 10000
timeout: 3000
retryAttempts: 3
retryInterval: 1500
failedSlaveReconnectionInterval: 3000
failedSlaveCheckInterval: 60000
password: null
subscriptionsPerConnection: 5
clientName: null
loadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}
subscriptionConnectionMinimumIdleSize: 1
subscriptionConnectionPoolSize: 50
slaveConnectionMinimumIdleSize: 24
slaveConnectionPoolSize: 64
masterConnectionMinimumIdleSize: 24
masterConnectionPoolSize: 64
readMode: "SLAVE"
subscriptionMode: "SLAVE"
nodeAddresses:
- "redis://127.0.0.1:6380"
- "redis://127.0.0.1:6381"
- "redis://127.0.0.1:6382"
- "redis://127.0.0.1:6383"
- "redis://127.0.0.1:6384"
- "redis://127.0.0.1:6385"
scanInterval: 1000
pingConnectionInterval: 30000
keepAlive: false
tcpNoDelay: true
threads: 16
nettyThreads: 32
# 编码
codec: !<org.redisson.codec.JsonJacksonCodec> {}
# 传输模式
transportMode : "NIO"
3.业务代码
TaskController.java
package org.wl.test.controller;
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;
import org.wl.test.service.TaskService;
/**
* @author wanglei
*/
@RestController
@RequestMapping("/task")
public class TaskController {
@Autowired
private TaskService carryService;
@GetMapping("/exec")
public String execTask() {
carryService.execTask();
return "OK";
}
}
TaskService.java
package org.wl.test.service;
/**
* @author wanglei
*/
public interface TaskService {
/**
* 获取任务信息
* @return 结果
*/
String getInfo();
/**
* 执行任务
*/
void execTask();
}
TaskServiceImpl.java
关键代码:executorService.registerWorkers(WorkerOptions.defaults().beanFactory(SpringUtil.getBeanFactory()));
package org.wl.test.service;
import cn.hutool.extra.spring.SpringUtil;
import org.redisson.api.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.wl.test.core.TaskScheduler;
import java.util.concurrent.TimeUnit;
/**
* @author wanglei
*/
@Service
public class TaskServiceImpl implements TaskService {
private final static Logger logger = LoggerFactory.getLogger(TaskServiceImpl.class);
/**
* 执行器名称
*/
public static final String EXECUTOR_NAME = "CARRY_EXECUTOR";
@Autowired
private RedissonClient redissonClient;
@Override
public void execTask() {
RScheduledExecutorService executorService = redissonClient.getExecutorService(EXECUTOR_NAME, ExecutorOptions.defaults());
// 一定要注入BeanFactory 否则会出现Bean空指针异常
executorService.registerWorkers(WorkerOptions.defaults().beanFactory(SpringUtil.getBeanFactory()));
logger.info("开启调度任务");
executorService.scheduleAtFixedRate(new TaskScheduler(), 0, 5,TimeUnit.SECONDS);
}
@Override
public String getInfo() {
return "Carry-Task";
}
}
TaskScheduler.java
package org.wl.test.core;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.wl.test.service.TaskService;
import java.io.Serializable;
import java.util.Date;
/**
* @author wanglei
*/
public class TaskScheduler implements Runnable, Serializable {
private final static Logger logger = LoggerFactory.getLogger(TaskScheduler.class);
private static final long serialVersionUID = -5077892891639409453L;
@Autowired
private TaskService carryService;
@Override
public void run() {
logger.info("任务执行时间: {}", new Date());
logger.info("任务信息: {}", carryService.getInfo());
}
}
调用接口,可以成功运行
再出问题
后续我在项目中改了BeanFactory,但是还报错,继续排查
java.lang.reflect.InvocationTargetException: null
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.redisson.executor.RedissonExecutorRemoteService.invokeMethod(RedissonExecutorRemoteService.java:107)
at org.redisson.RedissonRemoteService.lambda$executeMethod$11(RedissonRemoteService.java:434)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
at java.util.concurrent.FutureTask.run(FutureTask.java)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: Unable to initialize codec with ClassLoader parameter
at org.redisson.executor.TasksRunnerService.decode(TasksRunnerService.java:323)
at org.redisson.executor.TasksRunnerService.executeRunnable(TasksRunnerService.java:341)
at org.redisson.executor.TasksRunnerService.scheduleWithFixedDelay(TasksRunnerService.java:183)
... 13 common frames omitted
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'null': Unsatisfied dependency expressed through field 'xxxxx'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'xxxxx' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:659)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:639)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.processInjection(AutowiredAnnotationBeanPostProcessor.java:430)
at org.redisson.executor.SpringTasksInjector.inject(SpringTasksInjector.java:38)
at org.redisson.executor.TasksRunnerService.decode(TasksRunnerService.java:318)
... 15 common frames omitted
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'xxxx' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1799)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1355)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1309)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:656)
... 20 common frames omitted
第二次错误原因
spring-boot-devtools
在IDE环境下,spring-boot-devtools为了支持自动类加载,会使用org.springframework.boot.devtools.restart.classloader.RestartClassLoader。这可能会导致同一个类被不同的加载器加载的情况,我redisson配置的是Spring的类加载器,但是重启项目后bean被RestartClassLoader加载,导致Spring的加载器找不到Bean
第二次解决方法
去除项目中的所有热部署的依赖
例如:
<!-- spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
最后我将我上述的代码示例上传至代码仓库,供参考
更多推荐

所有评论(0)