SpringCloud源碼解析 -- Eureka原理探究

本文通過(guò)閱讀Eureka源碼,分享Eureka的實(shí)現(xiàn)原理。
本文主要梳理Eureka整體設(shè)計(jì)及實(shí)現(xiàn),并不一一列舉Eureka源碼細(xì)節(jié)。

源碼分析基于Spring Cloud Hoxton,Eureka版本為1.9

Eureka分為Eureka Client,Eureka Server,多個(gè)Eureka Server節(jié)點(diǎn)組成一個(gè)Eureka集群,服務(wù)通過(guò)Eureka Client注冊(cè)到Eureka Server。


CAP理論指出,一個(gè)分布式系統(tǒng)不可能同時(shí)滿足C(一致性)、A(可用性)和P(分區(qū)容錯(cuò)性)。
由于分布式系統(tǒng)中必須保證分區(qū)容錯(cuò)性,因此我們只能在A和C之間進(jìn)行權(quán)衡。
Zookeeper保證的是CP, 而Eureka則是保證AP。
為什么呢?
在注冊(cè)中心這種場(chǎng)景中,可用性比一致性更重要。
作為注冊(cè)中心,其實(shí)數(shù)據(jù)是不經(jīng)常變更的,只有服務(wù)發(fā)布,機(jī)器上下線,服務(wù)擴(kuò)縮容時(shí)才變更。
因此Eureka選擇AP,即使出問(wèn)題了,也返回舊數(shù)據(jù),保證服務(wù)能(最大程度)正常調(diào)用, 避免出現(xiàn)因?yàn)樽?cè)中心的問(wèn)題導(dǎo)致服務(wù)不可用這種得不償失的情況。
所以,Eureka各個(gè)節(jié)點(diǎn)都是平等的(去中心化的架構(gòu),無(wú)master/slave區(qū)分),掛掉的節(jié)點(diǎn)不會(huì)影響正常節(jié)點(diǎn)的工作,剩余的節(jié)點(diǎn)依然可以提供注冊(cè)和查詢服務(wù)。

Eureka Client

Eureka 1.9只要引入spring-cloud-starter-netflix-eureka-client依賴,即使不使用@EnableDiscoveryClient或@EnableEurekaClient注解,服務(wù)也會(huì)注冊(cè)到Eureka集群。

client主要邏輯在com.netflix.discovery.DiscoveryClient實(shí)現(xiàn),EurekaClientAutoConfiguration中構(gòu)建了其子類CloudEurekaClient。

定時(shí)任務(wù)

DiscoveryClient#initScheduledTasks方法設(shè)置定時(shí)任務(wù),主要有CacheRefreshThread,HeartbeatThread,以及InstanceInfoReplicator。

同步

服務(wù)注冊(cè)信息緩存在DiscoveryClient#localRegionApps變量中,CacheRefreshThread負(fù)責(zé)定時(shí)從Eureka Server讀取最新的服務(wù)注冊(cè)信息,更新到本地緩存。
CacheRefreshThread -> DiscoveryClient#refreshRegistry -> DiscoveryClient#fetchRegistry
當(dāng)存在多個(gè)Eureka Server節(jié)點(diǎn)時(shí),Client會(huì)與eureka.client.serviceUrl.defaultZone配置的第一個(gè)Server節(jié)點(diǎn)同步數(shù)據(jù),當(dāng)?shù)谝粋€(gè)Server節(jié)點(diǎn)同步失敗,才會(huì)同步第二個(gè)節(jié)點(diǎn),以此類推。

從DiscoveryClient#fetchRegistry可以看到,同步數(shù)據(jù)有兩個(gè)方法
(1)全量同步
由DiscoveryClient#getAndStoreFullRegistry方法實(shí)現(xiàn),通過(guò)Http Get調(diào)用Server接口apps/,
獲取Server節(jié)點(diǎn)中所有服務(wù)注冊(cè)信息替換DiscoveryClient#localRegionApps

