Spring Cloud 2.2.2 源碼之五十七nacos服務端處理實例心跳一
回顧客戶端實例心跳
在服務實例註冊之前,如果是臨時的服務實例,會先開啓心跳任務,不過心跳任務5
秒後會運行,第一次心跳的時候會帶上心跳內容,也就是服務實例的信息,避免實例不存在又要重新註冊一次:
心跳處理基本流程
調度心跳任務。
發送心跳:
uri
是/nacos/v1/ns/instance/beat
:
如果返回找不到服務就會進行註冊,所以你debug
的時候可能會發現已經有註冊的了,其實就是心跳去註冊的。
服務端處理心跳
InstanceController的beat
這裏會接受客戶端的心跳,如果是有beat
信息的話,說明是第一次,會帶有服務實例信息,因爲如果成功了服務端會下發不要帶beat
信息的參數,這樣客戶端第二次就不會帶beat
信息了。如果發現沒有該服務,又沒帶beat
信息,說明這個服務可能出了問題被移除過了,直接返回沒找到。如果沒有服務,但是發現有beat
信息,那就從beat
中獲取服務實例信息,進行註冊。然後創建一個RsInfo
,跟心跳實例刷新相關,開啓一次ClientBeatProcessor
處理任務,處理一次服務實例刷新。最後返回成功,並帶上心跳時間和客戶端是否要進行輕量級心跳發送標誌。
@CanDistro
@PutMapping("/beat")
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
public JSONObject beat(HttpServletRequest request) throws Exception {
JSONObject result = new JSONObject();
//設置要求的心跳間隔
result.put("clientBeatInterval", switchDomain.getClientBeatInterval());
String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID,
Constants.DEFAULT_NAMESPACE_ID);
String clusterName = WebUtils.optional(request, CommonParams.CLUSTER_NAME,
UtilsAndCommons.DEFAULT_CLUSTER_NAME);
String ip = WebUtils.optional(request, "ip", StringUtils.EMPTY);
int port = Integer.parseInt(WebUtils.optional(request, "port", "0"));
String beat = WebUtils.optional(request, "beat", StringUtils.EMPTY);
RsInfo clientBeat = null;
if (StringUtils.isNotBlank(beat)) {//如果有心跳內容,也就不是輕量級心跳,轉換爲RsInfo
clientBeat = JSON.parseObject(beat, RsInfo.class);
}
if (clientBeat != null) {
if (StringUtils.isNotBlank(clientBeat.getCluster())) {
clusterName = clientBeat.getCluster();//獲取集羣名
} else {
// fix #2533
clientBeat.setCluster(clusterName);
}
ip = clientBeat.getIp();
port = clientBeat.getPort();
}
if (Loggers.SRV_LOG.isDebugEnabled()) {
Loggers.SRV_LOG.debug("[CLIENT-BEAT] full arguments: beat: {}, serviceName: {}", clientBeat, serviceName);
}
//獲取相關服務實例
Instance instance = serviceManager.getInstance(namespaceId, serviceName, clusterName, ip, port);
//實例不存在
if (instance == null) {
if (clientBeat == null) {//如果心跳內容也沒有就返回找不到
result.put(CommonParams.CODE, NamingResponseCode.RESOURCE_NOT_FOUND);
return result;
}//否則根據心跳內容創建一個實例
instance = new Instance();
instance.setPort(clientBeat.getPort());
instance.setIp(clientBeat.getIp());
instance.setWeight(clientBeat.getWeight());
instance.setMetadata(clientBeat.getMetadata());
instance.setClusterName(clusterName);
instance.setServiceName(serviceName);
instance.setInstanceId(instance.getInstanceId());
instance.setEphemeral(clientBeat.isEphemeral());
//註冊實例
serviceManager.registerInstance(namespaceId, serviceName, instance);
}
Service service = serviceManager.getService(namespaceId, serviceName);
if (service == null) {
throw new NacosException(NacosException.SERVER_ERROR,
"service not found: " + serviceName + "@" + namespaceId);
}
if (clientBeat == null) {//不存在的話,要創建一個進行處理
clientBeat = new RsInfo();
clientBeat.setIp(ip);
clientBeat.setPort(port);
clientBeat.setCluster(clusterName);
}//開啓一次性心跳檢查任務
service.processClientBeat(clientBeat);
//成功返回,
result.put(CommonParams.CODE, NamingResponseCode.OK);
result.put("clientBeatInterval", instance.getInstanceHeartBeatInterval());//5秒間隔
result.put(SwitchEntry.LIGHT_BEAT_ENABLED, switchDomain.isLightBeatEnabled());//告訴客戶端不需要帶上心跳信息了,變成輕量級心跳了
return result;
}
Service的processClientBeat處理一次心跳
創建一個臨時心跳處理器,然後調度處理一次。
public void processClientBeat(final RsInfo rsInfo) {
ClientBeatProcessor clientBeatProcessor = new ClientBeatProcessor();
clientBeatProcessor.setService(this);
clientBeatProcessor.setRsInfo(rsInfo);
HealthCheckReactor.scheduleNow(clientBeatProcessor);
}
public static ScheduledFuture<?> scheduleNow(Runnable task) {
return EXECUTOR.schedule(task, 0, TimeUnit.MILLISECONDS);
}
下篇繼續。
好了,今天就到這裏了,希望對學習理解有幫助,大神看見勿噴,僅爲自己的學習理解,能力有限,請多包涵。