公司动态
解决项目启动报 Cannot invoke “com.alibaba.nacos.client.naming.backups.FailoverDataSource.getSwitch()“...
解决项目启动报java.lang.NullPointerException: Cannot invoke com.alibaba.nacos.client.naming.backups.FailoverDataSource.getSwitch() because the return value of com.alibaba.nacos.client.naming.backups.FailoverReactor.access$000(com.alibaba.nacos.client.naming.backups.FailoverReactor) is null问题发现问题解决排查方式一在本地或者测试环境模拟生产环境排查方式二使用命令java -jar执行一下生产的包问题发现最近在生产环境启动时报了一个错误束手无策因为本地和测试环境都没有出现这个问题。完整的错误信息如图环境如下Nacos版本Nacos2.2.3SpringCloud版本Spring Cloud 2023.0.13JDK版本17问题解决本来以为baidu能给我答案最后发现根本搜不到有关该错误的文章。然后就开始了苦逼的排查之路。排查方式一在本地或者测试环境模拟生产环境然后我本地搭建了一个和生产环境一模一样的Nacos版本然后将配置文件改成本地的地址示例代码如下spring:cloud:nacos:config:server-addr:127.0.0.1:8848discovery:server-addr:127.0.0.1:8848然后启动项目未发现异常情况。排查方式二使用命令java -jar执行一下生产的包找到从生产上下载的包打开cmd窗口执行命令java-jarxxx.jar然后发现问题出现了后来我又陆续切换了好几个分支打包后重试均出现该问题。神奇的很命令启动有这个问题Idea启动就没这个问题。只要能复现问题就好办然后就开始定位问题我把该项目的所有代码全删了然后打包重新运行发现正常启动没有异常错误。然后就一个类一个类的删最后定位到一个初始化Bean的方法上面示例代码如下RestControllerpublicclassAppControllerimplementsInitializingBean{ResourceprivateEchoServiceechoService;OverridepublicvoidafterPropertiesSet()throwsException{echoService.echo(hello);}}当我把afterPropertiesSet()方法中的代码注释打包后启动依旧没有发现问题。最终定位在RPC接口的问题上。回过头去看日志你会发现在日志中第一次出现错误的位置结合上下文基本上可以定位问题所在位置。然后再来分析为什么会出现这种情况在Spring Bean的afterPropertiesSet()初始化阶段访问了Nacos客户端相关资源Nacos服务发现来获取服务地址间接依赖Nacos但此时Nacos客户端尚未完成初始化导致依赖的FailoverReactor等组件为null最终触发空指针异常。虽然Nacos没有注册完成那为什么RPC接口却成功响应结果了呢Nacos服务发现的核心功能已临时可用但内部故障转移Failover组件的初始化滞后导致首次调用成功后后续的故障转移检查触发空指针异常。为什么该错误不会影响项目正常运行Nacos客户端的FailoverReactor故障转移组件属于辅助功能其初始化异常通常发生在独立的异步线程Nacos内部的定时任务线程每5秒执行一次所以导致日志不断增长为什么Idea启动正常使用命令启动就有问题两种运行环境下的初始化时序、类加载机制或线程调度存在差异导致Nacos客户端在IDEA中 “侥幸” 先于异步Feign调用完成初始化而打包后这种时序平衡被打破。IDEA运行默认会添加一些调试相关的JVM参数如-agentlib:jdwp用于调试这些参数可能会减慢JVM启动速度或禁用某些类加载 / 初始化优化如提前编译、类加载并行化知道问题所在和原因就可以很快解决这个问题我们可以等待所有Bean初始化完成后再去执行对应的方法这样就不会出现容器未初始化完成的情况示例代码如下RestControllerpublicclassAppController{ResourceprivateEchoServiceechoService;EventListener(ContextRefreshedEvent.class)// 指定监听的事件类型publicvoidhandleContextRefreshed(){echoService.echo(hello);System.out.println(容器就绪处理后续操作...);}}ContextRefreshedEvent是Spring框架中一个重要的容器事件当Spring应用上下文ApplicationContext完成初始化或刷新时触发。它标志着容器内所有Bean已完成创建、依赖注入和初始化包括PostConstruct方法执行完毕是监听容器初始化完成的核心事件。如果你的错误并不是该问题导致的就需要考虑是否是依赖导致检查依赖版本或者升级Nacos客户端解决该问题。