注意:Client請(qǐng)求Server端的服務(wù),都是通過(guò)EurekaHttpClient接口發(fā)起,該接口實(shí)現(xiàn)類EurekaHttpClientDecorator通過(guò)RequestExecutor接口將請(qǐng)求委托給其他EurekaHttpClient實(shí)現(xiàn)類,并提供execute方法給子類實(shí)現(xiàn)擴(kuò)展處理(該擴(kuò)展處理可以針對(duì)每一個(gè)EurekaHttpClient方法,類似AOP)。子類RetryableEurekaHttpClient#execute中,會(huì)獲取eureka.client.service-url.defaultZone中配置的地址,通過(guò)TransportClientFactory#newClient,構(gòu)造一個(gè)RestTemplateTransportClientFactory,再真正發(fā)起請(qǐng)求。

(2)增量同步
由DiscoveryClient#getAndUpdateDelta方法實(shí)現(xiàn),通過(guò)Http Get調(diào)用Server接口apps/delta,獲取最新ADDED、MODIFIED,DELETED操作,更新本地緩存。
如果獲取最新操作失敗,則會(huì)發(fā)起全量同步。

配置:
eureka.client.fetch-registry,是否定時(shí)同步信息,默認(rèn)true
eureka.client.registry-fetch-interval-seconds,間隔多少秒同步一次服務(wù)注冊(cè)信息,默認(rèn)30

心跳

HeartbeatThread -> DiscoveryClient#renew -> EurekaHttpClient#sendHeartBeat
通過(guò)Http Put調(diào)用Server接口apps/{appName}/{instanceId}
appName是服務(wù)的spring.application.name,instanceId是服務(wù)IP加服務(wù)端口。

注意:如果Server返回NOT_FOUND狀態(tài),則重新注冊(cè)。

配置:
eureka.client.register-with-eureka,當(dāng)前應(yīng)用是否注冊(cè)到Eureka集群,默認(rèn)true
eureka.instance.lease-renewal-interval-in-seconds,間隔多少秒發(fā)送一次心跳,默認(rèn)30

注冊(cè)

DiscoveryClient#構(gòu)造函數(shù) -> DiscoveryClient#register
通過(guò)Http Post調(diào)用Server接口apps/{appName},發(fā)送當(dāng)前應(yīng)用的注冊(cè)信息到Server。
配置:
eureka.client.register-with-eureka,當(dāng)前應(yīng)用是否注冊(cè)到Eureka集群,默認(rèn)true
eureka.client.should-enforce-registration-at-init,是否在初始化時(shí)注冊(cè),默認(rèn)false

InstanceInfoReplicator

InstanceInfoReplicator任務(wù)會(huì)去監(jiān)測(cè)應(yīng)用自身的IP信息以及配置信息是否發(fā)生改變,如果發(fā)生改變,則會(huì)重新發(fā)起注冊(cè)。
配置:
eureka.client.initial-instance-info-replication-interval-seconds,間隔多少秒檢查一次自身信息,默認(rèn)40

下線

EurekaClientAutoConfiguration配置了CloudEurekaClient的銷毀方法

@Bean(destroyMethod = "shutdown")

DiscoveryClient#shutdown方法完成下線的處理工作,包括取消定時(shí)任務(wù),調(diào)用unregister方法(通過(guò)Http Delete調(diào)用Server接口apps/{appName}/{id}),取消監(jiān)控任務(wù)等

Eureka Server

@EnableEurekaServer引入EurekaServerMarkerConfiguration,EurekaServerMarkerConfiguration構(gòu)建EurekaServerMarkerConfiguration.Marker。
EurekaServerAutoConfiguration會(huì)在Spring上下文中存在EurekaServerMarkerConfiguration.Marker時(shí)生效,構(gòu)造Server端組件類。

