(nacos)1.4.1注册过程源码解析

299 阅读3分钟

源码环境搭建

源码下载地址github.com/alibaba/nac…

文件目录

20220831-111120.jpg
由于nacos采用了protobuf,在consistency模块下会有一些类缺失。

protobuf是谷歌内部的混合语言数据标准。通过将结构化的数据进行序列化(串行化),用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

解决方案

  1. 插件下载 飞书20220831-112950.jpg
  2. 通过mvn copmpile来在target自动生成

启动
添加参数-Dnacos.standalone=true(单机启动)

飞书20220831-112950.jpg

服务注册(客户端)

导入nacos依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

20220831-154625.jpg
找到springboot自动装配下扫描类路径下的META-INF/spring.factories文件

20220831-154925.jpg 配置文件

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled",
      matchIfMissing = true)
@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,
      AutoServiceRegistrationAutoConfiguration.class,
      NacosDiscoveryAutoConfiguration.class })
public class NacosServiceRegistryAutoConfiguration {

   @Bean
   public NacosServiceRegistry nacosServiceRegistry(
         NacosDiscoveryProperties nacosDiscoveryProperties) {
      return new NacosServiceRegistry(nacosDiscoveryProperties);
   }

   @Bean
   @ConditionalOnBean(AutoServiceRegistrationProperties.class)
   public NacosRegistration nacosRegistration(
         ObjectProvider<List<NacosRegistrationCustomizer>> registrationCustomizers,
         NacosDiscoveryProperties nacosDiscoveryProperties,
         ApplicationContext context) {
      return new NacosRegistration(registrationCustomizers.getIfAvailable(),
            nacosDiscoveryProperties, context);
   }

   @Bean
   @ConditionalOnBean(AutoServiceRegistrationProperties.class)
   public NacosAutoServiceRegistration nacosAutoServiceRegistration(
         NacosServiceRegistry registry,
         AutoServiceRegistrationProperties autoServiceRegistrationProperties,
         NacosRegistration registration) {
       //核心 
      return new NacosAutoServiceRegistration(registry,
            autoServiceRegistrationProperties, registration);
   }

}

NacosAutoServiceRegistration
看上面配置可知NacosAutoServiceRegistration作为Bean是要依赖于前两项的Bean(NacosServiceRegistry和NacosRegistration)
类结构图

NacosAutoServiceRegistration.png 实现了ApplicationListener事件监听机制,那么就会重写他的onApplicationEvent()

AbstractAutoServiceRegistration#bind(WebServerInitializedEvent event)

public void onApplicationEvent(WebServerInitializedEvent event) {
    this.bind(event);
}
public void bind(WebServerInitializedEvent event) {
    ApplicationContext context = event.getApplicationContext();
    if (!(context instanceof ConfigurableWebServerApplicationContext) || !"management".equals(((ConfigurableWebServerApplicationContext)context).getServerNamespace())) {
        this.port.compareAndSet(0, event.getWebServer().getPort());
        //开始
        this.start();
    }
}
public void start() {
    if (!this.isEnabled()) {
        if (logger.isDebugEnabled()) {
            logger.debug("Discovery Lifecycle disabled. Not starting");
        }

    } else {
        if (!this.running.get()) {
            this.context.publishEvent(new InstancePreRegisteredEvent(this, this.getRegistration()));
            //主要看这个注册
            this.register();
            if (this.shouldRegisterManagement()) {
                this.registerManagement();
            }

            this.context.publishEvent(new InstanceRegisteredEvent(this, this.getConfiguration()));
            this.running.compareAndSet(false, true);
        }

    }
}
public void register(Registration registration) {

   if (StringUtils.isEmpty(registration.getServiceId())) {
      log.warn("No service to register for nacos client...");
      return;
   }
   //nacos名字服务
   NamingService namingService = namingService();
   //注册服务id
   String serviceId = registration.getServiceId();
   //分组名
   String group = nacosDiscoveryProperties.getGroup();
   //当前实例信息
   Instance instance = getNacosInstanceFromRegistration(registration);

   try {
      //注册实例
      namingService.registerInstance(serviceId, group, instance);
      log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
            instance.getIp(), instance.getPort());
   }
   catch (Exception e) {
      log.error("nacos registry, {} register failed...{},", serviceId,
            registration.toString(), e);
      // rethrow a RuntimeException if the registration is failed.
      // issue : https://github.com/alibaba/spring-cloud-alibaba/issues/1132
      rethrowRuntimeException(e);
   }
}
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
    NamingUtils.checkInstanceIsLegal(instance);
    String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
    if (instance.isEphemeral()) {
        //构建心跳
        BeatInfo beatInfo = this.beatReactor.buildBeatInfo(groupedServiceName, instance);
        //心跳执行器
        this.beatReactor.addBeatInfo(groupedServiceName, beatInfo);
    }
    //注册服务
    this.serverProxy.registerService(groupedServiceName, groupName, instance);
}
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
    LogUtils.NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}", new Object[]{this.namespaceId, serviceName, instance});
    Map<String, String> params = new HashMap(16);
    params.put("namespaceId", this.namespaceId);
    params.put("serviceName", serviceName);
    params.put("groupName", groupName);
    params.put("clusterName", instance.getClusterName());
    params.put("ip", instance.getIp());
    params.put("port", String.valueOf(instance.getPort()));
    params.put("weight", String.valueOf(instance.getWeight()));
    params.put("enable", String.valueOf(instance.isEnabled()));
    params.put("healthy", String.valueOf(instance.isHealthy()));
    params.put("ephemeral", String.valueOf(instance.isEphemeral()));
    params.put("metadata", JacksonUtils.toJson(instance.getMetadata()));
    //这里就是给nacos服务端发送远程请求去注册(/nacos/v1/ns/instance)
    this.reqApi(UtilAndComs.nacosUrlInstance, params, "POST");
}

