Redis-Redisson-升级3.17.*报错:Unrecognized token ‘classpath‘

注意

        3.11.2是老版本,本文主要讲3.11.2升级到最新的3.17.4中遇到的问题,如果想直接使用3.17,可以直接跳过3.11看3.17即可。

错误展示:Unrecognized token 'classpath'

Caused by: java.lang.IllegalArgumentException: Can't parse config
	at org.redisson.spring.starter.RedissonAutoConfiguration.redisson(RedissonAutoConfiguration.java:138)
	at org.redisson.spring.starter.RedissonAutoConfiguration$$EnhancerBySpringCGLIB$$25ac00c4.CGLIB$redisson$5()
	at org.redisson.spring.starter.RedissonAutoConfiguration$$EnhancerBySpringCGLIB$$25ac00c4$$FastClassBySpringCGLIB$$b30f0a32.invoke()
	at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
	at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331)
	at org.redisson.spring.starter.RedissonAutoConfiguration$$EnhancerBySpringCGLIB$$25ac00c4.redisson()
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
	... 61 common frames omitted
Caused by: com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'classpath': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
 at [Source: (String)"classpath:singleServerConfig.yaml"; line: 1, column: 10]
	at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2391)
	at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:745)

 错误排查:

        根据断点,系统是在类“com.fasterxml.jackson.databind.ObjectMapper”中的“readValue(String content, JavaType valueType)”中的代码“return (T) _readMapAndClose(_jsonFactory.createParser(content), valueType);”抛出的异常,异常信息如下:Redis-Redisson-升级3.17.*报错:Unrecognized token ‘classpath‘_第1张图片

         如上:3.17*版本系统会抛出:“Unrecognized field "pingTimeout" (class org.redisson.config.SingleServerConfig), not marked as ignorable”,但是在我们的console输出的信息如下:

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.redisson.api.RedissonClient]: Factory method 'redisson' threw exception; nested exception is java.lang.IllegalArgumentException: Can't parse config
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653)
	... 33 common frames omitted
Caused by: java.lang.IllegalArgumentException: Can't parse config
	at org.redisson.spring.starter.RedissonAutoConfiguration.redisson(RedissonAutoConfiguration.java:151)
	at org.redisson.spring.starter.RedissonAutoConfiguration$$EnhancerBySpringCGLIB$$ce6efe30.CGLIB$redisson$5()
	at org.redisson.spring.starter.RedissonAutoConfiguration$$EnhancerBySpringCGLIB$$ce6efe30$$FastClassBySpringCGLIB$$3728d689.invoke()
	at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
	at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331)
	at org.redisson.spring.starter.RedissonAutoConfiguration$$EnhancerBySpringCGLIB$$ce6efe30.redisson()
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
	... 34 common frames omitted