Eureka Server也要使用DiscoveryClient,拉取其他Server節(jié)點(diǎn)的服務(wù)注冊(cè)信息或者將自身注冊(cè)到Eureka集群中。

啟動(dòng)同步

Server啟動(dòng)時(shí),需要從相鄰Server節(jié)點(diǎn)獲取服務(wù)注冊(cè)信息,同步到自身內(nèi)存。

Server的服務(wù)注冊(cè)信息存放在AbstractInstanceRegistry#registry變量中,類型為ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>。
外層Map Key為appName,外層Map Key為instanceId,Lease代表Client與Server之間維持的一個(gè)契約。InstanceInfo保存具體的服務(wù)注冊(cè)信息,如instanceId,appName,ipAddr,port等。

EurekaServerBootstrap是Server端的啟動(dòng)引導(dǎo)類,EurekaServerInitializerConfiguration實(shí)現(xiàn)了Lifecycle接口,start方法調(diào)用eurekaServerBootstrap.contextInitialized完成Server端初始化。
eurekaServerBootstrap.contextInitialized -> EurekaServerBootstrap#initEurekaServerContext -> PeerAwareInstanceRegistryImpl#syncUp -> AbstractInstanceRegistry#register
PeerAwareInstanceRegistryImpl#syncUp調(diào)用DiscoveryClient#getApplications方法,獲取相鄰server節(jié)點(diǎn)的所有服務(wù)注冊(cè)信息,再調(diào)用AbstractInstanceRegistry#register方法,注冊(cè)到AbstractInstanceRegistry#registry變量中。

AbstractInstanceRegistry#register

public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
    try {
        read.lock();
        Map<String, Lease<InstanceInfo>> gMap = registry.get(registrant.getAppName());
        REGISTER.increment(isReplication);
        ...
        // #1
        Lease<InstanceInfo> existingLease = gMap.get(registrant.getId());   
        if (existingLease != null && (existingLease.getHolder() != null)) {
            Long existingLastDirtyTimestamp = existingLease.getHolder().getLastDirtyTimestamp();
            Long registrationLastDirtyTimestamp = registrant.getLastDirtyTimestamp();
            ...
            // #2
            if (existingLastDirtyTimestamp > registrationLastDirtyTimestamp) {  
                registrant = existingLease.getHolder();
            }
        } else {
            synchronized (lock) {
                if (this.expectedNumberOfClientsSendingRenews > 0) {
                    this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews + 1;
                    // #3
                    updateRenewsPerMinThreshold();  
                }
            }
            logger.debug("No previous lease information found; it is new registration");
        }
        Lease<InstanceInfo> lease = new Lease<InstanceInfo>(registrant, leaseDuration);
        if (existingLease != null) {
            lease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp());
        }
        // #4
        gMap.put(registrant.getId(), lease);    
        ...
        registrant.setActionType(ActionType.ADDED);
        // #5
        recentlyChangedQueue.add(new RecentlyChangedItem(lease));   
        registrant.setLastUpdatedTimestamp();
        invalidateCache(registrant.getAppName(), registrant.getVIPAddress(), registrant.getSecureVipAddress()); 
        logger.info("Registered instance {}/{} with status {} (replication={})",
                registrant.getAppName(), registrant.getId(), registrant.getStatus(), isReplication);
    } finally {
        read.unlock();
    }
}

#1 通過(guò)appName,instanceId查詢已有的Lease
#2 如果該服務(wù)已存在Lease,并且LastDirtyTimestamp的值更大,使用已存在的Lease。
#3 更新numberOfRenewsPerMinThreshold,該值用于自我保護(hù)模式。
#4 構(gòu)建一個(gè)新的Lease,添加到AbstractInstanceRegistry#registry緩存中。
#5 添加recentlyChangedQueue,apps/delta接口從中獲取最新變更操作。

提供服務(wù)

Server通過(guò)ApplicationsResource/ApplicationResource/InstanceResource對(duì)外提供Http服務(wù)。

