程序員筆記| 詳解Eureka 緩存機制

引言

Eureka是Netflix開源的、用於實現服務註冊和發現的服務。Spring Cloud Eureka基於Eureka進行二次封裝,增長了更人性化的UI,使用更爲方便。可是因爲Eureka自己存在較多緩存,服務狀態更新滯後,最多見的情況是:服務下線後狀態沒有及時更新,服務消費者調用到已下線的服務致使請求失敗。本文基於Spring Cloud Eureka 1.4.4.RELEASE,在默認region和zone的前提下,介紹Eureka的緩存機制。緩存

1、AP特性

從CAP理論看,Eureka是一個AP系統,優先保證可用性(A)和分區容錯性(P),不保證強一致性(C),只保證最終一致性,所以在架構中設計了較多緩存。架構

Eureka高可用架構

2、服務狀態

Eureka服務狀態enum類:com.netflix.appinfo.InstanceInfo.InstanceStatusapp

狀態 說明 狀態 說明
UP 在線 OUT_OF_SERVICE 失效
DOWN 下線 UNKNOWN 未知
STARTING 正在啓動

3、Eureka Server

在Eureka高可用架構中,Eureka Server也能夠做爲Client向其餘server註冊,多節點相互註冊組成Eureka集羣,集羣間相互視爲peer。Eureka Client向Server註冊、續約、更新狀態時,接受節點更新本身的服務註冊信息後,逐個同步至其餘peer節點。分佈式

【注意】若是server-A向server-B節點單向註冊,則server-A視server-B爲peer節點,server-A接受的數據會同步給server-B,但server-B接受的數據不會同步給server-A。spa

3.1 緩存機制

Eureka Server存在三個變量:(registry、readWriteCacheMap、readOnlyCacheMap)保存服務註冊信息,默認狀況下定時任務每30s將readWriteCacheMap同步至readOnlyCacheMap,每60s清理超過90s未續約的節點,Eureka Client每30s從readOnlyCacheMap更新服務註冊信息,而UI則從registry更新服務註冊信息。.net

三級緩存設計

緩存 類型 說明
registry ConcurrentHashMap 實時更新,類AbstractInstanceRegistry成員變量,UI端請求的是這裏的服務註冊信息
readWriteCacheMap Guava Cache/LoadingCache 實時更新,類ResponseCacheImpl成員變量,緩存時間180秒
readOnlyCacheMap ConcurrentHashMap 週期更新,類ResponseCacheImpl成員變量,默認每30s從readWriteCacheMap更新,Eureka client默認從這裏更新服務註冊信息,可配置直接從readWriteCacheMap更新

緩存相關配置code

配置 默認 說明
eureka.server.useReadOnlyResponseCache true Client從readOnlyCacheMap更新數據,false則跳過readOnlyCacheMap直接從readWriteCacheMap更新
eureka.server.responsecCacheUpdateIntervalMs 30000 readWriteCacheMap更新至readOnlyCacheMap週期,默認30s
eureka.server.evictionIntervalTimerInMs 60000 清理未續約節點(evict)週期,默認60s
eureka.instance.leaseExpirationDurationInSeconds 90 清理未續約節點超時時間,默認90s

關鍵類cdn

類名 說明
com.netflix.eureka.registry.AbstractInstanceRegistry 保存服務註冊信息,持有registry和responseCache成員變量
com.netflix.eureka.registry.ResponseCacheImpl 持有readWriteCacheMap和readOnlyCacheMap成員變量

4、Eureka Client

Eureka Client存在兩種角色:服務提供者服務消費者,做爲服務消費者通常配合Ribbon或Feign(Feign內部使用Ribbon)使用。Eureka Client啓動後,做爲服務提供者當即向Server註冊,默認狀況下每30s續約(renew);做爲服務消費者當即向Server全量更新服務註冊信息,默認狀況下每30s增量更新服務註冊信息;Ribbon延時1s向Client獲取使用的服務註冊信息,默認每30s更新使用的服務註冊信息,只保存狀態爲UP的服務。server

二級緩存

緩存 類型 說明
localRegionApps AtomicReference 週期更新,類DiscoveryClient成員變量,Eureka Client保存服務註冊信息,啓動後當即向Server全量更新,默認每30s增量更新
upServerListZoneMap ConcurrentHashMap 週期更新,類LoadBalancerStats成員變量,Ribbon保存使用且狀態爲UP的服務註冊信息,啓動後延時1s向Client更新,默認每30s更新

緩存相關配置

配置 默認 說明
eureka.instance.leaseRenewalIntervalInSeconds 30 Eureka Client 續約週期,默認30s
eureka.client.registryFetchIntervalSeconds 30 Eureka Client 增量更新週期,默認30s(正常狀況下增量更新,超時或與Server端不一致等狀況則全量更新)
ribbon.ServerListRefreshInterval 30000 Ribbon 更新週期,默認30s

關鍵類

