Eureka簡介:
Eureka是Spring Cloud Netflix微服務套件中的一部分,可以與Springboot構建的微服務很容易的整合起來。
Eureka包含了服務器端和客戶端組件。
- 服務器端,也被稱作是服務註冊中心,用於提供服務的註冊與發現。Eureka支持高可用的配置,當集羣中有分片出現故障時,Eureka就會轉入自動保護模式,它允許分片故障期間繼續提供服務的發現和註冊,當故障分片恢復正常時,集羣中其他分片會把他們的狀態再次同步回來。
- 客戶端組件包含服務消費者與服務生產者。在應用程序運行時,Eureka客戶端向註冊中心註冊自身提供的服務並週期性的發送心跳來更新它的服務租約。同時也可以從服務端查詢當前註冊的服務信息並把他們緩存到本地並週期性的刷新服務狀態。
Eureka 服務的註冊發現:
- 服務生產者啓動時,向服務註冊中心註冊自己提供的服務
- 服務消費者啓動時,在服務註冊中心訂閱自己所需要的服務
- 註冊中心返回服務提供者的地址信息給消費者
- 消費者從提供者中調用服務
Eureka 的基本規則:
- 服務啓動時會生成服務的基本信息對象InstanceInfo,然後在啓動時會register到服務治理中心
- 註冊完成後會從服務治理中心拉取所有的服務信息,緩存在本地
- 之後服務會被30s(可配置)發送一個心跳信息,續約服務
- 如果服務治理中心在90s內沒有收到一個服務的續約,就會認爲服務已經掛了,會把服務註冊信息刪掉
- 服務停止前,服務會主動發送一個停止請求,服務治理中心會刪除這個服務的信息
- 如果Eureka Server收到的心跳包不足正常值的85%(可配置)就會進入自我保護模式,在這種模式下,Eureka Server不會刪除任何服務信息
兩個比較重要的配置
服務過期時間配置:eureka.instance.lease-expiration-duration-in-seconds
服務刷新時間配置:eureka.instance.lease-renewal-interval-in-seconds
Eureka 服務註冊中心緩存:
Eureka用一個Map來保存所有服務及映射的機器信息
private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
= new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();
- 服務註冊時,會把服務的信息寫到這個
registry
中 - 服務從治理中心拉取服務列表信息時,不會從這個
registry
中拉取,而是從一個ResponseCache
中拉取,這樣讀寫分離的原因應該是爲了支持高併發
而ResponseCache
又分爲了兩個部分,一個是ReadWriteMap
,一個是ReadOnlyMap
ReadWriteMap
的數據是從registry
中來的,可以認爲是registry
的緩存,當服務註冊時,除了把信息寫到registry
中外,還會讓ReadWriteMap
主動過期,使得會去從registry
重新拉取數據ReadOnlyMap
的數據是從ReadWriteMap
來的,可以認爲是ReadWriteMap
的緩存,當服務需要獲取服務列表是,會直接取這個ReadOnlyMap
的數據,當這個數據不存在時,纔會從ReadWriteMap
中更新ReadWriteMap
與registry
的數據是實時一致的,但是ReadWriteMap
與ReadOnlyMap
不是實時一致的- 有定時任務會定時從
ReadWriteMap
同步到ReadOnlyMap
,這個時間配置是:eureka.server.responseCacheUpdateInvervalMs
- EurekaServer內部有定時任務,每隔檢查過期實例時間,掃描
Registry
裏面過期的實例並刪除,並且使對應的ReadWriteMap
緩存失效,這個時間是eureka.server.eviction-interval-timer-in-ms
Eureka 服務註冊細節:
1)客戶端的動作:
public void run() {
try {
discoveryClient.refreshInstanceInfo();
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);
}
}
- 在run方法中,會先去檢測服務信息是否發生了變更,如果發生了,會調用 discoveryClient.register();方法重新註冊,第一次的時候也會認爲是發生了變更,從而發起第一次註冊。
- 在finally塊中,schedule了this,說明會在指定時間後再次執行這個方法
2)服務端的動作
private ConcurrentLinkedQueue<RecentlyChangedItem> recentlyChangedQueue =
new ConcurrentLinkedQueue<RecentlyChangedItem>();
- 首先會處理上面所說的
registry
以及它的緩存對象 - 其次會把這次的註冊信息添加到一個“最近變更隊列中”
- 然後會把註冊的信息發送到其他的Eureka節點,發送的方式就是調用其他節點的
register
方法
Eureka 服務的續約:
在服務啓動時,會創建一個後臺心跳任務,定時去續約服務信息
scheduler.schedule(
new TimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
new HeartbeatThread()
),
renewalIntervalInSecs, TimeUnit.SECONDS);
HeartbeatThread就是心跳線程,這裏把它包裝成了一個TimedSupervisorTask
HeartbeatThread:
private class HeartbeatThread implements Runnable {
public void run() {
/**
* renew()方法就是服務續約的方法,裏面的邏輯就是向服務治理中心發送了一個http請求。
* 如果http請求返回的狀態碼是404,則會認爲這個服務沒有註冊或者註冊了但是已經失效,
* 因此會直接調用register()方法進行註冊
*/
if (renew()) {
lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis();
}
}
}
Eureka 服務的取消:
服務停止前會向服務治理中心發一個服務取消的請求,用於註銷服務。收到服務註銷的請求之後,服務治理中心會做以下操作
- 從registry中刪除對應的服務信息
- 使ReadWriteMap緩存失效
- 將服務取消的信息加入到最近變更隊列中