摘要: 原創出處 http://www.iocoder.cn/Eureka/instance-registry-override-status/ 「芋道源碼」歡迎轉載,保留摘要,謝謝!
本文主要基於 Eureka 1.8.X 版本
- 1. 概述
- 2. 應用實例覆蓋狀態變更接口
- 3. 應用實例覆蓋狀態刪除接口
- 4. 應用實例覆蓋狀態映射
- 請支持正版。下載盜版,等於主動編寫低級 BUG 。
- 程序猿DD —— 《Spring Cloud微服務實戰》
- 周立 —— 《Spring Cloud與Docker微服務架構實戰》
- 兩書齊買,京東包郵。
- PUT
apps/${APP_NAME}/${INSTANCE_ID}/status
- DELETE
apps/${APP_NAME}/${INSTANCE_ID}/status
調用
PeerAwareInstanceRegistryImpl#statusUpdate(...)
方法,更新應用實例覆蓋狀態。實現代碼如下:public boolean statusUpdate(final String appName, final String id,final InstanceStatus newStatus, String lastDirtyTimestamp,final boolean isReplication) {if (super.statusUpdate(appName, id, newStatus, lastDirtyTimestamp, isReplication)) {// Eureka-Server 集羣同步replicateToPeers(Action.StatusUpdate, appName, id, null, newStatus, isReplication);return true;}return false;}- 調用父類
AbstractInstanceRegistry#statusUpdate(...)
方法,更新應用實例覆蓋狀態。
- 調用父類
- 第 6 至 7 行 :獲取讀鎖。在 《Eureka源碼解析 —— 應用實例註冊發現 (九)之歲月是把萌萌的讀寫鎖》 詳細解析。
- 第 8 至 9 行 :添加覆蓋狀態變更次數到監控。配合 Netflix Servo 實現監控信息採集。
- 第 10 至 15 行 :獲得租約。
- 第 16 至 18 行 :租約不存在,返回更新失敗。
- 第 20 至 21 行 :設置租約最後更新時間( 續租 )。
- 第 23 至 29 行 :持有租約的應用實例不存在,理論來說不會出現,防禦性編程。
- 第 31 行 :應用實例當前狀態和覆該狀態不一致時才更新覆蓋狀態。
- 第 32 至 36 行 :當覆蓋狀態是
InstanceStatus.UP
,設置租約的開始服務的時間戳(只有第一次有效)。 第 37 至 39 行 :添加到應用實例覆蓋狀態映射(
overriddenInstanceStatusMap
)。此處英文"NAC"
可能是"Network Access Control"
的縮寫,感興趣的可以看看 《Network Access Control》 。overriddenInstanceStatusMap
屬性代碼如下:/*** 應用實例覆蓋狀態映射* key:應用實例編號*/protected final ConcurrentMap<String, InstanceStatus> overriddenInstanceStatusMap = CacheBuilder.newBuilder().initialCapacity(500).expireAfterAccess(1, TimeUnit.HOURS).<String, InstanceStatus>build().asMap();- 有效期 1 小時。每次訪問後會刷新有效期,在後文你會看到對其的訪問。
第 40 至 43 行 :設置應用實例的覆蓋狀態。用於 Eureka-Server 集羣同步。
- 第 46 至 47 行 :設置應用實例狀態。設置後,Eureka-Client 拉取註冊信息,被更新覆蓋狀態的應用實例就是設置的狀態。
- 第 48 至 55 行 :設置應用實例的數據不一致時間。用於 Eureka-Server 集羣同步。
- 第 56 至 58 行 :添加應用實例到最近租約變更記錄隊列。
- 第 59 至 60 行 :設置應用實例的最後更新時間(
lastUpdatedTimestamp
)。lastUpdatedTimestamp
主要用於記錄最後更新時間,無實際業務用途。 - 第 61 至 62 行 :設置響應緩存過期。
- 第 64 行 :返回更新成功。
- 第 68 行 :釋放讀鎖。
- 請求參數
newStatusValue
,設置應用實例的狀態。大多數情況下,newStatusValue
要和應用實例實際的狀態一致,因爲該應用實例的 Eureka-Client 不會從 Eureka-Server 拉取到該應用狀態newStatusValue
。另外一種方式,不傳遞該參數,相當於UNKNOWN
狀態,這樣,Eureka-Client 會主動向 Eureka-Server 再次發起註冊,具體原因在 [「4.3 續租場景」] 詳細解析,更加推薦的方式。 調用父類
AbstractInstanceRegistry#deleteStatusOverride(...)
方法,刪除應用實例覆蓋狀態。實現代碼如下:public boolean deleteStatusOverride(String appName, String id,InstanceStatus newStatus,String lastDirtyTimestamp,boolean isReplication) {if (super.deleteStatusOverride(appName, id, newStatus, lastDirtyTimestamp, isReplication)) {// Eureka-Server 集羣同步replicateToPeers(Action.DeleteStatusOverride, appName, id, null, null, isReplication);return true;}return false;}- 調用父類
AbstractInstanceRegistry#deleteStatusOverride(...)
方法,刪除應用實例覆蓋狀態。
- 調用父類
- 第 7 至 8 行 :獲取讀鎖。在 《Eureka源碼解析 —— 應用實例註冊發現 (九)之歲月是把萌萌的讀寫鎖》 詳細解析。
- 第 9 至 10 行 :添加覆蓋狀態刪除次數到監控。配合 Netflix Servo 實現監控信息採集。
- 第 11 至 16 行 :獲得租約。
- 第 17 至 19 行 :租約不存在,返回更新失敗。
- 第 21 至 22 行 :設置租約最後更新時間( 續租 )。
- 第 24 至 30 行 :持有租約的應用實例不存在,理論來說不會出現,防禦性編程。
- 第 32 至 33 行 :移除出應用實例覆蓋狀態映射(
overriddenInstanceStatusMap
)。 - 第 34 行 :應用實例的覆蓋狀態存在才設置狀態。
- 第 35 至 36 行 :設置應用實例的覆蓋狀態爲 InstanceStatus.UNKNOWN。用於 Eureka-Server 集羣同步。
- 第 37 至 38 行 :設置應用實例的狀態爲
newStatus
。設置後,Eureka-Client 拉取註冊信息,被更新覆蓋狀態的應用實例就是設置的狀態。 - 第 39 至 48 行 :設置應用實例的數據不一致時間。用於 Eureka-Server 集羣同步。
- 第 49 至 51 行 :添加應用實例到最近租約變更記錄隊列。
- 第 52 至 53 行 :設置應用實例的最後更新時間(
lastUpdatedTimestamp
)。lastUpdatedTimestamp
主要用於記錄最後更新時間,無實際業務用途。 - 第 54 至 55 行 :設置響應緩存過期。
- 第 57 行 :返回更新成功。
- 第 61 行 :釋放讀鎖。
調用
#getInstanceInfoOverrideRule()
方法,獲取應用實例狀態覆蓋規則( InstanceStatusOverrideRule )。在 PeerAwareInstanceRegistryImpl 裏該方法實現代碼如下:private final InstanceStatusOverrideRule instanceStatusOverrideRule;public PeerAwareInstanceRegistryImpl(EurekaServerConfig serverConfig,EurekaClientConfig clientConfig,ServerCodecs serverCodecs,EurekaClient eurekaClient) {// ... 省略其它方法this.instanceStatusOverrideRule = new FirstMatchWinsCompositeRule(new DownOrStartingRule(),new OverrideExistsRule(overriddenInstanceStatusMap),new LeaseExistsRule());}protected InstanceStatusOverrideRule getInstanceInfoOverrideRule() {return this.instanceStatusOverrideRule;}#apply(...)
方法參數instanceInfo
代表的是關注狀態的應用實例,和方法參數existingLease
裏的應用實例不一定是同一個,在 「4.1.6 總結」 詳細解析。com.netflix.eureka.registry.rule.StatusOverrideResult
,狀態覆蓋結果。當匹配成功,返回matches = true
;否則,返回matches = false
。- AsgEnabledRule ,亞馬遜 AWS 專用,跳過。
rules
屬性,複合規則集合。在 PeerAwareInstanceRegistryImpl 裏,我們可以看到該屬性爲 [ DownOrStartingRule , OverrideExistsRule , LeaseExistsRule ] 。defaultRule
屬性,默認規則,值爲 AlwaysMatchInstanceStatusRule 。#apply()
方法,優先使用複合規則(rules
),順序匹配,直到匹配成功 。當未匹配成功,使用默認規則(defaultRule
) 。- 注意,使用的是
instanceInfo
。 statusOverrides
屬性,應用實例覆蓋狀態映射。在 PeerAwareInstanceRegistryImpl 裏,使用AbstractInstanceRegistry.overriddenInstanceStatusMap
屬性賦值。- 上文我們提到
AbstractInstanceRegistry.overriddenInstanceStatusMap
每次訪問刷新有效期,如果調用到 OverrideExistsRule ,則會不斷刷新。從 DownOrStartingRule 看到,instanceInfo
處於InstanceInfo.InstanceStatus.DOWN
或者InstanceInfo.InstanceStatus.STARTING
纔不會繼續調用 OverrideExistsRule 匹配,AbstractInstanceRegistry.overriddenInstanceStatusMap
纔有可能過期。 - 注意,使用的是
existingLease
,並且非 Eureka-Server 請求。 - 注意,使用的是
instanceInfo
。 - 應用實例狀態是最重要的屬性,沒有之一,因而在最終實例狀態的計算,以可信賴爲主。
- DownOrStartingRule ,
instanceInfo
處於STARTING
或者DOWN
狀態,應用實例可能不適合提供服務( 被請求 ),考慮可信賴,返回instanceInfo
的狀態。 - OverrideExistsRule ,當存在覆蓋狀態(
statusoverrides
) ,使用該狀態,比較好理解。 - LeaseExistsRule ,來自 Eureka-Client 的請求( 非 Eureka-Server 集羣請求),當 Eureka-Server 的實例狀態存在,並且處於
UP
或則OUT_OF_SERVICE
,保留當前狀態。原因,禁止 Eureka-Client 主動在這兩個狀態之間切換。如果要切換,使用應用實例覆蓋狀態變更與刪除接口。 - AlwaysMatchInstanceStatusRule ,使用
instanceInfo
的狀態返回,以保證能匹配到狀態。 - 在下文中,你會看到,
#getOverriddenInstanceStatus()
方法會在註冊和續租使用到。結合上圖,我們在 「4.2 註冊場景」 和 「4.3 續租場景」 也會詳細解析。 - 在下文中,你會看到,
#getOverriddenInstanceStatus()
方法會在註冊和續租使用到,方法參數instanceInfo
情況如下:- 註冊時 :請求參數
instanceInfo
,和existingLease
的應用實例屬性不相等( 如果考慮 Eureka-Server 的LastDirtyTimestamp
更大的情況,則類似 續租時的情況 ) 。 - 續租時 :使用 Eureka-Server 的
existingLease
的應用實例,兩者相等。 - 總的來說,可以將
instanceInfo
理解成請求方的狀態。
- 註冊時 :請求參數
- DownOrStartingRule ,
- 第 7 行 :獲得已存在的租約(
existingLease
) 。 - 第 15 行 :創建新的租約(
lease
)。 - 第 24 至 28 行 :設置應用實例的覆蓋狀態(
overridestatus
),避免註冊應用實例後,丟失覆蓋狀態。 - 第 30 至 32 行 :獲得應用實例最終狀態。注意下,不考慮第 9 行代碼的情況,
registrant
和existingLease
的應用實例不是同一個對象。 - 第 33 只 34 行 :設置應用實例的狀態。
- 第 15 至 17 行 :獲得應用實例的最終狀態。
- 第 18 至 24 行 :應用實例的最終狀態爲
UNKNOWN
,無法續約 。返回false
後,請求方( Eureka-Client 或者 Eureka-Server 集羣其他節點 )會發起註冊,在 《Eureka 源碼解析 —— 應用實例註冊發現(二)之續租》 有詳細解析。爲什麼會是UNKNOWN
呢?在 「3. 應用實例覆蓋狀態刪除接口」 傳遞應用實例狀態爲UNKNOWN
。 - 第 25 至 36 行 :應用實例的狀態與最終狀態不相等,使用最終狀態覆蓋應用實例的狀態。爲什麼會不相等呢?
#renew(...)
和#statusUpdate(...)
可以無鎖,並行執行,如果#renew(...)
執行完第 16 行代碼,獲取到overriddenInstanceStatus
後,恰巧#statusUpdate(...)
執行完更新應用實例狀態newStatus
,又恰好兩者不相等,使用overriddenInstanceStatus
覆蓋掉應用實例的newStatus
狀態。- 那豈不是覆蓋狀態(
overriddenstatus
)反倒被覆蓋???不會,在下一次心跳,應用實例的狀態會被修正回來。當然,如果應用實例狀態如果爲UP
或者STARTING
不會被修正,也不應該被修正。
1. 概述
本文主要分享 應用實例的覆蓋狀態屬性。
這裏要注意下,不是應用實例的狀態( status
),而是覆蓋狀態( overridestatus
) 。代碼如下:
|
調用 Eureka-Server HTTP Restful 接口 apps/${APP_NAME}/${INSTANCE_ID}/status
對應用實例覆蓋狀態的變更,從而達到主動的、強制的變更應用實例狀態。注意,實際不會真的修改 Eureka-Client 應用實例的狀態,而是修改在 Eureka-Server 註冊的應用實例的狀態。
通過這樣的方式,Eureka-Client 在獲取到註冊信息時,並且配置 eureka.shouldFilterOnlyUpInstances = true
,過濾掉非 InstanceStatus.UP
的應用實例,從而避免調動該實例,以達到應用實例的暫停服務( InstanceStatus.OUT_OF_SERVICE
),而無需關閉應用實例。
因此,大多數情況下,調用該接口的目的,將應用實例狀態在 ( InstanceStatus.UP
) 和 ( InstanceStatus.OUT_OF_SERVICE
) 之間切換。引用官方代碼上的註釋如下:
AbstractInstanceRegistry#statusUpdate
方法註釋
Updates the status of an instance.
Normally happens to put an instance between {@link InstanceStatus#OUT_OF_SERVICE} and {@link InstanceStatus#UP} to put the instance in and out of traffic.
推薦 Spring Cloud 書籍:
接口 apps/${APP_NAME}/${INSTANCE_ID}/status
實際是兩個:
下面,我們逐節分享這兩接口的代碼實現。
2. 應用實例覆蓋狀態變更接口
應用實例覆蓋狀態變更接口,映射 InstanceResource#statusUpdate()
方法,實現代碼如下:
|
2.1 更新應用實例覆蓋狀態
調用 AbstractInstanceRegistry#statusUpdate(...)
方法,更新應用實例覆蓋狀態,實現代碼如下:
|
3. 應用實例覆蓋狀態刪除接口
當我們不需要應用實例的覆蓋狀態時,調度接口接口進行刪除。關聯官方 issue#89
:Provide an API to remove all overridden status。
應用實例覆蓋狀態刪除接口,映射 InstanceResource#deleteStatusUpdate()
方法,實現代碼如下:
|
3.1 刪除應用實例覆蓋狀態
調用父類 AbstractInstanceRegistry#deleteStatusOverride(...)
方法,刪除應用實例覆蓋狀態。實現代碼如下:
|
4. 應用實例覆蓋狀態映射
雖然我們在上面代碼,使用覆蓋狀態( overridestatus
)設置到應用實例的狀態( status
),實際調用 AbstractInstanceRegistry#getOverriddenInstanceStatus(...)
方法,根據應用實例狀態覆蓋規則( InstanceStatusOverrideRule )進行計算最終應用實例的狀態。實現代碼如下:
|
4.1 應用實例狀態覆蓋規則
com.netflix.eureka.registry.rule.InstanceStatusOverrideRule
,應用實例狀態覆蓋規則接口。接口代碼如下:
|
實現類關係如下:
4.1.1 FirstMatchWinsCompositeRule
com.netflix.eureka.registry.rule.FirstMatchWinsCompositeRule
,複合規則,以第一個匹配成功爲準。實現代碼如下:
|
4.1.2 DownOrStartingRule
com.netflix.eureka.registry.rule.DownOrStartingRule
,匹配 InstanceInfo.InstanceStatus.DOWN
或者 InstanceInfo.InstanceStatus.STARTING
狀態。實現 #apply(...)
代碼如下:
|
4.1.3 OverrideExistsRule
com.netflix.eureka.registry.rule.OverrideExistsRule
,匹配應用實例覆蓋狀態映射( statusOverrides
) 。實現 #apply(...)
代碼如下:
|
4.1.4 LeaseExistsRule
com.netflix.eureka.registry.rule.LeaseExistsRule
,匹配已存在租約的應用實例的 nstanceStatus.OUT_OF_SERVICE
或者 InstanceInfo.InstanceStatus.UP
狀態。實現 #apply(...)
代碼如下:
|
4.1.5 AlwaysMatchInstanceStatusRule
com.netflix.eureka.registry.rule.AlwaysMatchInstanceStatusRule
,總是匹配關注狀態的實例對象( instanceInfo
)的狀態。實現 #apply(...)
代碼如下:
|
4.1.6 總結
我們將 PeerAwareInstanceRegistryImpl 的應用實例覆蓋狀態規則梳理如下:
4.2 註冊場景
|
4.3 續租場景
|
4.4 下線場景
|
4.5 過期場景
同 「4.4 下線場景」 相同。
5. 客戶端調用接口
對應用實例覆蓋狀態的變更和刪除接口調用,點擊如下方法查看,非常易懂,本文就不囉嗦了: