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

下篇繼續。

好了,今天就到這裏了,希望對學習理解有幫助,大神看見勿噴,僅爲自己的學習理解,能力有限,請多包涵。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章