Nacos啟動時報錯No DataSource set排查

2023-07-29 06:00:41

問題描述

最近在學習Nacos元件,使用的是最新版本:2.2.3。
在本地虛擬機器器CentOS 8.5.2111環境中安裝Nacos,並使用standalone模式啟動,同時設定使用外部MySQL資料庫,MySQL版本為:8.0.34。
關於資料庫的設定引數如下:

spring.sql.init.platform=mysql
db.num=1
db.url.0=jdbc:mysql://192.168.2.100:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
db.user.0=nacos
db.password.0=nacos

上述設定引數都是參考Nacos官方手冊來進行的。

進入到nacos安裝目錄執行如下啟動操作時報錯了。

cd nacos/bin
sh startup.sh -m standalone

報錯資訊如下:

cat ../logs/start.out

2023-07-28 22:31:42,401 ERROR Application run failed

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'configOpsController' defined in URL [jar:file:/root/opt/nacos/target/nacos-server.jar!/BOOT-INF/lib/nacos-config-2.2.3.jar!/com/alibaba/nacos/config/server/controller/ConfigOpsController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'externalDumpService': Invocation of init method failed; nested exception is ErrCode:500, ErrMsg:Nacos Server did not start because dumpservice bean construction failure :
No DataSource set
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1372)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1222)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:745)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:423)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1317)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
	at com.alibaba.nacos.Nacos.main(Nacos.java:35)
	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.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
	at org.springframework.boot.loader.PropertiesLauncher.main(PropertiesLauncher.java:467)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'externalDumpService': Invocation of init method failed; nested exception is ErrCode:500, ErrMsg:Nacos Server did not start because dumpservice bean construction failure :
No DataSource set
	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:160)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:440)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1796)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1391)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
	... 27 common frames omitted
Caused by: com.alibaba.nacos.api.exception.NacosException: Nacos Server did not start because dumpservice bean construction failure :
No DataSource set
	at com.alibaba.nacos.config.server.service.dump.DumpService.dumpOperate(DumpService.java:261)
	at com.alibaba.nacos.config.server.service.dump.ExternalDumpService.init(ExternalDumpService.java:61)
	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.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:389)
	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:333)
	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:157)
	... 40 common frames omitted
Caused by: java.lang.IllegalStateException: No DataSource set ## 從報錯資訊看似乎沒有設定資料來源
	at org.springframework.util.Assert.state(Assert.java:76)
	at org.springframework.jdbc.support.JdbcAccessor.obtainDataSource(JdbcAccessor.java:86)
	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:376)
	at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:465)
	at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:475)
	at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:508)
	at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:515)
	at com.alibaba.nacos.config.server.service.repository.extrnal.ExternalConfigInfoPersistServiceImpl.findConfigMaxId(ExternalConfigInfoPersistServiceImpl.java:632)
	at com.alibaba.nacos.config.server.service.dump.processor.DumpAllProcessor.process(DumpAllProcessor.java:51)
	at com.alibaba.nacos.config.server.service.dump.DumpService.dumpConfigInfo(DumpService.java:318)
	at com.alibaba.nacos.config.server.service.dump.DumpService.dumpOperate(DumpService.java:230)
	... 48 common frames omitted

從啟動紀錄檔資訊看,似乎是資料來源未設定,但是明明已經設定了資料來源啊???另外很神奇的是偶爾有啟動成功的時候!至此開始懵逼了。

逐步分析並解決

單純從啟動紀錄檔start.out的資訊看,得不到任何有價值的提示,於是又轉到nacos.log,所幸在這裡得到了一些有價值的異常資訊。

Caused by: com.mysql.cj.exceptions.UnableToConnectException: Public Key Retrieval is not allowed
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:61)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:85)
	at com.mysql.cj.protocol.a.authentication.CachingSha2PasswordPlugin.nextAuthenticationStep(CachingSha2PasswordPlugin.java:130)
	... 160 common frames omitted

於是先Google一下,確實有人也碰到這個問題:Connection Java - MySQL : Public Key Retrieval is not allowed
解決辦法:在JDBC連線url上設定引數allowPublicKeyRetrieval=true

進一步瞭解才知道引數allowPublicKeyRetrieval的具體用法,官方解釋如下:

大意就是:如果MySQL賬戶使用的認證模式為sha256_password,則使用者端與伺服器端在傳遞資訊的時候必須要保護密碼,基於一些安全性的原因該引數預設值為false,但是這樣就會導致MySQL使用者端與伺服器端無法正常建立連線。因此,在確保MySQL伺服器在一個安全的網路環境下,不會遭受到中間人攻擊時可以將該引數值設定為true,這樣才能讓使用者端與伺服器端正常連線。

另外,從MySQL伺服器這一側來講,預設提供了2種認證外掛,即:sha256_passwordcaching_sha2_password
而且,從MySQL 8.0.34之後預設的認證外掛為caching_sha2_password,這個也可以從MySQL的變數值看出來,如下:

mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.34    |
+-----------+
1 row in set (0.00 sec)

mysql> select @@default_authentication_plugin;
+---------------------------------+
| @@default_authentication_plugin |
+---------------------------------+
| caching_sha2_password           |
+---------------------------------+
1 row in set, 1 warning (0.00 sec)

mysql>

根據MySQL官方手冊可以知道,認證外掛caching_sha2_password的功能實際上是外掛sha256_password的超集,也是使用sha256_password模式進行使用者認證,所以這就跟使用者端連線引數allowPublicKeyRetrieval的用法聯絡在了一起。

至此,問題源頭得到梳理並解決!

最後總結

我看網上也有人遇到類似的問題,參考:
nacos設定mysql資料來源啟動出現No DataSource set的問題
nacos啟動失敗,No DataSource set
nacos啟動報錯出現no datasource set或者Access denied for user ‘root‘@‘localhost‘ (using password: YES)

雖然從nacos的報錯紀錄檔來看都有一個No DataSource set的資訊,但是原因卻各自不同,要想找到真正的原因,需要從nacos.log檔案種去尋找有明確意義的提示資訊。
注意:nacos.log紀錄檔檔案記錄的資訊會比start.out記錄的資訊更加詳細,所以在nacos啟動失敗時一定要從nacos.log分析可能的原因,切記!

最後,如果無法從紀錄檔找到任何線索,就只能手動去debug原始碼啦,參考:IntelliJ IDEA中執行Nacos官方原始碼

【參考】
Connection Java - MySQL : Public Key Retrieval is not allowed
MySQL .NET Connection String Options
6.4.1.3 SHA-256 Pluggable Authentication
MySQL 8.0.34對其自帶身份驗證標記棄用