前の分析TaskExecuteEngine
とその 2 つのサブカテゴリNacosDelayTaskExecuteEngine
およびNacosExecuteTaskExecuteEngine
. まだ見ていない友人は、ここをクリックして5. Nacos サービス登録サーバーのソース コード分析 (4)を表示できます。この記事では、NacosTask
最初から分析し、その後のロジックを分析します。
タスク分析
NacosTask
boolean shouldProcess()
実行するかどうかを判断する方法は 1 つしかありません。
AbstractExecuteTask
との 2 つの抽象サブクラスがあります。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
遅延する必要がないため、デフォルトでは 1 つずつ実行されます。またNacosDelayTaskExecuteEngine
、主に失敗した遅延処理のために、遅延処理が発生します。失敗なので100msはまだ処理失敗間隔が短いのでコレクションに入れ、遅延時間経過後に一律に処理する.ここでの遅延時間は1秒である.
最後のコードの分析を続けましょう
delayTaskEngine.getPushExecutor().doPushWithCallback(each, subscriber, wrapper,
new ServicePushCallback(each, subscriber, wrapper.getOriginalData(), delayTask.isPushToAll()));
最初のステップは、このクラスを取得delayTaskEngine.getPushExecutor()
、追跡、分析し、コンストラクタによって渡されたことを確認することです。追跡すると、このクラスが managed であることがわかります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回の記事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();
}
}
1 つはここに建てられますNotifySubscriberRequest
。前に分析したコードを思い出してください。1 つはRequest
ネットワーク要求のタイプを表し、1 つの処理が必要です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;
}
要約する
この記事の内容は多くはなく、主に前回の記事の内容についてです。次の記事では、登録サーバーをもう一度整理しますが、結局のところ、説明するためにいくつかの章に分かれており、知識のポイントは比較的断片化されています。以下のリンクをクリックして直接表示できます。