Principle of automatic service registration when SpringCloud Alibaba Nacos client starts

Posted by chelsea7 on Thu, 20 Jan 2022 01:57:09 +0100

I Problem elicitation

The separate nacos project is divided into client side and server side. The client side will call register to register with the server side. However, when we do micro services, we do not introduce the jar package of the separate nacos client, but the jar package of springcloud alibaba nacos. After we introduce this jar package, You can automatically register this service with the nacos server when the project starts. What is the principle of automatic registration of the springcloud alibaba nacos package?

II The relationship among nacos, springcloud, Alibaba and springcloud

1.naocs

We can download the nacos project separately, which mainly includes modules such as client and server. As shown in the figure below, nacos all is the whole nacos project

If we want to run this nacos project alone, we need to manually call the register method of the client module to register the service with the server module, but obviously we won't use it, because every popular framework now basically needs to be integrated with spring. After all, in the era of spring, Otherwise, you won't have many people to use a product after it comes out

2.spring cloud

Springcloud is a complete set of spring's solution for implementing microservices. It integrates many components that are not officially developed by spring. These components will be integrated into springcloud for the convenience of developers. It is precisely because there are many third-party components in the market that springcloud must have a standard to integrate these components, The spring cloud commons package is an abstract standard for these third-party packages, that is, if other third-party boxes want to be integrated into spring cloud, they need to be implemented according to this standard

3.spring cloud alibaba

The springcloud alibaba project contains many framework components officially developed by alibaba for microservices, such as nacos, sentinel, seata, etc., but as we said above, now your single native component basically needs to be integrated with spring, Therefore, spring cloud alibaba is equivalent to the adhesive between these native components and spring cloud. Many starter packages have been developed to integrate these components into spring cloud. Take nacos for example. We usually rely on the following package

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

This package is a module in the springcloud alibaba project

III How does automatic registration work?

Let's give the answer first. In fact, the principle of automatic registration of nacos in sc alibaba is very simple. In fact, it uses the event mechanism of spring to complete it. As we mentioned above, the spring cloud commons package mainly implements the standard of integration into sc, so why do you need this standard? Because there may be many third-party components that want to be integrated into sc, sc needs to provide a set of abstract standards for these third-party components to implement. nacos uses the event mechanism of spring to implement it, which is a standard for the registry in sc. So what is this standard? Let's look at the springcloud commons package and come to the AbstractAutoServiceRegistration class. This class mainly completes automatic registration. It completes an abstract process of automatic registration, and the specific registration logic is handed over to the specific registration center. Firstly, it is an abstract class. This class implements the ApplicationListener interface, and the generic type of the interface is WebServerInitializedEvent. The function of this class is that it has the function of listener. The event it listens to is WebServerInitializedEvent. When it hears this event, it will call onApplicationEvent method, so let's go to its onApplicationEvent method

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);
   }

}

You can see that the start method is finally called, and a register method is called in the start method, which is related to registration

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

It calls the register method of serviceRegistry, which is an interface. It defines methods such as service registration, service destruction and service update, which proves that there must be corresponding implementation classes to implement it

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);

}

Summary: the function of this class is to abstract the template process of automatic registration. The principle of realizing automatic registration is that it implements the ApplicationListener interface. When the webserverinitializedevevent event is received, it will call the registration method, For the specific registration logic, it is left to the specific implementation class to implement (for example, nacos, here call the nacos client to complete the registration)

Since AbstractAutoServiceRegistration and ServiceRegistry are abstract classes or interfaces, there must be corresponding implementation classes to implement them. As we said above, this is the standard specified by sc, so the specific implementation should be provided by a specific framework, so there must be an implementation of this standard in 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);
   }
}

In the springcloud Alibaba Nacos discovery package, we can find the configuration class NacosDiscoveryAutoConfiguration. From this configuration class, we can see that it injects three beans into the container, and these three beans are implemented by inheriting the sc standard. First, take a look at the 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();

    //Convert to Instance object according to registration object
    //The registration object is an Instance object defined by sc, and the Instance object is an Instance object of nacos
   Instance instance = getNacosInstanceFromRegistration(registration);

   try {
       //Call the registration api on the client side of Nacos to register the service on the server side
      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);
   }
}

This class is the implementation of SC Alibaba nacos's ServiceRegistry interface. The register method above calls the nacos native registration api, but this class must be managed by spring to take effect? Therefore, you also need to extend springboot in spring Add the full class name of this class to the factories file

Summary: when we add the springcloud Alibaba nacos discovery package to the pom file, we can load the NacosDiscoveryAutoConfiguration into the spring container when the springboot starts, and then we can add the NacosServiceRegistry, NacosRegistration and NacosAutoServiceRegistration marked with @ bean annotation into the spring container, NacosAutoServiceRegistration implements the ApplicationListener interface, so it has the function of listener. The event it listens to is WebInitializedEvent, and this event occurs when the springboot container is started (at this time, the beans in the container have been initialized), that is, when the springboot container is started, This class will listen to the WebInitializedEvent event and call its onApplicationEvent method. In this method, the registration method is called. The specific registration logic will be handed over to the specific registration component. Here is the NacosServiceRegistry, which calls the nacos native registration api. That's all, The automatic registration of the entire spring cloud Alibaba nacos discovery package has been completed

Topics: Java Back-end Spring Cloud Nacos