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

通過 Ribbon 查詢 Nacos 服務實例

開發 前端
Nacos 提供了開放 API 可通過 /nacos/v1/ns/instance/list 獲取服務列表。如果我們采用 spring-cloud 方式去獲取服務,最終會通過 Nacos Client + loadbalancer 的方式進行客戶端負載均衡。

本文轉載自微信公眾號「運維開發故事」,作者老鄭。轉載本文請聯系運維開發故事公眾號。

Nacos 服務列表管理

Nacos 提供了開放 API 可通過 /nacos/v1/ns/instance/list 獲取服務列表。如果我們采用 spring-cloud 方式去獲取服務,最終會通過 Nacos Client + loadbalancer 的方式進行客戶端負載均衡。

Ribbon 源碼解析

Ribbon 簡介

Spring Cloud Ribbon 是 Netflix Ribbon 實現的一套客戶端負載均衡工具 簡單的說,Ribbon 是 Netflix 發布的開源項目,主要功能是提供客戶端的復雜算法和服務調用。 Ribbon 客戶端組件提供一系列完善的配置項如超時、重試等。簡單的說,就是配置文件中列出 load Balancer (簡稱 LB)后面所有的機器,Ribbon 會自動的幫助你基于某種規則(如簡單輪詢,隨機鏈接等)去鏈接這些機器。我們很容易使用 Ribbon 自定義的負載均衡算法。

Ribbon 使用

