1. 程式人生 > >eureka 心跳機制 原始碼解析

eureka 心跳機制 原始碼解析

今天看了一篇檔案,介紹了一個eureka心跳中的一個續租間隔時間的問題,看了文章中的一些內容想了解一下eureka心跳機制的原理以及原始碼

首先我從DiscoveryClient這個類開始,講這個類之前,我們先來了解一下幾個概念

  • Register:服務註冊
    當Eureka客戶端向Eureka Server註冊時,它提供自身的元資料,比如IP地址、埠,執行狀況指示符URL,主頁等。

  • Renew:服務續約
    Eureka客戶會每隔30秒傳送一次心跳來續約。 通過續約來告知Eureka Server該Eureka客戶仍然存在,沒有出現問題。 正常情況下,如果Eureka Server在90秒沒有收到Eureka客戶的續約,它會將例項從其登錄檔中刪除。 建議不要更改續約間隔。

  • Fetch Registries:獲取註冊列表資訊
    Eureka客戶端從伺服器獲取登錄檔資訊,並將其快取在本地。客戶端會使用該資訊查詢其他服務,從而進行遠端呼叫。該註冊列表資訊定期(每30秒鐘)更新一次。每次返回註冊列表資訊可能與Eureka客戶端的快取資訊不同, Eureka客戶端自動處理。如果由於某種原因導致註冊列表資訊不能及時匹配,Eureka客戶端則會重新獲取整個登錄檔資訊。 Eureka伺服器快取註冊列表資訊,整個登錄檔以及每個應用程式的資訊進行了壓縮,壓縮內容和沒有壓縮的內容完全相同。Eureka客戶端和Eureka 伺服器可以使用JSON / XML格式進行通訊。在預設的情況下Eureka客戶端使用壓縮JSON格式來獲取註冊列表的資訊。

  • Cancel:服務下線
    Eureka客戶端在程式關閉時向Eureka伺服器傳送取消請求。 傳送請求後,該客戶端例項資訊將從伺服器的例項登錄檔中刪除。該下線請求不會自動完成,它需要呼叫以下內容:
    DiscoveryManager.getInstance().shutdownComponent();

  • Eviction 服務剔除
    在預設的情況下,當Eureka客戶端連續90秒沒有向Eureka伺服器傳送服務續約,即心跳,Eureka伺服器會將該服務例項從服務註冊列表刪除,即服務剔除。

再我們來看一下在DiscoveryClient類,這個類對服務註冊,服務續約,服務列表獲取等相關操作都有相應的方法,可以說是一個比較重要的類。下面我開始給大家講一下這幾個比較重要的方法

第一個:自身服務註冊方法

 boolean register() throws Throwable {
        logger.info("DiscoveryClient_" + this.appPathIdentifier + ": registering service...");

        EurekaHttpResponse httpResponse;
        try {
            httpResponse = this.eurekaTransport.registrationClient.register(this.instanceInfo);
        } catch (Exception var3) {
            logger.warn("{} - registration failed {}", new Object[]{"DiscoveryClient_" + this.appPathIdentifier, var3.getMessage(), var3});
            throw var3;
        }

        if (logger.isInfoEnabled()) {
            logger.info("{} - registration status: {}", "DiscoveryClient_" + this.appPathIdentifier, httpResponse.getStatusCode());
        }

        return httpResponse.getStatusCode() == 204;
    }
AbstractJerseyEurekaHttpClient:
   public EurekaHttpResponse<Void> register(InstanceInfo info) {
        String urlPath = "apps/" + info.getAppName();
        ClientResponse response = null;

        EurekaHttpResponse var5;
        try {
            Builder resourceBuilder = this.jerseyClient.resource(this.serviceUrl).path(urlPath).getRequestBuilder();
            this.addExtraHeaders(resourceBuilder);
            response = (ClientResponse)((Builder)((Builder)((Builder)resourceBuilder.header("Accept-Encoding", "gzip")).type(MediaType.APPLICATION_JSON_TYPE)).accept(new String[]{"application/json"})).post(ClientResponse.class, info);
            var5 = EurekaHttpResponse.anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build();
        } finally {
            if (logger.isDebugEnabled()) {
                logger.debug("Jersey HTTP POST {}/{} with instance {}; statusCode={}", new Object[]{this.serviceUrl, urlPath, info.getId(), response == null ? "N/A" : response.getStatus()});
            }

            if (response != null) {
                response.close();
            }

        }

        return var5;
    }

 

InstanceInfo在我的理解是註冊到註冊中心的元素資訊,包括ip,port等,AbstractJerseyEurekaHttpClient方法中的register方法中其實大致的可以理解成將自身的一些資訊通過http請求註冊到註冊中心,上面的請求的地址也就是自己在配置檔案配置的eureka-server地址