服务注册(服务端)

根据刚刚服务端发起的注册地址/nacos/v1/ns/instance,我们在nacos源码找到对应的接口层 InstanceController#register(HttpServletRequest request)

public String register(HttpServletRequest request) throws Exception {
    
    final String namespaceId = WebUtils
            .optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
    final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
    NamingUtils.checkServiceNameFormat(serviceName);
    //从请求中解析实例
    final Instance instance = parseInstance(request);
    //注册实例
    serviceManager.registerInstance(namespaceId, serviceName, instance);
    return "ok";
}
public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
    //当前服务第一次注册时,创建空的实例并未放置实例进去
    createEmptyService(namespaceId, serviceName, instance.isEphemeral());
    //从map中获取service
    Service service = getService(namespaceId, serviceName);

    if (service == null) {
        throw new NacosException(NacosException.INVALID_PARAM,
                "service not found, namespace: " + namespaceId + ", service: " + serviceName);
    }
    //新增实例(写时复制)
    addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
}
private List<Instance> addIpAddresses(Service service, boolean ephemeral, Instance... ips) throws NacosException {
    return updateIpAddresses(service, UtilsAndCommons.UPDATE_INSTANCE_ACTION_ADD, ephemeral, ips);
}
public List<Instance> updateIpAddresses(Service service, String action, boolean ephemeral, Instance... ips)
        throws NacosException {

    Datum datum = consistencyService
            .get(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), ephemeral));
    //获取当前已注册的服务集群节点
    List<Instance> currentIPs = service.allIPs(ephemeral);
    Map<String, Instance> currentInstances = new HashMap<>(currentIPs.size());
    Set<String> currentInstanceIds = Sets.newHashSet();
    //将已注册的服务集群节点信息复制
    for (Instance instance : currentIPs) {
        currentInstances.put(instance.toIpAddr(), instance);
        currentInstanceIds.add(instance.getInstanceId());
    }

    Map<String, Instance> instanceMap;
    if (datum != null && null != datum.value) {
        instanceMap = setValid(((Instances) datum.value).getInstanceList(), currentInstances);
    } else {
        instanceMap = new HashMap<>(ips.length);
    }

    for (Instance instance : ips) {
        if (!service.getClusterMap().containsKey(instance.getClusterName())) {//判断要注册的实例属不属于
            Cluster cluster = new Cluster(instance.getClusterName(), service);
            cluster.init();
            service.getClusterMap().put(instance.getClusterName(), cluster);
            Loggers.SRV_LOG
                    .warn("cluster: {} not found, ip: {}, will create new cluster with default configuration.",
                            instance.getClusterName(), instance.toJson());
        }
        //移除掉节点
        if (UtilsAndCommons.UPDATE_INSTANCE_ACTION_REMOVE.equals(action)) {
            instanceMap.remove(instance.getDatumKey());
        } else {
            //判断当前实例是否已经注册过
            Instance oldInstance = instanceMap.get(instance.getDatumKey());
            if (oldInstance != null) {
                //注册过
                instance.setInstanceId(oldInstance.getInstanceId());
            } else {
                //未注册生成id
                instance.setInstanceId(instance.generateInstanceId(currentInstanceIds));
            }
            //存放到临时map中
            instanceMap.put(instance.getDatumKey(), instance);
        }

    }

    if (instanceMap.size() <= 0 && UtilsAndCommons.UPDATE_INSTANCE_ACTION_ADD.equals(action)) {
        throw new IllegalArgumentException(
                "ip list can not be empty, service: " + service.getName() + ", ip list: " + JacksonUtils
                        .toJson(instanceMap.values()));
    }

    return new ArrayList<>(instanceMap.values());
}