精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Nacos 配置中心源碼分析

開發 前端
本文主要和大家一起以源碼的角度來分析 Nacos 配置中心的配置信息獲取,以及配置信息動態同步的過程和原理。

[[416104]]

本文主要和大家一起以源碼的角度來分析 Nacos 配置中心的配置信息獲取,以及配置信息動態同步的過程和原理。環境介紹和使用 環境介紹:

  • Jdk 1.8
  • nacos-server-1.4.2
  • spring-boot-2.3.5.RELEASE
  • spring-cloud-Hoxton.SR8
  • spring-cloiud-alibab-2.2.5.RELEASE

如果我們需要使用 Nacos 作為配置中心,我們首先需要導入 Nacos Config 的依賴信息,如下所示:

  1. <dependency> 
  2.   <groupId>com.alibaba.cloud</groupId> 
  3.   <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> 
  4. </dependency> 

然后再 bootstartp.yml 文件中配置 Nacos 服務信息。

  1. spring: 
  2.   cloud: 
  3.     nacos: 
  4.       config: 
  5.         server-addr: 127.0.0.1:8848 

客戶端初始化

主要是通過 NacosConfigBootstrapConfiguration 類來進行初始化 NacosConfigManager 、NacosPropertySourceLocator

  1. @Configuration(proxyBeanMethods = false
  2. @ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", matchIfMissing = true
  3. public class NacosConfigBootstrapConfiguration { 
  4.  
  5.  @Bean 
  6.  @ConditionalOnMissingBean 
  7.  public NacosConfigManager nacosConfigManager( 
  8.    NacosConfigProperties nacosConfigProperties) { 
  9.   return new NacosConfigManager(nacosConfigProperties); 
  10.  } 
  11.      
  12.     @Bean 
  13.  public NacosPropertySourceLocator nacosPropertySourceLocator( 
  14.    NacosConfigManager nacosConfigManager) { 
  15.   return new NacosPropertySourceLocator(nacosConfigManager); 
  16.  } 
  17.     // ... 

在 NacosConfigManager 的構造方法中會調用 createConfigService 方法來創建 ConfigService 實例,內部調用工廠方法 ConfigFactory#createConfigService 通過反射實例化一個com.alibaba.nacos.client.config.NacosConfigService 的實例對象。

  1. public static ConfigService createConfigService(Properties properties) throws NacosException { 
  2.     try { 
  3.         Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.config.NacosConfigService"); 
  4.         Constructor constructor = driverImplClass.getConstructor(Properties.class); 
  5.         ConfigService vendorImpl = (ConfigService) constructor.newInstance(properties); 
  6.         return vendorImpl; 
  7.     } catch (Throwable e) { 
  8.         throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e); 
  9.     } 

NacosPropertySourceLocator 繼承 PropertySourceLocator(PropertySourceLocator接口支持擴展自定義配置加載到 Spring Environment中)通過 locate 加載配置信息。

  1. @Override 
  2. public PropertySource<?> locate(Environment env) { 
  3.  nacosConfigProperties.setEnvironment(env); 
  4.  ConfigService configService = nacosConfigManager.getConfigService(); 
  5.  
  6.  if (null == configService) { 
  7.   log.warn("no instance of config service found, can't load config from nacos"); 
  8.   return null
  9.  } 
  10.  long timeout = nacosConfigProperties.getTimeout(); 
  11.  nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService, 
  12.    timeout); 
  13.  String name = nacosConfigProperties.getName(); 
  14.  
  15.  String dataIdPrefix = nacosConfigProperties.getPrefix(); 
  16.  if (StringUtils.isEmpty(dataIdPrefix)) { 
  17.   dataIdPrefix = name
  18.  } 
  19.  
  20.  if (StringUtils.isEmpty(dataIdPrefix)) { 
  21.   dataIdPrefix = env.getProperty("spring.application.name"); 
  22.  } 
  23.  
  24.  CompositePropertySource composite = new CompositePropertySource( 
  25.    NACOS_PROPERTY_SOURCE_NAME); 
  26.  
  27.        // 共享配置 
  28.  loadSharedConfiguration(composite); 
  29.  // 拓展配置 
  30.        loadExtConfiguration(composite); 
  31.  // 應用配置 
  32.        loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env); 
  33.  return composite; 

配置讀取過程

配置加載有三個方法 loadSharedConfiguration、loadSharedConfiguration、 loadApplicationConfiguration 以 loadApplicationConfiguration 繼續跟進。

  1. private void loadApplicationConfiguration( 
  2.     CompositePropertySource compositePropertySource, String dataIdPrefix, 
  3.     NacosConfigProperties properties, Environment environment) { 
  4.     String fileExtension = properties.getFileExtension(); 
  5.     String nacosGroup = properties.getGroup(); 
  6.     // load directly once by default 
  7.     loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup, 
  8.                            fileExtension, true); 
  9.     // load with suffix, which have a higher priority than the default 
  10.     loadNacosDataIfPresent(compositePropertySource, 
  11.                            dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true); 
  12.     // Loaded with profile, which have a higher priority than the suffix 
  13.     for (String profile : environment.getActiveProfiles()) { 
  14.         String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension; 
  15.         loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup, 
  16.                                fileExtension, true); 
  17.     } 
  18.  