類名 說明
com.netflix.discovery.DiscoveryClient Eureka Client 負責註冊、續約和更新,方法initScheduledTasks()分別初始化續約和更新定時任務
com.netflix.loadbalancer.PollingServerListUpdater Ribbon 更新使用的服務註冊信息,start初始化更新定時任務
com.netflix.loadbalancer.LoadBalancerStats Ribbon,保存使用且狀態爲UP的服務註冊信息

5、默認配置下服務消費者最長感知時間

Eureka Client 時間 說明
上線 30(readOnly)+30(Client)+30(Ribbon)=90s readWrite -> readOnly -> Client -> Ribbon 各30s
正常下線 30(readonly)+30(Client)+30(Ribbon)=90s 服務正常下線(kill或kill -15殺死進程)會給進程善後機會,DiscoveryClient.shutdown()將向Server更新自身狀態爲DOWN,而後發送DELETE請求註銷本身,registry和readWriteCacheMap實時更新,故UI將再也不顯示該服務實例
非正常下線 30+60(evict)*2+30+30+30= 240s 服務非正常下線(kill -9殺死進程或進程崩潰)不會觸發DiscoveryClient.shutdown()方法,Eureka Server將依賴每60s清理超過90s未續約服務從registry和readWriteCacheMap中刪除該服務實例

考慮以下狀況

  • 0s時服務未通知Eureka Client直接下線;
  • 29s時第一次過時檢查evict未超過90s;
  • 89s時第二次過時檢查evict未超過90s;
  • 149s時第三次過時檢查evict未續約時間超過了90s,故將該服務實例從registry和readWriteCacheMap中刪除;
  • 179s時定時任務從readWriteCacheMap更新至readOnlyCacheMap;
  • 209s時Eureka Client從Eureka Server的readOnlyCacheMap更新;
  • 239s時Ribbon從Eureka Client更新。

所以,極限狀況下服務消費者最長感知時間將無限趨近240s。

6、應對措施

服務註冊中心在選擇使用Eureka時說明已經接受了其優先保證可用性(A)和分區容錯性(P)、不保證強一致性(C)的特色。若是須要優先保證強一致性(C),則應該考慮使用ZooKeeper等CP系統做爲服務註冊中心。分佈式系統中通常配置多節點,單個節點服務上線的狀態更新滯後並無什麼影響,這裏主要考慮服務下線後狀態更新滯後的應對措施。

6.1 Eureka Server

  • 1.縮短readOnlyCacheMap更新週期。縮短該定時任務週期可減小滯後時間。

    eureka.server.responsecCacheUpdateIntervalMs: 10000  # Eureka Server readOnlyCacheMap更新週期
    複製代碼
  • 2.關閉readOnlyCacheMap。中小型系統能夠考慮該方案,Eureka Client直接從readWriteCacheMap更新服務註冊信息。

    eureka.server.useReadOnlyResponseCache: false        # 是否使用readOnlyCacheMap
    複製代碼

6.2 Eureka Client

  • 1.服務消費者使用容錯機制。如Spring Cloud Retry和Hystrix,Ribbon、Feign、Zuul均可以配置Retry,服務消費者訪問某個已下線節點時通常報ConnectTimeout,這時能夠經過Retry機制重試下一個節點。

  • 2.服務消費者縮短更新週期。Eureka Client和Ribbon二級緩存影響狀態更新,縮短這兩個定時任務週期可減小滯後時間,例如配置:

    eureka.client.registryFetchIntervalSeconds: 5        # Eureka Client更新週期
    ribbon.ServerListRefreshInterval: 2000               # Ribbon更新週期
    複製代碼
  • 3.服務提供者保證服務正常下線。服務下線時使用kill或kill -15命令,避免使用kill -9命令,kill或kill -15命令殺死進程時將觸發Eureka Client的shutdown()方法,主動刪除Server的registry和readWriteCacheMap中的註冊信息,沒必要依賴Server的evict清除。

  • 4.服務提供者延遲下線。服務下線以前先調用接口使Eureka Server中保存的服務狀態爲DOWN或OUT_OF_SERVICE後再下線,兩者時間差根據緩存機制和配置決定,好比默認狀況下調用接口後延遲90s再下線服務便可保證服務消費者不會調用已下線服務實例。

7、網關實現服務下線實時感知

在軟件工程中,沒有一個問題是中間層解決不了的,而網關是服務提供者和服務消費者的中間層。以Spring Cloud Zuul網關爲例,網關做爲Eureka Client保存了服務註冊信息,服務消費者經過網關將請求轉發給服務提供者,只須要作到服務提供者下線時通知網關在本身保存的服務列表中使該服務失效。爲了保持網關的獨立性,可實現一個獨立服務接收下線通知並協調網關集羣。下篇文章將詳細介紹網關如何實現服務下線實時感知,敬請期待!

做者:馮永彪 內容來源:宜信技術學院

相關文章
相關標籤/搜索