天天看點

Spring Cloud:詳解Eureka 緩存機制。引言AP特性服務狀态Eureka ServerEureka Client預設配置下服務消費者最長感覺時間應對措施網關實作服務下線實時感覺

引言

Eureka是Netflix開源的、用于實作服務注冊和發現的服務。Spring Cloud Eureka基于Eureka進行二次封裝,增加了更人性化的UI,使用更為友善。但是由于Eureka本身存在較多緩存,服務狀态更新滞後,最常見的狀況是:服務下線後狀态沒有及時更新,服務消費者調用到已下線的服務導緻請求失敗。本文基于Spring Cloud Eureka 1.4.4.RELEASE,在預設region和zone的前提下,介紹Eureka的緩存機制。

AP特性

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

Spring Cloud:詳解Eureka 緩存機制。引言AP特性服務狀态Eureka ServerEureka Client預設配置下服務消費者最長感覺時間應對措施網關實作服務下線實時感覺

服務狀态

Eureka服務狀态enum類:

com.netflix.appinfo.InstanceInfo.InstanceStatus

狀态 說明 狀态 說明
UP 線上 OUT_OF_SERVICE 失效
DOWN 下線 UNKNOWN 未知
STARTING 正在啟動

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。

緩存機制

Eureka Server存在三個變量:(registry、readWriteCacheMap、readOnlyCacheMap)儲存服務注冊資訊,預設情況下定時任務每30s将readWriteCacheMap同步至readOnlyCacheMap,每60s清理超過90s未續約的節點,Eureka Client每30s從readOnlyCacheMap更新服務注冊資訊,而UI則從registry更新服務注冊資訊。

Spring Cloud:詳解Eureka 緩存機制。引言AP特性服務狀态Eureka ServerEureka Client預設配置下服務消費者最長感覺時間應對措施網關實作服務下線實時感覺

三級緩存

緩存 類型 說明
registry ConcurrentHashMap 實時更新,類AbstractInstanceRegistry成員變量,UI端請求的是這裡的服務注冊資訊
readWriteCacheMap Guava Cache/LoadingCache 實時更新,類ResponseCacheImpl成員變量,緩存時間180秒
readOnlyCacheMap ConcurrentHashMap 周期更新,類ResponseCacheImpl成員變量,預設每30s從readWriteCacheMap更新,Eureka client預設從這裡更新服務注冊資訊,可配置直接從readWriteCacheMap更新

緩存相關配置

配置 預設 說明

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

關鍵類

類名 說明

com.netflix.eureka.registry.AbstractInstanceRegistry

儲存服務注冊資訊,持有registry和responseCache成員變量

com.netflix.eureka.registry.ResponseCacheImpl

持有readWriteCacheMap和readOnlyCacheMap成員變量

Eureka Client

Eureka Client存在兩種角色:服務提供者和服務消費者,作為服務消費者一般配合Ribbon或Feign(Feign内部使用Ribbon)使用。Eureka Client啟動後,作為服務提供者立即向Server注冊,預設情況下每30s續約(renew);作為服務消費者立即向Server全量更新服務注冊資訊,預設情況下每30s增量更新服務注冊資訊;Ribbon延時1s向Client擷取使用的服務注冊資訊,預設每30s更新使用的服務注冊資訊,隻儲存狀态為UP的服務。

二級緩存

緩存 類型 說明
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的服務注冊資訊

預設配置下服務消費者最長感覺時間

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。

Spring Cloud:詳解Eureka 緩存機制。引言AP特性服務狀态Eureka ServerEureka Client預設配置下服務消費者最長感覺時間應對措施網關實作服務下線實時感覺

應對措施

服務注冊中心在選擇使用Eureka時說明已經接受了其優先保證可用性(A)和分區容錯性(P)、不保證強一緻性(C)的特點。如果需要優先保證強一緻性(C),則應該考慮使用ZooKeeper等CP系統作為服務注冊中心。分布式系統中一般配置多節點,單個節點服務上線的狀态更新滞後并沒有什麼影響,這裡主要考慮服務下線後狀态更新滞後的應對措施。

Eureka Server

  • 縮短readOnlyCacheMap更新周期。縮短該定時任務周期可減少滞後時間。
eureka.server.responsecCacheUpdateIntervalMs: 10000  # Eureka Server readOnlyCacheMap更新周期
           
  • 關閉readOnlyCacheMap。中小型系統可以考慮該方案,Eureka Client直接從readWriteCacheMap更新服務注冊資訊。
eureka.server.useReadOnlyResponseCache: false        # 是否使用readOnlyCacheMap
           

Eureka Client

  • 服務消費者使用容錯機制。如Spring Cloud Retry和Hystrix,Ribbon、Feign、Zuul都可以配置Retry,服務消費者通路某個已下線節點時一般報ConnectTimeout,這時可以通過Retry機制重試下一個節點。
  • 服務消費者縮短更新周期。Eureka Client和Ribbon二級緩存影響狀态更新,縮短這兩個定時任務周期可減少滞後時間,例如配置:
eureka.client.registryFetchIntervalSeconds: 5        # Eureka Client更新周期
ribbon.ServerListRefreshInterval: 2000               # Ribbon更新周期
           
  • 服務提供者保證服務正常下線。服務下線時使用kill或kill -15指令,避免使用kill -9指令,kill或kill -15指令殺死程序時将觸發Eureka Client的shutdown()方法,主動删除Server的registry和readWriteCacheMap中的注冊資訊,不必依賴Server的evict清除。
  • .服務提供者延遲下線。服務下線之前先調用接口使Eureka Server中儲存的服務狀态為DOWN或OUT_OF_SERVICE後再下線,二者時間差根據緩存機制和配置決定,比如預設情況下調用接口後延遲90s再下線服務即可保證服務消費者不會調用已下線服務執行個體。

網關實作服務下線實時感覺

在軟體工程中,沒有一個問題是中間層解決不了的,而網關是服務提供者和服務消費者的中間層。以Spring Cloud Zuul網關為例,網關作為Eureka Client儲存了服務注冊資訊,服務消費者通過網關将請求轉發給服務提供者,隻需要做到服務提供者下線時通知網關在自己儲存的服務清單中使該服務失效。為了保持網關的獨立性,可實作一個獨立服務接收下線通知并協調網關叢集。