第二:服務列表獲取方法(獲取服務列表分為兩種:全量獲取和增量獲取)

下面這個是全量獲取

首先我們來看一下這個方法的原始碼

    private boolean fetchRegistry(boolean forceFullRegistryFetch) {
        Stopwatch tracer = this.FETCH_REGISTRY_TIMER.start();

        label122: {
            boolean var4;
            try {
                Applications applications = this.getApplications();
                // 下面是判斷是否將註冊服務列表的二級快取禁用或者並且是第一次獲取服務列表的時候
                // 如果是就直接拿獲取呼叫更新介面
                //https://www.cnblogs.com/fangfuhai/p/7070325.html   這個地址是一些eurek的配置詳情
                if (!this.clientConfig.shouldDisableDelta() && Strings.isNullOrEmpty(this.clientConfig.getRegistryRefreshSingleVipAddress()) && !forceFullRegistryFetch && applications != null && applications.getRegisteredApplications().size() != 0 && applications.getVersion().longValue() != -1L) {
                    this.getAndUpdateDelta(applications);
                } else {
                  // 否則就進入第一次獲取服務列表的方法
                    logger.info("Disable delta property : {}", this.clientConfig.shouldDisableDelta());
                    logger.info("Single vip registry refresh property : {}", this.clientConfig.getRegistryRefreshSingleVipAddress());
                    logger.info("Force full registry fetch : {}", forceFullRegistryFetch);
                    logger.info("Application is null : {}", applications == null);
                    logger.info("Registered Applications size is zero : {}", applications.getRegisteredApplications().size() == 0);
                    logger.info("Application version is -1: {}", applications.getVersion().longValue() == -1L);
        //下面這個方法就是直接發http請求到eureka server去獲取服務列表
                    this.getAndStoreFullRegistry();
                }
            //設定AppsHashCode(值是applications計算出來的hash值),客戶端讀取到之後更新好自己的Apps 
             //  快取之後進行的增量獲取會對比這個AppsHashCode,如果不一樣,就會進行一次全量Apps資訊請求
                applications.setAppsHashCode(applications.getReconcileHashCode());
                this.logTotalInstances();
                break label122;
            } catch (Throwable var8) {
                logger.error("DiscoveryClient_" + this.appPathIdentifier + " - was unable to refresh its cache! status = " + var8.getMessage(), var8);
                var4 = false;
            } finally {
                if (tracer != null) {
                    tracer.stop();
                }

            }

            return var4;
        }

        this.onCacheRefreshed();
        this.updateInstanceRemoteStatus();
        return true;
    }

接下來就看一下getAndStoreFullRegistry這個方法,主要就是發請求到eureka-server去獲取註冊的服務列表

    private void getAndStoreFullRegistry() throws Throwable {
        long currentUpdateGeneration = this.fetchRegistryGeneration.get();
        logger.info("Getting all instance registry info from the eureka server");
        Applications apps = null;
    // 發請求到eureka-server獲取到服務列表applications
        EurekaHttpResponse<Applications> httpResponse = this.clientConfig.getRegistryRefreshSingleVipAddress() == null ? this.eurekaTransport.queryClient.getApplications((String[])this.remoteRegionsRef.get()) : this.eurekaTransport.queryClient.getVip(this.clientConfig.getRegistryRefreshSingleVipAddress(), (String[])this.remoteRegionsRef.get());
        if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {
            apps = (Applications)httpResponse.getEntity();
        }

        logger.info("The response status is {}", httpResponse.getStatusCode());
        if (apps == null) {
            logger.error("The application is null for some reason. Not storing this information");
        } else if (this.fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1L)) {
            this.localRegionApps.set(this.filterAndShuffle(apps));
            logger.debug("Got full registry with apps hashcode {}", apps.getAppsHashCode());
        } else {
            logger.warn("Not updating applications as another thread is updating it already");
        }

    }

