5. 服务变动通知客户端流程
服务注册和订阅逻辑
服务注册逻辑
在客户端向 Nacos 服务端发起注册时,服务端会发布 ClientRegisterServiceEvent 事件,ClientRegisterServiceEvent 会通知到 ClientServiceIndexesManager 的 onEvent 方法,该方法最终会调用 ClientServiceIndexesManager 的 addPublisherIndexes 方法
// ClientServiceIndexesManager
private void addPublisherIndexes(Service service, String clientId) {
// 把 clientId 写入注册表
publisherIndexes.computeIfAbsent(service, (key) -> new ConcurrentHashSet<>());
publisherIndexes.get(service).add(clientId);
// 发布服务改变事件
NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true));
}
- addPublisherIndexes 最终会将 service 和 clientId 加入到 publisherIndexes。
- 在添加注册表完成后,会发布 ServiceEvent.ServiceChangedEvent 事件。
服务订阅逻辑
在客户端发起服务实例查询时,会调用订阅接口,服务端在处理订阅请求时,会发布 ClientSubscribeServiceEvent 事件,ClientSubscribeServiceEvent 事件最终也是由 ClientServiceIndexesManager 的 onEvent 方法处理,ClientServiceIndexesManager 的 onEvent 方法最终会调用 ClientServiceIndexesManager 的 addSubscriberIndexes 方法
// ClientServiceIndexesManager
private void addSubscriberIndexes(Service service, String clientId) {
// 判断订阅表是否存在该 service
subscriberIndexes.computeIfAbsent(service, (key) -> new ConcurrentHashSet<>());
// Fix #5404, 添加订阅表,只有第一次添加需要通知事件
if (subscriberIndexes.get(service).add(clientId)) {
NotifyCenter.publishEvent(new ServiceEvent.ServiceSubscribedEvent(service, clientId));
}
}
- addSubscriberIndexes 最终会将 service 和 clientId 加入到 subscriberIndexes。
- 在添加注册表完成后,会发布 ServiceEvent.ServiceSubscribedEvent 事件。
事件处理
以上发布的 ServiceEvent.ServiceChangedEvent 事件和 ServiceEvent.ServiceSubscribedEvent 事件,都会交由 NamingSubscriberServiceV2Impl 的 onEvent() 方法处理
// NamingSubscriberServiceV2Impl
private final PushDelayTaskExecuteEngine delayTaskEngine;
public NamingSubscriberServiceV2Impl(ClientManagerDelegate clientManager,
ClientServiceIndexesManager indexesManager, ServiceStorage serviceStorage,
NamingMetadataManager metadataManager, PushExecutorDelegate pushExecutor, SwitchDomain switchDomain) {
this.clientManager = clientManager;
this.indexesManager = indexesManager;
this.delayTaskEngine = new PushDelayTaskExecuteEngine(clientManager, indexesManager, serviceStorage,
metadataManager, pushExecutor, switchDomain);
NotifyCenter.registerSubscriber(this, NamingEventPublisherFactory.getInstance());
}
@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 = serviceChangedEvent.getService();
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()));
}
}
- onEvent 方法会从事件中获取 service ,然后调用 delayTaskEngine 提交一个 PushDelayTask 任务,delayTaskEngine 的实现类为 PushDelayTaskExecuteEngine,PushDelayTaskExecuteEngine 对象在 NamingSubscriberServiceV2Impl 构造方法中被创建
PushDelayTaskExecuteEngine
PushDelayTaskExecuteEngine 类结构