主要通過 loadNacosDataIfPresent 讀取配置信息, 其實我們可以通過參數看出,主要配置文件包含以下部分:dataId, group, fileExtension

  1. private void loadNacosDataIfPresent(final CompositePropertySource composite, 
  2.                                     final String dataId, final String group, String fileExtension, 
  3.                                     boolean isRefreshable) { 
  4.     if (null == dataId || dataId.trim().length() < 1) { 
  5.         return
  6.     } 
  7.     if (null == group || group.trim().length() < 1) { 
  8.         return
  9.     } 
  10.     NacosPropertySource propertySource = this.loadNacosPropertySource(dataId, group
  11.                                                                       fileExtension, isRefreshable); 
  12.     this.addFirstPropertySource(composite, propertySource, false); 

然后調用 loadNacosPropertySource 最后一步步的會調用到 NacosConfigService#getConfigInner

  1. private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException { 
  2.         group = null2defaultGroup(group); 
  3.         ParamUtils.checkKeyParam(dataId, group); 
  4.         ConfigResponse cr = new ConfigResponse(); 
  5.          
  6.         cr.setDataId(dataId); 
  7.         cr.setTenant(tenant); 
  8.         cr.setGroup(group); 
  9.          
  10.         // 優先使用本地配置 
  11.         String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant); 
  12.         if (content != null) { 
  13.             LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}, config={}", agent.getName(), 
  14.                     dataId, group, tenant, ContentUtils.truncateContent(content)); 
  15.             cr.setContent(content); 
  16.             configFilterChainManager.doFilter(null, cr); 
  17.             content = cr.getContent(); 
  18.             return content; 
  19.         } 
  20.          
  21.         try { 
  22.             // 獲取遠程配置 
  23.             String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs); 
  24.             cr.setContent(ct[0]); 
  25.              
  26.             configFilterChainManager.doFilter(null, cr); 
  27.             content = cr.getContent(); 
  28.              
  29.             return content; 
  30.         } catch (NacosException ioe) { 
  31.             if (NacosException.NO_RIGHT == ioe.getErrCode()) { 
  32.                 throw ioe; 
  33.             } 
  34.             LOGGER.warn("[{}] [get-config] get from server error, dataId={}, group={}, tenant={}, msg={}"
  35.                     agent.getName(), dataId, group, tenant, ioe.toString()); 
  36.         } 
  37.          
  38.         LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}, config={}", agent.getName(), 
  39.                 dataId, group, tenant, ContentUtils.truncateContent(content)); 
  40.         content = LocalConfigInfoProcessor.getSnapshot(agent.getName(), dataId, group, tenant); 
  41.         cr.setContent(content); 
  42.         configFilterChainManager.doFilter(null, cr); 
  43.         content = cr.getContent(); 
  44.         return content; 
  45.     } 

加載遠程配置

