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
redisson与spring boot兼容版本

<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>

最后我将我上述的代码示例上传至代码仓库,供参考

carry-redissson

Logo

更多推荐