Nacos 2.1.X
注册实例
入口
com.alibaba.nacos.naming.remote.rpc.handler.InstanceRequestHandler#handle
Service service = Service.newService(request.getNamespace(), request.getGroupName(), request.getServiceName(), true);
switch (request.getType()) {
case NamingRemoteConstants.REGISTER_INSTANCE:
return registerInstance(service, request, meta);
}
接口流程
registerInstance(Service service, Instance instance, String clientId):
clientOperationService.registerInstance(service, request.getInstance(), meta.getConnectionId());
NotifyCenter.publishEvent(new RegisterInstanceTraceEvent());
registerInstance(Service service, Instance instance, String clientId):
//获取一个服务实例
//如果 singletonRepository 没有service 则发布 MetadataEvent.ServiceMetadataEvent 并设置到Map中
//ConcurrentHashMap<Service, Service> singletonRepository
//ConcurrentHashMap<String, Set<Service>> namespaceSingletonMaps
Service singleton = ServiceManager.getInstance().getSingleton(service);
if (!singleton.isEphemeral()) {
//2.1.x 开始 临时实例走 gRpc 持久实例还是走http
//也就是只优化了常用的临时实例 不常用的就没有优化
throw new NacosRuntimeException();
}
//根据 clientId 获取一个 client
//这个对象就是对应一个连接端信息 一个连接就有一个 clientId 对应有一个 client
//ConcurrentMap<String, IpPortBasedClient> clients
Client client = clientManager.getClient(clientId);
InstancePublishInfo instanceInfo = getPublishInfo(instance);
//维护 ConcurrentHashMap<Service, InstancePublishInfo> publishers
client.addServiceInstance(singleton, instanceInfo);
client.setLastUpdatedTime();
client.recalculateRevision();
//给 client 赋值
NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent();
NotifyCenter.publishEvent(new MetadataEvent.InstanceMetadataEvent();
这个主流程可以看出来,其实就做了2件事就是
- 组装各种对象 放入各种 Map 中
- 发布各种事件
我们看下发布的事件:
RegisterInstanceTraceEvent
ServiceMetadataEvent
ClientRegisterServiceEvent
InstanceMetadataEvent
如果要分析注册 那么应该看 ClientRegisterServiceEvent
事件处理
Client 注册 事件处理
com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager#handleClientOperation
//ConcurrentMap<Service, Set<String>> publisherIndexes : Set<String> By service Map
publisherIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>());
publisherIndexes.get(service).add(clientId);
//这里发布了服务变更事件 也就是说注册已经完成了 也就是说注册其实就是写入这个Map
NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true));
那这些 Map 到底有什么用呢?可以梳理一下查询服务逻辑看一下是怎么关联的
服务订阅
入口
com.alibaba.nacos.naming.remote.rpc.handler.SubscribeServiceRequestHandler#handle
Service service = Service.newService(namespaceId, groupName, serviceName, true);
Subscriber subscriber = new Subscriber(meta.getClientIp(), meta.getClientVersion(), app, meta.getClientIp(),
namespaceId, groupedServiceName, 0, request.getClusters());
// 这一句比较关键 下边详细分析
ServiceInfo serviceInfo = ServiceUtil.selectInstancesWithHealthyProtection(serviceStorage.getData(service),
metadataManager.getServiceMetadata(service).orElse(null), subscriber.getCluster(), false,
true, subscriber.getIp());
if (request.isSubscribe()) {
clientOperationService.subscribeService(service, subscriber, meta.getConnectionId());
NotifyCenter.publishEvent(new SubscribeServiceTraceEvent(System.currentTimeMillis(),
meta.getClientIp(), service.getNamespace(), service.getGroup(), service.getName()));
}
//从这里看一看出服务列表就是在 serviceInfo 中
return new SubscribeServiceResponse(ResponseCode.SUCCESS.getCode(), "success", serviceInfo);
ServiceUtil.selectInstancesWithHealthyProtection(serviceStorage.getData(service),
metadataManager.getServiceMetadata(service).orElse(null), subscriber.getCluster(), false,
true, subscriber.getIp()):
serviceStorage.getData(service):
// 看下缓存有不 没有就创建一个
return serviceDataIndexes.containsKey(service) ? serviceDataIndexes.get(service) : getPushData(service);
getPushData(service):
ServiceInfo result = emptyServiceInfo(service);
//ConcurrentHashMap<Service, Service> singletonRepository 这个有了
Service singleton = ServiceManager.getInstance().getSingleton(service);
//根据 Service 获取所有 Instances
result.setHosts(getAllInstancesFromIndex(singleton));
serviceDataIndexes.put(singleton, result);
return result;
List<Instance> getAllInstancesFromIndex(Service service):
//ConcurrentMap<Service, Set<String>> publisherIndexes 根据service获取 Set<clientId>
for (String each : serviceIndexesManager.getAllClientsRegisteredService(service)) {
//ConcurrentMap<String, IpPortBasedClient> clients
//ConcurrentHashMap<Service, InstancePublishInfo> publishers
Optional<InstancePublishInfo> instancePublishInfo = getInstanceInfo(each, service);
if (instancePublishInfo.isPresent()) {
InstancePublishInfo publishInfo = instancePublishInfo.get();
if (publishInfo instanceof BatchInstancePublishInfo) {
BatchInstancePublishInfo batchInstancePublishInfo = (BatchInstancePublishInfo) publishInfo;
List<Instance> batchInstance = parseBatchInstance(service, batchInstancePublishInfo, clusters);
result.addAll(batchInstance);
} else {
Instance instance = parseInstance(service, instancePublishInfo.get());
result.add(instance);
clusters.add(instance.getClusterName());
}
}
}
//维护 ConcurrentMap<Service, Set<String>> serviceClusterIndex
serviceClusterIndex.put(service, clusters);
return new LinkedList<>(result);
//serviceInfo - 包含了 instance set
//serviceMetadata - null
//cluster - request.getClusters()
//healthyOnly - false
//enableOnly - true
//subscriberIp - RequestMeta::getClientIp()
//这个方法就是筛选一下健康的实例
selectInstancesWithHealthyProtection(ServiceInfo serviceInfo, ServiceMetadata serviceMetadata, String cluster, boolean healthyOnly, boolean enableOnly, String subscriberIp):
服务订阅
再入口处会判断 request.isSubscribe()
则执行订阅逻辑
clientOperationService.subscribeService(service, subscriber, meta.getConnectionId());
NotifyCenter.publishEvent(new SubscribeServiceTraceEvent()
service - 根据 namespaceId, groupName, serviceName, true 创建的 service
subscriber - 消费者的 Subscriber subscriber
clientId - 消费者的 clientId
subscribeService(Service service, Subscriber subscriber, String clientId):
// 从缓存中获取一次 namespace、group、name 一致就认为一致
// ConcurrentHashMap<Service, Service> singletonRepository
Service singleton = ServiceManager.getInstance().getSingletonIfExist(service).orElse(service);
//这个是查询消费的缓存 ClientManagerDelegate
//这里应该是建立Grpc连接的时候就已经维护好的这个client了
//ConcurrentMap<String, ConnectionBasedClient> clients
Client client = clientManager.getClient(clientId);
//维护订阅关系 这里 client 是消费者的一个实例连接 也就是说一个连接 一个服务 就对应一个 Subscriber
//ConcurrentHashMap<Service, Subscriber> subscribers
client.addServiceSubscriber(singleton, subscriber);
client.setLastUpdatedTime();
NotifyCenter.publishEvent(new ClientOperationEvent.ClientSubscribeServiceEvent(singleton, clientId);
订阅事件处理
com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager#handleClientOperation
分析处理:event instanceof ClientOperationEvent.ClientSubscribeServiceEvent
其中:
//singleton - 提供者的 Service service
//clientId - 消费者的 clientId
ClientSubscribeServiceEvent(singleton, clientId):
addSubscriberIndexes(service, clientId):
//ConcurrentMap<Service, Set<String>> subscriberIndexes
//空的就塞入 ConcurrentHashSet
//这里是是把 提供者的Service 和 消费者的 clientId 关联起来
subscriberIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>());
//添加clientId
if (subscriberIndexes.get(service).add(clientId)) {
//这里官方的注释说是 第一次才会发送通知事件
NotifyCenter.publishEvent(new ServiceEvent.ServiceSubscribedEvent(service, clientId));
}
com.alibaba.nacos.naming.push.v2.NamingSubscriberServiceV2Impl#onEvent
分析处理:event instanceof ServiceEvent.ServiceSubscribedEvent
//这里就是异步 processTasks() 中处理 PushDelayTask 实现了
delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay(), subscribedEvent.getClientId()));
//ScheduledExecutorService processingExecutor
com.alibaba.nacos.common.task.engine.NacosDelayTaskExecuteEngine#processTasks
//keys.addAll(tasks.keySet());
Collection<Object> keys = getAllTaskKeys();
for (Object taskKey : keys) {
AbstractDelayTask task = removeTask(taskKey);
//taskProcessors.containsKey(key) 没有缓存 所以
//this.defaultTaskProcessor = new PushDelayTaskProcessor(this)
NacosTaskProcessor processor = getProcessor(taskKey);
if (!processor.process(task)) {
//失败就重试
retryFailedTask(taskKey, task);
}
}
com.alibaba.nacos.naming.push.v2.task.PushDelayTaskExecuteEngine.PushDelayTaskProcessor#process
PushDelayTask pushDelayTask = (PushDelayTask) task;
Service service = pushDelayTask.getService();
//这个 PushExecuteTask 就是一个 Runnable
//然后用这个 executeEngine = new NacosExecuteTaskExecuteEngine(EnvUtil.FUNCTION_MODE_NAMING, Loggers.SRV_LOG);
// executeEngine defaultTaskProcessor is null
//所以就用executeWorkers = new TaskExecuteWorker[dispatchWorkerCount];
//executeWorkers[idx].process(task);
//queue.put(task);
//然后在 TaskExecuteWorker.realWorker = new InnerWorker(this.name); run()
NamingExecuteTaskDispatcher.getInstance()
.dispatchAndExecuteTask(service, new PushExecuteTask(service, executeEngine, pushDelayTask));
return true;
com.alibaba.nacos.naming.push.v2.task.PushExecuteTask#run
PushDataWrapper wrapper = generatePushData();
ClientManager clientManager = delayTaskEngine.getClientManager();
//在 com.alibaba.nacos.naming.push.v2.NamingSubscriberServiceV2Impl#onEvent
//delayTask.isPushToAll() == false 所以返回 delayTask.getTargetClients();
//也就是本次连接的客户端
//其实到这里我们就会发现 如果 delayTask.isPushToAll() == true就会给所有客户端发消息 那就是服务注册的逻辑了
for (String each : getTargetClientIds()) {
Client client = clientManager.getClient(each);
Subscriber subscriber = clientManager.getClient(each).getSubscriber(service);
delayTaskEngine.getPushExecutor()
.doPushWithCallback(each, subscriber, wrapper,
new ServicePushCallback(each, subscriber, wrapper.getOriginalData(), delayTask.isPushToAll()));
}