记录一次Nacos2.1.4版本注册中心,与定时任务锁SchedulerLock配置冲突,导致NacosWatch 创建异常

一、问题描述

首先看一下程序上的版本依赖
SpringBoot 2.1.4
Nacos 2.1.4
shedlock2.2.1
背景:
之前项目是好好的,用的是Eurake + config 现在开始进行改造打算用上Nacos。

二、配置相关

ShedlockConfig

@Bean
    public LockProvider lockProvider(DataSource dataSource) {
        return new JdbcTemplateLockProvider(dataSource);
    }

    @Bean
    public ScheduledLockConfiguration scheduledLockConfiguration(LockProvider lockProvider) {
        return ScheduledLockConfigurationBuilder
                .withLockProvider(lockProvider)
                // 线程池10个线程
                .withPoolSize(10)
                .withDefaultLockAtMostFor(Duration.ofMinutes(10))
                .build();
    }

Nacos 就是加Jar包,写yml文件。略略略

三、报错信息

这里只找了一部分比较关键的

2022-11-09:11:07:31.631 [main] ERROR o.s.b.SpringApplication - [reportFailure,858] - Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'nacosWatch' defined in class path resource [com/alibaba/cloud/nacos/discovery/NacosDiscoveryClientConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.alibaba.cloud.nacos.discovery.NacosWatch]: Factory method 'nacosWatch' threw exception; nested exception is java.lang.ClassCastException: com.sun.proxy.$Proxy249 cannot be cast to org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.alibaba.cloud.nacos.discovery.NacosWatch]: Factory method 'nacosWatch' threw exception; nested exception is java.lang.ClassCastException: com.sun.proxy.$Proxy249 cannot be cast to org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:622)
	... 19 common frames omitted
Caused by: java.lang.ClassCastException: com.sun.proxy.$Proxy249 cannot be cast to org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler

四、问题解析

1、首先看到这个问题,发现是Nacos 中的com.alibaba.cloud.nacos.discovery.NacosWatch 这个类报的错误。
2、再分析一下报错原因
Factory method ‘nacosWatch’ threw exception; nested exception is java.lang.ClassCastException: com.sun.proxy.$Proxy249 cannot be cast to org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
简单来说就是在类型转换的时候转换失败了。我理解就是获取到了不是他自己的Bean 对象。
3、看一下NacosWatch 类。

@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.nacos.discovery.watch.enabled", matchIfMissing = true)
public NacosWatch nacosWatch(NacosServiceManager nacosServiceManager,
		NacosDiscoveryProperties nacosDiscoveryProperties,
		ObjectProvider<ThreadPoolTaskScheduler> taskExecutorObjectProvider) {
	return new NacosWatch(nacosServiceManager, nacosDiscoveryProperties,
			taskExecutorObjectProvider);
}

这里大家就能看到问题了,首先这个是一个配置类,在Spring 装配这个类的时候会把这个参数进行注入。回想一下Spring Bean 的装配流程。加载XML 扫描Class,变成一个一个的BeanDefinition,实例化,设置属性,,,,扯得有点远了。
总之这个方法会在IoC 容器中找一找看看有没有这几个类型的Bean 对象。(这个也是导致错误的原因)
4、再看一下new NacosWatch 方法都做了什么处理。

// 上面的构造方法
public NacosWatch(NacosServiceManager nacosServiceManager,
		NacosDiscoveryProperties properties,
		ObjectProvider<ThreadPoolTaskScheduler> taskScheduler) {
	this.nacosServiceManager = nacosServiceManager;
	this.properties = properties;
	this.taskScheduler = taskScheduler.stream().findAny()
			.orElseGet(NacosWatch::getTaskScheduler);
}
// 这个就是在容器获取不到对象的时候会调用这个方法创建一个对象
private static ThreadPoolTaskScheduler getTaskScheduler() {
	ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
	taskScheduler.setBeanName("Nacos-Watch-Task-Scheduler");
	taskScheduler.initialize();
	return taskScheduler;
}

5、粘一个错误的Debug 信息。
叭了叭,到这里判断的时候value 有值了,这个值是哪污染的呢?这个问题纠结了好久,突然想到是不是定时任务那块创建的。
在这里插入图片描述
6、排查问题就是猜猜猜,试试试。观察一下分布式定时任务的代码。

//再观察一下 这个配置类
@Bean
public ScheduledLockConfiguration scheduledLockConfiguration(LockProvider lockProvider) {
     return ScheduledLockConfigurationBuilder
             .withLockProvider(lockProvider)
             // 线程池10个线程
             .withPoolSize(10)
             .withDefaultLockAtMostFor(Duration.ofMinutes(10))
             .build();
 }

进入到 withLockProvider 这个方法里面。
发现这里会创建一个taskSchedule 类型的对象。
在这里插入图片描述
巴拉巴拉再对比一下Nacos 的获取到的类信息。
在这里插入图片描述
是不是感觉似曾相识。
总结一下问题原因,是因为NacosWatch 这个在创建的时候会判断是否已经有这个有的话就不会再去,创建。正好因为分布式定时任务,创建了一个,Nacos 获取到这个类。结果类型转换错误了。。。

五、解决办法

一、关闭Nacos 的watch

spring:
  cloud:
    nacos:
      discovery:
        watch:
          enabled: false

二、修改定时配置的方式

@Bean(destroyMethod = "shutdown")
public ScheduledExecutorService scheduledExecutorService(){
    return Executors.newScheduledThreadPool(10);
}
//    @Bean
//    public ScheduledLockConfiguration scheduledLockConfiguration(LockProvider lockProvider) {
//        return ScheduledLockConfigurationBuilder
//                .withLockProvider(lockProvider)
//                // 线程池10个线程
//                .withPoolSize(10)
//                .withDefaultLockAtMostFor(Duration.ofMinutes(10))
//                .build();
//    }

代替原有的配置。
目前只是初步解决方案。

结束

文章最后,首先要感谢一下我的老师,景天老师。帮我排查了指导问题。
文章还有好多的知识上的漏洞,作者也在慢慢的完善,进步。
借老师一句话,用屁股都能想明白,这就是依赖冲突导致的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

早起的小青年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值