eureka緩存細節以及生產環境的最佳配置
eureka作為spring cloud微服務架構里的注冊中心,是非常核心的一個組件,它避免了復雜的選主算法,架構比較簡單,搭個demo也確實很快,但是如果要用於生產環境,還是得注意很多東西,尤其是下線延遲…
服務獲取中的緩存問題
本節的內容都是從這個issue翻譯的:Documentation: changing Eureka renewal frequency WILL break the self-preservation feature of the server
為什么修改client的默認心跳時間,會導致自我保護模式失效?
Eureka Service會認為客戶端是以30s的頻率來發送心跳的。服務端期望收到的最大心跳時間是:
n instances x 2(60s/30s) x threshold
如果是2個實例,Eureka會期望每分鍾有:2 instances x 2 x 85% =3.4個心跳,也就是說需要3個心跳。
如果client的心跳改成15s,掛掉一個,另一個在1min內會發出4個心跳,而這時候的閾值還是3.4個,自我保護模式就失效了。
核心原因就是在Eureka Server計算期望心跳數的時候寫死了每分鍾的心跳間隔,即30秒,所以他永遠會是*2(感覺像是新手寫的代碼啊啊啊 -_-)
還有一個參數可以調整,eureka.server.renewalThresholdUpdateIntervalMs,心跳閾值重新計算的周期,默認15分鍾,可以改短一點,2min
客戶端首次注冊時間為什么要30s?如何改進?
首次注冊行為是和首次心跳綁定在一起的,首次心跳發送以后會收到not found的響應,client就知道還沒注冊過,client就會馬上注冊。首次心跳由參數
eureka.instance.leaseRenewalIntervalInSeconds控制的,默認30
可以通過eureka.client.initialInstanceInfoReplicationIntervalSeconds參數來加快首次注冊的速度。他是控制首次改變實例狀態(UP/DOWN )的時間,啟動的時候狀態肯定是需要改變的,所以他可以用來加快首次注冊速度,並且改變這個值不會影響到保護模式
另外如果你使用的是spring cloud eureka的話沒首次注冊延遲的問題,他會馬上注冊
其他影響快速獲取服務信息的因素
【服務端緩存】
因為服務端默認會有個read only response cache(下面會細說),每30秒更新一次(eureka.server.response-cache-update-interval-ms),所以可能注冊了不是馬上能看到(雖然通過rest api不能看到,但是你可以在web ui上看到,因為ui沒有緩存)
【客戶端緩存】
Eureka Client緩存的定期更新周期,他由eureka.client.registryFetchIntervalSeconds控制,默認30秒, 改成5秒
【Ribbon緩存】
如果你采用Ribbon來訪問服務,那么這里會有個緩存(他的數據來源是本地Eureka Client緩存),他由ribbon. ServerListRefreshInterval控制,默認30秒, 改成2秒
怎么更快的踢掉沒有心跳的機器
eureka.instance.leaseExpirationDurationInSeconds,這個值用來控制多久踢掉機器,默認是3個心跳周期,有點久,可以考慮改成2個,他不會影響到保護模式(如果開啟自我保護模式,心跳間隔因為上面的bug不能改,只能改這個了 -_-)
服務端緩存細節
Eureka內部的緩存分很多級,主要有registry、readWriterCacheMap、readOnlyCacheMap;另外還有一個維護最近180s增量的隊列recentlyChangedQueue
寫操作
包括注冊、取消注冊等,都直接操作在registry上,同時也會更新recentlyChangedQueue和readWriterCacheMap
讀操作
讀默認是從readOnlyCacheMap讀取,讀不到的話再從readWriterCacheMap,還沒有再從registry
濫用緩存的讀操作
這個讀操作的三級緩存結構,非常讓人困惑,registry已經是ConcurrentHashMap,純內存操作,性能非常高了,為什么前面還要加兩級緩存;readWriterCacheMap的數據是在寫入以后responseCacheAutoExpirationInSeconds(默認180)秒內失效,readOnlyCacheMap則是一個定時任務,每responseCacheUpdateIntervalMs(默認30)秒從readWriterCacheMap獲取最新數據
去掉readOnlyCacheMap
從CAP理論上看,Eureka是一個AP系統,但是在C層面這么弱,就是因為各種無謂的緩存造成的,看了下readWriterCacheMap去掉比較難,但是readOnlyCacheMap有一個開關useReadOnlyResponseCache,果斷關掉!!
Time Lag
最后再來看下Eureka wiki中提到的2min time lag問題,其實分多個角度看,不一定是2min
服務正常上線/修改,最大可能會有120s滯后
- 30(首次注冊 init registe) + 30(readOnlyCacheMap)+30(client fetch interval)+30(ribbon)=120
- 如果是在Spring Cloud環境下使用這些組件(Eureka, Ribbon),不會有首次注冊30秒延遲的問題,服務啟動后會馬上注冊,所以從注冊到發現,最多可能是90s。
服務異常下線:最大可能會有270s滯后
- 定時清理任務每eureka.server. evictionIntervalTimerInMs(默認60)執行一次清理任務
- 每次清理任務會把90秒(3個心跳周期,eureka.instance.leaseExpirationDurationInSeconds)沒收到心跳的踢除,但是根據官方的說法 ,因為代碼實現的bug,這個時間其實是兩倍,即180秒,也就是說如果一個客戶端因為網絡問題或者主機問題異常下線,可能會在180秒后才剔除
- 讀取端,因為readOnlyCacheMap以及客戶端緩存的存在,可能會在30(readOnlyCacheMap)+30(client fetch interval)+30(ribbon)=90
- 所以極端情況最終可能會是180+90=270
生產環境最佳配置
總結前面3點,經過梳理后,推薦的生產環境最佳配置如下:(可用於中小規模環境):
Eureka Server端配置
1 |
## 中小規模下,自我保護模式坑比好處多,所以關閉它 |
服務提供者和clinet配置
## 心跳間隔,5秒
eureka.instance.leaseRenewalIntervalInSeconds=5
## 沒有心跳的淘汰時間,10秒
eureka.instance.leaseExpirationDurationInSeconds=10
# 定時刷新本地緩存時間
eureka.client.registryFetchIntervalSeconds=5
# ribbon緩存時間
ribbon.ServerListRefreshInterval=2000
改成上面配置后:
-
正常上線下線客戶端最大感知時間:eureka.client.registryFetchIntervalSeconds+ribbon. ServerListRefreshInterval = 7秒
-
異常下線客戶端最大感知時間:
2*eureka.instance.leaseExpirationDurationInSeconds+
eureka.server.evictionIntervalTimerInMs+
eureka.client.registryFetchIntervalSeconds+
ribbon. ServerListRefreshInterval = 3
一些參考: