Spring Cloud Nacos source code explanation (2) - Nacos client service registration source code analysis

Nacos client service registration source code analysis

Service Registration Information

​ Let's start with Nacos-Client, then when it comes to the client, it involves service registration. Let's first understand what information the Nacos client will pass to the server. Let's start directly from the NamingTest of the Nacos Client project

public class NamingTest {
    
    
    
    @Test
    public void testServiceList() throws Exception {
    
    
        
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848");
        properties.put(PropertyKeyConst.USERNAME, "nacos");
        properties.put(PropertyKeyConst.PASSWORD, "nacos");
        
        Instance instance = new Instance();
        instance.setIp("1.1.1.1");
        instance.setPort(800);
        instance.setWeight(2);
        Map<String, String> map = new HashMap<String, String>();
        map.put("netType", "external");
        map.put("version", "2.0");
        instance.setMetadata(map);
    
        NamingService namingService = NacosFactory.createNamingService(properties);
        namingService.registerInstance("nacos.test.1", instance);
        
        ThreadUtils.sleep(5000L);
        
        List<Instance> list = namingService.getAllInstances("nacos.test.1");
        
        System.out.println(list);
        
        ThreadUtils.sleep(30000L);
        //        ExpressionSelector expressionSelector = new ExpressionSelector();
        //        expressionSelector.setExpression("INSTANCE.metadata.registerSource = 'dubbo'");
        //        ListView<String> serviceList = namingService.getServicesOfServer(1, 10, expressionSelector);
        
    }
}

​ In fact, this is a test class for client registration. It imitates the process of registering a real service into Nacos, including NacosServer connection, instance creation, instance property assignment, and registered instance, so this includes service registration. The core code, only from the code analysis here, it can be seen that when Nacos registers a service instance, it contains two types of information: Nacos Server connection information and instance information.

Nacos Server connection information

Nacos Server connection information, stored in Properties, includes the following information:

  • Server address: Nacos server address, the attribute key is serverAddr;
  • Username: the username to connect to the Nacos service, the attribute key is username, and the default value is nacos;
  • Password: the password to connect to the Nacos service, the attribute key is password, and the default value is nacos;

instance information

The registered instance information is carried by the Instance object, and the registered instance information is divided into two parts: basic instance information and metadata.

Basic instance information includes :

  • instanceId: the unique ID of the instance;
  • ip: Instance IP, the address provided to consumers for communication;
  • port: port, the port provided for consumers to access;
  • weight: weight, the authority of the current instance, floating point type (default 1.0D);
  • healthy: health status, default true;
  • enabled: whether the instance is ready to receive requests, the default is true;
  • ephemeral: Whether the instance is transient, the default is true;
  • clusterName: the name of the cluster to which the instance belongs;
  • serviceName: service information of the instance;

The Instance class contains not only the basic information of the instance, but also the metadata (data describing the data) used to store metadata . The type is HashMap. From the current demo, we can know that two data are stored:

  • netType: As the name suggests, the network type, the value here is external, which means external network;
  • version: version, Nacos version, here is the big version 2.0.

In addition to the "custom" information in the Demo, some default information is also defined in the Instance class, which is provided through the get method:

public long getInstanceHeartBeatInterval() {
    
    
    return getMetaDataByKeyWithDefault(PreservedMetadataKeys.HEART_BEAT_INTERVAL,
                                       Constants.DEFAULT_HEART_BEAT_INTERVAL);
}

public long getInstanceHeartBeatTimeOut() {
    
    
    return getMetaDataByKeyWithDefault(PreservedMetadataKeys.HEART_BEAT_TIMEOUT,
                                       Constants.DEFAULT_HEART_BEAT_TIMEOUT);
}

public long getIpDeleteTimeout() {
    
    
    return getMetaDataByKeyWithDefault(PreservedMetadataKeys.IP_DELETE_TIMEOUT,
                                       Constants.DEFAULT_IP_DELETE_TIMEOUT);
}

public String getInstanceIdGenerator() {
    
    
    return getMetaDataByKeyWithDefault(PreservedMetadataKeys.INSTANCE_ID_GENERATOR,
                                       Constants.DEFAULT_INSTANCE_ID_GENERATOR);
}

The above get method will be used when metadata default values ​​are required:

  • preserved.heart.beat.interval: The key of the heartbeat interval, the default is 5s, that is, a heartbeat is performed every 5 seconds by default;
  • preserved.heart.beat.timeout: the heartbeat timeout key, the default is 15s, that is, if the heartbeat is not received within 15 seconds by default, the instance will be marked as unhealthy;
  • preserved.ip.delete.timeout: The key for instance IP to be deleted, the default is 30s, that is, if the heartbeat is not received for 30 seconds, the instance will be removed;
  • preserved.instance.id.generator: instance ID generator key, the default is simple;

These are the default values ​​provided by Nacos, that is, when the current instance is registered, it will tell Nacos Server: What are the corresponding values ​​of my heartbeat interval, heartbeat timeout, etc., and you can judge whether my instance is healthy according to this value.

With this information, we basically already know what parameters need to be passed when registering an instance, and what parameters need to be configured.

NamingService interface

