nacos-配置变化事件通知EnvironmentChangeEvent 源码解析

69 阅读1分钟

版本

  • spring-cloud-nacos-config 2021.0.5.0
  • spring-alibaba-cloud 2021.0.5.0
  • spring-boot 2.7.14

配置

application.yml

spring:
  application:
    name: wf-activity-web
  config:
    # ?refreshEnabled=true
    import:
      - optional:nacos:web.yml

nacos配置

比如启动给的时候配置文件是空的,后续在nacos管理后台新增或者修改 x: 100 此时会触发EnvironmentChangeEvent 事件。

x: 100

源码解析

nacos 客户端 在启动时候 启动一个异步任务 定时调度com.alibaba.nacos.client.config.impl.ClientWorker.ConfigRpcTransportClient#startInternal

@Override
public void startInternal() {
    executor.schedule(() -> {
        while (!executor.isShutdown() && !executor.isTerminated()) {
            try {
                listenExecutebell.poll(5L, TimeUnit.SECONDS);
                if (executor.isShutdown() || executor.isTerminated()) {
                    continue;
                }
                // 核心逻辑
                executeConfigListen();
            } catch (Throwable e) {
                LOGGER.error("[ rpc listen execute ] [rpc listen] exception", e);
            }
        }
    }, 0L, TimeUnit.MILLISECONDS);
    
}

如果数据 md5变化就推送事件 com.alibaba.cloud.nacos.refresh.NacosContextRefresher#registerNacosListener

private void registerNacosListener(final String groupKey, final String dataKey) {
   String key = NacosPropertySourceRepository.getMapKey(dataKey, groupKey);
   Listener listener = listenerMap.computeIfAbsent(key,
         lst -> new AbstractSharedListener() {
            @Override
            public void innerReceive(String dataId, String group,
                  String configInfo) {
               refreshCountIncrement();
               nacosRefreshHistory.addRefreshRecord(dataId, group, configInfo);
               // 发送 刷新时间RefreshEvent
               applicationContext.publishEvent(
                     new RefreshEvent(this, null, "Refresh Nacos config"));
               if (log.isDebugEnabled()) {
                  log.debug(String.format(
                        "Refresh Nacos config group=%s,dataId=%s,configInfo=%s",
                        group, dataId, configInfo));
               }
            }
         });
   try {
      configService.addListener(dataKey, groupKey, listener);
      log.info("[Nacos Config] Listening config: dataId={}, group={}", dataKey,
            groupKey);
   }
   catch (NacosException e) {
      log.warn(String.format(
            "register fail for nacos listener ,dataId=[%s],group=[%s]", dataKey,
            groupKey), e);
   }
}

这个时候 发送了一个org.springframework.cloud.endpoint.event.RefreshEvent 事件,这个时候找下消费者

发现 org.springframework.cloud.endpoint.event.RefreshEventListener#onApplicationEvent消费了

@Override
public void onApplicationEvent(ApplicationEvent event) {
   if (event instanceof ApplicationReadyEvent) {
      handle((ApplicationReadyEvent) event);
   }
   else if (event instanceof RefreshEvent) {
      // 刷新事件
      handle((RefreshEvent) event);
   }
}

进入handle事件

public void handle(RefreshEvent event) {
  // this.ready.get() 这个是在 spring ready 的时候发送的事件改成true 默认false,
   if (this.ready.get()) { // don't handle events before app is ready
      log.debug("Event received " + event.getEventDesc());
      Set<String> keys = this.refresh.refresh();
      log.info("Refresh keys changed: " + keys);
   }
}

进入 refresh方法 org.springframework.cloud.context.refresh.ContextRefresher#refresh

public synchronized Set<String> refresh() {
   Set<String> keys = refreshEnvironment();
   this.scope.refreshAll();
   return keys;
}

进入 refreshEnvironment 方法

public synchronized Set<String> refresh() {
   Set<String> keys = refreshEnvironment();
   this.scope.refreshAll();
   return keys;
}

public synchronized Set<String> refreshEnvironment() {
   Map<String, Object> before = extract(this.context.getEnvironment().getPropertySources());
   updateEnvironment();
   Set<String> keys = changes(before, extract(this.context.getEnvironment().getPropertySources())).keySet();
   //发送 EnvironmentChangeEvent 事件
   this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));
   return keys;
}

这个时候如果想监听nacos 配置变化 加一个EnvironmentChangeEvent 监听器即可