首先需要定義 RestTemplate 使用 Ribbon 策略;

  1. @Configuration 
  2. public class RestTemplateConfig { 
  3.     @LoadBalanced 
  4.     @Bean 
  5.     public RestTemplate restTemplate() { 
  6.         return new RestTemplate(); 
  7.     } 

本地使用 RestTemplate 調用遠程接口;

  1. @Autowired 
  2. private RestTemplate restTemplate; 
  3. @RequestMapping(value = "/echo/{id}", method = RequestMethod.GET) 
  4. public String echo(@PathVariable Long id) { 
  5.     return restTemplate.getForObject("http://member-service/member/get/" + id, String.class); 

Ribbon 源碼分析

RestTemplate 繼承 InterceptingHttpAccessor 通過 interceptors 字段接受 HttpRequestInterceptor 請求攔截器。對于 Ribbion 初始化類是 RibbonAutoConfiguration 中的, 它在 spring-cloud-netflix-ribbon 中定義。但是它在初始化之前,又需要加載 RibbonAutoConfiguration 配置,它是在 spring-cloud-common 中。具體的代碼如下:

  1. @Configuration(proxyBeanMethods = false
  2. // 工程中一定存在 RestTemplate 類 
  3. @ConditionalOnClass(RestTemplate.class) 
  4. // 容器中一定存在 LoadBalancerClient 類 Bean 實例 
  5. @ConditionalOnBean(LoadBalancerClient.class) 
  6. @EnableConfigurationProperties(LoadBalancerRetryProperties.class) 
  7. public class LoadBalancerAutoConfiguration { 
  8.  
  9.    // 獲取 Spring 容器中所有的 RestTemplate 實例 
  10.    @LoadBalanced 
  11.    @Autowired(required = false
  12.    private List<RestTemplate> restTemplates = Collections.emptyList(); 
  13.  
  14.    // 獲取 Spring 容器中 LoadBalancerRequestTransformer 實例 
  15.    @Autowired(required = false
  16.    private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList(); 
  17.  
  18.    // 在 Bean 初始化完成后會調用 afterSingletonsInstantiated 方法 
  19.    // 這里是一個 lambda 表達式方式的實現, 主要是為 restTemplate 實例設置 RestTemplateCustomizer 
  20.    @Bean 
  21.    public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated( 
  22.          final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) { 
  23.       return () -> restTemplateCustomizers.ifAvailable(customizers -> { 
  24.          for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) { 
  25.             for (RestTemplateCustomizer customizer : customizers) { 
  26.                customizer.customize(restTemplate); 
  27.             } 
  28.          } 
  29.       }); 
  30.    } 
  31.  
  32.    // LoadBalancerRequestFactory 工廠類 
  33.    // 主要是用來提供 LoadBalancerClient 實例和 LoadBalancerRequestTransformer 
  34.    @Bean 
  35.    @ConditionalOnMissingBean 
  36.    public LoadBalancerRequestFactory loadBalancerRequestFactory( 
  37.          LoadBalancerClient loadBalancerClient) { 
  38.       return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers); 
  39.    } 
  40.  
  41.    // LoadBalancerInterceptor 攔截器  
  42.    @Configuration(proxyBeanMethods = false
  43.    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate"
  44.    static class LoadBalancerInterceptorConfig { 
  45.  
  46.       // 創建默認的攔截器 LoadBalancerInterceptor 的實例 
  47.       @Bean 
  48.       public LoadBalancerInterceptor ribbonInterceptor( 
  49.             LoadBalancerClient loadBalancerClient, 
  50.             LoadBalancerRequestFactory requestFactory) { 
  51.          return new LoadBalancerInterceptor(loadBalancerClient, requestFactory); 
  52.       } 
  53.  
  54.       // 如果沒有 RestTemplateCustomizer 實例才會創建 
  55.       // 這里就就會為咱們所有的  restTemplate 實例添加 loadBalancerInterceptor 攔截器 
  56.       @Bean 
  57.       @ConditionalOnMissingBean 
  58.       public RestTemplateCustomizer restTemplateCustomizer( 
  59.             final LoadBalancerInterceptor loadBalancerInterceptor) { 
  60.          return restTemplate -> { 
  61.             List<ClientHttpRequestInterceptor> list = new ArrayList<>( 
  62.                   restTemplate.getInterceptors()); 
  63.             list.add(loadBalancerInterceptor); 
  64.             restTemplate.setInterceptors(list); 
  65.          }; 
  66.       } 
  67.  
  68.    } 
  69.   // ... 
  70.  

針對下面的代碼我們可以總結一下:

如果需要使用負載均衡,工程下面必須要有 RestTemplate 類, 然后Spring 容器中要有 LoadBalancerClient 的實例。

LoadBalancerClient 在 spring-cloud-netflix-ribbon 中只有一個實現類: RibbonLoadBalancerClient

利用 Spring 的 SmartInitializingSingleton 拓展點,在 restTemplateCustomizer() 中為所有的 RestTemplate 添加 LoadBalancerInterceptor 攔截器

其實 LoadBalancer 的本質就是通過攔截器。利用 RestTemplate 的拓展點來實現請求服務的負載均衡。

LoadBalancerInterceptor

LoadBalancerInterceptor 攔截器會將請求交給 LoadBalancerClient 去處理,首先會選擇一個 ILoadBalancer 的實現來處理獲取和選擇服務,然后通過 serviceName 和負載均衡算法去選擇 Server 對象。最后執行請求。

  1. public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor { 
  2.  
  3.    // 負載均衡 
  4.    private LoadBalancerClient loadBalancer; 
  5.  
  6.    // 構建請求  
  7.    private LoadBalancerRequestFactory requestFactory; 
  8.  
  9.    // ... 
  10.  
  11.    @Override 
  12.    public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, 
  13.          final ClientHttpRequestExecution execution) throws IOException { 
  14.       final URI originalUri = request.getURI(); 
  15.       String serviceName = originalUri.getHost();  
  16.       return this.loadBalancer.execute(serviceName, 
  17.             this.requestFactory.createRequest(request, body, execution)); 
  18.    } 
  19.  

RibbonLoadBalancerClient

我們通過跟蹤 this.loadBalancer.execute 代碼發現。最終所有的請求都交由 RibbonLoadBalancerClient 去處理。它實現了。LoadBalancerClient 接口, 代碼如下:

  1. public interface ServiceInstanceChooser { 
  2.  
  3.   // 通過 serviceId 選擇具體的服務實例 
  4.   ServiceInstance choose(String serviceId); 
  5.  
  6.  
  7. public interface LoadBalancerClient extends ServiceInstanceChooser { 
  8.  
  9.   <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException; 
  10.   <T> T execute(String serviceId, ServiceInstance serviceInstance, 
  11.          LoadBalancerRequest<T> request) throws IOException; 
  12.   // 將服務實例信息替換還具體的 IP 信息  
  13.   URI reconstructURI(ServiceInstance instance, URI original); 
  14.  

我們先來分析 RibbonLoadBalancerClient 的 choose 方法

  1. @Override 
  2. public ServiceInstance choose(String serviceId) { 
  3.    return choose(serviceId, null); 
  4.  
  5. // 通過服務名選擇具體的服務實例 
  6. public ServiceInstance choose(String serviceId, Object hint) { 
  7.    Server server = getServer(getLoadBalancer(serviceId), hint); 
  8.    if (server == null) { 
  9.       return null
  10.    } 
  11.    return new RibbonServer(serviceId, server, isSecure(server, serviceId), 
  12.          serverIntrospector(serviceId).getMetadata(server)); 
  13.  
  14. // 通過服務名選擇一個負載均衡器, 默認是 `ZoneAwareLoadBalancer` 
  15. protected ILoadBalancer getLoadBalancer(String serviceId) { 
  16.    return this.clientFactory.getLoadBalancer(serviceId); 
  17.  
  18. // 獲取服務 
  19. protected Server getServer(ILoadBalancer loadBalancer) { 
  20.    return getServer(loadBalancer, null); 
  21. protected Server getServer(ILoadBalancer loadBalancer, Object hint) { 
  22.    if (loadBalancer == null) { 
  23.       return null
  24.    } 
  25.    // Use 'default' on a null hint, or just pass it on
  26.    return loadBalancer.chooseServer(hint != null ? hint : "default"); 

LoadBalancerInterceptor 執行的時候是直接委托執行的 loadBalancer.execute() 這個方法:

  1. // LoadBalancerRequest 是通過 LoadBalancerRequestFactory.createRequest(request, body, execution) 創建 
  2. // 它實現 LoadBalancerRequest 接口是用的一個匿名內部類,泛型類型是ClientHttpResponse 
  3. // 因為最終執行的顯然還是執行器:ClientHttpRequestExecution.execute() 
  4. @Override 
  5. public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException { 
  6.   return execute(serviceId, request, null); 
  7.  
  8. public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException { 
  9.   // 拿到負載均衡器,然后拿到一個serverInstance實例 
  10.   ILoadBalancer loadBalancer = getLoadBalancer(serviceId); 
  11.   Server server = getServer(loadBalancer, hint); 
  12.   if (server == null) { // 若沒找到就直接拋出異常。這里使用的是IllegalStateException這個異常 
  13.     throw new IllegalStateException("No instances available for " + serviceId); 
  14.   } 
  15.  
  16.   // 把Server適配為RibbonServer  isSecure:客戶端是否安全 
  17.   // serverIntrospector內省  參考配置文件:ServerIntrospectorProperties 
  18.   RibbonServer ribbonServer = new RibbonServer(serviceId, server, 
  19.       isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server)); 
  20.  
  21.   //調用本類的重載接口方法 
  22.   return execute(serviceId, ribbonServer, request); 
  23.  
  24. // 它的參數是 ServiceInstance --> 已經確定了唯一的Server實例 
  25. @Override 
  26. public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException { 
  27.  
  28.   // 拿到 Server,RibbonServer 是 execute 時的唯一實現 
  29.   Server server = null
  30.   if (serviceInstance instanceof RibbonServer) { 
  31.     server = ((RibbonServer) serviceInstance).getServer(); 
  32.   } 
  33.   if (server == null) { 
  34.     throw new IllegalStateException("No instances available for " + serviceId); 
  35.   } 
  36.  
  37.   // 執行的上下文是和serviceId綁定的 
  38.   RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId); 
  39.   ...  
  40.   // 真正的向server發送請求,得到返回值 
  41.   // 因為有攔截器,所以這里肯定說執行的是InterceptingRequestExecution.execute()方法 
  42.   // so會調用ServiceRequestWrapper.getURI(),從而就會調用reconstructURI()方法 
  43.     T returnVal = request.apply(serviceInstance); 
  44.     return returnVal; 
  45.   ... // 異常處理 

returnVal 是一個 ClientHttpResponse,最后交給 handleResponse()方法來處理異常情況(若存在的話),若無異常就交給提取器提值:responseExtractor.extractData(response),這樣整個請求就算全部完成了。

ZoneAwareLoadBalancer

負載均衡器 ZoneAwareLoadBalancer 的類圖結構如下圖所示。它 DynamicServerListLoadBalancer 它的父類, 核心方法 重置和初始化:restOfInit(clientConfig) 更新服務列表:updateListOfServers(); 這個方需要調用到 ServerList.getUpdatedListOfServers() 這里就會調用到具體的注冊中心實現,以 Nacos 為例他的實現就是 NacosServerList#getUpdatedListOfServers();

  • 更新所有服務列表:updateAllServerList();
  • 設置所有服務列表 setServersList() ZoneAwareLoadBalancer 它的核心方法:
  • 選擇服務實例 chooseServer()
  • 選擇負載均衡器 getLoadBalancer
  • 選擇區域內的服務實例:zoneLoadBalancer.chooseServer

Ribbon 總結

針對 @LoadBalanced 下的 RestTemplate 的使用,我總結如下:

  • 傳入的String類型的url必須是絕對路徑(http://...),否則拋出異常:java.lang.IllegalArgumentException: URI is not absolute
  • serviceId 不區分大小寫(http://order-service/...效果同http://OERDER-SERVICE/...)
  • serviceId 后請不要跟 port 端口號

最后,需要特別指出的是:標注有@LoadBalanced 的 RestTemplate 只能填寫 serviceId 而不能再寫 IP地址/域名去發送請求了, 若你的項目中兩種 case 都有需要,需要定義多個 RestTemplate 分別應對不同的使用場景

Nacos 服務查詢

客戶端查詢

如果我們使用默認的 Nacos 客戶端,那么走的就是 NacosServerList#getUpdatedListOfServers();接口來查詢服務列表。

  1. public class NacosServerList extends AbstractServerList<NacosServer> { 
  2.  
  3.   private NacosDiscoveryProperties discoveryProperties; 
  4.  
  5.   @Override 
  6.   public List<NacosServer> getUpdatedListOfServers() { 
  7.     return getServers(); 
  8.   } 
  9.      
  10.     private List<NacosServer> getServers() { 
  11.     try { 
  12.       String group = discoveryProperties.getGroup(); 
  13.       // discoveryProperties.namingServiceInstance()  
  14.             // 最終通過反射獲取 com.alibaba.nacos.client.naming.NacosNamingService 實例 
  15.             List<Instance> instances = discoveryProperties.namingServiceInstance() 
  16.           .selectInstances(serviceId, grouptrue); 
  17.       return instancesToServerList(instances); 
  18.     } 
  19.     catch (Exception e) { 
  20.       throw new IllegalStateException( 
  21.           "Can not get service instances from nacos, serviceId=" + serviceId, 
  22.           e); 
  23.     } 
  24.   } 

然后調用 selectInstances 方法

  1. @Override 
  2. public List<Instance> selectInstances(String serviceName, String groupName, List<String> clusters, boolean healthy, 
  3.                                       boolean subscribe) throws NacosException { 
  4.  
  5.     ServiceInfo serviceInfo; 
  6.     // subscribe 默認傳的是 true 
  7.     if (subscribe) { 
  8.         serviceInfo = hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName), 
  9.                                                  StringUtils.join(clusters, ",")); 
  10.     } else { 
  11.         serviceInfo = hostReactor 
  12.             .getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName, groupName), 
  13.                                               StringUtils.join(clusters, ",")); 
  14.     } 
  15.     return selectInstances(serviceInfo, healthy); 

其實核心的邏輯在 hostReactor.getServiceInfo 在查詢服務信息里面會把當前的 serviceName、 clusters 轉換為 key, 然后通過 getServiceInfo0 方法查詢服務信息這里主要是查詢的是本地的數據。

如果 null == serviceObj 會在 updateServiceNow 里面去調用 /instance/list接口查詢服務信息

  1. public ServiceInfo getServiceInfo(final String serviceName, final String clusters) { 
  2.          
  3.         NAMING_LOGGER.debug("failover-mode: " + failoverReactor.isFailoverSwitch()); 
  4.         String key = ServiceInfo.getKey(serviceName, clusters); 
  5.         if (failoverReactor.isFailoverSwitch()) { 
  6.             return failoverReactor.getService(key); 
  7.         } 
  8.          
  9.         ServiceInfo serviceObj = getServiceInfo0(serviceName, clusters); 
  10.          
  11.         if (null == serviceObj) { 
  12.             serviceObj = new ServiceInfo(serviceName, clusters); 
  13.              
  14.             serviceInfoMap.put(serviceObj.getKey(), serviceObj); 
  15.              
  16.             updatingMap.put(serviceName, new Object()); 
  17.             updateServiceNow(serviceName, clusters); 
  18.             updatingMap.remove(serviceName); 
  19.              
  20.         } else if (updatingMap.containsKey(serviceName)) { 
  21.             // UPDATE_HOLD_INTERVAL 為常量默認金輝進去 
  22.             if (UPDATE_HOLD_INTERVAL > 0) { 
  23.                 // hold a moment waiting for update finish 
  24.                 synchronized (serviceObj) { 
  25.                     try { 
  26.                         // 最大等待時間 5s, 在更新 serviceObj 之后, 就會執行 notifyAll() 
  27.                         // 方法入口 updateService(String serviceName, String clusters) 
  28.                         // 最大延遲 2s DEFAULT_DELAY = 1 
  29.                         serviceObj.wait(UPDATE_HOLD_INTERVAL); 
  30.                     } catch (InterruptedException e) { 
  31.                         NAMING_LOGGER 
  32.                                 .error("[getServiceInfo] serviceName:" + serviceName + ", clusters:" + clusters, e); 
  33.                     } 
  34.                 } 
  35.             } 
  36.         } 
  37.          
  38.       // 通過 Schedule 更新 服務信息  
  39.         scheduleUpdateIfAbsent(serviceName, clusters); 
  40.          
  41.       // 獲取最新的值 
  42.         return serviceInfoMap.get(serviceObj.getKey()); 
  43.     } 

代碼看到這里我們不難理解,為什么第一次 Ribbon 調用的時候都會比較慢,因為它回去初始化服務列表,然后通過 Nacos Client 去 Nacos 查詢服務實例信息。

服務端處理

服務端通過 /instance/list 接口來處理服務實例信息查詢請求。首先服務實例信息都是被存儲在 ConcurrentHashMap 中

  1. /** 
  2.  * Map(namespace, Map(group::serviceName, Service)). 
  3.  */ 
  4. private final Map<String, Map<String, Service>> serviceMap = new ConcurrentHashMap<>(); 

在我們查詢的過程中主要是通過 ServiceManager 來進行管理, 核心的入口方法在 InstanceController#doSrvIpxt 中

  1. public ObjectNode doSrvIpxt(String namespaceId, String serviceName, String agent, String clusters, String clientIP, 
  2.           int udpPort, String env, boolean isCheck, String app, String tid, boolean healthyOnly) throws Exception { 
  3.        
  4.       ClientInfo clientInfo = new ClientInfo(agent); 
  5.       ObjectNode result = JacksonUtils.createEmptyJsonNode(); 
  6.       Service service = serviceManager.getService(namespaceId, serviceName); 
  7.       long cacheMillis = switchDomain.getDefaultCacheMillis(); 
  8.        
  9.       // now try to enable the push 
  10.       try { 
  11.           if (udpPort > 0 && pushService.canEnablePush(agent)) { 
  12.                
  13.               pushService 
  14.                       .addClient(namespaceId, serviceName, clusters, agent, new InetSocketAddress(clientIP, udpPort), 
  15.                               pushDataSource, tid, app); 
  16.               cacheMillis = switchDomain.getPushCacheMillis(serviceName); 
  17.           } 
  18.       } catch (Exception e) { 
  19.           Loggers.SRV_LOG 
  20.                   .error("[NACOS-API] failed to added push client {}, {}:{}", clientInfo, clientIP, udpPort, e); 
  21.           cacheMillis = switchDomain.getDefaultCacheMillis(); 
  22.       } 
  23.        
  24.       if (service == null) { 
  25.           if (Loggers.SRV_LOG.isDebugEnabled()) { 
  26.               Loggers.SRV_LOG.debug("no instance to serve for service: {}", serviceName); 
  27.           } 
  28.           result.put("name", serviceName); 
  29.           result.put("clusters", clusters); 
  30.           result.put("cacheMillis", cacheMillis); 
  31.           result.replace("hosts", JacksonUtils.createEmptyArrayNode()); 
  32.           return result; 
  33.       } 
  34.        
  35.       checkIfDisabled(service); 
  36.        
  37.       List<Instance> srvedIPs; 
  38.        
  39.       // 查詢所有的服務 
  40.       // 內部會更新服務列表 
  41.       // allInstances.addAll(persistentInstances); 
  42.       // allInstances.addAll(ephemeralInstances); 
  43.       srvedIPs = service.srvIPs(Arrays.asList(StringUtils.split(clusters, ","))); 
  44.        
  45.       // filter ips using selector: 
  46.       if (service.getSelector() != null && StringUtils.isNotBlank(clientIP)) { 
  47.           srvedIPs = service.getSelector().select(clientIP, srvedIPs); 
  48.       } 
  49.        
  50.       if (CollectionUtils.isEmpty(srvedIPs)) { 
  51.            
  52.           if (Loggers.SRV_LOG.isDebugEnabled()) { 
  53.               Loggers.SRV_LOG.debug("no instance to serve for service: {}", serviceName); 
  54.           } 
  55.            
  56.           if (clientInfo.type == ClientInfo.ClientType.JAVA 
  57.                   && clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) { 
  58.               result.put("dom", serviceName); 
  59.           } else { 
  60.               result.put("dom", NamingUtils.getServiceName(serviceName)); 
  61.           } 
  62.            
  63.           result.put("name", serviceName); 
  64.           result.put("cacheMillis", cacheMillis); 
  65.           result.put("lastRefTime", System.currentTimeMillis()); 
  66.           result.put("checksum", service.getChecksum()); 
  67.           result.put("useSpecifiedURL"false); 
  68.           result.put("clusters", clusters); 
  69.           result.put("env", env); 
  70.           result.set("hosts", JacksonUtils.createEmptyArrayNode()); 
  71.           result.set("metadata", JacksonUtils.transferToJsonNode(service.getMetadata())); 
  72.           return result; 
  73.       } 
  74.        
  75.       Map<Boolean, List<Instance>> ipMap = new HashMap<>(2); 
  76.       ipMap.put(Boolean.TRUE, new ArrayList<>()); 
  77.       ipMap.put(Boolean.FALSE, new ArrayList<>()); 
  78.        
  79.       for (Instance ip : srvedIPs) { 
  80.           ipMap.get(ip.isHealthy()).add(ip); 
  81.       } 
  82.        
  83.       if (isCheck) { 
  84.           result.put("reachProtectThreshold"false); 
  85.       } 
  86.        
  87.       double threshold = service.getProtectThreshold(); 
  88.        
  89.       if ((float) ipMap.get(Boolean.TRUE).size() / srvedIPs.size() <= threshold) { 
  90.            
  91.           Loggers.SRV_LOG.warn("protect threshold reached, return all ips, service: {}", serviceName); 
  92.           if (isCheck) { 
  93.               result.put("reachProtectThreshold"true); 
  94.           } 
  95.            
  96.           ipMap.get(Boolean.TRUE).addAll(ipMap.get(Boolean.FALSE)); 
  97.           ipMap.get(Boolean.FALSE).clear(); 
  98.       } 
  99.        
  100.       if (isCheck) { 
  101.           result.put("protectThreshold", service.getProtectThreshold()); 
  102.           result.put("reachLocalSiteCallThreshold"false); 
  103.            
  104.           return JacksonUtils.createEmptyJsonNode(); 
  105.       } 
  106.        
  107.       ArrayNode hosts = JacksonUtils.createEmptyArrayNode(); 
  108.        
  109.       for (Map.Entry<Boolean, List<Instance>> entry : ipMap.entrySet()) { 
  110.           List<Instance> ips = entry.getValue(); 
  111.            
  112.           if (healthyOnly && !entry.getKey()) { 
  113.               continue
  114.           } 
  115.            
  116.           for (Instance instance : ips) { 
  117.                
  118.               // remove disabled instance: 
  119.               if (!instance.isEnabled()) { 
  120.                   continue
  121.               } 
  122.                
  123.               ObjectNode ipObj = JacksonUtils.createEmptyJsonNode(); 
  124.                
  125.               ipObj.put("ip", instance.getIp()); 
  126.               ipObj.put("port", instance.getPort()); 
  127.               // deprecated since nacos 1.0.0: 
  128.               ipObj.put("valid", entry.getKey()); 
  129.               ipObj.put("healthy", entry.getKey()); 
  130.               ipObj.put("marked", instance.isMarked()); 
  131.               ipObj.put("instanceId", instance.getInstanceId()); 
  132.               ipObj.set("metadata", JacksonUtils.transferToJsonNode(instance.getMetadata())); 
  133.               ipObj.put("enabled", instance.isEnabled()); 
  134.               ipObj.put("weight", instance.getWeight()); 
  135.               ipObj.put("clusterName", instance.getClusterName()); 
  136.               if (clientInfo.type == ClientInfo.ClientType.JAVA 
  137.                       && clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) { 
  138.                   ipObj.put("serviceName", instance.getServiceName()); 
  139.               } else { 
  140.                   ipObj.put("serviceName", NamingUtils.getServiceName(instance.getServiceName())); 
  141.               } 
  142.                
  143.               ipObj.put("ephemeral", instance.isEphemeral()); 
  144.               hosts.add(ipObj); 
  145.                
  146.           } 
  147.       } 
  148.        
  149.       result.replace("hosts", hosts); 
  150.       if (clientInfo.type == ClientInfo.ClientType.JAVA 
  151.               && clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) { 
  152.           result.put("dom", serviceName); 
  153.       } else { 
  154.           result.put("dom", NamingUtils.getServiceName(serviceName)); 
  155.       } 
  156.       result.put("name", serviceName); 
  157.       result.put("cacheMillis", cacheMillis); 
  158.       result.put("lastRefTime", System.currentTimeMillis()); 
  159.       result.put("checksum", service.getChecksum()); 
  160.       result.put("useSpecifiedURL"false); 
  161.       result.put("clusters", clusters); 
  162.       result.put("env", env); 
  163.       result.replace("metadata", JacksonUtils.transferToJsonNode(service.getMetadata())); 
  164.       return result; 
  165.   } 

在上面的核心邏輯主要是:

  • 調用 service.srvIPs 方法查詢所有的服務實例信息
  • Cluster#allIPs會將所有的服務注冊信息寫到服務注冊列表。

參考鏈接

https://nacos.io

https://zhuanlan.zhihu.com

https://blog.csdn.net/f641385712/article/details/100788040

 

責任編輯:武曉燕 來源: 運維開發故事
相關推薦

2022-03-10 07:41:36

調用服務Nacos

2009-10-09 17:18:13

RHEL配置NIS

2009-12-11 13:59:35

F#

2010-03-02 14:06:37

WCF服務實例管理模式

2009-08-14 17:04:19

Windows后臺服務

2025-03-07 08:17:36

2010-02-26 14:49:10

WCF服務實例單一性

2010-09-24 19:12:11

SQL隱性事務

2013-03-19 10:35:24

Oracle

2013-02-26 10:23:52

F5電信

2024-07-02 10:58:53

2024-09-04 10:44:19

2022-07-08 08:37:23

Nacos服務注冊動態配置

2017-10-21 21:46:32

2025-06-03 08:25:00

Nacos開發服務

2023-04-10 23:05:54

NacosOpenFeignRibbon

2021-09-06 06:45:07

NacosUdp通信

2017-09-05 14:05:11

微服務spring clou路由

2020-10-09 18:41:55

AWS云服務云計算

2020-10-13 14:03:50

搭建ngrok服務
點贊
收藏

51CTO技術棧公眾號

黄色av免费在线看| 国产欧美一区二区三区在线看蜜臂| 亚洲天堂1区| 国产精品人妖ts系列视频| 亚洲自拍偷拍第一页| 国产无套粉嫩白浆内谢| av一区二区在线观看| 欧美精选一区二区| 成人性免费视频| lutube成人福利在线观看| 国产福利一区二区三区视频在线| 91精品国产高清久久久久久91| 91精品国自产在线| a级日韩大片| 欧美日韩的一区二区| 91免费黄视频| 欧美成人二区| 91麻豆精品秘密| 97人人香蕉| 国产精品成人无码| 日韩一级欧洲| 久久最新资源网| 中文字幕人妻一区二区三区在线视频| 亚洲啊v在线免费视频| 精品污污网站免费看| 成人黄色av片| 国产探花视频在线观看| 国产精品久久午夜| 欧美精品亚洲| 无码精品视频一区二区三区 | 亚洲精品欧美在线| 日韩精品久久久毛片一区二区| 亚洲欧美高清视频| 国产一区二三区| 国产精品小说在线| 神马久久久久久久| 一道本一区二区| 久久久午夜视频| avove在线播放| 99久久婷婷| 中文字幕精品在线| 美女100%无挡| 美女毛片一区二区三区四区| 亚洲成在人线av| 欧美丰满熟妇bbb久久久| 成人黄色91| 欧美丰满高潮xxxx喷水动漫| 超碰人人草人人| 男人亚洲天堂| 欧美高清视频在线高清观看mv色露露十八 | 免费在线黄色影片| 91免费看视频| 人偷久久久久久久偷女厕| 牛牛热在线视频| 久久久无码精品亚洲日韩按摩| 久久亚洲免费| 国内av一区二区三区| 国产夜色精品一区二区av| 鲁片一区二区三区| 久草在线免费福利资源| 国产午夜精品一区二区三区嫩草| 成人av动漫在线| 亚洲欧洲精品一区二区三区不卡| 成人毛片网站| 日韩有码第一页| www.性欧美| 久久久神马电影| 毛片网站在线观看| 中文在线一区二区| 国产高清免费在线| 色屁屁www国产馆在线观看| 亚洲国产精品久久不卡毛片| 成人性生活视频免费看| 理论不卡电影大全神| 色综合天天狠狠| 欧美日韩中文不卡| 一本色道69色精品综合久久| 亚洲缚视频在线观看| 国产伦精品一区二区三区妓女| 久久综合亚洲| 日韩一区二区精品视频| 欧美被狂躁喷白浆精品| 国产视频一区免费看| 国产精品久久久久免费a∨大胸 | 91资源在线观看| 欧美视频在线免费| 亚洲一级免费在线观看| 97色成人综合网站| 亚洲视频第一页| 欧美黄色aaa| 性一交一乱一区二区洋洋av| 国产欧美日韩精品丝袜高跟鞋| www.av黄色| 久久综合99re88久久爱| 一本—道久久a久久精品蜜桃| √8天堂资源地址中文在线| 色婷婷av一区二区三区软件| 午夜精品免费看| 极品尤物一区| www.xxxx精品| 波多野结衣国产| 精品一区二区三区在线播放视频 | 欧美男gay| 久久夜色精品亚洲噜噜国产mv| 青青草成人av| 国产一区二区在线观看视频| 久久精品综合一区| 国产成人l区| 欧美性猛交xxxx乱大交蜜桃| 丰满少妇中文字幕| 欧美猛男做受videos| 欧美激情亚洲视频| 中文字幕视频一区二区| 99re成人在线| 中文字幕精品在线播放| 三级成人在线| 日韩不卡在线观看| 精品无码久久久久成人漫画| 玖玖国产精品视频| 国产精品一区二区av| 黄色网址在线免费播放| 日本高清无吗v一区| 精品影片一区二区入口| 综合视频在线| 国产男人精品视频| 国产露出视频在线观看| 狠狠色噜噜狠狠狠狠97| 国产人妖在线观看| 欧美/亚洲一区| 国产欧美一区二区白浆黑人| 国产香蕉在线| 第一福利永久视频精品| 亚洲婷婷在线观看| 欧美日本不卡高清| 亚洲综合在线播放| 国产激情在线视频| 日韩西西人体444www| 美国黄色片视频| 久久99精品久久久久婷婷| 色综合久久av| 91tv亚洲精品香蕉国产一区| 亚洲人成自拍网站| 在线免费黄色av| 91视频国产资源| 免费欧美一级视频| 婷婷五月色综合香五月| 欧美黑人xxxx| 亚洲精品无amm毛片| 亚洲一区二区三区四区在线| 日本精品一二三区| 亚洲黑丝一区二区| 精品一区二区久久久久久久网站| 18video性欧美19sex高清| 精品国产一区二区亚洲人成毛片| 成人免费看片98| 成人高清av在线| 凹凸国产熟女精品视频| 全球av集中精品导航福利| 7m第一福利500精品视频| 亚洲色大成网站www| 色综合久久久久综合体| 好吊视频在线观看| 欧美a级一区二区| 一区精品视频| 一区二区三区在线免费看| 欧美精品videossex性护士| 黄色美女一级片| 欧美性极品少妇精品网站| japanese中文字幕| 麻豆专区一区二区三区四区五区| 国产又黄又爽免费视频| 97久久综合精品久久久综合| 欧美亚洲成人网| 国产中文字幕在线观看| 欧美精品v日韩精品v韩国精品v| 九九热国产在线| av一区二区久久| 天天干在线影院| 雨宫琴音一区二区三区| 国产精品一区二区不卡视频| 亚洲一区二区三区四区| 久久久久北条麻妃免费看| 天天操天天爱天天干| 91福利精品视频| 无码黑人精品一区二区| 99天天综合性| 五月婷婷丁香色| 激情综合电影网| 亚洲国产一区二区精品视频| 亚洲精品一区在线| 国产精品99免视看9| 成人在线免费看片| 亚洲老司机av| av免费在线观看不卡| 欧美午夜激情在线| 极品久久久久久| 久久久久国产精品麻豆| 亚洲天堂一区二区在线观看| 久久99伊人| 996这里只有精品| 欧美手机在线| 国产乱码精品一区二区三区日韩精品| 日本精品在线一区| 午夜精品久久久久久久99热浪潮 | 久久免费高清视频| 色欧美激情视频在线| 亚洲级视频在线观看免费1级| 999香蕉视频| 色偷偷综合网| 久久亚洲免费| 国产成人精品亚洲线观看| 国产成人91久久精品| 日韩影视在线| 日韩一区二区精品视频| 蜜桃成人在线视频| 亚洲福利在线视频| 成人av一区二区三区在线观看| 欧美又粗又大又爽| 亚洲男人的天堂在线视频| 一区二区三区四区亚洲| 日韩在线视频免费看| 2020国产精品| 日本69式三人交| 国产成人欧美日韩在线电影| 岛国毛片在线播放| 日本午夜一区二区| 男女啪啪网站视频| 久久av一区| 凹凸国产熟女精品视频| 日韩视频在线一区二区三区| 青春草国产视频| 欧美日本亚洲韩国国产| 青青草免费在线视频观看| 日韩国产欧美一区二区| 日本在线播放一区| 在线日本制服中文欧美| 久久av免费观看| 日韩电影在线观看完整免费观看| 粉嫩高清一区二区三区精品视频| 久久中文字幕一区二区| 91色精品视频在线| aa亚洲一区一区三区| 国产在线日韩在线| 欧美一级网址| 成人黄色av播放免费| 欧美一级做a| 91热福利电影| 91综合久久爱com| 动漫3d精品一区二区三区| 亚洲图色一区二区三区| 99在线观看视频| 91精品入口| 国产精品国产精品| 国产精品qvod| 噜噜噜噜噜久久久久久91| 国产中文字幕一区二区三区| 台湾成人av| 三上亚洲一区二区| 18视频在线观看娇喘| 欧美国产综合| www.99热这里只有精品| 亚洲欧美日韩国产一区二区| 人妻丰满熟妇av无码区app| 日本强好片久久久久久aaa| 色播五月激情五月| 国产精品羞羞答答xxdd| 午夜不卡久久精品无码免费| 99久久伊人网影院| 色综合99久久久无码国产精品| 国产精品国产馆在线真实露脸| 天堂网avav| 精品久久久久久久久久国产| www.com亚洲| 欧美一级欧美一级在线播放| 免费看黄网站在线观看| 亚洲日韩中文字幕在线播放| 欧美成年黄网站色视频| 久久久女女女女999久久| 欧美性猛交xxx高清大费中文| 国产日韩欧美中文| 豆花视频一区二区| 日韩精品久久一区| 91精品婷婷色在线观看| 国产伦精品一区二区三区四区视频_| 久久经典综合| 91性高潮久久久久久久| 91偷拍与自偷拍精品| jizz日本在线播放| 亚洲午夜激情网站| 国产精品成人久久久| 精品久久国产字幕高潮| 福利片在线观看| 久久久天堂国产精品女人| 懂色aⅴ精品一区二区三区| 99三级在线| 91日韩视频| 91视频最新入口| 国产精品1区二区.| 亚洲成人黄色av| 亚洲午夜视频在线观看| 亚洲天堂手机在线| 亚洲精品一区二区网址 | 97人人爽人人喊人人模波多 | 国产日韩综合| 国内av免费观看| 91丨porny丨户外露出| 欧美三级免费看| 欧美久久久久中文字幕| 可以免费看污视频的网站在线| 欧美激情欧美激情| 婷婷久久免费视频| 欧美视频1区| 亚洲免费大片| 少妇欧美激情一区二区三区| 欧美激情中文字幕一区二区| 亚洲综合一二三| 欧美一级夜夜爽| 欧美三级电影一区二区三区| 欧美在线欧美在线| 高清日韩欧美| 免费在线精品视频| 九九国产精品视频| www久久久久久久| 色哟哟日韩精品| 日韩精品视频在线观看一区二区三区| 欧美国产激情18| 一区二区三区在线免费看| 青青草免费在线视频观看| 激情综合网激情| 成年人看的免费视频| 色94色欧美sute亚洲线路一ni| 天天干视频在线| 国产做受69高潮| 国产区精品视频在线观看豆花| 男人c女人视频| 国v精品久久久网| 欧美丰满艳妇bbwbbw| 日韩视频在线永久播放| 成人影院在线看| 亚洲综合一区二区不卡| 你懂的亚洲视频| 欧美专区第二页| 一区二区三区不卡视频| wwwav网站| 欧美激情亚洲自拍| 韩国女主播一区二区三区| 男人天堂av片| av中文一区二区三区| 中文字幕亚洲精品一区| 亚洲精品日韩在线| 黑人巨大精品| 日韩av一区二区三区美女毛片| 久久三级福利| 欧美巨胸大乳hitomi| 欧美日韩精品一区二区三区| 婷婷在线视频| 91久久偷偷做嫩草影院| 黄色成人在线网站| 国产精品久久久久久亚洲色| 欧美日韩国产在线看| 九九热视频在线观看| 国产色婷婷国产综合在线理论片a| 爽成人777777婷婷| 男生和女生一起差差差视频| 亚洲伊人色欲综合网| 天天摸天天干天天操| 国产98色在线| 最新精品国产| 亚洲黄色免费在线观看| 在线观看www91| av软件在线观看| 精品久久sese| 日韩福利电影在线观看| 成人做爰69片免网站| 日韩欧美精品三级| 英国三级经典在线观看| 水蜜桃一区二区| 国产老女人精品毛片久久| 日本熟妇成熟毛茸茸| 亚洲欧美制服中文字幕| 高清精品久久| 国产欧美在线一区| 中文字幕一区二区三中文字幕| 亚洲精品一区二区三区不卡| 日本免费久久高清视频| 亚洲成人tv| 亚洲av无码国产精品久久| 欧美蜜桃一区二区三区| 99riav视频在线观看| 亚洲一区二区三区涩| 成人黄色777网| 888奇米影视| 久久久久久久久国产| 日韩成人免费| 国产 xxxx| 日韩视频一区二区三区| 九九热线视频只有这里最精品| 欧美视频在线第一页| 亚洲国产高清aⅴ视频| 人妻精品无码一区二区|