Spring Cloud Nacos Source Code Explanation (4) - Nacos Server Service Registration Source Code Analysis

Nacos server service registration source code analysis

server call interface

​ We already know that when the client registers the service, it actually calls the NamingService.registerInstance method to complete the instance registration, and at the end we also told everyone that the service registration is actually the corresponding interface called nacos/ v1/ns/instance, then we will find this interface on the server side first, and then look at the operation of the specific server side.

[External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (img-HqhidJZd-1677029146341)(1561217892717-1418fb9b-7faa-4324-87b9-f1740329f564.jpeg)]

​ This is the Nacos architecture diagram we saw from the Nacos official website. In fact, here we can already analyze that the interface we are looking for should be in the NamingService service. From the source code point of view, in fact, through this project structure diagram We can also clearly see the naming submodule, and we have analyzed with you in the first class of the source code that this naming is actually to realize the registration of the service.

[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-o0jP48fo-1677029146343)(20190703005808640.png)]

​ Then let's look down at the controller in this project , because we know that all the interfaces are actually in the controller. From these Controllers, we will clearly see an InstanceController , so it is obvious that the registered instance must be related to it

[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-NF0ihqte-1677029146345)(image-20211019160948545.png)]

​ So we open the InstanceController to study it in depth. At this time, we will find that the value in the @RequestMapping annotation is the registration interface we access

[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-QUvapo4d-1677029146346)(image-20211019161614434.png)]

[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-FjkJ5rvQ-1677029146346)(image-20211019161726808.png)]

​ Next, let's look for the method register of the POST request type of the RESTful API interface. In this method, it actually accepts the user request, parses the received information, restores it to Instance, and then calls the registerInstance method to complete the registration. This method is It is the core of server registration

@CanDistro
@PostMapping
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
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 = HttpRequestInstanceBuilder.newBuilder()
        .setDefaultInstanceEphemeral(switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build();
	//注册服务实例
    getInstanceOperator().registerInstance(namespaceId, serviceName, instance);
    return "ok";
}

Everyone, let's pay attention to this method in this position

getInstanceOperator().registerInstance(namespaceId, serviceName, instance);

​ Among them, getInstanceOperator() is to judge whether to adopt the Grpc protocol. Obviously, this position is instanceServiceV2

private InstanceOperator getInstanceOperator() {
    
    
    return upgradeJudgement.isUseGrpcFeatures() ? instanceServiceV2 : instanceServiceV1;
}

service registration

instanceServiceV2.registerInstance

​ In fact, instanceServiceV2 is InstanceOperatorClientImpl, so let's look at the registerInstance method here

@Override
public void registerInstance(String namespaceId, String serviceName, Instance instance) {
    
    
    //判断是否为瞬时对象(临时客户端)
    boolean ephemeral = instance.isEphemeral();
    //获取客户端ID
    String clientId = IpPortBasedClient.getClientId(instance.toInetAddr(), ephemeral);
    //通过客户端ID创建客户端连接
    createIpPortClientIfAbsent(clientId);
    //获取服务
    Service service = getService(namespaceId, serviceName, ephemeral);
    //具体注册服务
    clientOperationService.registerInstance(service, instance, clientId);
}

​ Here we need to analyze some details. In fact, the Client model has been added after Nacos2.0 . A client gRPC persistent connection corresponds to a Client, and each Client has its own unique id (clientId) . Client is responsible for managing a client's service instance registration Publish and service subscription Subscribe. We can see that this model is actually an interface (for the convenience of everyone, the comments have been changed to Chinese)

public interface Client {
    
    
    // 客户端id/gRPC的connectionId
    String getClientId();

    // 是否临时客户端
    boolean isEphemeral();
    // 客户端更新时间
    void setLastUpdatedTime();
    long getLastUpdatedTime();

    // 服务实例注册/注销/查询
    boolean addServiceInstance(Service service, InstancePublishInfo instancePublishInfo);
    InstancePublishInfo removeServiceInstance(Service service);
    InstancePublishInfo getInstancePublishInfo(Service service);
    Collection<Service> getAllPublishedService();

    // 服务订阅/取消订阅/查询订阅
    boolean addServiceSubscriber(Service service, Subscriber subscriber);
    boolean removeServiceSubscriber(Service service);
    Subscriber getSubscriber(Service service);
    Collection<Service> getAllSubscribeService();
    // 生成同步给其他节点的client数据
    ClientSyncData generateSyncData();
    // 是否过期
    boolean isExpire(long currentTime);
    // 释放资源
    void release();
}

EphemeralClientOperationServiceImpl.registerInstance

​EphemeralClientOperationServiceImpl is actually responsible for processing service registration, so let's look at the specific methods

 @Override