Caused by: com.fasterxml.jackson.core.JsonParseException: Unexpected character ('-' (code 45)) in numeric value: expected digit (0-9) to follow minus sign, for valid numeric value
 at [Source: (String)"---
singleServerConfig:

最终查询官方手册可知,是没有“pingTimeout”属性。其他排查方法一致。 

解决方法

方法一:删除singleServerConfig.yaml配置

新版本的redis中不需要单独配置singleServerConfig.yaml。删除spring.redis.redisson.config配置即可

### 删除此配置
spring:
  redis:
    redisson:
      config: classpath:singleServerConfig.yaml

方法二:删除singleServerConfig.yaml配置 

将application.yml里面的config改为“file”,修改后的application.yml参数:

spring:
  redis:
    redisson:
      file: classpath:singleServerConfig.yml

 修改singleServerConfig.yaml内容,在3.17版本中是没有:“pingTimeout”、“reconnectionTimeout”、“failedAttempts”三个参数。修改后singleServerConfig.yaml内容见3.17.4配置。

3.11.2版本

pom.xml


  
    org.springframework.boot
    spring-boot-starter-web
    2.1.6.RELEASE
  
  
    org.redisson
    redisson-spring-boot-starter
    3.11.2
  
  
    org.springframework.boot
    spring-boot-starter-test
    2.1.6.RELEASE
  
  
    org.projectlombok
    lombok
    1.18.8
  
  
    junit
    junit
    4.12
  

application.yml

#spring相关配置
spring:
  redis:
    redisson:
      config: classpath:singleServerConfig.yml

singleServerConfig.yml

---
singleServerConfig:
  #如果当前连接池里的连接数量超过了最小空闲连接数,而同时有连接空闲时间超过了该数值,
  #那么这些连接将会自动被关闭,并从连接池里去掉。时间单位是毫秒。
  #默认值:10000
  idleConnectionTimeout: 10000
  pingTimeout: 1000
  #同任何节点建立连接时的等待超时。时间单位是毫秒。
  #默认值:10000
  connectTimeout: 10000
  #等待节点回复命令的时间。该时间从命令发送成功时开始计时。
  #默认值:3000
  timeout: 3000
  #如果尝试达到 retryAttempts(命令失败重试次数)
  #仍然不能将命令发送至某个指定的节点时,将抛出错误。如果尝试在此限制之内发送成功,
  #则开始启用 timeout(命令等待超时) 计时。
  #默认值:3
  retryAttempts: 3
  #在某个节点执行相同或不同命令时,连续失败failedAttempts(执行失败最大次数)时,
  #该节点将被从可用节点列表里清除,直到 reconnectionTimeout(重新连接时间间隔) 超时以后再次尝试。
  #默认值:1500
  retryInterval: 1500
  #重新连接时间间隔
  reconnectionTimeout: 3000
  #执行失败最大次数
  failedAttempts: 3
  #密码
  password: pass
  #每个连接的最大订阅数量。
  #默认值:5
  subscriptionsPerConnection: 5
  #在Redis节点里显示的客户端名称。
  clientName: null
  #在Redis节点
  address: "redis://node2:6379"
  #从节点发布和订阅连接的最小空闲连接数
  #默认值:1
  subscriptionConnectionMinimumIdleSize: 1
  #用于发布和订阅连接的连接池最大容量。连接池的连接数量自动弹性伸缩。
  #默认值:50
  subscriptionConnectionPoolSize: 50
  #节点最小空闲连接数
  #默认值:32
  connectionMinimumIdleSize: 32
  #节点连接池大小
  #默认值:64
  connectionPoolSize: 64
#这个线程池数量被所有RTopic对象监听器,RRemoteService调用者和RExecutorService任务共同共享。
#默认值: 当前处理核数量 * 2
threads: 8
#这个线程池数量是在一个Redisson实例内,被其创建的所有分布式数据类型和服务,
#以及底层客户端所一同共享的线程池里保存的线程数量。
#默认值: 当前处理核数量 * 2
nettyThreads: 8
#Redisson的对象编码类是用于将对象进行序列化和反序列化,以实现对该对象在Redis里的读取和存储。
#默认值: org.redisson.codec.JsonJacksonCodec
codec: ! {}
#传输模式
#默认值:TransportMode.NIO
transportMode: "NIO"

3.17.4版本

pom.xml


  
    org.springframework.boot
    spring-boot-starter-web
    2.6.0
  
  
    org.redisson
    redisson-spring-boot-starter
    3.17.4
  
  
    org.springframework.boot
    spring-boot-starter-test
    2.6.0
  
  
    org.projectlombok
    lombok
    1.18.8
  
  
    junit
    junit
    4.12
  

application.yml

spring:
  redis:
    redisson:
      file: classpath:singleServerConfig.yml
#      config:|
#        singleServerConfig:
#          idleConnectionTimeout: 10000
#          connectTimeout: 10000
#          timeout: 3000
#          retryAttempts: 3
#          retryInterval: 1500
#          password: pass
#          subscriptionsPerConnection: 5
#          clientName: null
#          address: "redis://node2:6379"
#          subscriptionConnectionMinimumIdleSize: 1
#          subscriptionConnectionPoolSize: 50
#          connectionMinimumIdleSize: 32
#          connectionPoolSize: 64
#        threads: 8
#        nettyThreads: 8
#        codec: ! {}
#        transportMode: "NIO"

####file和config二选一即可

singleServerConfig.yml

---
singleServerConfig:
  #如果当前连接池里的连接数量超过了最小空闲连接数,而同时有连接空闲时间超过了该数值,
  #那么这些连接将会自动被关闭,并从连接池里去掉。时间单位是毫秒。
  #默认值:10000
  idleConnectionTimeout: 10000
  #同任何节点建立连接时的等待超时。时间单位是毫秒。
  #默认值:10000
  connectTimeout: 10000
  #等待节点回复命令的时间。该时间从命令发送成功时开始计时。
  #默认值:3000
  timeout: 3000
  #如果尝试达到 retryAttempts(命令失败重试次数)
  #仍然不能将命令发送至某个指定的节点时,将抛出错误。如果尝试在此限制之内发送成功,
  #则开始启用 timeout(命令等待超时) 计时。
  #默认值:3
  retryAttempts: 3
  #在某个节点执行相同或不同命令时,连续失败failedAttempts(执行失败最大次数)时,
  #该节点将被从可用节点列表里清除,直到 reconnectionTimeout(重新连接时间间隔) 超时以后再次尝试。
  #默认值:1500
  retryInterval: 1500
  #密码
  password: pass
  #每个连接的最大订阅数量。
  #默认值:5
  subscriptionsPerConnection: 5
  #在Redis节点里显示的客户端名称。
  clientName: null
  #在Redis节点
  address: "redis://node2:6379"
  #从节点发布和订阅连接的最小空闲连接数
  #默认值:1
  subscriptionConnectionMinimumIdleSize: 1
  #用于发布和订阅连接的连接池最大容量。连接池的连接数量自动弹性伸缩。
  #默认值:50
  subscriptionConnectionPoolSize: 50
  #节点最小空闲连接数
  #默认值:32
  connectionMinimumIdleSize: 32
  #节点连接池大小
  #默认值:64
  connectionPoolSize: 64
#这个线程池数量被所有RTopic对象监听器,RRemoteService调用者和RExecutorService任务共同共享。
#默认值: 当前处理核数量 * 2
threads: 8
#这个线程池数量是在一个Redisson实例内,被其创建的所有分布式数据类型和服务,
#以及底层客户端所一同共享的线程池里保存的线程数量。
#默认值: 当前处理核数量 * 2
nettyThreads: 8
#Redisson的对象编码类是用于将对象进行序列化和反序列化,以实现对该对象在Redis里的读取和存储。
#默认值: org.redisson.codec.JsonJacksonCodec
codec: ! {}
#传输模式
#默认值:TransportMode.NIO
transportMode: "NIO"

启动

@SpringBootApplication
public class RedissionStart {

    public static void main(String[] args) {
        SpringApplication.run(RedissionStart.class);
        System.out.println("redission,启动成功...");
    }
}

可重入锁操作

@Slf4j
@Component
public class RLockOpertions {

    @Autowired
    RedissonClient redissonClient;

    public void  lock() throws InterruptedException{
        log.info("线程:{},进入方法",Thread.currentThread().getName());
        RLock rLock = redissonClient.getLock("lock");
        //加锁:锁的有效期默认30秒
        rLock.lock();
        long timeToLive = rLock.remainTimeToLive();
        log.info("线程:{},获得锁,锁存活时间:{}S",Thread.currentThread().getName(),timeToLive/1000);
        //休眠一下
        Thread.sleep(2000);
        //如果主线程未释放,且当前锁未调用unlock方法,则进入到watchDog机制
        //如果主线程未释放,且当前锁调用unlock方法,则直接释放锁
        rLock.unlock();
        log.info("线程:{},释放锁",Thread.currentThread().getName());
    }

    public void  lockLaseTime() throws InterruptedException{
        log.info("线程:{},进入方法",Thread.currentThread().getName());
        RLock rLock = redissonClient.getLock("lockLaseTime");
        //加锁 上面是默认30秒,
        //这里可以手动设置锁的有效时间,锁到期后会自动释放的
        rLock.lock(10,TimeUnit.SECONDS);
        long timeToLive = rLock.remainTimeToLive();
        log.info("线程:{},获得锁,锁存活时间:{}S",Thread.currentThread().getName(),timeToLive/1000);
        //休眠一下
        Thread.sleep(2000);
        //如果主线程未释放,且当前锁未调用unlock方法,则锁到期后会自动释放的
        //如果主线程未释放,且当前锁调用unlock方法,则直接释放锁
        rLock.unlock();
        log.info("线程:{},释放锁",Thread.currentThread().getName());
    }

    public void  tryLock() throws InterruptedException {
        log.info("线程:{},进入方法",Thread.currentThread().getName());
        RLock rLock = redissonClient.getLock("tryLock");
        //tryLock()方法是有返回值的,它表示用来尝试获取锁,
        //如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false .
        boolean flag = rLock.tryLock();
        if (flag){
            long timeToLive = rLock.remainTimeToLive();
            log.info("线程:{},获得锁,锁存活时间:{}S,加锁状态:{}",Thread.currentThread().getName(),timeToLive/1000,flag);
            //休眠一下
            Thread.sleep(2000);
            //如果主线程未释放,且当前锁未调用unlock方法,则进入到watchDog机制
            //如果主线程未释放,且当前锁调用unlock方法,则直接释放锁
            rLock.unlock();
            log.info("线程:{},释放锁",Thread.currentThread().getName());
        }else {
            log.info("线程:{},获得锁失败",Thread.currentThread().getName());
        }
    }

    public void  tryLockWaitTime() throws InterruptedException {
        log.info("线程:{},进入方法",Thread.currentThread().getName());
        RLock rLock = redissonClient.getLock("tryLockWaitTime");
        //tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,
        //只不过区别在于这个方法在拿不到锁时会等待一定的时间,
        //在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
        boolean flag = rLock.tryLock(6, TimeUnit.SECONDS);
        if (flag){
            long timeToLive = rLock.remainTimeToLive();
            log.info("线程:{},获得锁,锁存活时间:{}S,加锁状态:{}",Thread.currentThread().getName(),timeToLive/1000,flag);
            //休眠一下
            Thread.sleep(7000);
            //如果主线程未释放,且当前锁未调用unlock方法,则进入到watchDog机制
            //如果主线程未释放,且当前锁调用unlock方法,则直接释放锁
            rLock.unlock();
            log.info("线程:{},释放锁",Thread.currentThread().getName());
        }else {
            log.info("线程:{},获得锁失败",Thread.currentThread().getName());
        }

    }


    public void  tryLockleasTime() throws InterruptedException {
        log.info("线程:{},进入方法",Thread.currentThread().getName());
        RLock rLock = redissonClient.getLock("tryLockleasTime");
        //比上面多一个参数,多添加一个锁的有效时间
        boolean flag = false;

            flag = rLock.tryLock(12,10, TimeUnit.SECONDS);
            if (flag){
                long timeToLive = rLock.remainTimeToLive();
                log.info("线程:{},获得锁,锁存活时间:{}S,加锁状态:{}",Thread.currentThread().getName(),timeToLive/1000,flag);
                //休眠一下
                Thread.sleep(3000);
                log.info("线程:{},释放锁",Thread.currentThread().getName());
                rLock.unlock();
            }else {
                log.info("线程:{},获得锁失败",Thread.currentThread().getName());
            }


    }

}

测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class RLockOpertionsTest {

    @Autowired
    RLockOpertions rLockOpertions;

    @Test
    public void  lockTest() throws InterruptedException{
        //模拟并发,2个线程同时强同一对象
        CountDownLatch countDownLatch = new CountDownLatch(2);
        new Thread("name-A"){
            @SneakyThrows
            public void run(){
                countDownLatch.countDown();
                rLockOpertions.lock();
            }
        }.start();
        new Thread("name-B"){
            @SneakyThrows
            public void run(){
                countDownLatch.countDown();
                rLockOpertions.lock();
            }
        }.start();
        countDownLatch.await();
        Thread.sleep(600000);
    }

    @Test
    public void  lockLaseTimeTest() throws InterruptedException{
        //模拟并发,2个线程同时强同一对象
        CountDownLatch countDownLatch = new CountDownLatch(2);
        new Thread("name-A"){
            @SneakyThrows
            public void run(){
                countDownLatch.countDown();
                rLockOpertions.lockLaseTime();
            }
        }.start();
        new Thread("name-B"){
            @SneakyThrows
            public void run(){
                countDownLatch.countDown();
                rLockOpertions.lockLaseTime();
            }
        }.start();
        countDownLatch.await();
        Thread.sleep(600000);
    }

    @Test
    public void  tryLockTest() throws InterruptedException {
        //模拟并发,2个线程同时强同一对象
        CountDownLatch countDownLatch = new CountDownLatch(2);
        new Thread("name-A"){
            @SneakyThrows
            public void run(){
                countDownLatch.countDown();
                rLockOpertions.tryLock();
            }
        }.start();
        new Thread("name-B"){
            @SneakyThrows
            public void run(){
                countDownLatch.countDown();
                rLockOpertions.tryLock();
            }
        }.start();
        countDownLatch.await();
        Thread.sleep(600000);
    }


    @Test
    public void tryLockWaitTimeTest() throws InterruptedException {
        //模拟并发,2个线程同时强同一对象
        CountDownLatch countDownLatch = new CountDownLatch(2);
        new Thread("name-A"){
            @SneakyThrows
            public void run(){
                countDownLatch.countDown();
                rLockOpertions.tryLockWaitTime();
            }
        }.start();
        new Thread("name-B"){
            @SneakyThrows
            public void run(){
                countDownLatch.countDown();
                rLockOpertions.tryLockWaitTime();
            }
        }.start();
        countDownLatch.await();
        Thread.sleep(600000);
    }

    @Test
    public void tryLockleasTimeTimeTest() throws InterruptedException {
        //模拟并发,2个线程同时强同一对象
        CountDownLatch countDownLatch = new CountDownLatch(5);
        new Thread("name-A"){
            @SneakyThrows
            public void run(){
                countDownLatch.countDown();
                rLockOpertions.tryLockleasTime();
            }
        }.start();
        new Thread("name-B"){
            @SneakyThrows
            public void run(){
                countDownLatch.countDown();
                rLockOpertions.tryLockleasTime();
            }
        }.start();
        new Thread("name-C"){
            @SneakyThrows
            public void run(){
                countDownLatch.countDown();
                rLockOpertions.tryLockleasTime();
            }
        }.start();
        new Thread("name-D"){
            @SneakyThrows
            public void run(){
                countDownLatch.countDown();
                rLockOpertions.tryLockleasTime();
            }
        }.start();
        new Thread("name-E"){
            @SneakyThrows
            public void run(){
                countDownLatch.countDown();
                rLockOpertions.tryLockleasTime();
            }
        }.start();
        countDownLatch.await();
        Thread.sleep(600000);
    }

}

你可能感兴趣的:(环境搭建专题,redis,java)