003-Nacos 2.1.x 注册实例源码分析

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件事就是

  1. 组装各种对象 放入各种 Map 中
  2. 发布各种事件

我们看下发布的事件:
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()));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值