SpringBoot整合Nacos服務發現原理

2024年2月6日 18点热度 0人点赞

前言

上兩篇文章我分析了在SpringBoot啟動時候會註冊該服務到Nacos,那我們考慮過服務發現時候為什麼會從Nacos獲取服務實例嗎?服務發現的底層是Ribbon,默認不是從Nacos獲取服務實例列表的,可以從RibbonClientConfiguration的自動配置類得到答案,代碼截圖如下:

RibbonClientConfiguration自動配置類部分截圖

可以看到默認的服務是ConfigurationBasedServerList,那麼為什麼引入Nacos會從Nacos服務器獲取到服務列表嗎?下面我就揭開它的神秘面紗。

源碼分析

上文說過,引入Nacos服務發現與註冊的jar包以後,會在spring.factories文件中有以下配置:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration,\
  com.alibaba.cloud.nacos.ribbon.RibbonNacosAutoConfiguration,\
  com.alibaba.cloud.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration,\
  com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration,\
  com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration,\
  com.alibaba.cloud.nacos.discovery.reactive.NacosReactiveDiscoveryClientConfiguration,\
  com.alibaba.cloud.nacos.discovery.configclient.NacosConfigServerAutoConfiguration,\
  com.alibaba.cloud.nacos.NacosServiceAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
  com.alibaba.cloud.nacos.discovery.configclient.NacosDiscoveryClientConfigServiceBootstrapConfiguration
org.springframework.context.ApplicationListener=\
  com.alibaba.cloud.nacos.discovery.logging.NacosLoggingListener

這裡面引入了一個自動配置類RibbonNacosAutoConfiguration,可以從名字看出來時Nacos與Ribbon的整合配置類,該類的定義截圖如下:

RibbonNacosAutoConfiguration自動配置類

其中引入了一個@RibbonClients(defaultConfiguration = NacosRibbonClientConfiguration.class)註解,這個註解裡面內容如下:


package org.springframework.cloud.netflix.ribbon;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration(proxyBeanMethods = false)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@Documented
@Import(RibbonClientConfigurationRegistrar.class)
public @interface RibbonClients {
   RibbonClient[] value() default {};
   Class<?>[] defaultConfiguration() default {};
}

可以看出導入了一個RibbonClientConfigurationRegistrar類,該類實現了ImportBeanDefinitionRegistrar接口,由SpringBoot原理會調用registerBeanDefinitions方法,該方法的定義如下:

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
      BeanDefinitionRegistry registry) {
   Map<String, Object> attrs = metadata
         .getAnnotationAttributes(RibbonClients.class.getName(), true);
   if (attrs != null && attrs.containsKey("value")) {
      AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");
      for (AnnotationAttributes client : clients) {
         registerClientConfiguration(registry, getClientName(client),
               client.get("configuration"));
      }
   }
   if (attrs != null && attrs.containsKey("defaultConfiguration")) {
      String name;
      if (metadata.hasEnclosingClass()) {
         name = "default."   metadata.getEnclosingClassName();
      }
      else {
         name = "default."   metadata.getClassName();
      }
      registerClientConfiguration(registry, name,
            attrs.get("defaultConfiguration"));
   }
   Map<String, Object> client = metadata
         .getAnnotationAttributes(RibbonClient.class.getName(), true);
   String name = getClientName(client);
   if (name != null) {
      registerClientConfiguration(registry, name, client.get("configuration"));
   }

在@RibbonClient註解上配置了一個defaultConfiguration的屬性是NacosRibbonClientConfiguration,那麼會調用到registerClientConfiguration方法,該方法如下:

private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
      Object configuration) {
   BeanDefinitionBuilder builder = BeanDefinitionBuilder
         .genericBeanDefinition(RibbonClientSpecification.class);
   builder.addConstructorArgValue(name);
   builder.addConstructorArgValue(configuration);
   registry.registerBeanDefinition(name   ".RibbonClientSpecification",
         builder.getBeanDefinition());
}

往容器中自動註入了一個Bean類型是RibbonClientSpecification, 名稱是default.com.alibaba.cloud.nacos.ribbon.RibbonNacosAutoConfiguration.RibbonClientSpecification的Bean,構造了RibbonClientSpecification的構造函數的兩個參數,分別為name,configuration,由Ribbon的原理可知會加載RibbonClientSpecification類的configuration屬性,會把configuration對應類裡面的對象全部加載到容器中,那麼我們就關註這個configuration的類型是NacosRibbonClientConfiguration,這裡面都有什麼內容:

package com.alibaba.cloud.nacos.ribbon;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ServerList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.netflix.ribbon.PropertiesFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@ConditionalOnRibbonNacos
public class NacosRibbonClientConfiguration {
   @Autowired
   private PropertiesFactory propertiesFactory;
   @Bean
   @ConditionalOnMissingBean
   public ServerList<?> ribbonServerList(IClientConfig config,
         NacosDiscoveryProperties nacosDiscoveryProperties) {
      if (this.propertiesFactory.isSet(ServerList.class, config.getClientName())) {
         ServerList serverList = this.propertiesFactory.get(ServerList.class, config,
               config.getClientName());
         return serverList;
      }
      NacosServerList serverList = new NacosServerList(nacosDiscoveryProperties);
      serverList.initWithNiwsConfig(config);
      return serverList;
   }
   @Bean
   @ConditionalOnMissingBean
   public NacosServerIntrospector nacosServerIntrospector() {
      return new NacosServerIntrospector();
   }
}

可以看出新創建出一個NacosServerList對象,裡面包含了NacosServer對象,NacosServer對象會連接服務器獲取到所有已經註冊的對象,這樣就能解釋了為什麼引入Nacos以後會從Nacos服務器獲取已註冊的實例。

好了,至此SpringBoot整合Nacos服務註冊原理和配置中心的原理已經分析完了,如果喜歡我的文章請點贊關註,我會持續更新源碼解析的文章,請大傢持續關註哦。