1 Nacos动态刷新
不需要开发,基于文件刷新机制就可以满足
增加Nacos的POM依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
bootstrap.properties配置Nacos Config
spring.application.name=gateway-inner spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 spring.cloud.nacos.config.server-addr=127.0.0.1:8848 spring.cloud.nacos.config.group=DEFAULT_GROUP
-
2 原理
2.1 流程图
2.2 源码分析
2.2.1 文件更新监听
com.alibaba.nacos.client.config.impl.ClientWorker
com.alibaba.nacos.client.config.impl.CacheData
// Nacos客户端长连接 public ClientWorker(final HttpAgent agent, final ConfigFilterChainManager configFilterChainManager, final Properties properties) { this.agent = agent; this.configFilterChainManager = configFilterChainManager; this.executor = Executors.newScheduledThreadPool(1, new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setName("com.alibaba.nacos.client.Worker." + agent.getName()); t.setDaemon(true); return t; } }); // 10毫秒检测配置更新 this.executor.scheduleWithFixedDelay(new Runnable() { @Override public void run() { try { checkConfigInfo(); } catch (Throwable e) { LOGGER.error("[" + agent.getName() + "] [sub-check] rotate check error", e); } } }, 1L, 10L, TimeUnit.MILLISECONDS); }
class LongPollingRunnable implements Runnable { private final int taskId; public LongPollingRunnable(int taskId) { this.taskId = taskId; } @Override public void run() { List<CacheData> cacheDatas = new ArrayList<CacheData>(); List<String> inInitializingCacheList = new ArrayList<String>(); try { // 1:检查服务配置 List<String> changedGroupKeys = checkUpdateDataIds(cacheDatas, inInitializingCacheList); if (!CollectionUtils.isEmpty(changedGroupKeys)) { LOGGER.info("get changedGroupKeys:" + changedGroupKeys); } for (String groupKey : changedGroupKeys) { try { // 2:获取服务配置 ConfigResponse response = getServerConfig(dataId, group, tenant, 3000L); } } for (CacheData cacheData : cacheDatas) { if (!cacheData.isInitializing() || inInitializingCacheList .contains(GroupKey.getKeyTenant(cacheData.dataId, cacheData.group, cacheData.tenant))) { // 3:检查监听 cacheData.checkListenerMd5(); cacheData.setInitializing(false); } } } } }
2.2.2 发布RefreshEvent事件
com.alibaba.cloud.nacos.refresh.NacosContextRefresher
· 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); applicationContext.publishEvent( new RefreshEvent(this, null, "Refresh Nacos config")); } }); try { configService.addListener(dataKey, groupKey, listener); } }
2.2.3 处理RefreshEvent事件发布RefreshScopeRefreshedEvent事件
org.springframework.cloud.endpoint.event.RefreshEventListener
@Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationReadyEvent) { handle((ApplicationReadyEvent) event); } else if (event instanceof RefreshEvent) { handle((RefreshEvent) event); } } public void handle(RefreshEvent event) { if (this.ready.get()) { // don't handle events before app is ready Set<String> keys = this.refresh.refresh(); } }
org.springframework.cloud.context.refresh.ContextRefresher
public synchronized Set<String> refresh() { Set<String> keys = refreshEnvironment(); this.scope.refreshAll(); return keys; } public void refreshAll() { super.destroy(); this.context.publishEvent(new RefreshScopeRefreshedEvent()); }
2.2.4 处理RefreshScopeRefreshedEvent发布RefreshRoutesEvent
org.springframework.cloud.gateway.route.RouteRefreshListener
@Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextRefreshedEvent) { ContextRefreshedEvent refreshedEvent = (ContextRefreshedEvent) event; if (!WebServerApplicationContext.hasServerNamespace( refreshedEvent.getApplicationContext(), "management")) { reset(); } } else if (event instanceof RefreshScopeRefreshedEvent || event instanceof InstanceRegisteredEvent) { reset(); } else if (event instanceof ParentHeartbeatEvent) { ParentHeartbeatEvent e = (ParentHeartbeatEvent) event; resetIfNeeded(e.getValue()); } else if (event instanceof HeartbeatEvent) { HeartbeatEvent e = (HeartbeatEvent) event; resetIfNeeded(e.getValue()); } } private void reset() { this.publisher.publishEvent(new RefreshRoutesEvent(this)); }
2.2.5 处理RefreshRoutesEvent
org.springframework.cloud.gateway.route.CachingRouteDefinitionLocator
- org.springframework.cloud.gateway.route.CachingRouteLocator