上篇分析TaskExecuteEngine
以及其两个子类NacosDelayTaskExecuteEngine
和NacosExecuteTaskExecuteEngine
。没看过的小伙伴可以点击这里进行查看。本篇从NacosTask
开始分析,并分析后续的逻辑。
NacosTask
只有一个方法boolean shouldProcess()
,即判断是否应该执行。
它有有两个抽象的子类,分别是AbstractExecuteTask
和AbstractDelayTask
AbstractExecuteTask
实现很简单,就是为true
需要执行。
@Override
public boolean shouldProcess() {
return true;
}
AbstractDelayTask
是延迟执行的,所以它有个延迟机制的判断。
@Override
public boolean shouldProcess() {
// 如果当前时间减去上次时间还未到达任务执行的间隔时间则返回false
return (System.currentTimeMillis() - this.lastProcessTime >= this.taskInterval);
}
// 抽象类,由子类判断添加的任务是否可以合并
public abstract void merge(AbstractDelayTask task);
这里还是用上篇分析的PushDelayTask
分析下这个方法的作用
@Override
public void merge(AbstractDelayTask task) {
if (!(task instanceof PushDelayTask)) {
return;
}
PushDelayTask oldTask = (PushDelayTask) task;
if (isPushToAll() || oldTask.isPushToAll()) {
// 可以push到所有的的这一类,无需特殊处理
pushToAll = true;
targetClients = null;
} else {
// 设置个集合,将连接放入,后续要一个个的推,这部分是针对失败的数据
targetClients.addAll(oldTask.getTargetClients());
}
setLastProcessTime(Math.min(getLastProcessTime(), task.getLastProcessTime()));
Loggers.PUSH.info("[PUSH] Task merge for {}", service);
}
这里也和之前分析的NacosDelayTaskExecuteEngine
和NacosExecuteTaskExecuteEngine
。因为NacosExecuteTaskExecuteEngine
无需延迟,所以默认就是需要执行,来一个执行一个。而NacosDelayTaskExecuteEngine
则会有个延迟的处理,主要针对失败的,延迟处理。因为既然是失败的,100ms对于失败的处理来说,间隔时间还是短了一些,所以放入到集合中,等延迟时间到了后再统一处理,这里的延迟时间的1s。
下面我们继续上次的代码往后分析
delayTaskEngine.getPushExecutor().doPushWithCallback(each, subscriber, wrapper,
new ServicePushCallback(each, subscriber, wrapper.getOriginalData(), delayTask.isPushToAll()));
第一步是获取delayTaskEngine.getPushExecutor()
,跟踪这个类分析,发现是由构造方法传入的。再往上跟踪,可以看到该类是归spring
托管的。注入的PushExecutorDelegate
。
这个Delegate
单词翻译是委托,说明这个是委托模式。看下这个类的一些成员变量和核心方法。
// rpc执行类,V2版本使用
private final PushExecutorRpcImpl rpcPushExecuteService;
// udp执行类,V1版本使用
private final PushExecutorUdpImpl udpPushExecuteService;
public PushExecutorDelegate(PushExecutorRpcImpl rpcPushExecuteService, PushExecutorUdpImpl udpPushExecuteService) {
this.rpcPushExecuteService = rpcPushExecuteService;
this.udpPushExecuteService = udpPushExecuteService;
}
private PushExecutor getPushExecuteService(String clientId, Subscriber subscriber) {
Optional<SpiPushExecutor> result = SpiImplPushExecutorHolder.getInstance()
.findPushExecutorSpiImpl(clientId, subscriber);
if (result.isPresent()) {
return result.get();
}
// use nacos default push executor
// 根据连接的客户端id识别是由upd推送还是rpc推送
return clientId.contains(IpPortBasedClient.ID_DELIMITER) ? udpPushExecuteService : rpcPushExecuteService;
}
本篇分析的2.2.0
的源码,自然是rpc版本的推送。至于为什么弃用upd
,大家可以看下第一篇的 1、Nacos 服务注册客户端源码分析中的引文,这里不做解释了。
我们顺着思路继续分析com.alibaba.nacos.naming.push.v2.executor.PushExecutorRpcImpl#doPushWithCallback
@Override
public void doPushWithCallback(String clientId, Subscriber subscriber, PushDataWrapper data,
NamingPushCallback callBack) {
// 获取服务信息
ServiceInfo actualServiceInfo = getServiceInfo(data, subscriber);
callBack.setActualServiceInfo(actualServiceInfo);
// 构建一个NotifySubscriberRequest,通过grpc向客户端发送信息
pushService.pushWithCallback(clientId, NotifySubscriberRequest.buildNotifySubscriberRequest(actualServiceInfo),
callBack, GlobalExecutor.getCallbackExecutor());
}
public void pushWithCallback(String connectionId, ServerRequest request, PushCallBack requestCallBack,
Executor executor) {
// 拿到客户端的连接
Connection connection = connectionManager.getConnection(connectionId);
if (connection != null) {
try {
// 发送异步请求
connection.asyncRequest(request, new AbstractRequestCallBack(requestCallBack.getTimeout()) {
@Override
public Executor getExecutor() {
return executor;
}
@Override
public void onResponse(Response response) {
if (response.isSuccess()) {
requestCallBack.onSuccess();
} else {
requestCallBack.onFail(new NacosException(response.getErrorCode(), response.getMessage()));
}
}
@Override
public void onException(Throwable e) {
requestCallBack.onFail(e);
}
});
} catch (ConnectionAlreadyClosedException e) {
connectionManager.unregister(connectionId);
requestCallBack.onSuccess();
} catch (Exception e) {
Loggers.REMOTE_DIGEST
.error("error to send push response to connectionId ={},push response={}", connectionId,
request, e);
requestCallBack.onFail(e);
}
} else {
requestCallBack.onSuccess();
}
}
这里构建了一个NotifySubscriberRequest
。回忆一下我们之前分析的代码。一个Request
代表着一类网络请求,一定有着一个Handler
处理。这里是由com.alibaba.nacos.client.naming.remote.gprc.NamingPushRequestHandler
来处理。
可以看到这个包在client
包中。也就是grpc
的双向流的处理。可以直接接受服务的请求,客户端直接处理。我们简单看下客户端的处理。
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 notifyRequest = (NotifySubscriberRequest) request;
// 处理逻辑
serviceInfoHolder.processServiceInfo(notifyRequest.getServiceInfo());
return new NotifySubscriberResponse();
}
return null;
}
}
public ServiceInfo processServiceInfo(ServiceInfo serviceInfo) {
String serviceKey = serviceInfo.getKey();
if (serviceKey == null) {
return null;
}
// 获取老的服务
ServiceInfo oldService = serviceInfoMap.get(serviceInfo.getKey());
if (isEmptyOrErrorPush(serviceInfo)) {
//empty or error push, just ignore
return oldService;
}
// 放入客户端的缓存
serviceInfoMap.put(serviceInfo.getKey(), serviceInfo);
// 对比下是否发生改变
boolean changed = isChangedServiceInfo(oldService, serviceInfo);
if (StringUtils.isBlank(serviceInfo.getJsonFromServer())) {
serviceInfo.setJsonFromServer(JacksonUtils.toJson(serviceInfo));
}
MetricsMonitor.getServiceInfoMapSizeMonitor().set(serviceInfoMap.size());
if (changed) {
NAMING_LOGGER.info("current ips:({}) service: {} -> {}", serviceInfo.ipCount(), serviceInfo.getKey(),
JacksonUtils.toJson(serviceInfo.getHosts()));
// 若改变,发送改变事件
NotifyCenter.publishEvent(new InstancesChangeEvent(notifierEventScope, serviceInfo.getName(), serviceInfo.getGroupName(),
serviceInfo.getClusters(), serviceInfo.getHosts()));
// 磁盘缓存也写一份
DiskCache.write(serviceInfo, cacheDir);
}
return serviceInfo;
}
本篇的内容不多,主要是对上篇的一些扫尾的内容。下一篇我准备将这个注册服务端再整理一遍,毕竟分了几个章节进行讲解,知识点也比较零碎。敬请期待后续的总集篇。