​ The NamingService interface is a unified interface provided by the Nacos naming service. You can find it by looking at the corresponding source code. It provides a large number of instance-related interface methods:

  • Service instance registration

    void registerInstance(...) throws NacosException;
    
  • Service instance logout

    void deregisterInstance(...) throws NacosException;
    
  • Get a list of service instances

    List<Instance> getAllInstances(...) throws NacosException;
    
  • Query health service instance

    List<Instance> selectInstances(...) throws NacosException;
    
  • Query healthy service instances in the cluster

    List<Instance> selectInstances(....List<String> clusters....)throws NacosException;
    
  • Use a load balancing strategy to select a healthy service instance

    Instance selectOneHealthyInstance(...) throws NacosException;
    
  • Subscribe to service events

    void subscribe(...) throws NacosException;
    
  • Unsubscribe from service events

    void unsubscribe(...) throws NacosException;
    
  • Get all (or specified) service names

    ListView<String> getServicesOfServer(...) throws NacosException;
    
  • Get all subscribed services

     List<ServiceInfo> getSubscribeServices() throws NacosException;
    
  • Get the status of Nacos service

    String getServerStatus();
    
  • Actively shut down the service

    void shutDown() throws NacosException
    

A large number of overloaded methods are provided in these methods, which are applied to different scenarios and different types of instances or services, so we only need to use different methods in different situations.

The NamingService is instantiated through the NamingFactory class and the above Nacos service information. It can be seen from the code that the reflection mechanism is used to instantiate the NamingService. The specific implementation class is NacosNamingService:

public static NamingService createNamingService(Properties properties) throws NacosException {
    
    
    try {
    
    
        Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService");
        Constructor constructor = driverImplClass.getConstructor(Properties.class);
        return (NamingService) constructor.newInstance(properties);
    } catch (Throwable e) {
    
    
        throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);
    }
}

Implementation of NacosNamingService

​ In the sample code, the registerInstance method of NamingService is used to register the service instance. This method receives two parameters, the service name and the instance object. The biggest function of this method is to set the grouping information of the current instance. We know that in Nacos, instances are isolated from the environment layer by layer through Namespace, group, Service, Cluster, etc. The default grouping is set here as "DEFAULT_GROUP".

@Override
public void registerInstance(String serviceName, Instance instance) throws NacosException {
    
    
    registerInstance(serviceName, Constants.DEFAULT_GROUP, instance);
}

The registerInstance method to be called immediately is as follows. This method implements two functions:

​ First, check whether the heartbeat time setting is correct (the heartbeat defaults to 5 seconds)

​ Second, perform the service registration operation through the NamingClientProxy proxy

@Override
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
    
    
    NamingUtils.checkInstanceIsLegal(instance);//检查心跳
    clientProxy.registerService(serviceName, groupName, instance);//通过代理执行服务注册操作
}

Through clientProxy, we found that the specific implementation of the NamingClientProxy proxy interface is completed by NamingClientProxyDelegate, which can be seen from the NacosNamingService construction method.

public NacosNamingService(Properties properties) throws NacosException {
    
    
    init(properties);
}

initialization in the init method

private void init(Properties properties) throws NacosException {
    
    
    ValidatorUtils.checkInitParam(properties);
    this.namespace = InitUtils.initNamespaceForNaming(properties);
    InitUtils.initSerialization();
    InitUtils.initWebRootContext(properties);
    initLogName(properties);

    this.changeNotifier = new InstancesChangeNotifier();
    NotifyCenter.registerToPublisher(InstancesChangeEvent.class, 16384);
    NotifyCenter.registerSubscriber(changeNotifier);
    this.serviceInfoHolder = new ServiceInfoHolder(namespace, properties);
    this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, properties, changeNotifier);//在这里进行了初始化,并看出使用的是NamingClientProxyDelegate来完成的
}

Implemented in NamingClientProxyDelegate

According to the above analysis and reading of the source code, we can find that NamingClientProxy calls registerService actually calls the corresponding method of NamingClientProxyDelegate:

@Override
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
    
    
    getExecuteClientProxy(instance).registerService(serviceName, groupName, instance);
}

It is not the proxy implementation class that actually invokes the registration service, but selects the corresponding client proxy to make the request according to whether the current instance is a transient object:

If the current instance is a transient object, use the gRPC protocol (NamingGrpcClientProxy) to make the request, otherwise use the http protocol (NamingHttpClientProxy) to make the request. The default is a transient object, that is to say, in version 2.0, the gRPC protocol is used by default to interact with Nacos services.

private NamingClientProxy getExecuteClientProxy(Instance instance) {
    
    
    return instance.isEphemeral() ? grpcClientProxy : httpClientProxy;
}

Implemented in NamingGrpcClientProxy

Regarding the gRPC protocol (NamingGrpcClientProxy), we will continue to expand it. We will mainly focus on the implementation of the registerService method. There are actually two things done here.

  1. The currently registered instance information is cached for recovery. The cached data structure is ConcurrentMap<String, Instance>, the key is "serviceName@@groupName", and the value is the previously encapsulated instance information.
  2. Another thing is to encapsulate the parameters, call the service and process the result based on gRPC.
@Override
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
    
    
    NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance {}", namespaceId, serviceName,
                       instance);
    redoService.cacheInstanceForRedo(serviceName, groupName, instance);//缓存数据
    doRegisterService(serviceName, groupName, instance);//基于gRPC进行服务的调用
}

Summary process

Summary flow chart:

insert image description here

Guess you like

Origin blog.csdn.net/qq_27566167/article/details/129155285