上篇文章我们看了统一的事件通知NotifyCenter这个类,将客户端注册服务的流程梳理到了事件通知这一步,接下来我们接着看获取到注册通知之后的处理逻辑。
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);
}
我们之前看过这个代码,再次从这里出发。
@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));
}
我们看下注册事件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
除了了ServiceChangedEvent
,onEvent
代码如下:
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
是干嘛的,方便理解后续逻辑。
我们先看父类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);
}
这里插一嘴,最快上手去了解一个类的方式,就是去看构造方法,因为这可以最快的知道类中全局变量的作用以及初始化方式。
可以看到,构造方法做了三件事:
AbstractDelayTask
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
。我们不再继续探讨这个类的作用了,以免陷入细节过深而在上层设计中迷失。
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()));
截止到目前,我们仍然在客户端注册的流程中漫步,没有找到确切执行任务的代码。下篇我们从PushDelayTaskExecuteEngine
开始,继续探索。