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

上篇分析TaskExecuteEngine以及其两个子类NacosDelayTaskExecuteEngineNacosExecuteTaskExecuteEngine。没看过的小伙伴可以点击这里进行查看5、Nacos 服务注册服务端源码分析(四)。本篇从NacosTask开始分析,并分析后续的逻辑。

Task分析

NacosTask只有一个方法boolean shouldProcess(),即判断是否应该执行。

它有有两个抽象的子类,分别是AbstractExecuteTaskAbstractDelayTask

NacosTask.png

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

这里也和之前分析的NacosDelayTaskExecuteEngineNacosExecuteTaskExecuteEngine对应。因为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;
}

总结

本篇的内容不多,主要是对上篇的一些扫尾的内容。下一篇我将这个注册服务端再整理一遍,毕竟分了几个章节进行讲解,知识点也比较零碎。可以点击下面的链接直接查看。

7、Nacos 服务注册服务端源码分析(总结篇)

猜你喜欢

转载自blog.csdn.net/qq_34626094/article/details/130516989