>>>> 😜😜😜 Github: 👉 github.com/black-ant CASE Backup: 👉 gitee.com/antblack/ca…

A. The preface

It is important to understand the whole process of the Nacos Client when some operation exceptions directly break down during Nacos requests.

The main process of Nacos is divided into the following parts:

  • Start and initialize the Nacos Client
  • Nacos Client service registration
  • Nacos Client service discovery

2. Start and initialize the Nacos Client

The automatic assembly class of Nacos is divided into two parts:

  • NacosConfigAutoConfiguration
  • NacosDiscoveryAutoConfiguration

The previous Nacos configuration loading process and priority already see the configuration process, this article focuses on NacosDiscoveryAutoConfiguration

2.1 Nacos Discovery auto-assembly class

During the startup of Nacos, the following objects are initialized in the configuration class:

- new NacosServiceManager() 
- newNacosServiceDiscovery(discoveryProperties, nacosServiceManager) : get the list from the cache -new NacosDiscoveryClient(nacosServiceDiscovery)
Copy the code

2.1.1 Processing of NacosWatch

// C- NacosWatch
public void start(a) {
    
   if (this.running.compareAndSet(false.true)) {
       
       // Step 1: Build the EventListener object
      EventListener eventListener = listenerMap.computeIfAbsent(buildKey(),
            event -> new EventListener() {
               @Override
               public void onEvent(Event event) {
                  if (event instanceofNamingEvent) { List<Instance> instances = ((NamingEvent) event).getInstances(); Optional<Instance> instanceOptional = selectCurrentInstance(instances); instanceOptional.ifPresent(currentInstance -> { resetIfNeeded(currentInstance); }); }}});// Step 2: Prepare the NamingService object by NacosProperties -> 2.1.2 Step 2
      NamingService namingService = nacosServiceManager
            .getNamingService(properties.getNacosProperties());
      try {
      
         namingService.subscribe(properties.getService(), properties.getGroup(),
               Arrays.asList(properties.getClusterName()), eventListener);
      }
      catch (Exception e) {
         log.error("namingService subscribe failed, properties:{}", properties, e);
      }

      this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(
            this::nacosServicesWatch, this.properties.getWatchDelay()); }}Copy the code

2.1.2 Initialization Process

Step1: NacosWatch initiates monitoring

// NamingService init process
private void init(Properties properties) throws NacosException {
    ValidatorUtils.checkInitParam(properties);
    this.namespace = InitUtils.initNamespaceForNaming(properties);
    InitUtils.initSerialization();
    initServerAddr(properties);
    // Initialize the Web container
    InitUtils.initWebRootContext();
    // Initialize the cache directory
    initCacheDir();
    // Initialize the log path
    initLogName(properties);
    
    this.eventDispatcher = new EventDispatcher();
    // The proxy object is used to call the remote Server
    this.serverProxy = new NamingProxy(this.namespace, this.endpoint, this.serverList, properties);
    // Used to send heartbeat of registered service to Nacos server
    this.beatReactor = new BeatReactor(this.serverProxy, initClientBeatThreadCount(properties));
    // HostReactor Is used to obtain, save, and update Service instance information
    this.hostReactor = new HostReactor(this.eventDispatcher, this.serverProxy, beatReactor, this.cacheDir,
            isLoadCacheAtStart(properties), initPollingThreadCount(properties));
}

Copy the code

Step 2: NamingFactory Builds the NamingService

// The final instance object is actually produced by reflectionClass<? > driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService");
Constructor constructor = driverImplClass.getConstructor(Properties.class);
NamingService vendorImpl = (NamingService) constructor.newInstance(properties);

// Workspace
private String namespace;
//
private String endpoint;
// The Server address corresponding to the service list is localhost:8848
private String serverList;
// Local cache address
private String cacheDir;
// Log name: usually naming.log
private String logName;

private HostReactor hostReactor;
private BeatReactor beatReactor;

private EventDispatcher eventDispatcher;
private NamingProxy serverProxy;

Copy the code

Step 3: Add listeners to EventDispatcher

public void subscribe(String serviceName, String groupName, List<String> clusters, EventListener listener)
        throws NacosException {
        
    // Update the Service with a listener
    eventDispatcher.addListener(hostReactor
                    .getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ",")),
            StringUtils.join(clusters, ","), listener);
}


public void addListener(ServiceInfo serviceInfo, String clusters, EventListener listener) {
    
    // Build an EventListener collection
    List<EventListener> observers = Collections.synchronizedList(new ArrayList<EventListener>());
    observers.add(listener);
    
    // ConcurrentMap<String, List<EventListener>> observerMap
    // There is a Notifier thread that continues processing instances through a while loop
    // /nacos/v1/ns/instance
    observers = observerMap.putIfAbsent(ServiceInfo.getKey(serviceInfo.getName(), clusters), observers);
    if(observers ! =null) {
        observers.add(listener);
    }
    
    // Refresh Service when change
    serviceChanged(serviceInfo);
}
Copy the code