AbstractInstanceRegistry負(fù)責(zé)實(shí)現(xiàn)cancle,register,renew,statusUpdate,deleteStatusOverride等操作的業(yè)務(wù)邏輯。
PeerAwareInstanceRegistryImpl通過(guò)replicateToPeers方法將操作同步到其他節(jié)點(diǎn),以保證集群節(jié)點(diǎn)數(shù)據(jù)同步。
PeerAwareInstanceRegistryImpl#replicateToPeers方法最后一個(gè)參數(shù)isReplication,決定是否需要進(jìn)行同步。
如果Server節(jié)點(diǎn)接收到其他Server節(jié)點(diǎn)發(fā)送的同步操作,是不需要再繼續(xù)向其他Server同步的,否則會(huì)引起循環(huán)更新。
該參數(shù)通過(guò)Http Requst的Header參數(shù)x-netflix-discovery-replication決定(只有Client發(fā)送的請(qǐng)求該參數(shù)才為true)。

數(shù)據(jù)一致

PeerAwareInstanceRegistryImpl#replicateToPeers方法通過(guò)PeerEurekaNodes#getPeerEurekaNodes獲取其他server節(jié)點(diǎn)地址,
PeerEurekaNodes#peerEurekaNodes變量維護(hù)了所有的Server節(jié)點(diǎn)信息。

PeerEurekaNodes通過(guò)peersUpdateTask任務(wù)定時(shí)從DNS或配置文件獲取最新的Server節(jié)點(diǎn)地址列表,并更新PeerEurekaNodes#peerEurekaNodes。
配置:
eureka.server.peer-eureka-nodes-update-interval-ms,間隔多少分鐘拉取一次Server節(jié)點(diǎn)地址列表,默認(rèn)10

PeerEurekaNode管理具體一個(gè)Server節(jié)點(diǎn),并負(fù)責(zé)向該Server節(jié)點(diǎn)同步register,cancel,heartbeat等操作。
PeerEurekaNode通過(guò)定時(shí)任務(wù)的方式同步這些操作。它維護(hù)了兩個(gè)TaskDispatcher,批處理調(diào)度器batchingDispatcher和非批處理調(diào)度器nonBatchingDispatcher。
PeerEurekaNode#構(gòu)造方法調(diào)用TaskDispatchers#createBatchingTaskDispatcher構(gòu)造TaskDispatcher

public static <ID, T> TaskDispatcher<ID, T> createBatchingTaskDispatcher(String id,
                                                                         int maxBufferSize,
                                                                         int workloadSize,
                                                                         int workerCount,
                                                                         long maxBatchingDelay,
                                                                         long congestionRetryDelayMs,
                                                                         long networkFailureRetryMs,
                                                                         TaskProcessor<T> taskProcessor) {
    final AcceptorExecutor<ID, T> acceptorExecutor = new AcceptorExecutor<>(
            id, maxBufferSize, workloadSize, maxBatchingDelay, congestionRetryDelayMs, networkFailureRetryMs
    );
    final TaskExecutors<ID, T> taskExecutor = TaskExecutors.batchExecutors(id, workerCount, taskProcessor, acceptorExecutor);
    return new TaskDispatcher<ID, T>() {
        public void process(ID id, T task, long expiryTime) {
            acceptorExecutor.process(id, task, expiryTime);
        }

        public void shutdown() {
            acceptorExecutor.shutdown();
            taskExecutor.shutdown();
        }
    };
}