第三:續約方法 Renew


    private void initScheduledTasks() {
        int renewalIntervalInSecs;
        int expBackOffBound;
//此客戶端是否獲取eureka伺服器登錄檔上的註冊資訊,預設為true
        if (this.clientConfig.shouldFetchRegistry()) {
            renewalIntervalInSecs = this.clientConfig.getRegistryFetchIntervalSeconds();
            expBackOffBound = this.clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
//這是心跳機制的主要程式碼,主要是將TimedSupervisorTask放到定時任務的執行緒池,定時的執行heatbeat執行緒去傳送請求
            this.scheduler.schedule(new TimedSupervisorTask("cacheRefresh", this.scheduler, this.cacheRefreshExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new DiscoveryClient.CacheRefreshThread()), (long)renewalIntervalInSecs, TimeUnit.SECONDS);
        }
        //  例項是否在eureka伺服器上註冊自己的資訊以供其他服務發現,預設為true
        if (this.clientConfig.shouldRegisterWithEureka()) {
            renewalIntervalInSecs = this.instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
            expBackOffBound = this.clientConfig.getHeartbeatExecutorExponentialBackOffBound();
            logger.info("Starting heartbeat executor: renew interval is: " + renewalIntervalInSecs);
            this.scheduler.schedule(new TimedSupervisorTask("heartbeat", this.scheduler, this.heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new DiscoveryClient.HeartbeatThread(null)), (long)renewalIntervalInSecs, TimeUnit.SECONDS);
            this.instanceInfoReplicator = new InstanceInfoReplicator(this, this.instanceInfo, this.clientConfig.getInstanceInfoReplicationIntervalSeconds(), 2);
            this.statusChangeListener = new StatusChangeListener() {
                public String getId() {
                    return "statusChangeListener";
                }

                public void notify(StatusChangeEvent statusChangeEvent) {
                    if (InstanceStatus.DOWN != statusChangeEvent.getStatus() && InstanceStatus.DOWN != statusChangeEvent.getPreviousStatus()) {
                        DiscoveryClient.logger.info("Saw local status change event {}", statusChangeEvent);
                    } else {
                        DiscoveryClient.logger.warn("Saw local status change event {}", statusChangeEvent);
                    }

                    DiscoveryClient.this.instanceInfoReplicator.onDemandUpdate();
                }
            };
            if (this.clientConfig.shouldOnDemandUpdateStatusChange()) {
                this.applicationInfoManager.registerStatusChangeListener(this.statusChangeListener);
            }

            this.instanceInfoReplicator.start(this.clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());
        } else {
            logger.info("Not registering with Eureka server per configuration");
        }

    }

到此,我這邊大概了講了一下一些服務註冊以及獲取服務的流程,想繼續在深入的,自己可以再看深究

相關推薦

eureka 心跳機制 原始碼解析

今天看了一篇檔案,介紹了一個eureka心跳中的一個續租間隔時間的問題,看了文章中的一些內容想了解一下eureka心跳機制的原理以及原始碼 首先我從DiscoveryClient這個類開始,講這個類之前,我們先來了解一下幾個概念 Register:服務註冊 當E

Android Handler訊息機制原始碼解析

好記性不如爛筆頭,今天來分析一下Handler的原始碼實現 Handler機制是Android系統的基礎,是多執行緒之間切換的基礎。下面我們分析一下Handler的原始碼實現。 Handler訊息機制有4個類合作完成,分別是Handler,MessageQueue,Looper,Message Handl

自定義View(二)View的事件分發機制原始碼解析

View的事件分發機制是Android中的一個難點,也是非常重要的知識點,充分理解和掌握事件分發機制有助於我們在自定義view的過程中更好地設計和解決事件相關問題。下面我們通過原始碼的角度去分析一下Android是怎麼處理view事件的。 一個事件(比如手指按下螢幕的down事件)首先傳遞到

Spring作用域 (Scope:Request,Session,Thread,Refresh) 的代理機制原始碼解析

Spring有很多Scope,比如Singleton,Prototype,Request,Session,SpringCloud又新增了Thread,Refresh。預設的Scope是Singleton,Spring容器內最多的就是Singleton型別的Bea

Android訊息機制原始碼解析(Handler)

Android訊息機制,其實也就是Handler機制,主要用於UI執行緒和子執行緒之間互動。眾所周知,一般情況下,出於安全的考慮,所有與UI控制元件的操作都要放在主執行緒即UI執行緒中,而一些耗時操作應當放在子執行緒中。當在子執行緒中完成耗時操作並要對UI控制元

Android View 事件分發機制 原始碼解析(ViewGroup篇)

1. 前言 android點選 事件一直以來都是很多安卓程式設計師的心病,之前通過demo模擬總結出一些經驗,但是不看原始碼的程式設計師不是好程式設計師,這段時間,系統的梳理了下整個事件傳遞的原始碼,希望可以幫助大家徹底理解andriod的點選

Qt高階——Qt訊號槽機制原始碼解析

一、訊號槽機制的原理 1、訊號槽簡介 訊號槽是觀察者模式的一種實現,特性如下: A、一個訊號就是一個能夠被觀察的事件,或者

[原始碼解析] 從TimeoutException看Flink的心跳機制

