一篇学会配置管理客户端流程

开发 架构
Nacos注册中心的主要流程基本上撸完了,下面开始撸配置中心。本文从示例入手走查了客户端的初始化流程,Listener的注册逻辑和执行逻辑。

[[416108]]

引言

Nacos注册中心的主要流程基本上撸完了,下面开始撸配置中心。本文从示例入手走查了客户端的初始化流程,Listener的注册逻辑和执行逻辑。

一、内容提要

示例

  • 通过示例构建ConfigService、注册了Listener分析其流程

Client初始化概览

  • 支持多种获取server地址方式(本地、endpoint)
  • 支持多种namespace设置(本地、阿里云)
  • 支持超时时间、重试时间等参数设置
  • 支持用户名和密码验证
  • 长轮询会从BlockingQueue中获取元素,队列有元素立即执行executeConfigListen,队列无元素阻塞5秒钟执行executeConfigListen()

Listener注册逻辑

  • client添加Listener后会在cacheMap中缓存CacheData
  • cacheMap中key由「dataId+group+tenant」拼接而成
  • 每个CacheData会绑定注册的Listener列表
  • 每个CacheData会绑定taskId,3000个不同的CacheData对应一个taskId
  • 设置isSyncWithServer=false表示 cache md5 data不是来自server同步
  • BlockingQueue中添加new Object() 供长轮询判断立即执行使用

配置变更执行逻辑

  • 执行逻辑由executeConfigListen方法实现
  • 当CacheData从Server同步后,会校验md5是否变更了,变更则回调注册的Listener完成通知
  • 注册Listener后会构建与server的RPC通道rpcClient
  • 向server发起变更查询请求configChangeListenRequest
  • Server端通过比较缓存的md5值,返回client变更的key列表
  • Client通过变更的key列表向server发起配置查询请求ConfigQueryRequest
  • 获取变更内容,并回调注册的Listener完成通知
  • 回调注册的Listener是通过线程池异步执行Runnble Job实现的