TaskDispatcher負(fù)責(zé)任務(wù)分發(fā),過(guò)期任務(wù)會(huì)被拋棄,如果兩個(gè)任務(wù)有相同id,則前一個(gè)任務(wù)則會(huì)被刪除。
AcceptorExecutor負(fù)責(zé)整合任務(wù),將任務(wù)放入批次中。
TaskExecutors將整合好的任務(wù)(批次)分給TaskProcessor處理,實(shí)際處理任務(wù)的是ReplicationTaskProcessor。
ReplicationTaskProcessor可以重復(fù)執(zhí)行失敗的任務(wù),ReplicationTaskProcessor#process(List<ReplicationTask> tasks)處理批次任務(wù),將tasks合并到一個(gè)請(qǐng)求,發(fā)送到下游Server接口peerreplication/batch/。
任務(wù)類為ReplicationTask,它提供了handleFailure方法,當(dāng)下游Server接口返回statusCode不在[200,300)區(qū)間,則調(diào)用該方法。

從TaskExecutors#BatchWorkerRunnable的run方法可以看到,
調(diào)用下游Server接口時(shí),如果下游返回503狀態(tài)或發(fā)生IO異常,會(huì)通過(guò)taskDispatcher.reprocess重新執(zhí)行任務(wù),以保證最終一致性。
如果發(fā)生其他異常,只打印日志,不重復(fù)執(zhí)行任務(wù)。

配置:
eureka.server.max-elements-in-peer-replication-pool,等待執(zhí)行任務(wù)最大數(shù)量,默認(rèn)為10000

需要注意一下PeerEurekaNode#heartbeat方法,心跳任務(wù)實(shí)現(xiàn)了handleFailure方法

public void handleFailure(int statusCode, Object responseEntity) throws Throwable {
    super.handleFailure(statusCode, responseEntity);
    if (statusCode == 404) {
        logger.warn("{}: missing entry.", getTaskName());
        if (info != null) {
            logger.warn("{}: cannot find instance id {} and hence replicating the instance with status {}",
                    getTaskName(), info.getId(), info.getStatus());
            register(info);
        }
    } 
    ...
}

如果下游server節(jié)點(diǎn)沒(méi)有找到服務(wù)注冊(cè)信息,就返回404狀態(tài),這時(shí)需要重新注冊(cè)該服務(wù)。這點(diǎn)很重要,它可以保證不同Server節(jié)點(diǎn)保持?jǐn)?shù)據(jù)一致。

假設(shè)有一個(gè)client,注冊(cè)到Eureka集群server1,server2,server3。下面來(lái)分析兩個(gè)場(chǎng)景
場(chǎng)景1. client啟動(dòng)時(shí),server1接收帶client的注冊(cè)信息,但同步給server2前宕機(jī)了,怎么辦?
這時(shí),client定時(shí)發(fā)起心跳,但它與server1心跳操作失敗,只能向server2發(fā)起心跳,server2返回404(NOT_FOUND狀態(tài)),client重新注冊(cè)。

場(chǎng)景2. server3與其他機(jī)器server1,server2之間出現(xiàn)了網(wǎng)絡(luò)分區(qū),這時(shí)client注冊(cè)到eureka集群。然后網(wǎng)絡(luò)恢復(fù)了,server3怎么同步數(shù)據(jù)呢?
當(dāng)server1向server3同步心跳時(shí),server3返回404,于是server1重新向server3注冊(cè)client信息,數(shù)據(jù)最終保持一致。

主動(dòng)失效

AbstractInstanceRegistry#deltaRetentionTimer任務(wù)會(huì)定時(shí)移除recentlyChangedQueue中過(guò)期的增量操作信息
配置:
eureka.server.delta-retention-timer-interval-in-ms,間隔多少秒清理一次過(guò)期的增量操作信息,默認(rèn)30
eureka.server.retention-time-in-m-s-in-delta-queue,增量操作保留多少分鐘,默認(rèn)3

