6、Nacos服务注册服务端源码分析(五)

上篇文章我们看了统一的事件通知NotifyCenter这个类,将客户端注册服务的流程梳理到了事件通知这一步,接下来我们接着看获取到注册通知之后的处理逻辑。


InstanceRequestHandler

private InstanceResponse registerInstance(Service service, InstanceRequest request, RequestMeta meta)
        throws NacosException {
    clientOperationService.registerInstance(service, request.getInstance(), meta.getConnectionId());
    NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(),
            meta.getClientIp(), true, service.getNamespace(), service.getGroup(), service.getName(),
            request.getInstance().getIp(), request.getInstance().getPort()));
    return new InstanceResponse(NamingRemoteConstants.REGISTER_INSTANCE);
}

我们之前看过这个代码,再次从这里出发。


EphemeralClientOperationServiceImpl

@Override
public void registerInstance(Service service, Instance instance, String clientId) throws NacosException {
    NamingUtils.checkInstanceIsLegal(instance);

    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()));
    }
    Client client = clientManager.getClient(clientId);
    if (!clientIsLegal(client, clientId)) {
        return;
    }
    InstancePublishInfo instanceInfo = getPublishInfo(instance);
    client.addServiceInstance(singleton, instanceInfo);
    client.setLastUpdatedTime();
    client.recalculateRevision();
    //发布服务注册事件
    NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId));
    NotifyCenter
            .publishEvent(new MetadataEvent.InstanceMetadataEvent(singleton, instanceInfo.getMetadataId(), false));
}

ClientServiceIndexesManager

我们看下注册事件ClientRegisterServiceEvent的订阅者是谁。
通过查看ClientRegisterServiceEvent的引用,我们发现这个客户端注册事件的订阅者是ClientServiceIndexesManager.
这个类在构造方法中向NotifyCenter注册了自己。

public ClientServiceIndexesManager() {
    NotifyCenter.registerSubscriber(this, NamingEventPublisherFactory.getInstance());
}

我们直接来看下注册事件是如何被处理的,那就直接找onEvent方法:

@Override
public void onEvent(Event event) {
    if (event instanceof ClientEvent.ClientDisconnectEvent) {
        handleClientDisconnect((ClientEvent.ClientDisconnectEvent) event);
    } else if (event instanceof ClientOperationEvent) {
        handleClientOperation((ClientOperationEvent) event);
    }
}

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

private final ConcurrentMap<Service, Set<String>> publisherIndexes = new ConcurrentHashMap<>();

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

又发布了一个事件,虽然整体上看起来发布的事件有点多,但多亏了NotifyCenter封装了发布-订阅模型,从而避免了面条代码和业务上的严重耦合。


NamingSubscriberServiceV2Impl

NamingSubscriberServiceV2Impl除了了ServiceChangedEventonEvent代码如下:

private final PushDelayTaskExecuteEngine delayTaskEngine;

@Override
public void onEvent(Event event) {
    if (event instanceof ServiceEvent.ServiceChangedEvent) {
        // If service changed, push to all subscribers.
        ServiceEvent.ServiceChangedEvent serviceChangedEvent = (ServiceEvent.ServiceChangedEvent) event;
        //获取Service
        Service service = serviceChangedEvent.getService();
        //将Service封装成PushDelayTask添加到一个延迟任务引擎中
        delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay()));
        //统计?不重要 
        MetricsMonitor.incrementServiceChangeCount(service.getNamespace(), service.getGroup(), service.getName());
    } else if (event instanceof ServiceEvent.ServiceSubscribedEvent) {
        // If service is subscribed by one client, only push this client.
        ServiceEvent.ServiceSubscribedEvent subscribedEvent = (ServiceEvent.ServiceSubscribedEvent) event;
        Service service = subscribedEvent.getService();
        delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay(),
                subscribedEvent.getClientId()));
    }
}

通过代码可以看到,订阅者获处理事件时,将Service封装成一个PushDelayTask之后添加到了delayTaskEngine
我们来看下这个PushDelayTaskExecuteEngine是干嘛的,方便理解后续逻辑。


PushDelayTaskExecuteEngine

6、Nacos服务注册服务端源码分析(五)_第1张图片