worker.getServerConfig 主要是獲取遠程配置, ClIentWorker 的 getServerConfig 定義如下:

  1. public String[] getServerConfig(String dataId, String group, String tenant, long readTimeout) 
  2.     throws NacosException { 
  3.     String[] ct = new String[2]; 
  4.     if (StringUtils.isBlank(group)) { 
  5.         group = Constants.DEFAULT_GROUP; 
  6.     } 
  7.  
  8.     HttpRestResult<String> result = null
  9.     try { 
  10.         Map<String, String> params = new HashMap<String, String>(3); 
  11.         if (StringUtils.isBlank(tenant)) { 
  12.             params.put("dataId", dataId); 
  13.             params.put("group"group); 
  14.         } else { 
  15.             params.put("dataId", dataId); 
  16.             params.put("group"group); 
  17.             params.put("tenant", tenant); 
  18.         } 
  19.         result = agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout); 
  20.     } catch (Exception ex) { 
  21.         String message = String 
  22.             .format("[%s] [sub-server] get server config exception, dataId=%s, group=%s, tenant=%s"
  23.                     agent.getName(), dataId, group, tenant); 
  24.         LOGGER.error(message, ex); 
  25.         throw new NacosException(NacosException.SERVER_ERROR, ex); 
  26.     } 
  27.  
  28.     switch (result.getCode()) { 
  29.         case HttpURLConnection.HTTP_OK: 
  30.             LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, result.getData()); 
  31.             ct[0] = result.getData(); 
  32.             if (result.getHeader().getValue(CONFIG_TYPE) != null) { 
  33.                 ct[1] = result.getHeader().getValue(CONFIG_TYPE); 
  34.             } else { 
  35.                 ct[1] = ConfigType.TEXT.getType(); 
  36.             } 
  37.             return ct; 
  38.         case HttpURLConnection.HTTP_NOT_FOUND: 
  39.             LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, null); 
  40.             return ct; 
  41.         case HttpURLConnection.HTTP_CONFLICT: { 
  42.             LOGGER.error( 
  43.                 "[{}] [sub-server-error] get server config being modified concurrently, dataId={}, group={}, " 
  44.                 + "tenant={}", agent.getName(), dataId, group, tenant); 
  45.             throw new NacosException(NacosException.CONFLICT, 
  46.                                      "data being modified, dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); 
  47.         } 
  48.         case HttpURLConnection.HTTP_FORBIDDEN: { 
  49.             LOGGER.error("[{}] [sub-server-error] no right, dataId={}, group={}, tenant={}", agent.getName(), 
  50.                          dataId, group, tenant); 
  51.             throw new NacosException(result.getCode(), result.getMessage()); 
  52.         } 
  53.         default: { 
  54.             LOGGER.error("[{}] [sub-server-error]  dataId={}, group={}, tenant={}, code={}", agent.getName(), 
  55.                          dataId, group, tenant, result.getCode()); 
  56.             throw new NacosException(result.getCode(), 
  57.                                      "http error, code=" + result.getCode() + ",dataId=" + dataId + ",group=" + group + ",tenant=" 
  58.                                      + tenant); 
  59.         } 
  60.     } 

agent 默認使用 MetricsHttpAgent 實現類

配置同步過程

Nacos 配置同步過程如下圖所示:

客戶端請求

客戶端初始請求配置完成后,會通過 WorkClient 進行長輪詢查詢配置, 它的構造方法如下:

  1. public ClientWorker(final HttpAgent agent, final ConfigFilterChainManager configFilterChainManager, 
  2.             final Properties properties) { 
  3.         this.agent = agent; 
  4.         this.configFilterChainManager = configFilterChainManager; 
  5.          
  6.         // Initialize the timeout parameter 
  7.          
  8.         init(properties); 
  9.  
  10.         // 檢查線程池 
  11.         this.executor = Executors.newScheduledThreadPool(1, new ThreadFactory() { 
  12.             @Override 
  13.             public Thread newThread(Runnable r) { 
  14.                 Thread t = new Thread(r); 
  15.                 t.setName("com.alibaba.nacos.client.Worker." + agent.getName()); 
  16.                 t.setDaemon(true); 
  17.                 return t; 
  18.             } 
  19.         }); 
  20.                  
  21.         // 長輪詢線程 
  22.         this.executorService = Executors 
  23.                 .newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() { 
  24.                     @Override 
  25.                     public Thread newThread(Runnable r) { 
  26.                         Thread t = new Thread(r); 
  27.                         t.setName("com.alibaba.nacos.client.Worker.longPolling." + agent.getName()); 
  28.                         t.setDaemon(true); 
  29.                         return t; 
  30.                     } 
  31.                 }); 
  32.          
  33.         this.executor.scheduleWithFixedDelay(new Runnable() { 
  34.             @Override 
  35.             public void run() { 
  36.                 try { 
  37.                     checkConfigInfo(); 
  38.                 } catch (Throwable e) { 
  39.                     LOGGER.error("[" + agent.getName() + "] [sub-check] rotate check error", e); 
  40.                 } 
  41.             } 
  42.         }, 1L, 10L, TimeUnit.MILLISECONDS); 
  43.     } 

這里初始化了兩個線程池:

  • 第一個線程池主要是用來初始化做長輪詢的;
  • 第二個線程池使用來做檢查的,會每間隔 10 秒鐘執行一次檢查方法 checkConfigInfo

checkConfigInfo