public void registerInstance(Service service, Instance instance, String clientId) {
    
    
    //确保Service单例存在
    Service singleton = ServiceManager.getInstance().getSingleton(service);
    //根据客户端id,找到客户端
    Client client = clientManager.getClient(clientId);
    if (!clientIsLegal(client, clientId)) {
    
    
        return;
    }
    //客户端Instance模型,转换为服务端Instance模型
    InstancePublishInfo instanceInfo = getPublishInfo(instance);
    //将Instance储存到Client里
    client.addServiceInstance(singleton, instanceInfo);
    client.setLastUpdatedTime();
    //建立Service与ClientId的关系
    NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId));
    NotifyCenter
        .publishEvent(new MetadataEvent.InstanceMetadataEvent(singleton, instanceInfo.getMetadataId(), false));
}

ServiceManager

​ The container of Service is ServiceManager , but under the com.alibaba.nacos.naming.core.v2 package, the Service in the container is a singleton.

public class ServiceManager {
    
    
    
    private static final ServiceManager INSTANCE = new ServiceManager();
    //单例Service,可以查看Service的equals和hasCode方法
    private final ConcurrentHashMap<Service, Service> singletonRepository;
    //namespace下的所有service
    private final ConcurrentHashMap<String, Set<Service>> namespaceSingletonMaps;
    .....
}

So it can be seen from this position that ServiceManager is responsible for managing the Service singleton when calling this registration method

//通过Map储存单例的Service
public Service getSingleton(Service service) {
    
    
    singletonRepository.putIfAbsent(service, service);
    Service result = singletonRepository.get(service);
    namespaceSingletonMaps.computeIfAbsent(result.getNamespace(), (namespace) -> new ConcurrentHashSet<>());
    namespaceSingletonMaps.get(result.getNamespace()).add(result);
    return result;
}

clientManager

​ This is an interface. Here we need to look at its corresponding implementation class ConnectionBasedClientManager . This implementation class is responsible for managing the mapping relationship between the long connection clientId and the Client model.

// 根据clientId查询Client
public Client getClient(String clientId) {
    
    
    return clients.get(clientId);
}

Client instanceAbstractClient

Responsible for storing the service registry of the current client, that is, the relationship between Service and Instance. Note that for a single client, only one instance of the same service can be registered .

//
@Override
public boolean addServiceInstance(Service service, InstancePublishInfo instancePublishInfo) {
    
    
    if (null == publishers.put(service, instancePublishInfo)) {
    
    
        MetricsMonitor.incrementInstanceCount();
    }
    NotifyCenter.publishEvent(new ClientEvent.ClientChangedEvent(this));
    Loggers.SRV_LOG.info("Client change for service {}, {}", service, getClientId());
    return true;
}

ClientOperationEvent.ClientRegisterServiceEvent

​ The purpose here is to filter the target service to obtain the final Instance list and establish the relationship between Service and Client. The purpose of establishing the relationship between Service and Client is to speed up the query.

​ Publish the ClientRegisterServiceEvent event, ClientServiceIndexesManager listens, and ClientServiceIndexesManager maintains two indexes:

  • Service and publish clientId
  • Service and subscription clientId
private final ConcurrentMap<Service, Set<String>> publisherIndexes = new ConcurrentHashMap<>();
    
private final ConcurrentMap<Service, Set<String>> subscriberIndexes = new ConcurrentHashMap<>();

private void handleClientOperation(ClientOperationEvent event) {
    
    
    Service service = event.getService();
    String clientId = event.getClientId();
    if (event instanceof ClientOperationEvent.ClientRegisterServiceEvent) {
    
    
        addPublisherIndexes(service, clientId);
    } else if (event instanceof ClientOperationEvent.ClientDeregisterServiceEvent) {
    
    
        removePublisherIndexes(service, clientId);
    } else if (event instanceof ClientOperationEvent.ClientSubscribeServiceEvent) {
    
    
        addSubscriberIndexes(service, clientId);
    } else if (event instanceof ClientOperationEvent.ClientUnsubscribeServiceEvent) {
    
    
        removeSubscriberIndexes(service, clientId);
    }
}

//建立Service与发布Client的关系
private void addPublisherIndexes(Service service, String clientId) {
    
    
    publisherIndexes.computeIfAbsent(service, (key) -> new ConcurrentHashSet<>());
    publisherIndexes.get(service).add(clientId);
    NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true));
}

After the index relationship is established, the ServiceChangedEvent will be triggered to represent the change of the service registry. There are two things to do immediately after the registry change: 1. Notify the subscribing client 2. Nacos cluster data synchronization . These two points will be discussed later.

Guess you like

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