AbstractInstanceRegistry#evictionTimer任務(wù)會(huì)定時(shí)剔除AbstractInstanceRegistry#registry中已經(jīng)過(guò)期的(太久沒(méi)收到心跳)服務(wù)注冊(cè)信息。
計(jì)算服務(wù)失效時(shí)間時(shí)還要加上補(bǔ)償時(shí)間,即計(jì)算本次任務(wù)執(zhí)行的時(shí)間和上次任務(wù)執(zhí)行的時(shí)間差,若超過(guò)eviction-interval-timer-in-ms配置值則加上超出時(shí)間差作為補(bǔ)償時(shí)間。
每次剔除服務(wù)的數(shù)量都有一個(gè)上限,為注冊(cè)服務(wù)數(shù)量*renewal-percent-threshold,Eureka會(huì)隨機(jī)剔除過(guò)期的服務(wù)。
配置:
eureka.server.eviction-interval-timer-in-ms,間隔多少秒清理一次過(guò)期的服務(wù),默認(rèn)60
eureka.instance.lease-expiration-duration-in-seconds,間隔多少秒沒(méi)收到心跳則判定服務(wù)過(guò)期,默認(rèn)90
eureka.server.renewal-percent-threshold,自我保護(hù)閥值因子,默認(rèn)0.85

自我保護(hù)機(jī)制

PeerAwareInstanceRegistryImpl#scheduleRenewalThresholdUpdateTask,定時(shí)更新numberOfRenewsPerMinThreshold,該值用于判定是否進(jìn)入自我保護(hù)模式,在自我保護(hù)模式下,AbstractInstanceRegistry#evictionTimer任務(wù)直接返回,不剔除過(guò)期服務(wù)。

numberOfRenewsPerMinThreshold計(jì)算在PeerAwareInstanceRegistryImpl#updateRenewsPerMinThreshold

protected void updateRenewsPerMinThreshold() {
    this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfClientsSendingRenews
            * (60.0 / serverConfig.getExpectedClientRenewalIntervalSeconds())
            * serverConfig.getRenewalPercentThreshold());
}

expectedNumberOfClientsSendingRenews -> 已注冊(cè)服務(wù)總數(shù)
60.0 / serverConfig.getExpectedClientRenewalIntervalSeconds() -> expected-client-renewal-interval-seconds配置了Client間隔多少秒發(fā)一次心跳,這里計(jì)算一個(gè)Client每分鐘發(fā)送心跳數(shù)量。
RenewalPercentThreshold 自我保護(hù)閥值因子。
可以看到,numberOfRenewsPerMinThreshold表示一分鐘內(nèi)Server接收心跳最低次數(shù),實(shí)際數(shù)量少于該值則進(jìn)入自我保護(hù)模式。
此時(shí)Eureka認(rèn)為客戶端與注冊(cè)中心出現(xiàn)了網(wǎng)絡(luò)故障(比如網(wǎng)絡(luò)故障或頻繁的啟動(dòng)關(guān)閉客戶端),不再剔除任何服務(wù),它要等待網(wǎng)絡(luò)故障恢復(fù)后,再退出自我保護(hù)模式。這樣可以最大程度保證服務(wù)間正常調(diào)用。

PeerAwareInstanceRegistryImpl#isLeaseExpirationEnabled方法判定當(dāng)前是否處于自我保護(hù)模式。該方法比較renewsLastMin中的值是否大于numberOfRenewsPerMinThreshold,AbstractInstanceRegistry#renewsLastMin統(tǒng)計(jì)一分鐘內(nèi)心跳次數(shù)。
配置:
eureka.server.enable-self-preservation,是否啟用自我保護(hù)機(jī)制,默認(rèn)為true
eureka.server.expected-client-renewal-interval-seconds,Client間隔多少秒發(fā)送一次心跳
eureka.server.renewal-percent-threshold,自我保護(hù)閥值因子,默認(rèn)0.85

狀態(tài)更新

