Nacos整合SpringCloud的自动注册原理

问题引出

单独的nacos项目中分为client端和server端,client端会调用register去向server端注册,但是我们在做微服务的时候引入的并不是单独的nacos客户端的jar包,而是springcloud alibaba nacos这个jar包,当我们引入这个jar包之后,就可以在项目启动的时候自动地往nacos服务端去注册这个服务了,那么springcloud alibaba nacos这个包能做到自动注册的原理是什么呢?

Nacos,SpringCloud Alibaba,SpringCloud三者之间的关系

1.naocs

nacos项目我们可以单独去download下来,里面主要包含了client和server等模块,如下图中的nacos-all就是整个nacos项目

我们如果想单独使用这个nacos项目去运行的话就需要去手动地去调用client模块的register方法去向server端模块去注册服务,但是很明显我们并不会这样去用,因为现在每出一个流行框架基本都需要去与spring进行集成,毕竟现在spring全家桶时代,不然你一个产品出来之后并不会有多少人去用

2.spring cloud

springcloud是spring对于实现微服务的一整套方案,里面集成了很多并不是spring官方开发的组件,这些组件会被集成到springcloud中去方便开发者的使用,也正是因为市场上有很多第三方组件,所以springcloud必须要有一个标准去集成这些组件,而其中的springcloud-commons包就是对于这些第三方包的一个抽象的标准,就是说如果有其他的第三方框架想集成到springcloud中来的话,就需要去根据这个标准去实现

3.spring cloud alibaba

springcloud alibaba这个项目里面包含了很多alibaba官方开发的用于微服务的框架组件,比如nacos,sentinel,seata等等,但是我们上面也说过现在你单独的一个原生的组件基本都要与spring去进行集成的,所以spring cloud alibaba就相当于这些原生的组件与springcloud 之间的粘合剂一样,里面开发了很多的starter包把这些组件去集成到springcloud中去了。就拿nacos来说,我们通常使用都是去依赖下面这个包

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

而这个包就是springcloud alibaba项目里面的一个模块

自动注册原理?

我们这里先说出答案,其实在sc alibaba中nacos实现自动注册原理很简单,它其实就是利用了spring的事件机制去完成的。我们上面说过springcloud-commons这个包主要是实现了集成到sc的标准,那么为什么需要这个标准呢?因为可能第三方的组件有很多,它们都想集成到sc中去,那么sc就需要提供一套抽象的标准去让这些第三方组件去实现。而nacos利用spring的事件机制去实现就是用的sc里面对于注册中心的一个标准。那么这个标准具体是怎样的呢?我们下面来看springcloud-commons这个包,来到AbstractAutoServiceRegistration这个类,这个类它主要是完成自动注册的,它完成了对自动注册的一个抽象流程,具体的注册逻辑交给具体的注册中心去完成。首先它是一个抽象类,这个类实现了ApplicationListener接口,并且接口泛型为WebServerInitializedEvent,作用就是这个类具有了监听器的功能,监听的事件为WebServerInitializedEvent,当监听到这个事件的时候就会调用onApplicationEvent方法,所以下面来到它的onApplicationEvent方法

public void onApplicationEvent(WebServerInitializedEvent event) {
   bind(event);
}

public void bind(WebServerInitializedEvent event) {
   ApplicationContext context = event.getApplicationContext();
   if (context instanceof ConfigurableWebServerApplicationContext) {
      if ("management".equals(((ConfigurableWebServerApplicationContext) context)
            .getServerNamespace())) {
         return;
      }
   }
   this.port.compareAndSet(0, event.getWebServer().getPort());
   this.start();
}

public void start() {
   if (!isEnabled()) {
      if (logger.isDebugEnabled()) {
         logger.debug("Discovery Lifecycle disabled. Not starting");
      }
      return;
   }

   // only initialize if nonSecurePort is greater than 0 and it isn't already running
   // because of containerPortInitializer below
   if (!this.running.get()) {
      this.context.publishEvent(
            new InstancePreRegisteredEvent(this, getRegistration()));
      register();
      if (shouldRegisterManagement()) {
         registerManagement();
      }
      this.context.publishEvent(
            new InstanceRegisteredEvent<>(this, getConfiguration()));
      this.running.compareAndSet(false, true);
   }

}

