Excellent Experience
Apollo / Nacos + Eueka 配置热更新导致应应重注册
产生原因
Apollo
- 使用
@ApolloConfigChangeListener监听器时,发布了EnvironmentChangeEvent事件
ConfigurationPropertiesRebinder处理@ConfigurationProperties配置类的动态更新LoggingRebinder处理日志配置的动态更新(主要是日志级别)
- 使用
@ApolloConfigChangeListener监听器时,调用了RefreshScope#refreshAll或RefreshScope#refresh方法,发布了RefreshScopeRefreshedEvent事件
销毁
@RefreshScope声明的bean
Nacos
Spring Cloud环境下有刷新自动配置,注册刷新事件监听器来监听RefreshEvent
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration org.springframework.cloud.endpoint.event.RefreshEventListener
- 上下文刷新器调用
refresh方法处理RefreshEvent
org.springframework.cloud.context.refresh.ContextRefresher org.springframework.cloud.context.refresh.ContextRefresher#refresh
ContextRefresher#refresh方法调用ContextRefresher#refreshEnvironment方法发布EnvironmentChangeEvent
org.springframework.cloud.context.refresh.ContextRefresher#refreshEnvironment
ContextRefresher#refresh方法调用RefreshScope#refreshAll方法发布RefreshScopeRefreshedEvent
org.springframework.cloud.context.scope.refresh.RefreshScope org.springframework.cloud.context.scope.refresh.RefreshScope#refreshAll
EurekaClientConfigurationRefresher监听RefreshScopeRefreshedEvent,重注册Eureka client
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration.EurekaClientConfigurationRefresher org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration.EurekaClientConfigurationRefresher#onApplicationEvent
- 如果在应用运行期 修改配置文件,
Nacos客户端会布RefreshEvent,最终应用上下文会发布RefreshScopeRefreshedEvent事件,Eureka client监听到该事件后会触发重注册操作
解决过程
- 跟踪
Eureka client注册流程,注册入口为EurekaAutoServiceRegistration.start方法
org.springframework.cloud.netflix.eureka.serviceregistry.EurekaAutoServiceRegistration
- 通过方法调用找到
EurekaClientConfigurationRefresher#onApplicationEvent方法,确定是因为RefreshScopeRefreshedEvent事件导致Eureka client重注册
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration.EurekaClientConfigurationRefresher#onApplicationEvent
- 跟踪
RefreshScopeRefreshedEvent事件发布入口,定位到代码中Apollo刷新配置参考了官方demo,调用了RefreshScope#refreshAll方法(RefreshScope#refresh效果一样)
Apollo客户端为了刷新@RefreshScope声明的类,在自定义@ApolloConfigChangeListener中调用了RefreshScope#refreshAll | RefreshScope#refresh方法Nacos客户端在配置更新后,会发布RefreshEvent事件
Apollo|Nacos客户端的问题最终都定位到RefreshScope的refresh | refreshAll方法上,需重写refresh|refreshAll方法,使其不发布RefreshScopeRefreshedEvent事件- 将扩展的
RefreshScope注册到Spring容器,替换原有RefreshScope,问题解决