InstanceInfo維護(hù)了狀態(tài)變量status和覆蓋狀態(tài)變量overriddenStatus。
status是Eureka Client本身發(fā)布的狀態(tài)。
overriddenstatus是手動(dòng)或通過(guò)工具強(qiáng)制執(zhí)行的狀態(tài)。
Server端提供服務(wù)apps/{appName}/{instanceId}/status,可以變更服務(wù)實(shí)例status以及overriddenStatus,從而主動(dòng)變更服務(wù)狀態(tài)。
注意,并不會(huì)修改Client端的服務(wù)狀態(tài),而是修改Server段服務(wù)注冊(cè)信息中保存的服務(wù)狀態(tài)。
而Server處理Client注冊(cè)或心跳時(shí),會(huì)使用overriddenstatus覆蓋status。
Eureka Client在獲取到注冊(cè)信息時(shí),會(huì)調(diào)用DiscoveryClient#shuffleInstances方法,過(guò)濾掉非InstanceStatus.UP狀態(tài)的服務(wù)實(shí)例,從而避免調(diào)動(dòng)該實(shí)例,以達(dá)到服務(wù)實(shí)例的暫停服務(wù),而無(wú)需關(guān)閉服務(wù)實(shí)例。

InstanceInfo還維護(hù)了lastDirtyTimestamp變量,代表服務(wù)注冊(cè)信息最后更新時(shí)間。
從InstanceResource可以看到,更新?tīng)顟B(tài)statusUpdate或者刪除狀態(tài)deleteStatusUpdate時(shí)都可以提供lastDirtyTimestamp,
而處理心跳的renewLease方法,必須有l(wèi)astDirtyTimestamp參數(shù),validateDirtyTimestamp方法負(fù)責(zé)檢驗(yàn)lastDirtyTimestamp參數(shù)

  1. 當(dāng)lastDirtyTimestamp參數(shù)等于當(dāng)前注冊(cè)信息中的lastDirtyTimestamp,返回處理成功。
  2. 當(dāng)lastDirtyTimestamp參數(shù)大于當(dāng)前注冊(cè)信息中的lastDirtyTimestamp,返回NOT_FOUND狀態(tài),表示Client的信息已經(jīng)過(guò)期,需要重新注冊(cè)。
  3. 當(dāng)lastDirtyTimestamp參數(shù)小于當(dāng)前注冊(cè)信息中的lastDirtyTimestamp,返回CONFLICT(409)狀態(tài),表示數(shù)據(jù)沖突,并返回當(dāng)前節(jié)點(diǎn)中該服務(wù)的注冊(cè)信息。
    這時(shí)如果心跳是Client發(fā)起的,Client會(huì)忽略409的返回狀態(tài)(DiscoveryClient#renew),但如果是其他Server節(jié)點(diǎn)同步過(guò)來(lái)的,發(fā)送心跳的Server節(jié)點(diǎn)會(huì)使用返回的服務(wù)注冊(cè)信息更新本節(jié)點(diǎn)的注冊(cè)信息(PeerEurekaNode#heartbeat)。

配置:
eureka.client.filter-only-up-instances,獲取實(shí)例時(shí)是否只保留UP狀態(tài)的實(shí)例,默認(rèn)為true
eureka.server.sync-when-timestamp-differs,當(dāng)時(shí)間戳不一致時(shí),是否進(jìn)行同步數(shù)據(jù),默認(rèn)為true

文本關(guān)于Eureka的分享就到這里,我們可以Eureka設(shè)計(jì)和實(shí)現(xiàn)都比較簡(jiǎn)單,但是非常實(shí)用。
我在深入閱讀Eureka源碼前猶豫了一段時(shí)間(畢竟Eureka 2.0 開(kāi)源流產(chǎn)),不過(guò)經(jīng)過(guò)一段時(shí)間深入學(xué)習(xí),收獲不少,希望這篇文章也可以給對(duì)Eureka感興趣的同學(xué)提供一個(gè)深入學(xué)習(xí)思路。

如果您覺(jué)得本文不錯(cuò),歡迎關(guān)注我的微信公眾號(hào),您的關(guān)注是我堅(jiān)持的動(dòng)力!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容