深入理解lastDirtyTimestamp(十二)

簡介

lastDirtyTimestamp在Eureka中承載了比較重要的作用骗灶,在續(xù)約贱鄙,設(shè)置覆蓋狀態(tài)螃征,刪除覆蓋狀態(tài)的

時候都有用到蝇狼。

定義: 實例的最后修改時間

Eureka Client

EurekaClient在系統(tǒng)啟動的時候,會啟動一個定時任務(wù)抗果,每40秒執(zhí)行一次筋帖,該定時任務(wù)負(fù)責(zé)比對

客戶端的信息,如果發(fā)生改變則更新lastDirtyTimestamp的值冤馏,同時對Eureka Server 重新發(fā)起注冊日麸。

public void run() {
    try {
        // 該方法負(fù)責(zé)比對客戶端存在的instance信息和實際的信息,是否發(fā)生改變
        // 比如: 客戶端的狀態(tài)逮光,IP代箭,配置信息發(fā)生改變
        discoveryClient.refreshInstanceInfo();
        
        // 判斷信息是否改變,是否需要重新注冊涕刚。通過isInstanceInfoDirty這個布爾值判斷
        Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
        if (dirtyTimestamp != null) {
            // 注冊
            discoveryClient.register();
            // 取消
            instanceInfo.unsetIsDirty(dirtyTimestamp);
        }
    } catch (Throwable t) {
        logger.warn("There was a problem with the instance info replicator", t);
    } finally {
        Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS);
        scheduledPeriodicRef.set(next);
    }
}

步驟說明:

1.判斷客戶端的狀態(tài)梢卸,IP,配置信息是否發(fā)生改變副女,如果發(fā)生改變則會調(diào)用setIsDirty() 蛤高, 設(shè)置最后更新時間

lastDirtyTimestamp , 同時設(shè)置isInstanceInfoDirty = true

public synchronized void setIsDirty() {
    isInstanceInfoDirty = true;
    lastDirtyTimestamp = System.currentTimeMillis();
}
  1. 判斷isInstanceInfoDirty 是否為true碑幅, 是的話戴陡,則返回最后修改時間
public synchronized Long isDirtyWithTime() {
    if (isInstanceInfoDirty) {
        return lastDirtyTimestamp;
    } else {
        return null;
    }
}
  1. 如果isDirtyWithTime()不為null, 則需要重新發(fā)起注冊

4.卸載isInstanceInfoDirty 的狀態(tài)沟涨,修改為false

接下來重點講一下refreshInstanceInfo方法恤批。

void refreshInstanceInfo() {
    // 1. 判斷數(shù)據(jù)中心的數(shù)據(jù)是否發(fā)生改變,
    applicationInfoManager.refreshDataCenterInfoIfRequired();
    // 2.判斷配置是否發(fā)生改變
    applicationInfoManager.refreshLeaseInfoIfRequired();

    InstanceStatus status;
    try {
        // 通過健康檢查器裹赴,獲取應(yīng)用的最新狀態(tài)
        status = getHealthCheckHandler().getStatus(instanceInfo.getStatus());
    } catch (Exception e) {
        logger.warn("Exception from healthcheckHandler.getStatus, setting status to DOWN", e);
        status = InstanceStatus.DOWN;
    }

    if (null != status) {
        // 設(shè)置狀態(tài)
        applicationInfoManager.setInstanceStatus(status);
    }
}

步驟說明:

1.refreshDataCenterInfoIfRequired()方法喜庞,里面主要是判斷應(yīng)用的IP地址是否發(fā)生改變诀浪。

public void refreshDataCenterInfoIfRequired() {
    // 獲取當(dāng)前的地址
    String existingAddress = instanceInfo.getHostName();

    // 獲取當(dāng)前實際的地址
    String newAddress;
    if (config instanceof RefreshableInstanceConfig) {
        newAddress = ((RefreshableInstanceConfig) config).resolveDefaultAddress(true);
    } else {
        newAddress = config.getHostName(true);
    }
    String newIp = config.getIpAddress();
    // 判斷新舊地址是否一致,如果不一致延都,則進(jìn)入if結(jié)構(gòu)
    if (newAddress != null && !newAddress.equals(existingAddress)) {
        logger.warn("The address changed from : {} => {}", existingAddress, newAddress);
        InstanceInfo.Builder builder = new InstanceInfo.Builder(instanceInfo);
        builder.setHostName(newAddress).setIPAddr(newIp).setDataCenterInfo(config.getDataCenterInfo());
        // 該方法最為重要雷猪,表示信息已經(jīng)發(fā)生改變,
        instanceInfo.setIsDirty();
    }
}
// InstanceInfo.java 
public synchronized void setIsDirty() {
    isInstanceInfoDirty = true;
    lastDirtyTimestamp = System.currentTimeMillis();
}

