精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

精品熟女一区二区三区| 免费网站在线高清观看| 欧美激情精品久久久久久免费| 色哟哟在线观看| 国内外激情在线| 国产精品a级| 成人污视频在线观看| 欧美精品一区在线观看| 国产网站欧美日韩免费精品在线观看 | 91制片厂毛片| 亚洲国产精品欧美久久 | 久久久久天天天天| 日韩女同一区二区三区| 国产一区二区三区亚洲综合| 成人性生交大片| 国产大片精品免费永久看nba| 久久久久久蜜桃一区二区| 国产又大又粗又长| 国产探花一区在线观看| 一区二区三区四区视频精品免费 | 99re这里只有精品首页| 日韩网站免费观看高清| 日韩极品视频在线观看 | 亚洲福利视频一区| 91精品黄色| 免费视频91蜜桃| 97青娱国产盛宴精品视频| 久久午夜色播影院免费高清| 美女视频黄免费的亚洲男人天堂| www.日日操| 蜜桃视频在线播放| 亚洲青色在线| 5566中文字幕一区二区电影 | 免费一区二区三区在线观看| cao在线视频| 免费欧美在线视频| 日韩精品一区二区三区第95| 中文字幕人妻无码系列第三区| 天天色棕合合合合合合合| 欧美777四色影| 精品久久久久久久久久久久久久久久久 | 欧美福利视频在线观看| 伊人影院综合在线| 国产理论电影在线| 国产在线国偷精品产拍免费yy| 亚洲欧美在线播放| http;//www.99re视频| 日日操免费视频| 国产一区二区三区黄网站| 欧洲国产伦久久久久久久| 国产三级精品在线不卡| 中文字幕资源站| 日本大胆欧美| 欧美日本精品一区二区三区| 91制片厂免费观看| 国产高潮流白浆喷水视频| 亚洲午夜伦理| 亚洲欧美激情另类校园| 日本69式三人交| 国产三级精品三级在线观看国产| 国产精品久线在线观看| 国产精品美乳一区二区免费| 一区视频免费观看| 久久视频社区| 欧美性黄网官网| 亚洲欧美在线网| 中文字幕第28页| 一区二区三区免费在线看| 午夜日韩在线电影| 奇米视频888战线精品播放| 日本三级视频在线| 欧美三级伦理在线| 在线播放国产精品二区一二区四区| 日日躁夜夜躁aaaabbbb| 国产精品亚洲成在人线| 亚洲三级理论片| 久久er99热精品一区二区三区| 天天躁日日躁狠狠躁喷水| 91在线播放网址| 日本一区二区视频| 日韩毛片久久久| 99久久伊人网影院| 91夜夜揉人人捏人人添红杏| 国产婷婷色一区二区在线观看| 成人看的视频| 久久手机精品视频| 亚洲av无码国产精品久久| 福利一区和二区| 精品国产1区2区| 亚洲精品国产suv一区88| 黄色一级大片在线免费看国产| 免费一区二区视频| 成人国产精品久久久久久亚洲| 久久老司机精品视频| 亚洲瘦老头同性70tv| 91精品国产麻豆| 国产裸体免费无遮挡| 久久uomeier| 一区二区国产视频| 91制片厂免费观看| 两个人看的在线视频www| 亚洲男人的天堂一区二区| 欧美在线视频一区二区三区| 日本中文字幕在线2020| 午夜亚洲国产au精品一区二区| 久久网站免费视频| 99热国产在线| 久久久精品天堂| 96成人在线视频| 亚洲欧洲精品视频| 麻豆一区二区99久久久久| 午夜精品久久久久久久久久久久| 蜜桃av免费观看| 婷婷综合电影| 亚洲精品综合精品自拍| 国产aⅴ激情无码久久久无码| 欧美日本三级| 在线观看久久av| 日韩欧美理论片| 99er精品视频| 69久久99精品久久久久婷婷| av无码av天天av天天爽| 一区二区日韩| 久久久99久久精品女同性| 日本一级特级毛片视频| 日韩影院二区| 亚洲网站在线看| 久久亚洲无码视频| 在线电影一区| 91精品国产亚洲| a v视频在线观看| 国产精品porn| 国产日韩欧美成人| 黄色片视频免费| 亚洲国产免费| 3d精品h动漫啪啪一区二区| 成人高潮成人免费观看| 国产天堂亚洲国产碰碰| 国产精品久久久久久久久久久久午夜片| 性一交一乱一色一视频麻豆| 国产精品久久久久影视| 国产一区二区三区免费在线观看| 国产一区欧美二区三区| 五月婷中文字幕| 亚洲国产视频一区二区| 少妇精品久久久久久久久久| 青青草在线播放| 亚洲第一主播视频| 国产精品成人免费一区久久羞羞| 成人亚洲免费| 一道本无吗dⅴd在线播放一区| 在线免费黄色av| 日本不卡高清视频| 国产成人精品免费视频| 神马久久高清| 国产精品色眯眯| 亚洲成人福利在线观看| 日韩高清在线观看一区二区| 亚洲国产成人精品一区二区 | 国产精品久久久免费看| 欧美女人交a| 91精品啪在线观看麻豆免费| 免费黄色电影在线观看| 亚洲男同性视频| 亚洲爆乳无码专区| 91精品一区| 欧美精品午夜视频| av观看在线免费| 亚洲午夜精品在线| 国产中文字幕一区二区| 亚洲九九精品| 欧美精品一区在线| 国产美女福利在线| 91精品久久久久久久久99蜜臂| 91精品国产闺蜜国产在线闺蜜| 国产成人精品免费网站| 裸体丰满少妇做受久久99精品| 男人的天堂在线视频免费观看 | 永久91嫩草亚洲精品人人| 久久久久久久色| 国产精品成人久久久| 国产成人免费视频网站高清观看视频 | 久久视频一区二区| 蜜臀av免费观看| 欧美精品色网| 欧洲久久久久久| 色综合一区二区日本韩国亚洲| 精品动漫一区二区三区在线观看| 伊人久久综合视频| 日本特黄久久久高潮| 亚洲精品一区二区三区av| 亚洲啊v在线免费视频| 欧美中文在线观看| 国产精品一品二区三区的使用体验| 亚洲精品一卡二卡| 丝袜美腿中文字幕| 丝袜亚洲精品中文字幕一区| 高清国语自产拍免费一区二区三区| 丝袜视频国产在线播放| 欧美日韩国产123区| 国产精品日日夜夜| 国产精品亲子乱子伦xxxx裸| 亚洲女则毛耸耸bbw| 日本欧美在线观看| 国产二区视频在线| 四虎视频在线精品免费网址| 久久久国产精品一区| 亚洲狼人综合网| 欧美亚洲一区三区| 免费黄色三级网站| 久久精品二区亚洲w码| 人妻激情另类乱人伦人妻| 国产一区二区在线| 国产精选一区二区| 成人动漫视频在线观看| 日韩av快播网址| 午夜影院免费体验区| 欧美性极品少妇| 国产三级av片| 亚洲午夜影视影院在线观看| 亚洲一二三四五六区| 久久综合成人精品亚洲另类欧美 | 久久久国产视频91| 国产免费av在线| 亚洲第一久久影院| 国产调教在线观看| 精品午夜一区二区三区在线观看| 一区二区视频在线观看| 亚洲精品国产嫩草在线观看| 一区二区精彩视频| 深夜福利一区二区| 国产一级淫片a视频免费观看| 亚洲欧美日韩小说| 少妇视频一区二区| 亚洲国产精品成人综合色在线婷婷 | 一本色道亚洲精品aⅴ| 麻豆传媒在线看| 久久精品国产在热久久| 男人日女人bb视频| 精品动漫3d一区二区三区免费| 亚洲一区精品视频| 日韩精品中文字幕吗一区二区| 国产日韩av在线播放| 成人福利一区二区| 国产精品扒开腿做爽爽爽男男| 国产视频第一区| 亚洲免费视频观看| 无码国产色欲xxxx视频| 精品国产乱码91久久久久久网站| 国产富婆一级全黄大片| 精品日韩成人av| 成人免费一级视频| 精品国产一区二区亚洲人成毛片| 性欧美videos另类hd| 岛国av一区二区三区| 香蕉成人在线视频| 国产精品久久久久久久裸模| 一道本在线免费视频| 久草精品在线观看| 91精品国产吴梦梦| 久久99国产精品久久99大师| 高清久久久久久| 国产噜噜噜噜久久久久久久久| 免费黄网在线观看| 久久久99久久精品女同性| www国产在线观看| 7777精品伊人久久久大香线蕉经典版下载| 在线播放一级片| 亚洲一级电影视频| 亚洲少妇xxx| 国产精品蜜臀在线观看| 欧美风情第一页| 国产欧美精品在线观看| 9.1在线观看免费| 大白屁股一区二区视频| 亚洲图片综合网| 国产91在线看| 国产精品jizz| 久久亚洲综合av| 国产一区第一页| 亚洲线精品一区二区三区八戒| 国产无人区码熟妇毛片多| 欧美最新大片在线看| 国产日韩免费视频| 日韩欧美中文免费| 一二区在线观看| 日韩欧美一卡二卡| 在线播放国产一区| 日韩一区二区在线看| 五月天激情四射| 欧美日韩电影在线播放| 丰满人妻一区二区三区免费| 亚洲男人av在线| 超碰免费公开在线| 欧美极品美女电影一区| 快播电影网址老女人久久| 国产精品视频一| 精品国产一区二区三区成人影院| 青青草成人激情在线| 欧美xxx在线观看| 日本成人黄色网| 成人综合在线视频| 欧美乱大交做爰xxxⅹ小说| 亚洲免费观看视频| 韩国av免费观看| 欧洲视频一区二区| 亚洲第一色网站| 日韩亚洲综合在线| 日韩脚交footjobhdboots| 91久久精品国产91性色| 99久久婷婷国产综合精品青牛牛 | 亚洲春色h网| 91免费国产精品| 青青青爽久久午夜综合久久午夜| 亚洲精品久久一区二区三区777 | 亚洲精品偷拍视频| 色天天综合网| 日韩精品一区二区三区久久| 国产综合色视频| 中文字幕丰满孑伦无码专区| 一区二区三区四区视频精品免费| 中文字幕无线码一区| 精品视频在线免费看| 在线免费看毛片| 亚洲免费视频网站| 精品捆绑调教一区二区三区| 九色91av视频| free性欧美16hd| 91久久伊人青青碰碰婷婷| 欧美日韩水蜜桃| 免费高清一区二区三区| 亚洲精品孕妇| 国产精九九网站漫画| 亚洲天堂网中文字| www日韩精品| 日韩精品福利在线| 国产一级片在线播放| 一区二区成人精品| 国产在线美女| 91青青草免费观看| 亚洲女同另类| 亚洲色图欧美自拍| 国产精品伦理在线| 亚洲国产无线乱码在线观看| 亚洲免费一在线| 色豆豆成人网| 欧美黑人3p| 免播放器亚洲| 中文字幕国内自拍| 欧美高清在线一区二区| 国产99久久久久久免费看| 国产亚洲精品久久| 欧美精品资源| 国产一区二区在线播放| 国产精品中文字幕亚洲欧美| 国产精品少妇在线视频| 久久精品视频在线看| 免费观看日批视频| 一区二区三区视频观看| 欧美成人资源| 91嫩草国产在线观看| 国产综合亚洲精品一区二| 亚洲高清在线不卡| 国产精品久久一级| 中日精品一色哟哟| 日韩亚洲在线观看| 午夜久久av| 欧美日韩成人免费视频| 免费一级片91| 成人在线观看高清| 精品噜噜噜噜久久久久久久久试看 | 99麻豆久久久国产精品免费| 在线观看亚洲欧美| 亚洲亚裔videos黑人hd| 成人福利一区二区| 成人午夜免费剧场| av爱爱亚洲一区| 国产成人av免费| 欧美xxxx做受欧美.88| 巨人精品**| 少妇网站在线观看| 一区2区3区在线看| 免费国产在线观看| 成人国产精品av| 日韩香蕉视频| 午夜视频你懂的| 国产欧美精品一区aⅴ影院 | 伊人狠狠色丁香综合尤物| 国产精品一区二区不卡| 日韩一区二区a片免费观看| 欧美日韩一区二区三区免费看| 69xxx在线| 人禽交欧美网站免费| 日韩在线卡一卡二| 三上悠亚ssⅰn939无码播放 | 国产精品区在线观看| 国内免费精品永久在线视频| 亚洲综合伊人| 九色在线视频观看| www.激情成人|