原先公司遇到的问题,当时总结的内容,这里迁移一下。
一、问题复现
在某个应用内,有一个namespace,application.yml 有如下配置
spring: application: name: wisdomclass-usercenter datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://${mysql.db.host}/${mysql.db.database}?useUnicode=true&characterEncoding=utf-8&mysqlEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useServerPrepStmts=true&cachePrepStmts=true&prepStmtCacheSize=250&prepStmtCacheSqlLimit=2048&autoReconnect=true&failOverReadOnly=false username: ${mysql.username} password: ${mysql.password} eureka: instance: lease-renewal-interval-in-seconds: ${eureka.instance.lease-renewal-interval-in-seconds} #EurekaClient向EurekaServer发送心跳时间间隔 prefer-ip-address: true client: registerWithEureka: true #是否注册自己 fetchRegistry: ${eureka.client.fetchRegistry} #是否拉取服务列表 registry-fetch-interval-seconds: ${eureka.client.registry-fetch-interval-seconds} #Client向Server拉取服务信息的频率,防止新上线的服务拉取不到 serviceUrl: #注册中心地址,默认zone是defaultZone defaultZone: ${eureka.client.serviceUrl.defaultZone}
在另一个namespace,backend.mysql中有如下配置:

还有一个namespace,backend.springcloud有如下配置:

现服务读取apollo去加载该应用,在配置文件这样配置 apollo.bootstrap.namespaces:application.yml,backend.mysql,backend.springcloud
会发现服务启动报错,异常的错误信息如下:
2020-02-19 21:22:40.568 WARN [main] [Jdk14Logger.java:99] - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration$EurekaClientConfigurationRefresher': Unsatisfied dependency expressed through field 'autoRegistration'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'eurekaAutoServiceRegistration' defined in class path resource [org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.class]: Unsatisfied dependency expressed through method 'eurekaAutoServiceRegistration' parameter 2; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'eurekaRegistration' defined in class path resource [org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.class]: Unsatisfied dependency expressed through method 'eurekaRegistration' parameter 1; nested exception is org.springframework.boot.context.properties.ConfigurationPropertiesBindException: Error creating bean with name 'eurekaInstanceConfigBean': Could not bind properties to 'EurekaInstanceConfigBean' : prefix=eureka.instance, ignoreInvalidFields=false, ignoreUnknownFields=true; nested exception is org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'eureka.instance.lease-renewal-interval-in-seconds' to int
发现datasource初始化成功,但eureka失败。
其实这个问题就是在使用apollo过程中对于这种变量${}获取时可能存在的情况,当变量声明与变量获取同一key命名时,就很有可能会出现这种问题。
二、原因分析
这里,先说一下,客户端在对apollo namespace配置加载时的规则:
根据客户端配置的“apollo.bootstrap.namespaces”值来决定,namespace在前先加载,若有重复key的配置,只会加载第一次的值,第二次的值直接过滤掉。它会把所有namespace的配置加载完后,再去启动spring容器。
出现上述问题的原因是,变量的命名与获取的地方为同一key,如“testKey:${testKey}”,和“testKey:1”在两个namespace,如果“testKey:${testKey}”加载在前,“testKey:1”就不会再进行加载,然后在实例化读取配置时,显然“${testKey}”这种值是错误的,导致异常。而如果“testKey:1”在前,那么“testKey:${testKey}”就不会被加载,实例化也不会出错。而对于上面的“${mysql.name}”的情况,因为apollo客户端会在加载完所有namespace的配置后,才启动容器,而且命名并不重复,所以变量可以正常获取。
三、总结
如果有变量取值的情况时,尽量保证变量名不要与取值的key相同。若相同,务必保证变量值在前加载。
发表评论