如上代碼所示晰房,如果hostName發(fā)生改變求摇,則會更新本地的Instance的信息,同時調(diào)用setIsDirty()方法殊者,表示信息已經(jīng)被改變与境。

更新isInstanceInfoDirty = true ,同時設(shè)置lastDirtyTimestamp 為系統(tǒng)當(dāng)前時間

2.refreshLeaseInfoIfRequired() 判斷配置中的猖吴,“租約過期時間” 摔刁, “續(xù)約時間” 是否發(fā)生改變,如果發(fā)生改變了海蔽,那么就需要

更新本地instance的信息共屈,同時調(diào)用setIsDirty()方法表示信息已經(jīng)被改變,需要重新注冊

public void refreshLeaseInfoIfRequired() {
    LeaseInfo leaseInfo = instanceInfo.getLeaseInfo();
    if (leaseInfo == null) {
        return;
    }
    // 租約過期時間准潭,默認(rèn)90秒
    int currentLeaseDuration = config.getLeaseExpirationDurationInSeconds();
    // 續(xù)約時間趁俊,默認(rèn)30秒
    int currentLeaseRenewal = config.getLeaseRenewalIntervalInSeconds();
    // 判斷時間是否一致
    if (leaseInfo.getDurationInSecs() != currentLeaseDuration || leaseInfo.getRenewalIntervalInSecs() != currentLeaseRenewal) {
        LeaseInfo newLeaseInfo = LeaseInfo.Builder.newBuilder()
                .setRenewalIntervalInSecs(currentLeaseRenewal)
                .setDurationInSecs(currentLeaseDuration)
                .build();
        instanceInfo.setLeaseInfo(newLeaseInfo);
        // 該方法最為重要域仇,表示信息已經(jīng)發(fā)生改變刑然,
        instanceInfo.setIsDirty();
    }
}
  1. 調(diào)用健康檢查器,獲取當(dāng)前的instance的狀態(tài)com.netflix.appinfo.HealthCheckCallbackToHandlerBridge
public class HealthCheckCallbackToHandlerBridge implements HealthCheckHandler {

    private final HealthCheckCallback callback;

    public HealthCheckCallbackToHandlerBridge() {
        callback = null;
    }

    public HealthCheckCallbackToHandlerBridge(HealthCheckCallback callback) {
        this.callback = callback;
    }

    @Override
    public InstanceInfo.InstanceStatus getStatus(InstanceInfo.InstanceStatus currentStatus) {
        // 判斷instance的狀態(tài)暇务。
        if (null == callback || InstanceInfo.InstanceStatus.STARTING == currentStatus
                || InstanceInfo.InstanceStatus.OUT_OF_SERVICE == currentStatus) { // Do not go to healthcheck handler if the status is starting or OOS.
            return currentStatus;
        }

        return callback.isHealthy() ? InstanceInfo.InstanceStatus.UP : InstanceInfo.InstanceStatus.DOWN;
    }
}

由上可知泼掠, 健康檢查器中的getStatus方法,判斷步驟

判斷callback是否為空 垦细, 如果為空择镇,則以當(dāng)前實例的狀態(tài)為準(zhǔn)(默認(rèn)為null , 如果我們想自己實現(xiàn)自定義的健康檢查括改,可以設(shè)置起來)
判斷傳入的當(dāng)前實例的狀態(tài)是否等于STARTING腻豌, OUT_OF_SERVICE 這兩個狀態(tài),如果等于嘱能,則以當(dāng)前實例的狀態(tài)為準(zhǔn)
使用callback.isHealty()判斷實例的健康狀態(tài)吝梅,然后返回UP或則DOWN

  1. 調(diào)用applicationInfoManager.setInstanceStatus(status); , 設(shè)置實例的狀態(tài)惹骂,如果傳入的狀態(tài)和實例的狀態(tài)一直苏携,則不會修改。

如果狀態(tài)不一致对粪,則會修改instance的狀態(tài)右冻,同時調(diào)用setIsDirty() 表示信息發(fā)生改變装蓬。

public synchronized InstanceStatus setStatus(InstanceStatus status) {
    // 判斷狀態(tài)是否一致,一致則不更新
    if (this.status != status) {
        InstanceStatus prev = this.status;
        this.status = status;
        //調(diào)用該方法纱扭,表示信息發(fā)生修改
        setIsDirty();
        return prev;
    }
    return null;
}

