ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2022-05-21 00:32:25  阅读:163  来源: 互联网

标签:Service service Nacos clientId String 源码 Client 服务端 客户端


服务端服务注册接口

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集群数据同步

标签:Service,service,Nacos,clientId,String,源码,Client,服务端,客户端
来源: https://www.cnblogs.com/ZT-666/p/16294217.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有