Nacos源码之服务端服务注册分析
服务端服务注册接口
客户端进行服务注册的时候,本质上其实就是调用服务端提供的服务注册接口(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集群数据同步。