PushDelayTaskExecuteEngine 类介绍
PushDelayTaskExecuteEngine 是推送延迟任务执行引擎,顾名思义是执行延迟任务,可以往执行引擎中添加任务,然后该任务会被延时执行。Nacos 定义了 NacosTaskProcessor 任务处理器接口,NacosTaskProcessor 有很多实现类,PushDelayTaskExecuteEngine 的父类AbstractNacosTaskExecuteEngine 会存储和调度这些任务处理器实现类,相当于这些任务处理器的执行引擎。
public abstract class AbstractNacosTaskExecuteEngine<T extends NacosTask> implements NacosTaskExecuteEngine<T> {
private final ConcurrentHashMap<Object, NacosTaskProcessor> taskProcessors = new ConcurrentHashMap<>();
private NacosTaskProcessor defaultTaskProcessor;
@Override
public void addProcessor(Object key, NacosTaskProcessor taskProcessor) {
taskProcessors.putIfAbsent(key, taskProcessor);
}
@Override
public void setDefaultTaskProcessor(NacosTaskProcessor defaultTaskProcessor) {
this.defaultTaskProcessor = defaultTaskProcessor;
}
}
在外部可以通过 addProcessor、setDefaultTaskProcessor 方法,来往执行引擎中心添加处理器。
在添加完处理器后,PushDelayTaskExecuteEngine 的父类 NacosDelayTaskExecuteEngine 会使用线程池启动一个 ProcessRunnable 任务,执行 AbstractNacosTaskExecuteEngine 保存的 NacosTaskProcessor
public NacosDelayTaskExecuteEngine(String name, int initCapacity, Logger logger, long processInterval) {
super(logger);
tasks = new ConcurrentHashMap<>(initCapacity);
processingExecutor = ExecutorFactory.newSingleScheduledExecutorService(new NameThreadFactory(name));
// 启动 ProcessRunnable 线程任务
processingExecutor
.scheduleWithFixedDelay(new ProcessRunnable(), processInterval, processInterval, TimeUnit.MILLISECONDS);
}
ProcessRunnable 类
private class ProcessRunnable implements Runnable {
@Override
public void run() {
try {
processTasks();
} catch (Throwable e) {
getEngineLog().error(e.toString(), e);
}
}
}
ProcessRunnable 实现了 Runnable 接口,所以线程执行时,会调用其 run() 方法。run 会调用了 processTasks()方法,在这个方法中就有很多的逻辑
// 任务池
protected final ConcurrentHashMap<Object, AbstractDelayTask> tasks;
/**
* process tasks in execute engine.
*/
protected void processTasks() {
// 获取全部的任务,进行遍历
Collection<Object> keys = getAllTaskKeys();
for (Object taskKey : keys) {
// 通过任务 key,将任务从 tasks Map 中删除,先删除任务,再执行任务内容
AbstractDelayTask task = removeTask(taskKey);
if (null == task) {
continue;
}
// 通过任务 key 获取对应的任务处理器,
NacosTaskProcessor processor = getProcessor(taskKey);
if (null == processor) {
getEngineLog().error("processor not found for task, so discarded. " + task);
continue;
}
try {
// 调用处理器的处理方法
if (!processor.process(task)) {
retryFailedTask(taskKey, task);
}
} catch (Throwable e) {
getEngineLog().error("Nacos task execute error ", e);
retryFailedTask(taskKey, task);
}
}
}
- 首先会有一个 Map 类型的任务池
tasks,从任务池中获取全部的任务,进行遍历。通过 taskKey 获取到具体的任务之后,再通过 taskKey 获取对应的处理器,最终调用处理器的处理方法。
获取处理器的方法
// AbstractNacosTaskExecuteEngine
@Override
public NacosTaskProcessor getProcessor(Object key) {
// 如果处理器中没有对应的 key,那么就返回默认的任务处理器
return taskProcessors.containsKey(key) ? taskProcessors.get(key) : defaultTaskProcessor;
}
- getProcessor() 方法从 taskProcessors Map 中获取对应的 NacosTaskProcessor 实现类,如果 taskProcessors 中没有,则会直接返回默认的
defaultTaskProcessor
tasks 任务则在 NacosDelayTaskExecuteEngine 的 addTask() 方法中被添加
@Override
public void addTask(Object key, AbstractDelayTask newTask) {
lock.lock();
try {
// 往任务池添加任务
AbstractDelayTask existTask = tasks.get(key);
if (null != existTask) {
newTask.merge(existTask);
}
tasks.put(key, newTask);
} finally {
lock.unlock();
}
}
- addTask 首先从 tasks 中获取 key 对应的任务,如果任务存在,在和 newTask 进行合并,合并完毕后,再将任务添加到 tasks 中。
服务变动/订阅事件处理
从服务注册和订阅发布事件后,会通知 NamingSubscriberServiceV2Impl 的 onEvent() 方法,而 onEvent() 方法则会调用 addTask 方法向 tasks 添加任务。
@Override
public void onEvent(Event event) {
if (!upgradeJudgement.isUseGrpcFeatures()) {
return;
}
if (event instanceof ServiceEvent.ServiceChangedEvent) {
// 如果服务变动,会通知该 service 所有的订阅者,更新本地缓存
ServiceEvent.ServiceChangedEvent serviceChangedEvent = (ServiceEvent.ServiceChangedEvent) event;
Service service = serviceChangedEvent.getService();
// 添加任务到 tasks
delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay()));
} else if (event instanceof ServiceEvent.ServiceSubscribedEvent) {
// 如果服务被一个客户端订阅,则只推送该客户端
ServiceEvent.ServiceSubscribedEvent subscribedEvent = (ServiceEvent.ServiceSubscribedEvent) event;
Service service = subscribedEvent.getService();
// 添加任务到 tasks
delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay(),
subscribedEvent.getClientId()));
}
}
- 在添加任务的时候,添加的是
PushDelayTask类型的任务,PushDelayTask类型的任务会交由NacosTaskProcessor.PushDelayTaskProcessor处理器进行处理。NacosTaskProcessor.PushDelayTaskProcessor在PushDelayTaskExecuteEngine初始化时,被设置为defaultTaskProcessor。
根据 AbstractNacosTaskExecuteEngine.getProcessor() 可知,在 taskProcessors 不存在 key 对应的 processor 时,会使用 defaultTaskProcessor,再根据 NacosDelayTaskExecuteEngine.addTask() 可知,向 taskProcessors 查询 processor 使用的 key 为 service,而 taskProcessors 不存在 service 对应的 taskProcessor,所以会使用 defaultTaskProcessor
// PushDelayTaskExecuteEngine
public PushDelayTaskExecuteEngine(ClientManager clientManager, ClientServiceIndexesManager indexesManager,
ServiceStorage serviceStorage, NamingMetadataManager metadataManager,
PushExecutor pushExecutor, SwitchDomain switchDomain) {
super(PushDelayTaskExecuteEngine.class.getSimpleName(), Loggers.PUSH);
this.clientManager = clientManager;
this.indexesManager = indexesManager;
this.serviceStorage = serviceStorage;
this.metadataManager = metadataManager;
this.pushExecutor = pushExecutor;
this.switchDomain = switchDomain;
setDefaultTaskProcessor(new PushDelayTaskProcessor(this));
}
PushDelayTaskProcessor 的 process 方法
@Override
public boolean process(NacosTask task) {
// 任务类型转换
PushDelayTask pushDelayTask = (PushDelayTask) task;
Service service = pushDelayTask.getService();
// 提交 PushExecuteTask 线程任务
NamingExecuteTaskDispatcher.getInstance()
.dispatchAndExecuteTask(service, new PushExecuteTask(service, executeEngine, pushDelayTask));
return true;
}
- process 方法首先从 task 中获取 service,然后向 NamingExecuteTaskDispatcher 添加一个
PushExecuteTask线程任务
PushExecuteTask 的 run 方法
@Override
public void run() {
try {
// 从注册表获取当前 service 最新的实例列表数据
PushDataWrapper wrapper = generatePushData();
ClientManager clientManager = delayTaskEngine.getClientManager();
// 获取客户端id,也就是 rpc 客户端连接 id
for (String each : getTargetClientIds()) {
// 获取客户端,通知服务变动
Client client = clientManager.getClient(each);
if (null == client) {
// means this client has disconnect
continue;
}
// 获取客户端的订阅者
Subscriber subscriber = client.getSubscriber(service);
// 回调客户端
delayTaskEngine.getPushExecutor().doPushWithCallback(each, subscriber, wrapper,
new NamingPushCallback(each, subscriber, wrapper.getOriginalData(), delayTask.isPushToAll()));
}
} catch (Exception e) {
Loggers.PUSH.error("Push task for service" + service.getGroupedServiceName() + " execute failed ", e);
delayTaskEngine.addTask(service, new PushDelayTask(service, 1000L));
}
}
private Collection<String> getTargetClientIds() {
// 通过 pushToAll 这个参数来控制是否推送给全部的 client
// 如果为 true 推送 service 全部的 client,如果为 false,需要指定 targetClients 参数,只推送该参数里面的 client
return delayTask.isPushToAll() ? delayTaskEngine.getIndexesManager().getAllClientsSubscribeService(service)
: delayTask.getTargetClients();
}
public Collection<String> getAllClientsSubscribeService(Service service) {
// 从订阅表获取 clientId
return subscriberIndexes.containsKey(service) ? subscriberIndexes.get(service) : new ConcurrentHashSet<>();
}
- PushExecuteTask 的 run 方法首先判断是否需要推送给所有订阅者,如果不需要,则根据 targetClients 推送指定的 client
- 然后再从 client 的 subscriberIndexes 找到 service 对应的 subscriber ,然后调用 pushExecutor 的 doPushWithCallBakc() 方法调用客户端的回调方法。
PushExecuteTask 的 isPushToAll 方法
NamingSubscriberServiceV2Impl 在处理服务注册和服务订阅时,会创建不同的 PushDelayTask 方法,服务变动时, pushToAll 会被设置为 ture,表示需要推送给所有订阅客户端,在服务订阅时,pushToAll 为 false,则只会推送给指定的 tagetClients
// PushDelayTask
private Set<String> targetClients;
// 服务变动事件,所使用的构造方法
public PushDelayTask(Service service, long delay) {
this.service = service;
pushToAll = true;
targetClients = null;
setTaskInterval(delay);
setLastProcessTime(System.currentTimeMillis());
}
// 服务订阅事件使用的构造方法
public PushDelayTask(Service service, long delay, String targetClient) {
this.service = service;
this.pushToAll = false;
this.targetClients = new HashSet<>(1);
// 把 clientId 添加到 targetClients 当中,这个 clientId 就是发起服务订阅的客户端ID
this.targetClients.add(targetClient);
setTaskInterval(delay);
setLastProcessTime(System.currentTimeMillis());
}
doPushWithCallback 方法
@Override
public void doPushWithCallback(String clientId, Subscriber subscriber, PushDataWrapper data, PushCallBack callBack) {
// 这里构建的是 NotifySubscriberRequest 类型请求参数
pushService.pushWithCallback(clientId, NotifySubscriberRequest.buildNotifySubscriberRequest(getServiceInfo(data, subscriber)),
callBack, GlobalExecutor.getCallbackExecutor());
}
- pushService 会向客户端发送一个 NotifySubscriberRequest,NotifySubscriberRequest 会客户端的在 NamingPushRequestHandler 中被处理
// NamingPushRequestHandler
public class NamingPushRequestHandler implements ServerRequestHandler {
private final ServiceInfoHolder serviceInfoHolder;
public NamingPushRequestHandler(ServiceInfoHolder serviceInfoHolder) {
this.serviceInfoHolder = serviceInfoHolder;
}
@Override
public Response requestReply(Request request) {
if (request instanceof NotifySubscriberRequest) {
// 类型转换
NotifySubscriberRequest notifyResponse = (NotifySubscriberRequest) request;
// 更新本地缓存数据
serviceInfoHolder.processServiceInfo(notifyResponse.getServiceInfo());
return new NotifySubscriberResponse();
}
return null;
}
}
- 客户端收到
NotifySubscriberRequest后,会更新本地serviceInfo缓存。

浙公网安备 33010602011771号