Loading

Nacos源码之服务端服务注册分析

服务端服务注册接口

image

客户端进行服务注册的时候,本质上其实就是调用服务端提供的服务注册接口(nacos/v1/ns/instance),这个接口所在位置为上图标记中,InstanceController和InstanceControllerV2这2个类都包含服务注册功能,内部实现其实都是调用了InstanceOperatorClientImpl.registerInstance()方法来实现服务注册

InstanceController.register方法

		@CanDistro
 @PostMapping
 @Secured(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";
 }

上面代码中我们需要注意一下这行代码:

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

其中的getInstanceOperator(),就是判断是否采用Grpc协议,很明显这个位置走的是instanceServiceV2:

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

instanceServiceV2.registerInstance方法

instanceServiceV2是InstanceOperatorClientImpl实例对象,所以我们来看这里面的registerInstance方法

		/**
     * This method creates {@code IpPortBasedClient} if it don't exist.
     */
    @Override
    public void registerInstance(String namespaceId, String serviceName, Instance instance) {
        //判断是否为瞬时对象(临时客户端)
        boolean ephemeral = instance.isEphemeral();
        //获取客户端ID,和Client模型相关
        String clientId = IpPortBasedClient.getClientId(instance.toInetAddr(), ephemeral);
        //通过客户端ID创建客户端连接
        createIpPortClientIfAbsent(clientId);
       //获取服务
        Service service = getService(namespaceId, serviceName, ephemeral);
        //具体注册服务
        clientOperationService.registerInstance(service, instance, clientId);
    }

Client模型

Nacos2.0以后新增Client模型一个客户端gRPC长连接对应一个Client,每个Client有自己唯一的id(clientId)。Client负责管理一个客户端的服务实例注册Publish和服务订阅Subscribe。这个模型其实就是一个接口

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

clientOperationService.registerInstance方法

clientOperationService为EphemeralClientOperationServiceImpl实例对象,具体的registerInstance注册方法:

		@Override
 public void registerInstance(Service service, Instance instance, String clientId) {
     //确保Service单例存在
     Service singleton = ServiceManager.getInstance().getSingleton(service);
     if (!singleton.isEphemeral()) {
         throw new NacosRuntimeException(NacosException.INVALID_PARAM,
                 String.format("Current service %s is persistent service, can't register ephemeral instance.",
                         singleton.getGroupedServiceName()));
     }
     //根据客户端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

Service的容器是ServiceManager,在com.alibaba.nacos.naming.core.v2包下,容器中Service都是单例。

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

所以从这个位置可以看出,当调用这个注册方法的时候ServiceManager负责管理Service单例

//通过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

这是一个接口,这里我们要看它对应的一个实现类ConnectionBasedClientManager,这个实现类负责管理长连接clientId与Client模型的映射关系

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

Client实例AbstractClient

负责存储当前客户端的服务注册表,即Service与Instance的关系。注意对于单个客户端来说,同一个服务只能注册一个实例

@Overridepublic 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

这里的目的是为了过滤目标服务得到最终Instance列表,建立Service与Client的关系,建立Service与Client的关系就是为了加速查询。

发布ClientRegisterServiceEvent事件,ClientServiceIndexesManager监听,ClientServiceIndexesManager维护了两个索引:

  • Service与发布clientId
  • Service与订阅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));}

这个索引关系建立以后,还会触发ServiceChangedEvent,代表服务注册表变更。对于注册表变更紧接着还要做两个事情:1.通知订阅客户端 2.Nacos集群数据同步

posted @ 2022-05-21 00:23  ZT丶  阅读(215)  评论(0编辑  收藏  举报