NacosDelayTaskExecuteEngine

我们先看父类NacosDelayTaskExecuteEngine的构造方法:

private final ScheduledExecutorService processingExecutor;

protected final ConcurrentHashMap<Object, AbstractDelayTask> tasks;

protected final ReentrantLock lock = new ReentrantLock();

public NacosDelayTaskExecuteEngine(String name, int initCapacity, Logger logger, long processInterval) {
    super(logger);
    tasks = new ConcurrentHashMap<>(initCapacity);
    processingExecutor = ExecutorFactory.newSingleScheduledExecutorService(new NameThreadFactory(name));
    processingExecutor
            .scheduleWithFixedDelay(new ProcessRunnable(), processInterval, processInterval, TimeUnit.MILLISECONDS);
}

这里插一嘴,最快上手去了解一个类的方式,就是去看构造方法,因为这可以最快的知道类中全局变量的作用以及初始化方式。

可以看到,构造方法做了三件事:

  1. 初始化一个内部缓存,缓存的value是AbstractDelayTask
  2. 初始化一个单线程的定时任务线程池
  3. 开启线程池,指定执行任务的延迟时间。scheduleWithFixedDelay第一个参数的api描述:the task to execute。也就是每次执行的时候,这个ProcessRunnable都会被执行。
private class ProcessRunnable implements Runnable {
    @Override
    public void run() {
        try {
            processTasks();
        } catch (Throwable e) {
            getEngineLog().error(e.toString(), e);
        }
    }
}

/**
 * process tasks in execute engine.
 */
protected void processTasks() {
	//从本地缓存中获取所有等待执行的任务
    Collection<Object> keys = getAllTaskKeys();
    //遍历去执行
    for (Object taskKey : keys) {
        AbstractDelayTask task = removeTask(taskKey);
        if (null == task) {
            continue;
        }
        //获取执行当前任务的处理器processor
        NacosTaskProcessor processor = getProcessor(taskKey);
        if (null == processor) {
            getEngineLog().error("processor not found for task, so discarded. " + task);
            continue;
        }
        try {
            // ReAdd task if process failed
            //执行任务,如果执行失败则会重新将当前任务放到本地缓存中
            if (!processor.process(task)) {
                retryFailedTask(taskKey, task);
            }
        } catch (Throwable e) {
            getEngineLog().error("Nacos task execute error ", e);
            retryFailedTask(taskKey, task);
        }
    }
}

接着看processor.process(task):

private static class PushDelayTaskProcessor implements NacosTaskProcessor {
    
    private final PushDelayTaskExecuteEngine executeEngine;
    
    public PushDelayTaskProcessor(PushDelayTaskExecuteEngine executeEngine) {
        this.executeEngine = executeEngine;
    }
    
    @Override
    public boolean process(NacosTask task) {
        PushDelayTask pushDelayTask = (PushDelayTask) task;
        Service service = pushDelayTask.getService();
        //我们又迎来了新的类NamingExecuteTaskDispatcher:分配任务
        NamingExecuteTaskDispatcher.getInstance()
                .dispatchAndExecuteTask(service, new PushExecuteTask(service, executeEngine, pushDelayTask));
        return true;
    }
}

这里出现了一个新的类NamingExecuteTaskDispatcher。我们不再继续探讨这个类的作用了,以免陷入细节过深而在上层设计中迷失。


NOW! 重新梳理一遍

  • 我们从NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId));开始
  • 先是找到了处理客户端注册事件的订阅者–> ClientServiceIndexesManager , 然后发现它在处理注册事件的时候又发布了一个新的事件 :NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true));
  • 接着我们找到了处理ServiceChangedEvent的订阅者NamingSubscriberServiceV2Impl,然后发现它对这个事件的处理是将Service封装成PushDelayTask添加到一个延迟任务引擎中 --> delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay()));
  • 这个任务引擎会定时处理PushDelayTask,从而继续处理客户端注册的任务

截止到目前,我们仍然在客户端注册的流程中漫步,没有找到确切执行任务的代码。下篇我们从PushDelayTaskExecuteEngine开始,继续探索。

你可能感兴趣的:(Nacos,nacos)