二、示例

  1. @Test 
  2. public void test01() throws Exception { 
  3.   String serverAddr = "localhost:8848"
  4.   String dataId = "test"
  5.   String group = "DEFAULT_GROUP"
  6.   Properties properties = new Properties(); 
  7.   properties.put("serverAddr", serverAddr); 
  8.   // 构建ConfigService 
  9.   ConfigService configService = NacosFactory.createConfigService(properties); 
  10.   configService.addListener(dataId, group, new Listener() { 
  11.     @Override 
  12.     public void receiveConfigInfo(String configInfo) { 
  13.       System.out.println("receive:" + configInfo); 
  14.     } 
  15.  
  16.     @Override 
  17.     public Executor getExecutor() { 
  18.       return null
  19.     } 
  20.   }); 
  21.   System.in.read(); 

备注: 示例中构建了ConfigService,注入Listener接受server配置变更通知。

二、Client初始化概览

NacosConfigService构造方法

  1. public NacosConfigService(Properties properties) throws NacosException { 
  2.   ValidatorUtils.checkInitParam(properties); 
  3.   // 注解@1 
  4.   initNamespace(properties); 
  5.   // 注解@2 
  6.   ServerListManager serverListManager = new ServerListManager(properties); 
  7.   // 注解@3 
  8.   serverListManager.start(); 
  9.   // 注解@4 
  10.   this.worker = new ClientWorker(this.configFilterChainManager, serverListManager, properties); 
  11.   // 将被废弃HttpAgent,先忽略 
  12.   // will be deleted in 2.0 later versions 
  13.   agent = new ServerHttpAgent(serverListManager); 

注解@1 设置namespace可以通过properties.setProperty(PropertyKeyConst.NAMESPACE),代码中会兼容阿里云环境,在此忽略,默认为空。

注解@2 初始化namespace、server地址等信息

注解@3 启动主要用于endpoint方式定时获取server地址,当本地传入isFixed=true

注解@4 clientWorker初始化

ClientWorker初始化

  1. public ClientWorker(final ConfigFilterChainManager configFilterChainManager, ServerListManager serverListManager, 
  2.             final Properties properties) throws NacosException { 
  3.  
  4.   this.configFilterChainManager = configFilterChainManager; 
  5.  
  6.   // 注解@5 
  7.   init(properties); 
  8.  
  9.   // 注解@6 
  10.   agent = new ConfigRpcTransportClient(properties, serverListManager); 
  11.  
  12.   // 调度线程池,「处理器核数」 
  13.   ScheduledExecutorService executorService = Executors 
  14.     .newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() { 
  15.       @Override 
  16.       public Thread newThread(Runnable r) { 
  17.         Thread t = new Thread(r); 
  18.         t.setName("com.alibaba.nacos.client.Worker"); 
  19.         t.setDaemon(true); 
  20.         return t; 
  21.       } 
  22.     }); 
  23.   agent.setExecutor(executorService); 
  24.  
  25.   // 注解@7 
  26.   agent.start(); 
  27.  

注解@5 初始化超时时间、重试时间等

  1. private void init(Properties properties) { 
  2.  
  3.     // 超时时间,默认30秒 
  4.     timeout = Math.max(ConvertUtils.toInt(properties.getProperty(PropertyKeyConst.CONFIG_LONG_POLL_TIMEOUT), 
  5.             Constants.CONFIG_LONG_POLL_TIMEOUT), Constants.MIN_CONFIG_LONG_POLL_TIMEOUT); 
  6.  
  7.  
  8.     // 重试时间,默认2秒 
  9.     taskPenaltyTime = ConvertUtils 
  10.             .toInt(properties.getProperty(PropertyKeyConst.CONFIG_RETRY_TIME), Constants.CONFIG_RETRY_TIME); 
  11.  
  12.  
  13.     // 开启配置删除同步,默认false 
  14.     this.enableRemoteSyncConfig = Boolean 
  15.             .parseBoolean(properties.getProperty(PropertyKeyConst.ENABLE_REMOTE_SYNC_CONFIG)); 

注解@6 gRPC config agent初始化

  1. public ConfigTransportClient(Properties properties, ServerListManager serverListManager) { 
  2.  
  3.     // 默认编码UTF-8 
  4.     String encodeTmp = properties.getProperty(PropertyKeyConst.ENCODE); 
  5.     if (StringUtils.isBlank(encodeTmp)) { 
  6.         this.encode = Constants.ENCODE; 
  7.     } else { 
  8.         this.encode = encodeTmp.trim(); 
  9.     } 
  10.     // namespace租户,默认空 
  11.     this.tenant = properties.getProperty(PropertyKeyConst.NAMESPACE); 
  12.  
  13.     this.serverListManager = serverListManager; 
  14.  
  15.     // 用户名和密码验证 
  16.     this.securityProxy = new SecurityProxy(properties, 
  17.             ConfigHttpClientManager.getInstance().getNacosRestTemplate()); 
  18.  

注解@7 gRPC agent启动

  1. public void start() throws NacosException { 
  2.     // 简单用户名和密码验证 
  3.     if (securityProxy.isEnabled()) { 
  4.         securityProxy.login(serverListManager.getServerUrls()); 
  5.  
  6.         this.executor.scheduleWithFixedDelay(new Runnable() { 
  7.             @Override 
  8.             public void run() { 
  9.                 securityProxy.login(serverListManager.getServerUrls()); 
  10.             } 
  11.         }, 0, this.securityInfoRefreshIntervalMills, TimeUnit.MILLISECONDS); 
  12.  
  13.     } 
  14.  
  15.     startInternal(); 
  1. @Override 
  2. public void startInternal() throws NacosException { 
  3.     executor.schedule(new Runnable() { 
  4.         @Override 
  5.         public void run() { 
  6.             while (true) { // 一直运行 
  7.                 try { 
  8.                     // 最长等待5秒 
  9.                     listenExecutebell.poll(5L, TimeUnit.SECONDS); 
  10.                     executeConfigListen(); 
  11.                 } catch (Exception e) { 
  12.                     LOGGER.error("[ rpc listen execute ] [rpc listen] exception", e); 
  13.                 } 
  14.             } 
  15.         } 
  16.     }, 0L, TimeUnit.MILLISECONDS); 
  17.  

小结: 线程会一直运行,从BlockingQueue中获取元素。队里不为空,获取后立即执行executeConfigListen();队列为空等待5秒后执行

executeConfigListen()。

三、Listener注册逻辑

  1. configService.addListener(dataId, group, new Listener() { 
  2.     @Override 
  3.     public void receiveConfigInfo(String configInfo) { 
  4.         System.out.println("receive:" + configInfo); 
  5.     } 
  6.  
  7.     @Override 
  8.     public Executor getExecutor() { 
  9.         return null
  10.     } 
  11. }); 
  12. public void addTenantListeners(String dataId, String group, List<? extends Listener> listeners) 
  13.         throws NacosException { 
  14.  
  15.     // 默认DEFAULT_GROUP 
  16.     group = null2defaultGroup(group); 
  17.  
  18.     // 租户,默认空 
  19.     String tenant = agent.getTenant(); 
  20.  
  21.     // 注解@8 
  22.     CacheData cache = addCacheDataIfAbsent(dataId, group, tenant); 
  23.  
  24.     synchronized (cache) { 
  25.         for (Listener listener : listeners) { 
  26.             cache.addListener(listener); 
  27.         } 
  28.         // cache md5 data是否来自server同步 
  29.         cache.setSyncWithServer(false); 
  30.        // BlockingQueue中添加new Object() 
  31.         agent.notifyListenConfig(); 
  32.     } 
  33.   

注解@8 构建缓存数据CacheData并放入cacheMap中,缓存的key为 「dataId+group+tenant」例如:test+DEFAULT_GROUP。

每个CacheData会绑定对应的taskId,每3000个CacheData对应一个taskId。其实从后面的代码中可以看出,每个taskId会对应一个gRPC Client。

  1. public CacheData addCacheDataIfAbsent(String dataId, String group, String tenant) throws NacosException { 
  2.  
  3.     // 从缓存中获取 
  4.     CacheData cache = getCache(dataId, group, tenant); 
  5.     if (null != cache) { 
  6.         return cache; 
  7.     } 
  8.     // 构造缓存key以+连接,test+DEFAULT_GROUP 
  9.     String key = GroupKey.getKeyTenant(dataId, group, tenant); 
  10.     synchronized (cacheMap) { 
  11.         CacheData cacheFromMap = getCache(dataId, group, tenant); 
  12.         // multiple listeners on the same dataid+group and race condition,so 
  13.         // double check again 
  14.         // other listener thread beat me to set to cacheMap 
  15.         if (null != cacheFromMap) { // 再检查一遍 
  16.             cache = cacheFromMap; 
  17.             // reset so that server not hang this check 
  18.             cache.setInitializing(true); // 缓存正在初始化 
  19.         } else { 
  20.             // 构造缓存数据对象 
  21.             cache = new CacheData(configFilterChainManager, agent.getName(), dataId, group, tenant); 
  22.             // 初始值taskId=0,注意此处每3000个CacheData共用一个taskId 
  23.             int taskId = cacheMap.get().size() / (int) ParamUtil.getPerTaskConfigSize(); 
  24.             cache.setTaskId(taskId); 
  25.             // fix issue # 1317 
  26.             if (enableRemoteSyncConfig) { // 默认false 
  27.                 String[] ct = getServerConfig(dataId, group, tenant, 3000L, false); 
  28.                 cache.setContent(ct[0]); 
  29.             } 
  30.         } 
  31.         Map<String, CacheData> copy = new HashMap<String, CacheData>(this.cacheMap.get()); 
  32.         // key = test+DEFAULT_GROUP 
  33.         copy.put(key, cache); 
  34.         // cacheMap = {test+DEFAULT_GROUP=CacheData [test, DEFAULT_GROUP]} 
  35.         cacheMap.set(copy); 
  36.  
  37.     } 
  38.     LOGGER.info("[{}] [subscribe] {}", agent.getName(), key); 
  39.  
  40.     MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size()); 
  41.  
  42.     return cache; 
属性 含义
name ConfigTransportClient名称,config_rpc_client
configFilterChainManager filter拦截链条,可以执行一些列拦截器
dataId dataId
group group名称,默认为DEFAULT_GROUP
tenant 租户名称
listeners 添加的Listener列表,线程安全CopyOnWriteArrayList
content 启动时会从本地文件读入,默认为null
md5 content的md5字符串

小结:添加监听器逻辑如下:构建CacheData,并缓存在cacheMap中,key是由「dataId+group+tenant」组成;每个CacheData会绑定了Listener列表,也绑定了taskId,3000个不同的CacheData对应一个taskId,对应一个gRPC通道实例;设置isSyncWithServer=false表示 cache md5 data不是来自server同步,BlockingQueue中添加new Object() 供前面提到的长轮询判断使用。

四、配置变更执行逻辑

上文中提到一个线程一直在轮询,轮询执行executeConfigListen方法,这个方法比较关键。

  1. public void executeConfigListen() { 
  2.  
  3.     Map<String/*taskId*/, List<CacheData>> listenCachesMap = new HashMap<String, List<CacheData>>(16); 
  4.     Map<String, List<CacheData>> removeListenCachesMap = new HashMap<String, List<CacheData>>(16); 
  5.     long now = System.currentTimeMillis(); 
  6.     // 超过5分钟 
  7.     boolean needAllSync = now - lastAllSyncTime >= ALL_SYNC_INTERNAL; 
  8.     for (CacheData cache : cacheMap.get().values()) { 
  9.         synchronized (cache) { 
  10.             // --------注解@9开始-------- 
  11.             if (cache.isSyncWithServer()) {  
  12.                 cache.checkListenerMd5(); // 内容有变更通知Listener执行 
  13.                 if (!needAllSync) { // 不超过5分钟则不再全局校验 
  14.                     continue
  15.                 } 
  16.             } 
  17.       // --------注解@9结束-------- 
  18.             if (!CollectionUtils.isEmpty(cache.getListeners())) { // 有添加Listeners 
  19.                 // get listen config 默认 false 
  20.                 if (!cache.isUseLocalConfigInfo()) { 
  21.                     List<CacheData> cacheDatas = listenCachesMap.get(String.valueOf(cache.getTaskId())); 
  22.                     if (cacheDatas == null) { 
  23.                         cacheDatas = new LinkedList<CacheData>(); 
  24.                         listenCachesMap.put(String.valueOf(cache.getTaskId()), cacheDatas); 
  25.                     } 
  26.                     // CacheData [test, DEFAULT_GROUP] 
  27.                     cacheDatas.add(cache); 
  28.  
  29.                 } 
  30.             } else if (CollectionUtils.isEmpty(cache.getListeners())) { // 没有添加Listeners 
  31.  
  32.                 if (!cache.isUseLocalConfigInfo()) { 
  33.                     List<CacheData> cacheDatas = removeListenCachesMap.get(String.valueOf(cache.getTaskId())); 
  34.                     if (cacheDatas == null) { 
  35.                         cacheDatas = new LinkedList<CacheData>(); 
  36.                         removeListenCachesMap.put(String.valueOf(cache.getTaskId()), cacheDatas); 
  37.                     } 
  38.                     cacheDatas.add(cache); 
  39.  
  40.                 } 
  41.             } 
  42.         } 
  43.  
  44.     } 
  45.  
  46.     boolean hasChangedKeys = false
  47.    
  48.   //-------------------注解@10开始--------------------------------- 
  49.    
  50.     if (!listenCachesMap.isEmpty()) { // 有Listeners 
  51.         for (Map.Entry<String, List<CacheData>> entry : listenCachesMap.entrySet()) { 
  52.             String taskId = entry.getKey(); 
  53.             List<CacheData> listenCaches = entry.getValue(); 
  54.  
  55.             ConfigBatchListenRequest configChangeListenRequest = buildConfigRequest(listenCaches); 
  56.             configChangeListenRequest.setListen(true); 
  57.             try { 
  58.                 // 注解@10.1 每个taskId构建rpcClient 
  59.                 RpcClient rpcClient = ensureRpcClient(taskId); 
  60.                 // 注解@10.2 
  61.                 ConfigChangeBatchListenResponse configChangeBatchListenResponse = (ConfigChangeBatchListenResponse) requestProxy( 
  62.                         rpcClient, configChangeListenRequest); 
  63.                 if (configChangeBatchListenResponse != null && configChangeBatchListenResponse.isSuccess()) { 
  64.  
  65.                     Set<String> changeKeys = new HashSet<String>(); 
  66.                     // handle changed keys,notify listener 
  67.                     // 有变化的configContext 
  68.                     if (!CollectionUtils.isEmpty(configChangeBatchListenResponse.getChangedConfigs())) { 
  69.                         hasChangedKeys = true
  70.                         for (ConfigChangeBatchListenResponse.ConfigContext changeConfig : configChangeBatchListenResponse.getChangedConfigs()) { 
  71.                             String changeKey = GroupKey 
  72.                                     .getKeyTenant(changeConfig.getDataId(), changeConfig.getGroup(), 
  73.                                             changeConfig.getTenant()); 
  74.                             changeKeys.add(changeKey); 
  75.                             boolean isInitializing = cacheMap.get().get(changeKey).isInitializing(); 
  76.                             // 注解@10.3 回调Listener 
  77.                             refreshContentAndCheck(changeKey, !isInitializing); 
  78.                         } 
  79.  
  80.                     } 
  81.  
  82.                     //handler content configs 
  83.                     for (CacheData cacheData : listenCaches) { 
  84.                         String groupKey = GroupKey 
  85.                                 .getKeyTenant(cacheData.dataId, cacheData.group, cacheData.getTenant()); 
  86.                         if (!changeKeys.contains(groupKey)) { // 注解@10.4 
  87.                             //sync:cache data md5 = server md5 && cache data md5 = all listeners md5. 
  88.                             synchronized (cacheData) { 
  89.                                 if (!cacheData.getListeners().isEmpty()) { 
  90.                                     cacheData.setSyncWithServer(true); 
  91.                                     continue
  92.                                 } 
  93.                             } 
  94.                         } 
  95.  
  96.                         cacheData.setInitializing(false); 
  97.                     } 
  98.  
  99.                 } 
  100.             } catch (Exception e) { 
  101.  
  102.                 LOGGER.error("Async listen config change error ", e); 
  103.                 try { 
  104.                     Thread.sleep(50L); 
  105.                 } catch (InterruptedException interruptedException) { 
  106.                     //ignore 
  107.                 } 
  108.             } 
  109.         } 
  110.     } 
  111.  //-------------------注解@10结束--------------------------------- 
  112.     if (!removeListenCachesMap.isEmpty()) { 
  113.         for (Map.Entry<String, List<CacheData>> entry : removeListenCachesMap.entrySet()) { 
  114.             String taskId = entry.getKey(); 
  115.             List<CacheData> removeListenCaches = entry.getValue(); 
  116.             ConfigBatchListenRequest configChangeListenRequest = buildConfigRequest(removeListenCaches); 
  117.             configChangeListenRequest.setListen(false); 
  118.             try { 
  119.                 // 向server发送Listener取消订阅请求ConfigBatchListenRequest#listen为false 
  120.                 RpcClient rpcClient = ensureRpcClient(taskId); 
  121.                 boolean removeSuccess = unListenConfigChange(rpcClient, configChangeListenRequest); 
  122.                 if (removeSuccess) { 
  123.                     for (CacheData cacheData : removeListenCaches) { 
  124.                         synchronized (cacheData) { 
  125.                             if (cacheData.getListeners().isEmpty()) { 
  126.                                 // 移除本地缓存 
  127.                                 ClientWorker.this 
  128.                                         .removeCache(cacheData.dataId, cacheData.group, cacheData.tenant); 
  129.                             } 
  130.                         } 
  131.                     } 
  132.                 } 
  133.  
  134.             } catch (Exception e) { 
  135.                 LOGGER.error("async remove listen config change error ", e); 
  136.             } 
  137.             try { 
  138.                 Thread.sleep(50L); 
  139.             } catch (InterruptedException interruptedException) { 
  140.                 //ignore 
  141.             } 
  142.         } 
  143.     } 
  144.     if (needAllSync) { 
  145.         lastAllSyncTime = now; 
  146.     } 
  147.     //If has changed keys,notify re sync md5. 
  148.     if (hasChangedKeys) { // key有变化触发下一轮轮询 
  149.         notifyListenConfig(); 
  150.     } 

注解@9 isSyncWithServer初始为false,在下文代码中校验结束后会设置为true,表示md5 cache data同步来自server。如果为true会校验Md5.

  1. void checkListenerMd5() { 
  2.     for (ManagerListenerWrap wrap : listeners) { 
  3.         if (!md5.equals(wrap.lastCallMd5)) { // 注解@9.1 
  4.             safeNotifyListener(dataId, group, content, type, md5, wrap); 
  5.         } 
  6.     } 

注解@9.1 配置内容有变更时,回调到我们示例中注册的Listener中。

  1. private void safeNotifyListener(final String dataId, final String group, final String content, final String type, 
  2.         final String md5, final ManagerListenerWrap listenerWrap) { 
  3.     final Listener listener = listenerWrap.listener; 
  4.     if (listenerWrap.inNotifying) { 
  5.         // ... 
  6.         return
  7.     } 
  8.     Runnable job = new Runnable() { 
  9.         @Override 
  10.         public void run() { 
  11.             long start = System.currentTimeMillis(); 
  12.             ClassLoader myClassLoader = Thread.currentThread().getContextClassLoader(); 
  13.             ClassLoader appClassLoader = listener.getClass().getClassLoader(); 
  14.             try { 
  15.                 if (listener instanceof AbstractSharedListener) { 
  16.                     AbstractSharedListener adapter = (AbstractSharedListener) listener; 
  17.                     adapter.fillContext(dataId, group); 
  18.                    // ... 
  19.                 } 
  20.                 Thread.currentThread().setContextClassLoader(appClassLoader); 
  21.  
  22.                 ConfigResponse cr = new ConfigResponse(); 
  23.                 cr.setDataId(dataId); 
  24.                 cr.setGroup(group); 
  25.                 cr.setContent(content); 
  26.                 // filter拦截继续过滤 
  27.                 configFilterChainManager.doFilter(null, cr); 
  28.  
  29.                 String contentTmp = cr.getContent(); 
  30.                 listenerWrap.inNotifying = true
  31.  
  32.                 // 注解@9.2 
  33.                 listener.receiveConfigInfo(contentTmp); 
  34.                 // compare lastContent and content 
  35.                 if (listener instanceof AbstractConfigChangeListener) { 
  36.                     Map data = ConfigChangeHandler.getInstance() 
  37.                             .parseChangeData(listenerWrap.lastContent, content, type); 
  38.                     ConfigChangeEvent event = new ConfigChangeEvent(data); 
  39.                     // 回调变更事件方法 
  40.                     ((AbstractConfigChangeListener) listener).receiveConfigChange(event); 
  41.                     listenerWrap.lastContent = content; 
  42.                 } 
  43.  
  44.                 listenerWrap.lastCallMd5 = md5; 
  45.                 // .. 
  46.             } catch (NacosException ex) { 
  47.                // ... 
  48.             } catch (Throwable t) { 
  49.                // ... 
  50.             } finally { 
  51.                 listenerWrap.inNotifying = false
  52.                 Thread.currentThread().setContextClassLoader(myClassLoader); 
  53.             } 
  54.         } 
  55.     }; 
  56.  
  57.     final long startNotify = System.currentTimeMillis(); 
  58.     try { 
  59.        // 注解@9.3 
  60.         if (null != listener.getExecutor()) { 
  61.             listener.getExecutor().execute(job); 
  62.         } else { 
  63.             try { 
  64.                 INTERNAL_NOTIFIER.submit(job); // 默认线程池执行,为5个线程 
  65.             } catch (RejectedExecutionException rejectedExecutionException) { 
  66.                 // ... 
  67.                 job.run(); 
  68.             } catch (Throwable throwable) { 
  69.                 // ... 
  70.                 job.run(); 
  71.             } 
  72.         } 
  73.     } catch (Throwable t) { 
  74.        // ... 
  75.     } 
  76.     final long finishNotify = System.currentTimeMillis(); 
  77.     // ... 

注解@9.2 回调注册Listener的receiveConfigInfo方法或者receiveConfigChange逻辑

注解@9.3 优先使用我们示例中注册提供的线程池执行job,如果没有设置使用默认线程池「INTERNAL_NOTIFIER」,默认5个线程

备注: 当CacheData从server同步后,会校验md5是否变更了,当变更时会回调到我们注册的Listener完成通知。通知任务被封装成Runnable任务,执行线程池可以自定义,默认为5个线程。

注解@10.1 每个taskId构建rpcClient,例如:taskId= config-0-c70e0314-4770-43f5-add4-f258a4083fd7;结合上下文每3000个CacheData对应一个rpcClient。

注解@10.2 向server发起configChangeListenRequest,server端由ConfigChangeBatchListenRequestHandler处理,还是比较md5

是否变更了,变更后server端返回变更的key列表。

注解@10.3 当server返回变更key列表时执行refreshContentAndCheck方法。

  1. private void refreshContentAndCheck(CacheData cacheData, boolean notify) { 
  2.     try { 
  3.         // 注解@10.3.1 
  4.         String[] ct = getServerConfig(cacheData.dataId, cacheData.group, cacheData.tenant, 3000L, notify); 
  5.         cacheData.setContent(ct[0]); 
  6.         if (null != ct[1]) { 
  7.             cacheData.setType(ct[1]); 
  8.         } 
  9.         if (notify) { // 记录日志 
  10.            // ... 
  11.         } 
  12.         // 注解@10.3.2 
  13.         cacheData.checkListenerMd5(); 
  14.     } catch (Exception e) { 
  15.         //... 
  16.     } 

注解@10.3.1 向server发起ConfigQueryRequest,查询配置内容

注解@10.3.2 回调注册的Listener逻辑见 注解@9

注解@10.4 key没有变化的,内容由server同步,设置SyncWithServer=true,下一轮逻辑会由 注解@9 部分执行

备注: 从整个注解@10 注册Listener后,会构建与server的RPC通道rpcClient;向server发起变更查询请求configChangeListenRequest,server端通过比较缓存的md5值,返回client变更的key列表;client通过变更的key列表向server发起配置查询请求ConfigQueryRequest,获取变更内容,并回调我们注册的Listener。

本文转载自微信公众号「瓜农老梁」,可以通过以下二维码关注。转载本文请联系瓜农老梁公众号。

 

责任编辑:武晓燕 来源: 瓜农老梁
相关推荐

2022-10-20 07:39:26

2022-08-26 09:29:01

Kubernetes策略Master

2021-06-09 19:23:52

MySQLROLE管理

2022-02-21 08:48:00

Pulsar部署配置

2011-03-24 13:00:31

配置nagios客户端

2022-02-07 11:01:23

ZooKeeper

2022-01-02 08:43:46

Python

2022-04-12 08:30:52

回调函数代码调试

2021-10-27 09:59:35

存储

2023-03-13 21:38:08

TCP数据IP地址

2021-07-16 22:43:10

Go并发Golang

2021-09-28 08:59:30

复原IP地址

2023-11-01 09:07:01

Spring装配源码

2021-07-02 08:51:29

源码参数Thread

2021-10-14 10:22:19

逃逸JVM性能

2021-10-29 07:35:32

Linux 命令系统

2021-04-29 10:18:18

循环依赖数组

2022-11-14 08:17:56

2022-03-11 10:21:30

IO系统日志

2021-05-11 08:54:59

建造者模式设计
点赞
收藏

51CTO技术栈公众号