HostReactor. GetServiceInfo results:

3. Nacos Client service registration

Service registration involves the following processes:

  • NacosRegistration: Saves basic service data
  • NacosServiceRegistry: Implements service registration
  • NacosServiceRegistryAutoConfiguration: Nacos automatic configuration class

Nacos service @ EnableDiscoveryClient is the starting point of registration, it will eventually call NacosAutoServiceRegistration:

  • AbstractAutoServiceRegistration
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient {
   // The default is true, and AutoServiceRegistration will be invoked when enabled
   boolean autoRegister(a) default true;
}
Copy the code

3.1 start

Different registry, will have different implementation class, here is the corresponding Nacos NacosAutoServiceRegistration

// C- NacosAutoServiceRegistration
public void start(a) {
   // If not enabled, return directly
   
   
   // 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()));
      
      / / call the Registry by registration: enclosing serviceRegistry. Register (getRegistration ());
      register();
      
      if (shouldRegisterManagement()) {
         registerManagement();
      }
      this.context.publishEvent(new InstanceRegisteredEvent<>(this, getConfiguration()));
      this.running.compareAndSet(false.true); }}Copy the code

3.2 NacosServiceRegistry initiates registration

public void register(Registration registration) {

   if (StringUtils.isEmpty(registration.getServiceId())) {
      return;
   }

   // Build the current ServiceId and Group
   NamingService namingService = namingService();
   String serviceId = registration.getServiceId();
   String group = nacosDiscoveryProperties.getGroup();

   // Includes IP, port, assigned name, and metadata
   Instance instance = getNacosInstanceFromRegistration(registration);

   try {
      // Register the current instance and add the heartbeat
      // serverProxy.registerService(groupedServiceName, groupName, instance);
      namingService.registerInstance(serviceId, group, instance);
   }
   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/1132rethrowRuntimeException(e); }}Copy the code

4. Discovery of services

We looked at it a little bit earlier in Nacos basics, but we’re just going to look at it again

Different versions of Nacos are acquired in different ways. Here we will focus on spring-cloud-starter-Alibaba-Nacos-Discovery: version 2.2.5.

4.1 Discovery of Nacos Server

Step 1: Initiate the starting point

private List<NacosServer> getServers(a) {
      String group = discoveryProperties.getGroup();
      List<Instance> instances = discoveryProperties.namingServiceInstance()
            .selectInstances(serviceId, group, true);
      return instancesToServerList(instances);
}
Copy the code

Step 2: Main process of discovery

At this point, the logic is concatenated, followed by a reverse call to the interface provided in Nacos Server

// C- NacosNamingService
public List<Instance> selectInstances(String serviceName, String groupName, List<String> clusters, boolean healthy,
        boolean subscribe) throws NacosException {
    ServiceInfo serviceInfo = hostReactor
                .getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName, groupName),
                        StringUtils.join(clusters, ","));
    return selectInstances(serviceInfo, healthy);
}
Copy the code

ServiceInfo: Map

serviceInfoMap
,>

public void updateServiceNow(String serviceName, String clusters) {
    ServiceInfo oldService = getServiceInfo0(serviceName, clusters);
    try {
        // Invoke the proxy class again
        String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUdpPort(), false);
        
        if(StringUtils.isNotEmpty(result)) { processServiceJson(result); }}catch (Exception e) {
        NAMING_LOGGER.error("[NA] failed to update serviceName: " + serviceName, e);
    } finally {
        if(oldService ! =null) {
            synchronized(oldService) { oldService.notifyAll(); }}}}// Initiate a remote call
public String queryList(String serviceName, String clusters, int udpPort, boolean healthyOnly)
        throws NacosException {
    
    final Map<String, String> params = new HashMap<String, String>(8);
    params.put(CommonParams.NAMESPACE_ID, namespaceId);
    params.put(CommonParams.SERVICE_NAME, serviceName);
    params.put("clusters", clusters);
    params.put("udpPort", String.valueOf(udpPort));
    params.put("clientIP", NetUtils.localIP());
    params.put("healthyOnly", String.valueOf(healthyOnly));
    
    // You can see the API here:
    return reqApi(UtilAndComs.nacosUrlBase + "/instance/list", params, HttpMethod.GET);
}

Copy the code

conclusion

This article is a little simple, not deep understanding, but it should be very useful, when there is a problem at the core of the break point, can save a lot of time

TODO: I will not draw the flow chart today, but I will have time to fill in one later