綜合以上代碼分析牍帚,我們可以發(fā)現(xiàn),當(dāng)客戶端的信息發(fā)生任何改變的時候跪但,都會調(diào)用setIsDirty() , 更新isInstanceInfoDirty = true 履羞,

同時設(shè)置lastDirtyTimestamp 為系統(tǒng)當(dāng)前時間 , 由此可見屡久,lastDirtyTimestamp 的定義為“實例的最后修改時間”

Eureka Server 用途

服務(wù)端在接收renew 忆首, stateUpdate, deleteStatusUpdate 的時候,都會要求客戶端傳入lastDirtyTimestamp 這個參數(shù) 被环,注冊的時候也會對這個值做對比

public Response renewLease(
        @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication,
        @QueryParam("overriddenstatus") String overriddenStatus,
        @QueryParam("status") String status,
        @QueryParam("lastDirtyTimestamp") String lastDirtyTimestamp) {
    // ......
    return response;
}
@Path("status")
public Response statusUpdate(
        @QueryParam("value") String newStatus,
        @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication,
        @QueryParam("lastDirtyTimestamp") String lastDirtyTimestamp) {
    // ......
}


@DELETE
@Path("status")
public Response deleteStatusUpdate(
        @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication,
        @QueryParam("value") String newStatusValue,
        @QueryParam("lastDirtyTimestamp") String lastDirtyTimestamp) {
    // ......
}

以上三個方法中糙及,當(dāng)服務(wù)端檢測到本地的instanceStatue需要更新的時候,也會更新Eureka Server本地的lastDirtyTimestamp 筛欢,下面針對

續(xù)約和注冊講一下在其中的用法浸锨。

續(xù)約

renew續(xù)約完成之后,會判斷傳入的lastDirtyTimestamp 和客戶端本地的lastDirtyTimestamp 是否一致版姑,如果客戶端的值大柱搜,那么就會返回404錯誤,客戶端就需要重新注冊了剥险, 具體機制可以查看深入理解Eureka 心跳續(xù)約(三)中的Eureka Server接收心跳那一小結(jié)聪蘸。

注冊

public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
    try {
        // .... 省略代碼
        Lease<InstanceInfo> existingLease = gMap.get(registrant.getId());
        //如果Eureka Server中該實例已經(jīng)存在
        if (existingLease != null && (existingLease.getHolder() != null)) {
            // 比較lastDirtyTimestamp , 以lastDirtyTimestamp大的為準(zhǔn)
            if (existingLastDirtyTimestamp > registrationLastDirtyTimestamp) {
                registrant = existingLease.getHolder();
            }
        }
       // .... 省略代碼
    } finally {
        read.unlock();
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末表制,一起剝皮案震驚了整個濱河市健爬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌么介,老刑警劉巖娜遵,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異壤短,居然都是意外死亡设拟,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門久脯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纳胧,“玉大人,你說我怎么就攤上這事桶现《阊牛” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵骡和,是天一觀的道長相赁。 經(jīng)常有香客問我相寇,道長,這世上最難降的妖魔是什么钮科? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任唤衫,我火速辦了婚禮,結(jié)果婚禮上绵脯,老公的妹妹穿的比我還像新娘佳励。我一直安慰自己,他們只是感情好蛆挫,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布赃承。 她就那樣靜靜地躺著,像睡著了一般悴侵。 火紅的嫁衣襯著肌膚如雪瞧剖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天可免,我揣著相機與錄音抓于,去河邊找鬼。 笑死浇借,一個胖子當(dāng)著我的面吹牛捉撮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播妇垢,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼巾遭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了修己?” 一聲冷哼從身側(cè)響起恢总,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤迎罗,失蹤者是張志新(化名)和其女友劉穎睬愤,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纹安,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡尤辱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了厢岂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片光督。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖塔粒,靈堂內(nèi)的尸體忽然破棺而出结借,到底是詐尸還是另有隱情,我是刑警寧澤卒茬,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布船老,位于F島的核電站咖熟,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏柳畔。R本人自食惡果不足惜馍管,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望薪韩。 院中可真熱鬧确沸,春花似錦、人聲如沸俘陷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拉盾。三九已至宛逗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盾剩,已是汗流浹背雷激。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留告私,地道東北人屎暇。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像驻粟,于是被迫代替她去往敵國和親根悼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容