eureka作為spring cloud微服務(wù)架構(gòu)里的注冊(cè)中心阳懂,是非常核心的一個(gè)組件宙暇。它本身的架構(gòu)避免了復(fù)雜的選主算法,比較簡(jiǎn)單刮萌,搭個(gè)demo也確實(shí)很快,但是如果要用于生產(chǎn)環(huán)境娘扩,還是得注意很多東西着茸,尤其是下線延遲...
一、服務(wù)獲取中的緩存問題
第一節(jié)的內(nèi)容都是從這個(gè)issue翻譯的:Documentation: changing Eureka renewal frequency WILL break the self-preservation feature of the server
1.1 為什么修改client的默認(rèn)心跳時(shí)間琐旁,會(huì)導(dǎo)致自我保護(hù)模式失效涮阔?
Eureka Service會(huì)認(rèn)為客戶端是以30s的頻率來發(fā)送心跳的。服務(wù)端期望收到的最大心跳時(shí)間是:
n instances x 2(60s/30s) x threshold
如果是2個(gè)實(shí)例灰殴,Eureka會(huì)期望每分鐘有:2 instances x 2 x 85% =3.4個(gè)心跳,也就是說需要3個(gè)心跳敬特。
如果client的心跳改成15s,掛掉一個(gè)牺陶,另一個(gè)在1min內(nèi)會(huì)發(fā)出4個(gè)心跳伟阔,而這時(shí)候的閾值還是3.4個(gè),自我保護(hù)模式就失效了掰伸。
核心原因就是在Eureka Server計(jì)算期望心跳數(shù)的時(shí)候?qū)懰懒嗣糠昼姷男奶g隔皱炉,即30秒,所以他永遠(yuǎn)會(huì)是*2(感覺像是新手寫的代碼啊啊啊 -_-)
還有一個(gè)參數(shù)可以調(diào)整狮鸭,eureka.server.renewalThresholdUpdateIntervalMs合搅,心跳閾值重新計(jì)算的周期多搀,默認(rèn)15分鐘,可以改短一點(diǎn)灾部,2min
1.2 客戶端首次注冊(cè)時(shí)間為什么要30s康铭?如何改進(jìn)?
首次注冊(cè)行為是和首次心跳綁定在一起的赌髓,首次心跳發(fā)送以后會(huì)收到not found的響應(yīng),client就知道還沒注冊(cè)過从藤,client就會(huì)馬上注冊(cè)。首次心跳由參數(shù)
eureka.instance.leaseRenewalIntervalInSeconds控制的春弥,默認(rèn)30
可以通過eureka.client.initialInstanceInfoReplicationIntervalSeconds參數(shù)來加快首次注冊(cè)的速度呛哟。他是控制首次改變實(shí)例狀態(tài)(UP/DOWN )的時(shí)間,啟動(dòng)的時(shí)候狀態(tài)肯定是需要改變的匿沛,所以他可以用來加快首次注冊(cè)速度扫责,并且改變這個(gè)值不會(huì)影響到保護(hù)模式
另外如果你使用的是spring cloud eureka的話沒首次注冊(cè)延遲的問題,他會(huì)馬上注冊(cè)
1.3 其他影響快速獲取服務(wù)信息的因素
【服務(wù)端緩存】
因?yàn)榉?wù)端默認(rèn)會(huì)有個(gè)read only response cache(下面會(huì)細(xì)說)逃呼,每30秒更新一次(eureka.server.response-cache-update-interval-ms),所以可能注冊(cè)了不是馬上能看到(雖然通過rest api不能看到鳖孤,但是你可以在web ui上看到,因?yàn)閡i沒有緩存)
【客戶端緩存】
Eureka Client緩存的定期更新周期抡笼,他由eureka.client.registryFetchIntervalSeconds控制苏揣,默認(rèn)30秒, 改成5秒
【Ribbon緩存】
如果你采用Ribbon來訪問服務(wù)推姻,那么這里會(huì)有個(gè)緩存(他的數(shù)據(jù)來源是本地Eureka Client緩存)平匈,他由ribbon. ServerListRefreshInterval控制,默認(rèn)30秒藏古, 改成2秒
1.4 怎么更快的踢掉沒有心跳的機(jī)器
eureka.instance.leaseExpirationDurationInSeconds增炭,這個(gè)值用來控制多久踢掉機(jī)器,默認(rèn)是3個(gè)心跳周期拧晕,有點(diǎn)久隙姿,可以考慮改成2個(gè),他不會(huì)影響到保護(hù)模式(如果開啟自我保護(hù)模式厂捞,心跳間隔因?yàn)?.1的bug不能改输玷,只能改這個(gè)了 -_-)
二、服務(wù)端緩存細(xì)節(jié)
Eureka內(nèi)部的緩存分很多級(jí)靡馁,主要有registry欲鹏、readWriterCacheMap、readOnlyCacheMap臭墨;另外還有一個(gè)維護(hù)最近180s增量的隊(duì)列recentlyChangedQueue
2.1 寫操作
包括注冊(cè)貌虾、取消注冊(cè)等,都直接操作在registry上裙犹,同時(shí)也會(huì)更新recentlyChangedQueue和readWriterCacheMap
2.2 讀操作
讀默認(rèn)是從readOnlyCacheMap讀取尽狠,讀不到的話再?gòu)膔eadWriterCacheMap,還是沒有再?gòu)膔egistry
2.3 濫用緩存的讀操作
這個(gè)讀操作的三級(jí)緩存結(jié)構(gòu)叶圃,非常讓人困惑袄膏,registry已經(jīng)是ConcurrentHashMap,純內(nèi)存操作掺冠,性能非常高了沉馆,為什么前面還要加兩級(jí)緩存;readWriterCacheMap的數(shù)據(jù)是在寫入以后responseCacheAutoExpirationInSeconds(默認(rèn)180)秒內(nèi)失效德崭,readOnlyCacheMap則是一個(gè)定時(shí)任務(wù)斥黑,每responseCacheUpdateIntervalMs(默認(rèn)30)秒從readWriterCacheMap獲取最新數(shù)據(jù)
2.4 去掉readOnlyCacheMap
從CAP理論上看,Eureka是一個(gè)AP系統(tǒng)眉厨,但是在C層面這么弱锌奴,就是因?yàn)楦鞣N無謂的緩存造成的,看了下readWriterCacheMap去掉比較難憾股,但是readOnlyCacheMap有一個(gè)開關(guān)useReadOnlyResponseCache鹿蜀,果斷關(guān)掉!服球!
三茴恰、Time Lag
最后再來看下Eureka wiki中提到的2min time lag問題,其實(shí)分多個(gè)角度看斩熊,不一定是2min
3.1 服務(wù)正常上線/修改往枣,最大可能會(huì)有120s滯后
1.直接使用Eureka:30(首次注冊(cè) init registe) + 30(readOnlyCacheMap)+30(client fetch interval)+30(ribbon cache)=120
2.在Spring Cloud環(huán)境下使用這些組件(Eureka, Ribbon):不會(huì)有首次注冊(cè)30秒延遲的問題,服務(wù)啟動(dòng)后會(huì)馬上注冊(cè),所以從注冊(cè)到發(fā)現(xiàn)粉渠,最多可能是90s分冈。
服務(wù)異常下線:最大可能會(huì)有270s滯后
1.定時(shí)清理任務(wù)每eureka.server. evictionIntervalTimerInMs(默認(rèn)60)執(zhí)行一次清理任務(wù)
2.每次清理任務(wù)會(huì)把90秒(3個(gè)心跳周期,eureka.instance.leaseExpirationDurationInSeconds)沒收到心跳的踢除渣叛,但是根據(jù)官方的說法 丈秩,因?yàn)榇a實(shí)現(xiàn)的bug,這個(gè)時(shí)間其實(shí)是兩倍淳衙,即180秒蘑秽,也就是說如果一個(gè)客戶端因?yàn)榫W(wǎng)絡(luò)問題或者主機(jī)問題異常下線,可能會(huì)在180秒后才剔除
3.讀取端箫攀,因?yàn)閞eadOnlyCacheMap以及客戶端緩存的存在肠牲,可能會(huì)在30(readOnlyCacheMap)+30(client fetch interval)+30(ribbon)=90
- 所以極端情況最終可能會(huì)是180+90=270
四、生產(chǎn)環(huán)境最佳配置
總結(jié)前面3點(diǎn)靴跛,經(jīng)過梳理后缀雳,推薦的生產(chǎn)環(huán)境最佳配置如下:(可用于中小規(guī)模環(huán)境):
Eureka Server端配置
## 中小規(guī)模下,自我保護(hù)模式坑比好處多梢睛,所以關(guān)閉它
eureka.server.enableSelfPreservation=false
## 心跳閾值計(jì)算周期肥印,如果開啟自我保護(hù)模式识椰,可以改一下這個(gè)配置
## eureka.server.renewalThresholdUpdateIntervalMs=120000
## 主動(dòng)失效檢測(cè)間隔,配置成5秒
eureka.server.evictionIntervalTimerInMs=5000
## 心跳間隔,5秒
eureka.instance.leaseRenewalIntervalInSeconds=5
## 沒有心跳的淘汰時(shí)間深碱,10秒
eureka.instance.leaseExpirationDurationInSeconds=10
## 禁用readOnlyCacheMap
eureka.server. useReadOnlyResponseCache=false
服務(wù)提供者和clinet配置
## 心跳間隔腹鹉,5秒
eureka.instance.leaseRenewalIntervalInSeconds=5
## 沒有心跳的淘汰時(shí)間,10秒
eureka.instance.leaseExpirationDurationInSeconds=10
# 定時(shí)刷新本地緩存時(shí)間
eureka.client.registryFetchIntervalSeconds=5
# ribbon緩存時(shí)間
ribbon.ServerListRefreshInterval=2000
改成上面配置后:
正常上線下線客戶端最大感知時(shí)間:eureka.client.registryFetchIntervalSeconds+ribbon. ServerListRefreshInterval = 7秒
異常下線客戶端最大感知時(shí)間:
2*eureka.instance.leaseExpirationDurationInSeconds+
eureka.server.evictionIntervalTimerInMs+
eureka.client.registryFetchIntervalSeconds+
ribbon. ServerListRefreshInterval = 32