本文通過(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ù)
- 當(dāng)lastDirtyTimestamp參數(shù)等于當(dāng)前注冊(cè)信息中的lastDirtyTimestamp,返回處理成功。
- 當(dāng)lastDirtyTimestamp參數(shù)大于當(dāng)前注冊(cè)信息中的lastDirtyTimestamp,返回NOT_FOUND狀態(tài),表示Client的信息已經(jīng)過(guò)期,需要重新注冊(cè)。
- 當(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)力!
