通過(guò) Ribbon 查詢 Nacos 服務(wù)實(shí)例
本文轉(zhuǎn)載自微信公眾號(hào)「運(yùn)維開(kāi)發(fā)故事」,作者老鄭。轉(zhuǎn)載本文請(qǐng)聯(lián)系運(yùn)維開(kāi)發(fā)故事公眾號(hào)。
Nacos 服務(wù)列表管理
Nacos 提供了開(kāi)放 API 可通過(guò) /nacos/v1/ns/instance/list 獲取服務(wù)列表。如果我們采用 spring-cloud 方式去獲取服務(wù),最終會(huì)通過(guò) Nacos Client + loadbalancer 的方式進(jìn)行客戶端負(fù)載均衡。
Ribbon 源碼解析
Ribbon 簡(jiǎn)介
Spring Cloud Ribbon 是 Netflix Ribbon 實(shí)現(xiàn)的一套客戶端負(fù)載均衡工具 簡(jiǎn)單的說(shuō),Ribbon 是 Netflix 發(fā)布的開(kāi)源項(xiàng)目,主要功能是提供客戶端的復(fù)雜算法和服務(wù)調(diào)用。 Ribbon 客戶端組件提供一系列完善的配置項(xiàng)如超時(shí)、重試等。簡(jiǎn)單的說(shuō),就是配置文件中列出 load Balancer (簡(jiǎn)稱 LB)后面所有的機(jī)器,Ribbon 會(huì)自動(dòng)的幫助你基于某種規(guī)則(如簡(jiǎn)單輪詢,隨機(jī)鏈接等)去鏈接這些機(jī)器。我們很容易使用 Ribbon 自定義的負(fù)載均衡算法。
Ribbon 使用
首先需要定義 RestTemplate 使用 Ribbon 策略;
- @Configuration
- public class RestTemplateConfig {
- @LoadBalanced
- @Bean
- public RestTemplate restTemplate() {
- return new RestTemplate();
- }
- }
本地使用 RestTemplate 調(diào)用遠(yuǎn)程接口;
- @Autowired
- private RestTemplate restTemplate;
- @RequestMapping(value = "/echo/{id}", method = RequestMethod.GET)
- public String echo(@PathVariable Long id) {
- return restTemplate.getForObject("http://member-service/member/get/" + id, String.class);
- }
Ribbon 源碼分析
RestTemplate 繼承 InterceptingHttpAccessor 通過(guò) interceptors 字段接受 HttpRequestInterceptor 請(qǐng)求攔截器。對(duì)于 Ribbion 初始化類是 RibbonAutoConfiguration 中的, 它在 spring-cloud-netflix-ribbon 中定義。但是它在初始化之前,又需要加載 RibbonAutoConfiguration 配置,它是在 spring-cloud-common 中。具體的代碼如下:
- @Configuration(proxyBeanMethods = false)
- // 工程中一定存在 RestTemplate 類
- @ConditionalOnClass(RestTemplate.class)
- // 容器中一定存在 LoadBalancerClient 類 Bean 實(shí)例
- @ConditionalOnBean(LoadBalancerClient.class)
- @EnableConfigurationProperties(LoadBalancerRetryProperties.class)
- public class LoadBalancerAutoConfiguration {
- // 獲取 Spring 容器中所有的 RestTemplate 實(shí)例
- @LoadBalanced
- @Autowired(required = false)
- private List<RestTemplate> restTemplates = Collections.emptyList();
- // 獲取 Spring 容器中 LoadBalancerRequestTransformer 實(shí)例
- @Autowired(required = false)
- private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
- // 在 Bean 初始化完成后會(huì)調(diào)用 afterSingletonsInstantiated 方法
- // 這里是一個(gè) lambda 表達(dá)式方式的實(shí)現(xiàn), 主要是為 restTemplate 實(shí)例設(shè)置 RestTemplateCustomizer
- @Bean
- public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
- final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
- return () -> restTemplateCustomizers.ifAvailable(customizers -> {
- for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
- for (RestTemplateCustomizer customizer : customizers) {
- customizer.customize(restTemplate);
- }
- }
- });
- }
- // LoadBalancerRequestFactory 工廠類
- // 主要是用來(lái)提供 LoadBalancerClient 實(shí)例和 LoadBalancerRequestTransformer
- @Bean
- @ConditionalOnMissingBean
- public LoadBalancerRequestFactory loadBalancerRequestFactory(
- LoadBalancerClient loadBalancerClient) {
- return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
- }
- // LoadBalancerInterceptor 攔截器
- @Configuration(proxyBeanMethods = false)
- @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
- static class LoadBalancerInterceptorConfig {
- // 創(chuàng)建默認(rèn)的攔截器 LoadBalancerInterceptor 的實(shí)例
- @Bean
- public LoadBalancerInterceptor ribbonInterceptor(
- LoadBalancerClient loadBalancerClient,
- LoadBalancerRequestFactory requestFactory) {
- return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
- }
- // 如果沒(méi)有 RestTemplateCustomizer 實(shí)例才會(huì)創(chuàng)建
- // 這里就就會(huì)為咱們所有的 restTemplate 實(shí)例添加 loadBalancerInterceptor 攔截器
- @Bean
- @ConditionalOnMissingBean
- public RestTemplateCustomizer restTemplateCustomizer(
- final LoadBalancerInterceptor loadBalancerInterceptor) {
- return restTemplate -> {
- List<ClientHttpRequestInterceptor> list = new ArrayList<>(
- restTemplate.getInterceptors());
- list.add(loadBalancerInterceptor);
- restTemplate.setInterceptors(list);
- };
- }
- }
- // ...
- }
針對(duì)下面的代碼我們可以總結(jié)一下:
如果需要使用負(fù)載均衡,工程下面必須要有 RestTemplate 類, 然后Spring 容器中要有 LoadBalancerClient 的實(shí)例。
LoadBalancerClient 在 spring-cloud-netflix-ribbon 中只有一個(gè)實(shí)現(xiàn)類: RibbonLoadBalancerClient
利用 Spring 的 SmartInitializingSingleton 拓展點(diǎn),在 restTemplateCustomizer() 中為所有的 RestTemplate 添加 LoadBalancerInterceptor 攔截器
其實(shí) LoadBalancer 的本質(zhì)就是通過(guò)攔截器。利用 RestTemplate 的拓展點(diǎn)來(lái)實(shí)現(xiàn)請(qǐng)求服務(wù)的負(fù)載均衡。
LoadBalancerInterceptor
LoadBalancerInterceptor 攔截器會(huì)將請(qǐng)求交給 LoadBalancerClient 去處理,首先會(huì)選擇一個(gè) ILoadBalancer 的實(shí)現(xiàn)來(lái)處理獲取和選擇服務(wù),然后通過(guò) serviceName 和負(fù)載均衡算法去選擇 Server 對(duì)象。最后執(zhí)行請(qǐng)求。
- public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
- // 負(fù)載均衡
- private LoadBalancerClient loadBalancer;
- // 構(gòu)建請(qǐng)求
- private LoadBalancerRequestFactory requestFactory;
- // ...
- @Override
- public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
- final ClientHttpRequestExecution execution) throws IOException {
- final URI originalUri = request.getURI();
- String serviceName = originalUri.getHost();
- return this.loadBalancer.execute(serviceName,
- this.requestFactory.createRequest(request, body, execution));
- }
- }
RibbonLoadBalancerClient
我們通過(guò)跟蹤 this.loadBalancer.execute 代碼發(fā)現(xiàn)。最終所有的請(qǐng)求都交由 RibbonLoadBalancerClient 去處理。它實(shí)現(xiàn)了。LoadBalancerClient 接口, 代碼如下:
- public interface ServiceInstanceChooser {
- // 通過(guò) serviceId 選擇具體的服務(wù)實(shí)例
- ServiceInstance choose(String serviceId);
- }
- public interface LoadBalancerClient extends ServiceInstanceChooser {
- <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
- <T> T execute(String serviceId, ServiceInstance serviceInstance,
- LoadBalancerRequest<T> request) throws IOException;
- // 將服務(wù)實(shí)例信息替換還具體的 IP 信息
- URI reconstructURI(ServiceInstance instance, URI original);
- }
我們先來(lái)分析 RibbonLoadBalancerClient 的 choose 方法
- @Override
- public ServiceInstance choose(String serviceId) {
- return choose(serviceId, null);
- }
- // 通過(guò)服務(wù)名選擇具體的服務(wù)實(shí)例
- public ServiceInstance choose(String serviceId, Object hint) {
- Server server = getServer(getLoadBalancer(serviceId), hint);
- if (server == null) {
- return null;
- }
- return new RibbonServer(serviceId, server, isSecure(server, serviceId),
- serverIntrospector(serviceId).getMetadata(server));
- }
- // 通過(guò)服務(wù)名選擇一個(gè)負(fù)載均衡器, 默認(rèn)是 `ZoneAwareLoadBalancer`
- protected ILoadBalancer getLoadBalancer(String serviceId) {
- return this.clientFactory.getLoadBalancer(serviceId);
- }
- // 獲取服務(wù)
- protected Server getServer(ILoadBalancer loadBalancer) {
- return getServer(loadBalancer, null);
- }
- protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
- if (loadBalancer == null) {
- return null;
- }
- // Use 'default' on a null hint, or just pass it on?
- return loadBalancer.chooseServer(hint != null ? hint : "default");
- }
LoadBalancerInterceptor 執(zhí)行的時(shí)候是直接委托執(zhí)行的 loadBalancer.execute() 這個(gè)方法:
- // LoadBalancerRequest 是通過(guò) LoadBalancerRequestFactory.createRequest(request, body, execution) 創(chuàng)建
- // 它實(shí)現(xiàn) LoadBalancerRequest 接口是用的一個(gè)匿名內(nèi)部類,泛型類型是ClientHttpResponse
- // 因?yàn)樽罱K執(zhí)行的顯然還是執(zhí)行器:ClientHttpRequestExecution.execute()
- @Override
- public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
- return execute(serviceId, request, null);
- }
- public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
- // 拿到負(fù)載均衡器,然后拿到一個(gè)serverInstance實(shí)例
- ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
- Server server = getServer(loadBalancer, hint);
- if (server == null) { // 若沒(méi)找到就直接拋出異常。這里使用的是IllegalStateException這個(gè)異常
- throw new IllegalStateException("No instances available for " + serviceId);
- }
- // 把Server適配為RibbonServer isSecure:客戶端是否安全
- // serverIntrospector內(nèi)省 參考配置文件:ServerIntrospectorProperties
- RibbonServer ribbonServer = new RibbonServer(serviceId, server,
- isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server));
- //調(diào)用本類的重載接口方法
- return execute(serviceId, ribbonServer, request);
- }
- // 它的參數(shù)是 ServiceInstance --> 已經(jīng)確定了唯一的Server實(shí)例
- @Override
- public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
- // 拿到 Server,RibbonServer 是 execute 時(shí)的唯一實(shí)現(xiàn)
- Server server = null;
- if (serviceInstance instanceof RibbonServer) {
- server = ((RibbonServer) serviceInstance).getServer();
- }
- if (server == null) {
- throw new IllegalStateException("No instances available for " + serviceId);
- }
- // 執(zhí)行的上下文是和serviceId綁定的
- RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
- ...
- // 真正的向server發(fā)送請(qǐng)求,得到返回值
- // 因?yàn)橛袛r截器,所以這里肯定說(shuō)執(zhí)行的是InterceptingRequestExecution.execute()方法
- // so會(huì)調(diào)用ServiceRequestWrapper.getURI(),從而就會(huì)調(diào)用reconstructURI()方法
- T returnVal = request.apply(serviceInstance);
- return returnVal;
- ... // 異常處理
- }
returnVal 是一個(gè) ClientHttpResponse,最后交給 handleResponse()方法來(lái)處理異常情況(若存在的話),若無(wú)異常就交給提取器提值:responseExtractor.extractData(response),這樣整個(gè)請(qǐng)求就算全部完成了。
ZoneAwareLoadBalancer
負(fù)載均衡器 ZoneAwareLoadBalancer 的類圖結(jié)構(gòu)如下圖所示。它 DynamicServerListLoadBalancer 它的父類, 核心方法 重置和初始化:restOfInit(clientConfig) 更新服務(wù)列表:updateListOfServers(); 這個(gè)方需要調(diào)用到 ServerList.getUpdatedListOfServers() 這里就會(huì)調(diào)用到具體的注冊(cè)中心實(shí)現(xiàn),以 Nacos 為例他的實(shí)現(xiàn)就是 NacosServerList#getUpdatedListOfServers();
- 更新所有服務(wù)列表:updateAllServerList();
- 設(shè)置所有服務(wù)列表 setServersList() ZoneAwareLoadBalancer 它的核心方法:
- 選擇服務(wù)實(shí)例 chooseServer()
- 選擇負(fù)載均衡器 getLoadBalancer
- 選擇區(qū)域內(nèi)的服務(wù)實(shí)例:zoneLoadBalancer.chooseServer
Ribbon 總結(jié)
針對(duì) @LoadBalanced 下的 RestTemplate 的使用,我總結(jié)如下:
- 傳入的String類型的url必須是絕對(duì)路徑(http://...),否則拋出異常:java.lang.IllegalArgumentException: URI is not absolute
- serviceId 不區(qū)分大小寫(xiě)(http://order-service/...效果同http://OERDER-SERVICE/...)
- serviceId 后請(qǐng)不要跟 port 端口號(hào)
最后,需要特別指出的是:標(biāo)注有@LoadBalanced 的 RestTemplate 只能填寫(xiě) serviceId 而不能再寫(xiě) IP地址/域名去發(fā)送請(qǐng)求了, 若你的項(xiàng)目中兩種 case 都有需要,需要定義多個(gè) RestTemplate 分別應(yīng)對(duì)不同的使用場(chǎng)景
Nacos 服務(wù)查詢
客戶端查詢
如果我們使用默認(rèn)的 Nacos 客戶端,那么走的就是 NacosServerList#getUpdatedListOfServers();接口來(lái)查詢服務(wù)列表。
- public class NacosServerList extends AbstractServerList<NacosServer> {
- private NacosDiscoveryProperties discoveryProperties;
- @Override
- public List<NacosServer> getUpdatedListOfServers() {
- return getServers();
- }
- private List<NacosServer> getServers() {
- try {
- String group = discoveryProperties.getGroup();
- // discoveryProperties.namingServiceInstance()
- // 最終通過(guò)反射獲取 com.alibaba.nacos.client.naming.NacosNamingService 實(shí)例
- List<Instance> instances = discoveryProperties.namingServiceInstance()
- .selectInstances(serviceId, group, true);
- return instancesToServerList(instances);
- }
- catch (Exception e) {
- throw new IllegalStateException(
- "Can not get service instances from nacos, serviceId=" + serviceId,
- e);
- }
- }
- }
然后調(diào)用 selectInstances 方法
- @Override
- public List<Instance> selectInstances(String serviceName, String groupName, List<String> clusters, boolean healthy,
- boolean subscribe) throws NacosException {
- ServiceInfo serviceInfo;
- // subscribe 默認(rèn)傳的是 true
- if (subscribe) {
- serviceInfo = hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName),
- StringUtils.join(clusters, ","));
- } else {
- serviceInfo = hostReactor
- .getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName, groupName),
- StringUtils.join(clusters, ","));
- }
- return selectInstances(serviceInfo, healthy);
- }
其實(shí)核心的邏輯在 hostReactor.getServiceInfo 在查詢服務(wù)信息里面會(huì)把當(dāng)前的 serviceName、 clusters 轉(zhuǎn)換為 key, 然后通過(guò) getServiceInfo0 方法查詢服務(wù)信息這里主要是查詢的是本地的數(shù)據(jù)。
如果 null == serviceObj 會(huì)在 updateServiceNow 里面去調(diào)用 /instance/list接口查詢服務(wù)信息
- public ServiceInfo getServiceInfo(final String serviceName, final String clusters) {
- NAMING_LOGGER.debug("failover-mode: " + failoverReactor.isFailoverSwitch());
- String key = ServiceInfo.getKey(serviceName, clusters);
- if (failoverReactor.isFailoverSwitch()) {
- return failoverReactor.getService(key);
- }
- ServiceInfo serviceObj = getServiceInfo0(serviceName, clusters);
- if (null == serviceObj) {
- serviceObj = new ServiceInfo(serviceName, clusters);
- serviceInfoMap.put(serviceObj.getKey(), serviceObj);
- updatingMap.put(serviceName, new Object());
- updateServiceNow(serviceName, clusters);
- updatingMap.remove(serviceName);
- } else if (updatingMap.containsKey(serviceName)) {
- // UPDATE_HOLD_INTERVAL 為常量默認(rèn)金輝進(jìn)去
- if (UPDATE_HOLD_INTERVAL > 0) {
- // hold a moment waiting for update finish
- synchronized (serviceObj) {
- try {
- // 最大等待時(shí)間 5s, 在更新 serviceObj 之后, 就會(huì)執(zhí)行 notifyAll()
- // 方法入口 updateService(String serviceName, String clusters)
- // 最大延遲 2s DEFAULT_DELAY = 1
- serviceObj.wait(UPDATE_HOLD_INTERVAL);
- } catch (InterruptedException e) {
- NAMING_LOGGER
- .error("[getServiceInfo] serviceName:" + serviceName + ", clusters:" + clusters, e);
- }
- }
- }
- }
- // 通過(guò) Schedule 更新 服務(wù)信息
- scheduleUpdateIfAbsent(serviceName, clusters);
- // 獲取最新的值
- return serviceInfoMap.get(serviceObj.getKey());
- }
代碼看到這里我們不難理解,為什么第一次 Ribbon 調(diào)用的時(shí)候都會(huì)比較慢,因?yàn)樗厝コ跏蓟?wù)列表,然后通過(guò) Nacos Client 去 Nacos 查詢服務(wù)實(shí)例信息。
服務(wù)端處理
服務(wù)端通過(guò) /instance/list 接口來(lái)處理服務(wù)實(shí)例信息查詢請(qǐng)求。首先服務(wù)實(shí)例信息都是被存儲(chǔ)在 ConcurrentHashMap 中
- /**
- * Map(namespace, Map(group::serviceName, Service)).
- */
- private final Map<String, Map<String, Service>> serviceMap = new ConcurrentHashMap<>();
在我們查詢的過(guò)程中主要是通過(guò) ServiceManager 來(lái)進(jìn)行管理, 核心的入口方法在 InstanceController#doSrvIpxt 中
- public ObjectNode doSrvIpxt(String namespaceId, String serviceName, String agent, String clusters, String clientIP,
- int udpPort, String env, boolean isCheck, String app, String tid, boolean healthyOnly) throws Exception {
- ClientInfo clientInfo = new ClientInfo(agent);
- ObjectNode result = JacksonUtils.createEmptyJsonNode();
- Service service = serviceManager.getService(namespaceId, serviceName);
- long cacheMillis = switchDomain.getDefaultCacheMillis();
- // now try to enable the push
- try {
- if (udpPort > 0 && pushService.canEnablePush(agent)) {
- pushService
- .addClient(namespaceId, serviceName, clusters, agent, new InetSocketAddress(clientIP, udpPort),
- pushDataSource, tid, app);
- cacheMillis = switchDomain.getPushCacheMillis(serviceName);
- }
- } catch (Exception e) {
- Loggers.SRV_LOG
- .error("[NACOS-API] failed to added push client {}, {}:{}", clientInfo, clientIP, udpPort, e);
- cacheMillis = switchDomain.getDefaultCacheMillis();
- }
- if (service == null) {
- if (Loggers.SRV_LOG.isDebugEnabled()) {
- Loggers.SRV_LOG.debug("no instance to serve for service: {}", serviceName);
- }
- result.put("name", serviceName);
- result.put("clusters", clusters);
- result.put("cacheMillis", cacheMillis);
- result.replace("hosts", JacksonUtils.createEmptyArrayNode());
- return result;
- }
- checkIfDisabled(service);
- List<Instance> srvedIPs;
- // 查詢所有的服務(wù)
- // 內(nèi)部會(huì)更新服務(wù)列表
- // allInstances.addAll(persistentInstances);
- // allInstances.addAll(ephemeralInstances);
- srvedIPs = service.srvIPs(Arrays.asList(StringUtils.split(clusters, ",")));
- // filter ips using selector:
- if (service.getSelector() != null && StringUtils.isNotBlank(clientIP)) {
- srvedIPs = service.getSelector().select(clientIP, srvedIPs);
- }
- if (CollectionUtils.isEmpty(srvedIPs)) {
- if (Loggers.SRV_LOG.isDebugEnabled()) {
- Loggers.SRV_LOG.debug("no instance to serve for service: {}", serviceName);
- }
- if (clientInfo.type == ClientInfo.ClientType.JAVA
- && clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) {
- result.put("dom", serviceName);
- } else {
- result.put("dom", NamingUtils.getServiceName(serviceName));
- }
- result.put("name", serviceName);
- result.put("cacheMillis", cacheMillis);
- result.put("lastRefTime", System.currentTimeMillis());
- result.put("checksum", service.getChecksum());
- result.put("useSpecifiedURL", false);
- result.put("clusters", clusters);
- result.put("env", env);
- result.set("hosts", JacksonUtils.createEmptyArrayNode());
- result.set("metadata", JacksonUtils.transferToJsonNode(service.getMetadata()));
- return result;
- }
- Map<Boolean, List<Instance>> ipMap = new HashMap<>(2);
- ipMap.put(Boolean.TRUE, new ArrayList<>());
- ipMap.put(Boolean.FALSE, new ArrayList<>());
- for (Instance ip : srvedIPs) {
- ipMap.get(ip.isHealthy()).add(ip);
- }
- if (isCheck) {
- result.put("reachProtectThreshold", false);
- }
- double threshold = service.getProtectThreshold();
- if ((float) ipMap.get(Boolean.TRUE).size() / srvedIPs.size() <= threshold) {
- Loggers.SRV_LOG.warn("protect threshold reached, return all ips, service: {}", serviceName);
- if (isCheck) {
- result.put("reachProtectThreshold", true);
- }
- ipMap.get(Boolean.TRUE).addAll(ipMap.get(Boolean.FALSE));
- ipMap.get(Boolean.FALSE).clear();
- }
- if (isCheck) {
- result.put("protectThreshold", service.getProtectThreshold());
- result.put("reachLocalSiteCallThreshold", false);
- return JacksonUtils.createEmptyJsonNode();
- }
- ArrayNode hosts = JacksonUtils.createEmptyArrayNode();
- for (Map.Entry<Boolean, List<Instance>> entry : ipMap.entrySet()) {
- List<Instance> ips = entry.getValue();
- if (healthyOnly && !entry.getKey()) {
- continue;
- }
- for (Instance instance : ips) {
- // remove disabled instance:
- if (!instance.isEnabled()) {
- continue;
- }
- ObjectNode ipObj = JacksonUtils.createEmptyJsonNode();
- ipObj.put("ip", instance.getIp());
- ipObj.put("port", instance.getPort());
- // deprecated since nacos 1.0.0:
- ipObj.put("valid", entry.getKey());
- ipObj.put("healthy", entry.getKey());
- ipObj.put("marked", instance.isMarked());
- ipObj.put("instanceId", instance.getInstanceId());
- ipObj.set("metadata", JacksonUtils.transferToJsonNode(instance.getMetadata()));
- ipObj.put("enabled", instance.isEnabled());
- ipObj.put("weight", instance.getWeight());
- ipObj.put("clusterName", instance.getClusterName());
- if (clientInfo.type == ClientInfo.ClientType.JAVA
- && clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) {
- ipObj.put("serviceName", instance.getServiceName());
- } else {
- ipObj.put("serviceName", NamingUtils.getServiceName(instance.getServiceName()));
- }
- ipObj.put("ephemeral", instance.isEphemeral());
- hosts.add(ipObj);
- }
- }
- result.replace("hosts", hosts);
- if (clientInfo.type == ClientInfo.ClientType.JAVA
- && clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) {
- result.put("dom", serviceName);
- } else {
- result.put("dom", NamingUtils.getServiceName(serviceName));
- }
- result.put("name", serviceName);
- result.put("cacheMillis", cacheMillis);
- result.put("lastRefTime", System.currentTimeMillis());
- result.put("checksum", service.getChecksum());
- result.put("useSpecifiedURL", false);
- result.put("clusters", clusters);
- result.put("env", env);
- result.replace("metadata", JacksonUtils.transferToJsonNode(service.getMetadata()));
- return result;
- }
在上面的核心邏輯主要是:
- 調(diào)用 service.srvIPs 方法查詢所有的服務(wù)實(shí)例信息
- Cluster#allIPs會(huì)將所有的服務(wù)注冊(cè)信息寫(xiě)到服務(wù)注冊(cè)列表。
參考鏈接
https://nacos.io
https://zhuanlan.zhihu.com
https://blog.csdn.net/f641385712/article/details/100788040