Skip to main content

Excellent Experience

Apollo / Nacos + Eueka 配置热更新导致应应重注册

产生原因

Apollo

  • 使用 @ApolloConfigChangeListener 监听器时,发布了 EnvironmentChangeEvent 事件

ConfigurationPropertiesRebinder 处理 @ConfigurationProperties 配置类的动态更新 LoggingRebinder 处理日志配置的动态更新(主要是日志级别)

  • 使用 @ApolloConfigChangeListener 监听器时,调用了 RefreshScope#refreshAllRefreshScope#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 客户端的问题最终都定位到 RefreshScoperefresh | refreshAll 方法上,需重写 refresh | refreshAll 方法,使其不发布 RefreshScopeRefreshedEvent 事件
  • 将扩展的 RefreshScope 注册到 Spring 容器,替换原有 RefreshScope,问题解决