在這個方法里面主要是分配任務,給每個 task 分配一個 taskId , 后面會去檢查本地配置和遠程配置,最終調用的是 LongPollingRunable 的 run 方法。

  1. public void checkConfigInfo() { 
  2.     // Dispatch taskes. 
  3.     int listenerSize = cacheMap.size(); 
  4.     // Round up the longingTaskCount. 
  5.     int longingTaskCount = (int) Math.ceil(listenerSize / ParamUtil.getPerTaskConfigSize()); 
  6.     if (longingTaskCount > currentLongingTaskCount) { 
  7.         for (int i = (int) currentLongingTaskCount; i < longingTaskCount; i++) { 
  8.             // The task list is no order.So it maybe has issues when changing. 
  9.             executorService.execute(new LongPollingRunnable(i)); 
  10.         } 
  11.         currentLongingTaskCount = longingTaskCount; 
  12.     } 

LongPollingRunnable

長輪詢線程實現,首先第一步檢查本地配置信息,然后通過 dataId 去檢查服務端是否有變動的配置信息,如果有就更新下來然后刷新配置。

  1. public void run() { 
  2.  
  3.         List<CacheData> cacheDatas = new ArrayList<CacheData>(); 
  4.         List<String> inInitializingCacheList = new ArrayList<String>(); 
  5.         try { 
  6.             // check failover config 
  7.             for (CacheData cacheData : cacheMap.values()) { 
  8.                 if (cacheData.getTaskId() == taskId) { 
  9.                     cacheDatas.add(cacheData); 
  10.                     try { 
  11.                         checkLocalConfig(cacheData); 
  12.                         if (cacheData.isUseLocalConfigInfo()) { 
  13.                             // 觸發回調 
  14.                             cacheData.checkListenerMd5(); 
  15.                         } 
  16.                     } catch (Exception e) { 
  17.                         LOGGER.error("get local config info error", e); 
  18.                     } 
  19.                 } 
  20.             } 
  21.  
  22.             // check server config 
  23.             List<String> changedGroupKeys = checkUpdateDataIds(cacheDatas, inInitializingCacheList); 
  24.             if (!CollectionUtils.isEmpty(changedGroupKeys)) { 
  25.                 LOGGER.info("get changedGroupKeys:" + changedGroupKeys); 
  26.             } 
  27.  
  28.             for (String groupKey : changedGroupKeys) { 
  29.                 String[] key = GroupKey.parseKey(groupKey); 
  30.                 String dataId = key[0]; 
  31.                 String group = key[1]; 
  32.                 String tenant = null
  33.                 if (key.length == 3) { 
  34.                     tenant = key[2]; 
  35.                 } 
  36.                 try { 
  37.                     String[] ct = getServerConfig(dataId, group, tenant, 3000L); 
  38.                     CacheData cache = cacheMap.get(GroupKey.getKeyTenant(dataId, group, tenant)); 
  39.                     cache.setContent(ct[0]); 
  40.                     if (null != ct[1]) { 
  41.                         cache.setType(ct[1]); 
  42.                     } 
  43.                     LOGGER.info("[{}] [data-received] dataId={}, group={}, tenant={}, md5={}, content={}, type={}"
  44.                                 agent.getName(), dataId, group, tenant, cache.getMd5(), 
  45.                                 ContentUtils.truncateContent(ct[0]), ct[1]); 
  46.                 } catch (NacosException ioe) { 
  47.                     String message = String 
  48.                         .format("[%s] [get-update] get changed config exception. dataId=%s, group=%s, tenant=%s"
  49.                                 agent.getName(), dataId, group, tenant); 
  50.                     LOGGER.error(message, ioe); 
  51.                 } 
  52.             } 
  53.             for (CacheData cacheData : cacheDatas) { 
  54.                 if (!cacheData.isInitializing() || inInitializingCacheList 
  55.                     .contains(GroupKey.getKeyTenant(cacheData.dataId, cacheData.group, cacheData.tenant))) { 
  56.                     cacheData.checkListenerMd5(); 
  57.                     cacheData.setInitializing(false); 
  58.                 } 
  59.             } 
  60.             inInitializingCacheList.clear(); 
  61.  
  62.             executorService.execute(this); 
  63.  
  64.         } catch (Throwable e) { 
  65.  
  66.             // If the rotation training task is abnormal, the next execution time of the task will be punished 
  67.             LOGGER.error("longPolling error : ", e); 
  68.             executorService.schedule(this, taskPenaltyTime, TimeUnit.MILLISECONDS); 
  69.         } 
  70.     } 

addTenantListeners

添加監聽,這里主要是通過 dataId , group 來獲取 cache 本地緩存的配置信息,然后再將 Listener 也傳給 cache 統一管理。

  1. public void addTenantListeners(String dataId, String group, List<? extends Listener> listeners) 
  2.         throws NacosException { 
  3.     group = null2defaultGroup(group); 
  4.     String tenant = agent.getTenant(); 
  5.     CacheData cache = addCacheDataIfAbsent(dataId, group, tenant); 
  6.     for (Listener listener : listeners) { 
  7.         cache.addListener(listener); 
  8.     } 

回調觸發

如果 md5 值發生變化過后就會調用 safeNotifyListener 方法然后將配置信息發送給對應的監聽器

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

服務端響應

當服務端收到請求后,會 hold 住當前請求,如果有變化就返回,如果沒有變化就等待超時之前返回無變化。

  1. /** 
  2.  * The client listens for configuration changes. 
  3.  */ 
  4. @PostMapping("/listener"
  5. @Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class) 
  6. public void listener(HttpServletRequest request, HttpServletResponse response) 
  7.     throws ServletException, IOException { 
  8.     request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED"true); 
  9.     String probeModify = request.getParameter("Listening-Configs"); 
  10.     if (StringUtils.isBlank(probeModify)) { 
  11.         throw new IllegalArgumentException("invalid probeModify"); 
  12.     } 
  13.  
  14.     probeModify = URLDecoder.decode(probeModify, Constants.ENCODE); 
  15.  
  16.     Map<String, String> clientMd5Map; 
  17.     try { 
  18.         clientMd5Map = MD5Util.getClientMd5Map(probeModify); 
  19.     } catch (Throwable e) { 
  20.         throw new IllegalArgumentException("invalid probeModify"); 
  21.     } 
  22.  
  23.     // do long-polling 
  24.     inner.doPollingConfig(request, response, clientMd5Map, probeModify.length()); 

LongPollingService

核心處理類 LongPollingService

  1. /** 
  2.    * Add LongPollingClient. 
  3.    * 
  4.    * @param req              HttpServletRequest. 
  5.    * @param rsp              HttpServletResponse. 
  6.    * @param clientMd5Map     clientMd5Map. 
  7.    * @param probeRequestSize probeRequestSize. 
  8.    */ 
  9.   public void addLongPollingClient(HttpServletRequest req, HttpServletResponse rsp, Map<String, String> clientMd5Map, 
  10.           int probeRequestSize) { 
  11.        
  12.       String str = req.getHeader(LongPollingService.LONG_POLLING_HEADER); 
  13.       String noHangUpFlag = req.getHeader(LongPollingService.LONG_POLLING_NO_HANG_UP_HEADER); 
  14.       String appName = req.getHeader(RequestUtil.CLIENT_APPNAME_HEADER); 
  15.       String tag = req.getHeader("Vipserver-Tag"); 
  16.       int delayTime = SwitchService.getSwitchInteger(SwitchService.FIXED_DELAY_TIME, 500); 
  17.        
  18.       // Add delay time for LoadBalance, and one response is returned 500 ms in advance to avoid client timeout. 
  19.       long timeout = Math.max(10000, Long.parseLong(str) - delayTime); 
  20.       if (isFixedPolling()) { 
  21.           timeout = Math.max(10000, getFixedPollingInterval()); 
  22.           // Do nothing but set fix polling timeout. 
  23.       } else { 
  24.           long start = System.currentTimeMillis(); 
  25.           List<String> changedGroups = MD5Util.compareMd5(req, rsp, clientMd5Map); 
  26.           if (changedGroups.size() > 0) { 
  27.               generateResponse(req, rsp, changedGroups); 
  28.               LogUtil.CLIENT_LOG.info("{}|{}|{}|{}|{}|{}|{}", System.currentTimeMillis() - start, "instant"
  29.                       RequestUtil.getRemoteIp(req), "polling", clientMd5Map.size(), probeRequestSize, 
  30.                       changedGroups.size()); 
  31.               return
  32.           } else if (noHangUpFlag != null && noHangUpFlag.equalsIgnoreCase(TRUE_STR)) { 
  33.               LogUtil.CLIENT_LOG.info("{}|{}|{}|{}|{}|{}|{}", System.currentTimeMillis() - start, "nohangup"
  34.                       RequestUtil.getRemoteIp(req), "polling", clientMd5Map.size(), probeRequestSize, 
  35.                       changedGroups.size()); 
  36.               return
  37.           } 
  38.       } 
  39.       String ip = RequestUtil.getRemoteIp(req); 
  40.        
  41.       // Must be called by http thread, or send response. 
  42.       final AsyncContext asyncContext = req.startAsync(); 
  43.        
  44.       // AsyncContext.setTimeout() is incorrect, Control by oneself 
  45.       asyncContext.setTimeout(0L); 
  46.        
  47.       ConfigExecutor.executeLongPolling( 
  48.               new ClientLongPolling(asyncContext, clientMd5Map, ip, probeRequestSize, timeout, appName, tag)); 
  49.   } 

參考鏈接

  • https://blog.csdn.net/jason_jiahongfei/article/details/108373442
  • https://www.cnblogs.com/lockedsher/articles/14447700.html

本文轉載自微信公眾號「運維開發故事」,可以通過以下二維碼關注。轉載本文請聯系運維開發故事公眾號。

 

責任編輯:姜華 來源: 運維開發故事
相關推薦

2023-11-17 09:02:51

Nacos配置中心

2021-06-10 06:57:39

Nacos配置模塊

2021-07-12 08:00:21

Nacos 服務注冊源碼分析

2022-06-13 09:58:06

NacosSpring

2024-12-10 08:27:28

2021-06-29 07:04:38

Nacos服務配置

2022-08-29 06:27:15

Nacos微服務

2022-08-30 22:12:19

Nacos組件服務注冊

2023-08-03 08:51:07

2021-08-02 07:35:19

Nacos配置中心namespace

2021-08-10 07:00:00

Nacos Clien服務分析

2021-07-16 06:56:50

Nacos注冊源碼

2021-07-02 22:23:50

Nacos配置模型

2021-02-10 09:54:15

分布式NacosApollo

2021-03-15 06:24:22

Nacos集群搭建微服務

2021-08-27 07:47:07

Nacos灰度源碼

2023-03-01 08:15:10

NginxNacos

2021-08-04 11:54:25

Nacos注冊中心設計

2024-12-27 12:10:58

2023-10-30 09:35:01

注冊中心微服務
點贊
收藏

51CTO技術棧公眾號

欧产日产国产69| 999热精品视频| 国产精品一级伦理| 九一久久久久久| 欧美韩日一区二区| 成年人在线观看av| 国产香蕉久久| 亚洲午夜视频在线| 日韩理论片在线观看| 91精品国产乱码久久久| 韩国欧美一区| 亚洲最大在线视频| 又色又爽又黄18网站| 二区三区不卡| 一区二区在线免费观看| 欧美日韩在线一区二区三区| 国产精品久久久久久免费| 亚洲高清二区| 久久精品精品电影网| 97人妻精品一区二区三区免费| 九九热线视频只有这里最精品| 亚洲精品视频一区| 日韩欧美一区二区三区四区| 亚洲国产精品欧美久久| 日韩高清不卡一区| 久久人人爽国产| 97在线观看免费高| 欧美男同视频网| 精品久久久久久久久久久久久久久| 成人免费无码av| 欧美性video| 国产精品欧美久久久久无广告 | 亚洲乱码国产乱码精品天美传媒| 成人毛片在线精品国产| 另类小说综合欧美亚洲| 欧美一区二区三区艳史| 青青草手机在线视频| 成人在线免费观看91| 亚洲国产精品va在线看黑人| 中文字幕在线视频一区二区| 精品视频一区二区三区四区五区| 亚洲成人自拍偷拍| 中国黄色录像片| 男人的天堂在线视频免费观看| 久久久国产精品午夜一区ai换脸| 国产精品久久久久久久久久久久冷| 一区二区美女视频| 蜜臀av在线播放一区二区三区| 91精品国产自产91精品| 免费在线视频观看| 欧美久久一区| 欧美老女人xx| 久草视频在线资源| 欧美一区网站| 欧美片一区二区三区| 成人免费精品动漫网站| 久久精品亚洲欧美日韩精品中文字幕| 一区二区亚洲精品国产| 极品人妻videosss人妻| 国产精品密蕾丝视频下载| 亚洲人成网在线播放| 91视频啊啊啊| 美女久久99| 亚洲香蕉在线观看| 一级黄色录像毛片| 欧美国产偷国产精品三区| 深夜福利一区二区| 情侣偷拍对白清晰饥渴难耐| 欧美成人激情| 欧美精品免费在线观看| 久久免费精彩视频| 亚洲激情在线| 欧美最猛性xxxx| 中文字幕在线日本| 久久机这里只有精品| 成人av.网址在线网站| 国产精品免费无遮挡| 国产成人三级在线观看| 国产在线一区二区三区四区| 美女毛片在线看| 国产精品免费视频观看| 欧美 日韩 国产 在线观看| 羞羞视频在线免费国产| 五月天中文字幕一区二区| 欧美国产亚洲一区| 国产亚洲人成a在线v网站| 欧美一卡二卡在线| 欧美双性人妖o0| 国产欧美久久一区二区三区| 日韩中文在线不卡| 国产一级视频在线| 狂野欧美一区| 91国产在线播放| 欧美成人综合在线| 中文字幕日本乱码精品影院| 国产精品成人久久电影| 8av国产精品爽爽ⅴa在线观看 | 91av中文字幕| 中文在线字幕免费观| 风间由美一区二区三区在线观看| 久久另类ts人妖一区二区| 97视频在线观看网站| 一区二区三区精品久久久| 日韩a在线播放| 日韩精品中文字幕吗一区二区| 日韩电视剧免费观看网站| 亚洲天堂精品一区| 亚洲全部视频| 91免费看片网站| 欧美视频综合| 一区二区三区免费看视频| 99草草国产熟女视频在线| 国产色99精品9i| 亚洲一级黄色片| 久久久夜色精品| 美女免费视频一区二区| 久久96国产精品久久99软件| www.在线视频| 欧美午夜精品一区| 亚洲精品中文字幕在线播放| 一本到12不卡视频在线dvd| 日本高清久久天堂| 丰满人妻av一区二区三区| 亚洲国产精品成人综合| 欧美,日韩,国产在线| 宅男噜噜噜66国产精品免费| 亚洲一区二区福利| 国产又黄又粗又爽| www.99精品| 日韩中文字幕在线不卡| 日本久久一区| 国产午夜精品全部视频在线播放 | 色噜噜狠狠成人网p站| www.四虎精品| 欧美黄免费看| 亚洲字幕一区二区| 黄色网页网址在线免费| 欧美日韩一卡二卡| 超碰人人干人人| 日本中文一区二区三区| 欧美成ee人免费视频| 2020国产在线| 亚洲电影中文字幕| 国产精品99精品| 丁香六月综合激情| 黄色成人在线免费观看| 国产视频网站一区二区三区| 久久黄色av网站| 国产又大又粗又长| 国产精品美女久久久久久 | 亚洲www免费| 亚洲三级免费看| 中文字幕在线欧美| 久久亚洲综合色| 国产aaa一级片| 精品高清在线| 国产精品一区二区三| 成人免费在线视频网| 欧美性色欧美a在线播放| 久久成人激情视频| 免费成人小视频| 伊人久久大香线蕉av一区| www一区二区三区| 欧美xxxx14xxxxx性爽| 亚洲成人精品女人久久久| 夜夜精品视频一区二区| 又黄又爽的网站| 香蕉久久国产| 五月婷婷综合色| 97久久精品一区二区三区的观看方式| 操91在线视频| 天堂国产一区二区三区| 色悠久久久久综合欧美99| 久久美女免费视频| 久久国产精品99久久人人澡| 无码人妻aⅴ一区二区三区日本| 136国产福利精品导航网址应用| 97精品视频在线观看| 韩国福利在线| 欧美一区二区三区视频免费| 国产无遮挡又黄又爽又色| 久久久精品黄色| 欧美在线a视频| 国产欧美短视频| 亚洲欧美丝袜| 国产精品对白久久久久粗| 日韩免费观看网站| av片哪里在线观看| 亚洲美女av黄| 国产一区二区网站| 精品国产91久久久久久老师| avhd101老司机| 夫妻av一区二区| 高清一区二区视频| 亚洲私拍自拍| 天天好比中文综合网| 一区二区三区在线免费看| 国产成人精品av在线| 污影院在线观看| 亚洲午夜小视频| 欧美自拍第一页| 欧美日韩免费一区二区三区| 日本在线视频中文字幕| 中文字幕一区日韩精品欧美| 中国极品少妇xxxx| 国产在线视频不卡二| 成人一级片网站| 欧美精选一区| 中国成人亚色综合网站| 欧美福利在线播放网址导航| 成人免费视频网址| 成人免费看黄| 久久久综合av| av在线下载| 在线看日韩欧美| 日韩三级电影网| 日韩精品一区二区三区蜜臀 | 91精品国产黑色紧身裤美女| 亚洲午夜18毛片在线看| 亚洲一区二区三区在线播放| 国产又黄又粗又猛又爽的| 久久久综合九色合综国产精品| 永久看看免费大片| 久久精品久久99精品久久| 激情六月丁香婷婷| av成人天堂| 青草网在线观看| 欧美一区亚洲| 综合久久国产| 久久综合电影| 亚洲看片网站| 成人影视亚洲图片在线| 日本一区视频在线| 亚洲第一论坛sis| 久久久久久久久久码影片| 盗摄牛牛av影视一区二区| 亚洲综合在线做性| 国产一区 二区| 成人激情视频在线| 96视频在线观看欧美| 国产乱人伦真实精品视频| 3d性欧美动漫精品xxxx软件| 97国产在线观看| 久久男人av资源站| 1769国产精品| 制服丝袜专区在线| 日韩美女av在线免费观看| 中文字幕一区久| 人体精品一二三区| 欧美日韩五码| 国产精品电影网| 国精品产品一区| 国产区亚洲区欧美区| 欧美亚洲综合视频| 91精品免费看| 日韩中文字幕无砖| 国产经品一区二区| 麻豆精品少妇| 久久久福利视频| 久操成人av| 图片区小说区区亚洲五月| 日韩欧美高清在线播放| 午夜在线视频免费观看| 欧美黄色一区| 欧美日韩不卡在线视频| 夜久久久久久| 狠狠热免费视频| 精品制服美女久久| 色哟哟在线观看视频| 成人免费视频免费观看| 亚洲狠狠婷婷综合久久久久图片| 国产亚洲一区字幕| 一本一本久久a久久| 亚洲精品福利视频网站| 精品久久免费视频| 色婷婷av一区| 国产特级aaaaaa大片| 亚洲二区在线播放视频| 国产特黄在线| 欧美成在线视频| 一二三四视频在线中文| 国产欧美日韩免费| 51亚洲精品| 欧美一区二区影视| 欧美日韩精品免费观看视频完整| 久久视频这里有精品| 青青草成人在线观看| 中文字幕人妻无码系列第三区| av电影在线观看完整版一区二区| 阿v天堂2014| 亚洲在线中文字幕| 婷婷激情五月综合| 日韩欧美国产wwwww| 国产精品一区在线看| 欧美国产日韩中文字幕在线| 欧美一级大黄| 成人免费在线看片| 三级电影一区| 国产资源在线视频| 精久久久久久久久久久| 黄色国产在线观看| 亚洲女人小视频在线观看| 日韩人妻精品中文字幕| 欧美成人欧美edvon| 成人精品一区二区三区免费| 久久久久久久久中文字幕| 欧美91在线|欧美| 欧美成人一区二区在线| 欧美特黄一区| 五月天av在线播放| 久久综合999| 国产精彩视频在线观看| 欧美日韩国产一二三| 色吊丝在线永久观看最新版本| 久久国产精品久久久| 九九热这里有精品| 欧美极品色图| 99综合视频| 性农村xxxxx小树林| 最新日韩av在线| 黄色一级视频免费看| 亚洲第一区第二区| 色噜噜狠狠狠综合欧洲色8| 国产欧美在线看| 欧美日韩伦理| 精品国产成人av在线免| 成人黄色网址在线观看| 欧美黑人猛猛猛| 欧美高清视频不卡网| 91电影在线播放| 国产精品91视频| 狠狠色丁香婷婷综合影院| 欧美变态另类刺激| a级高清视频欧美日韩| 国产一级性生活| 日韩美女视频在线| 手机电影在线观看| 91精品啪在线观看麻豆免费| 久久国产精品成人免费观看的软件| 久久久久久久片| 国产欧美一区二区三区鸳鸯浴| 日韩精品成人免费观看视频| 亚洲男人第一av网站| 中文字幕乱码中文乱码51精品| 国内精品二区| 亚洲一区二区三区高清不卡| 国产一级二级在线观看| 欧美三级免费观看| 国产小视频免费在线观看| 国产精品video| 成人写真视频| 五月天丁香花婷婷| 亚洲免费大片在线观看| 精品人妻伦一二三区久久| 欧美激情精品久久久久久大尺度| 婷婷综合国产| 丁香六月激情婷婷| 99久久国产综合色|国产精品| 久久久久久久久影院| 亚洲欧美国产日韩中文字幕| 国产欧美一区二区三区精品酒店| 日韩精品久久一区二区三区| 七七婷婷婷婷精品国产| 亚洲精品一区二区三区在线播放| 91麻豆精品国产91久久久| 性国产高清在线观看| 精品一区在线播放| 视频一区中文字幕| 蜜桃久久精品成人无码av| 欧美美女激情18p| 特级毛片在线| 久久久一本精品99久久精品66| 久久免费国产| 99鲁鲁精品一区二区三区| 精品少妇一区二区三区免费观看| 黄色在线观看www| 日韩精品电影网站| 国产精品18久久久久| 亚洲 欧美 日韩 综合| 一区二区中文字幕| 欧美精品三级在线| 国产偷人视频免费| 成人免费在线观看入口| 色呦呦视频在线| 国产日韩在线视频| 亚洲人成在线影院| 亚洲精品天堂网| 亚洲国产精品嫩草影院久久| 国产精品亚洲一区二区三区在线观看 | 北条麻妃在线视频观看| 国产精品乱人伦| 天天av天天翘| 成人信息集中地欧美| 亚洲一级在线| 91在线播放观看| 亚洲日韩第一页| 盗摄牛牛av影视一区二区| 久久撸在线视频| 精品久久久久久亚洲国产300| 夜级特黄日本大片_在线|