可以看到最终调到了start方法,而这个start方法里面又调了一个register方法,这个方法就和注册有关

protected void register() {
   this.serviceRegistry.register(getRegistration());
}

里面调用了serviceRegistry的register方法,这个serviceRegistry它是一个接口,里面定义了服务注册,服务销毁,服务更新等方法,证明了肯定有对应的实现类去实现了它

public interface ServiceRegistry<R extends Registration> {

   /**
    * Registers the registration. A registration typically has information about an
    * instance, such as its hostname and port.
    * @param registration registration meta data
    */
   void register(R registration);

   /**
    * Deregisters the registration.
    * @param registration registration meta data
    */
   void deregister(R registration);

   /**
    * Closes the ServiceRegistry. This is a lifecycle method.
    */
   void close();

   /**
    * Sets the status of the registration. The status values are determined by the
    * individual implementations.
    * @param registration The registration to update.
    * @param status The status to set.
    * @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
    */
   void setStatus(R registration, String status);

   /**
    * Gets the status of a particular registration.
    * @param registration The registration to query.
    * @param <T> The type of the status.
    * @return The status of the registration.
    * @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
    */
   <T> T getStatus(R registration);

}

小总结:这个类的作用就是抽象了自动注册的模板过程,而实现它能实现自动注册的原理在于它实现了ApplicationListener接口,当收到了WebServerInitializedEvent事件就会去调用注册方法,对于具体的注册逻辑就交给具体的实现类去实现(比如说nacos,这里就调用nacos client去完成注册)

既然AbstractAutoServiceRegistration和ServiceRegistry都是抽象类或者接口,那么肯定会有对应的实现类去实现它们,而我们上面也说了这是sc所指定的标准,所以具体的实现应该是由具体的一个框架去提供的,所以sc alibaba nacos里面就肯定会有对这个标准的实现

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

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

   @Bean
   @ConditionalOnBean(AutoServiceRegistrationProperties.class)
   public NacosRegistration nacosRegistration(
         NacosDiscoveryProperties nacosDiscoveryProperties,
         ApplicationContext context) {
      return new NacosRegistration(nacosDiscoveryProperties, context);
   }

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

在springcloud-alibaba-nacos-discovery包中我们可以找到NacosDiscoveryAutoConfiguration这个配置类,从这个配置类里面可以看到它往容器中注入了3个bean,而这个3个bean都是继承sc的标准去实现的,首先看下NacosServiceRegistry

@Override
public void register(Registration registration) {

   if (StringUtils.isEmpty(registration.getServiceId())) {
      log.warn("No service to register for nacos client...");
      return;
   }

   String serviceId = registration.getServiceId();
   String group = nacosDiscoveryProperties.getGroup();

    //根据registration对象转换成Instance对象
    //registration对象是sc定义的实例对象,而Instance对象是nacos的实例对象
   Instance instance = getNacosInstanceFromRegistration(registration);

   try {
       //调用nacos client端的注册api往server端注册服务
      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);
   }
}

这个类就是sc-alibaba-nacos对ServiceRegistry接口的实现,其中上面的register方法里面调用的是nacos原生的注册api,但是这个类要生效得被spring所管理吧?所以还需要去扩展springboot,在spring.factories文件中加入这个类的全类名

 总结

当我们在pom文件中加入springcloud-alibaba-nacos-discovery包的时候,在springboot启动的时候就能把NacosDiscoveryAutoConfiguration这个加载到spring容器中,进而就能够把里面被@Bean注解标注的NacosServiceRegistry,NacosRegistration ,NacosAutoServiceRegistration也加入到spring容器中,而NacosAutoServiceRegistration这个类实现了ApplicationListener接口,所以它具有了监听器的功能,监听的事件是WebInitializedEvent,而这个事件产生的时机是当springboot容器启动的时候(此时容器中的bean已经初始化完成了),也就是说在springboot启动的时候,这个类会监听到WebInitializedEvent事件进而调用其onApplicationEvent方法,而在这个方法中就是调用注册方法了,具体的注册逻辑会交给具体的注册组件去实现,这里就是NacosServiceRegistry调用了nacos原生的注册api,到此为止,整个springcloud-alibaba-nacos-discovery包的自动注册已经完成了

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值