# [原始碼解析] 從TimeoutException看Flink的心跳機制 [TOC] ## 0x00 摘要 本文從一個除錯時候常見的異常 "TimeoutException: Heartbeat of TaskManager timed out"切入,為大家剖析Flink的心跳機制。文中程式碼基於F

spring cloud eureka client原始碼解析

前言 eureka是springcloud 常用的註冊中心,這裡簡單介紹下,eureka client註冊邏輯的實現。eureka-client是1.6.2版本。 eureka server api參考 https://blog.csdn.net/qq_30

K8S 原始碼探祕 之 kubelet 同步 Node 狀態(kubelet 心跳機制分析)

一、引言        在 K8S 系統執行過程中,kubelet 需要定期向 API Server 上報節點執行狀態(也就是心跳訊息)        本文從原始碼角度分析下 kubelet 進行節點狀態上報

螞蟻金服通訊框架 SOFABolt 解析 | 超時控制機制心跳機制

SOFA Scalable Open Financial Architecture 是螞蟻金服自主研發的金融級分散式中介軟體,包含了構建金融級雲原生架構所需的各個元件,是在金融場景裡錘鍊出來的最佳實踐。 本文為《螞蟻金服通訊框架 SOFABolt 解析》系列第五篇,作者胡蘿蔔、丞一。

螞蟻金服通訊框架SOFABolt解析|超時控制機制心跳機制

SOFAScalable Open Financial Architecture 是螞蟻金服自主研發的金融級分散式中介軟體,包含了構建金融級雲原生架構所需的各個元件,是在金融場景裡錘鍊出來的最佳實踐。 本文為《螞蟻金服通訊框架 SOFABolt 解析》系列第五篇,作

OKHttp 3.10原始碼解析(三):快取機制

本篇我們來講解OKhttp的快取處理,在網路請求中合理地利用本地快取能有效減少網路開銷,提高響應速度。HTTP報頭也定義了很多控制快取策略的域,我們先來認識一下HTTP的快取策略。 一.HTTP快取策略 HTTP快取有多種規則,根據是否需要向伺服器發起請求來分類,我們將其分為兩大類:強制

Netflix Eureka原始碼分析(18)——eureka server網路故障時的的自我保護機制原始碼剖析

假如說,20個服務例項,結果在1分鐘之內,只有8個服務例項保持了心跳 --> eureka server是應該將剩餘的12個沒有心跳的服務例項都摘除嗎? 這個時候很可能說的是,eureka server自己網路故障了,那些服務沒問題的。只不過eureka server

Netflix Eureka原始碼分析(19)——eureka server叢集機制原始碼剖析:登錄檔同步以及高可用

(1)eureka core的BootStrap裡面,有一塊程式碼,是PeerEurekaNodes的程式碼,其實是在處理eureka server叢集資訊的初始化,會執行PeerEurekaNodes.start()方法 public class EurekaBootSt

jdk原始碼解析(七)——Java虛擬機器類載入機制

前面我們講解了class檔案的格式,以及它是什麼樣的。那麼接下來需要了解它怎麼被載入到jvm中呢?jvm的載入機制又是怎麼一個過程呢?本文參考了《Java 虛擬機器規範(Java SE 7 版)》的第五章內容來詳細解釋一下 虛擬機器類載入機制:虛擬機器把描述類的資料從cla

Android事件分發機制完全解析,帶你從原始碼的角度徹底理解(上)-郭霖

其實我一直準備寫一篇關於Android事件分發機制的文章,從我的第一篇部落格開始,就零零散散在好多地方使用到了Android事件分發的知識。也有好多朋友問過我各種問題,比如:onTouch和onTouchEvent有什麼區別,又該如何使用?為什麼給ListView引入了一

Android事件分發機制完全解析,帶你從原始碼的角度徹底理解(下)-郭霖

記得在前面的文章中,我帶大家一起從原始碼的角度分析了Android中View的事件分發機制,相信閱讀過的朋友對View的事件分發已經有比較深刻的理解了。 還未閱讀過的朋友,請先參考 Android事件分發機制完全解析,帶你從原始碼的角度徹底理解(上) 。 那麼今天我們將繼

原始碼解析ListView中的RecycleBin機制

在自定義Adapter時,我們常常會重寫Adapter的getView方法,該方法的簽名如下所示: public abstract View getView (int position, View convertView, ViewGroup parent

Android事件分發機制完全解析,帶你從原始碼的角度徹底理解(上)

其實我一直準備寫一篇關於Android事件分發機制的文章,從我的第一篇部落格開始,就零零散散在好多地方使用到了Android事件分發的知識。也有好多朋友問過我各種問題,比如:onTouch和onTouchEvent有什麼區別,又該如何使用?為什麼給ListView引入