程序員筆記 | 詳解Eureka緩存機(jī)制
Eureka是Netflix開(kāi)源的、用于實(shí)現(xiàn)服務(wù)注冊(cè)和發(fā)現(xiàn)的服務(wù)。Spring Cloud Eureka基于Eureka進(jìn)行二次封裝,增加了更人性化的UI,使用更為方便。但是由于Eureka本身存在較多緩存,服務(wù)狀態(tài)更新滯后,最常見(jiàn)的狀況是:服務(wù)下線(xiàn)后狀態(tài)沒(méi)有及時(shí)更新,服務(wù)消費(fèi)者調(diào)用到已下線(xiàn)的服務(wù)導(dǎo)致請(qǐng)求失敗。本文基于Spring Cloud Eureka 1.4.4.RELEASE,在默認(rèn)region和zone的前提下,介紹Eureka的緩存機(jī)制。
一、AP特性
從CAP理論看,Eureka是一個(gè)AP系統(tǒng),優(yōu)先保證可用性(A)和分區(qū)容錯(cuò)性(P),不保證強(qiáng)一致性(C),只保證最終一致性,因此在架構(gòu)中設(shè)計(jì)了較多緩存。
(Eureka高可用架構(gòu))
二、服務(wù)狀態(tài)
Eureka服務(wù)狀態(tài)enum類(lèi):
- com.netflix.appinfo.InstanceInfo.InstanceStatus
三、Eureka Server
在Eureka高可用架構(gòu)中,Eureka Server也可以作為Client向其他server注冊(cè),多節(jié)點(diǎn)相互注冊(cè)組成Eureka集群,集群間相互視為peer。Eureka Client向Server注冊(cè)、續(xù)約、更新?tīng)顟B(tài)時(shí),接受節(jié)點(diǎn)更新自己的服務(wù)注冊(cè)信息后,逐個(gè)同步至其他peer節(jié)點(diǎn)。
【注意】如果server-A向server-B節(jié)點(diǎn)單向注冊(cè),則server-A視server-B為peer節(jié)點(diǎn),server-A接受的數(shù)據(jù)會(huì)同步給server-B,但server-B接受的數(shù)據(jù)不會(huì)同步給server-A。
1. 緩存機(jī)制
Eureka Server存在三個(gè)變量:(registry、readWriteCacheMap、readOnlyCacheMap)保存服務(wù)注冊(cè)信息,默認(rèn)情況下定時(shí)任務(wù)每30s將readWriteCacheMap同步至readOnlyCacheMap,每60s清理超過(guò)90s未續(xù)約的節(jié)點(diǎn),Eureka Client每30s從readOnlyCacheMap更新服務(wù)注冊(cè)信息,而UI則從registry更新服務(wù)注冊(cè)信息。
三級(jí)緩存:
緩存相關(guān)配置:
關(guān)鍵類(lèi):
四、Eureka Client
Eureka Client存在兩種角色:服務(wù)提供者和服務(wù)消費(fèi)者,作為服務(wù)消費(fèi)者一般配合Ribbon或Feign(Feign內(nèi)部使用Ribbon)使用。Eureka Client啟動(dòng)后,作為服務(wù)提供者立即向Server注冊(cè),默認(rèn)情況下每30s續(xù)約(renew);作為服務(wù)消費(fèi)者立即向Server全量更新服務(wù)注冊(cè)信息,默認(rèn)情況下每30s增量更新服務(wù)注冊(cè)信息;Ribbon延時(shí)1s向Client獲取使用的服務(wù)注冊(cè)信息,默認(rèn)每30s更新使用的服務(wù)注冊(cè)信息,只保存狀態(tài)為UP的服務(wù)。
二級(jí)緩存:
緩存相關(guān)配置:
關(guān)鍵類(lèi):
五、默認(rèn)配置下服務(wù)消費(fèi)者最長(zhǎng)感知時(shí)間
考慮如下情況:
- 0s時(shí)服務(wù)未通知Eureka Client直接下線(xiàn);
- 29s時(shí)***次過(guò)期檢查evict未超過(guò)90s;
- 89s時(shí)第二次過(guò)期檢查evict未超過(guò)90s;
- 149s時(shí)第三次過(guò)期檢查evict未續(xù)約時(shí)間超過(guò)了90s,故將該服務(wù)實(shí)例從registry和readWriteCacheMap中刪除;
- 179s時(shí)定時(shí)任務(wù)從readWriteCacheMap更新至readOnlyCacheMap;
- 209s時(shí)Eureka Client從Eureka Server的readOnlyCacheMap更新;
- 239s時(shí)Ribbon從Eureka Client更新。
因此,極限情況下服務(wù)消費(fèi)者最長(zhǎng)感知時(shí)間將***趨近240s。
六、應(yīng)對(duì)措施
服務(wù)注冊(cè)中心在選擇使用Eureka時(shí)說(shuō)明已經(jīng)接受了其優(yōu)先保證可用性(A)和分區(qū)容錯(cuò)性(P)、不保證強(qiáng)一致性(C)的特點(diǎn)。如果需要優(yōu)先保證強(qiáng)一致性(C),則應(yīng)該考慮使用ZooKeeper等CP系統(tǒng)作為服務(wù)注冊(cè)中心。分布式系統(tǒng)中一般配置多節(jié)點(diǎn),單個(gè)節(jié)點(diǎn)服務(wù)上線(xiàn)的狀態(tài)更新滯后并沒(méi)有什么影響,這里主要考慮服務(wù)下線(xiàn)后狀態(tài)更新滯后的應(yīng)對(duì)措施。
1. Eureka Server
- 縮短readOnlyCacheMap更新周期。縮短該定時(shí)任務(wù)周期可減少滯后時(shí)間。
- eureka.server.responsecCacheUpdateIntervalMs: 10000 # Eureka Server readOnlyCacheMap更新周期
- eureka.server.useReadOnlyResponseCache: false # 是否使用readOnlyCacheMap
2. Eureka Client
- 服務(wù)消費(fèi)者使用容錯(cuò)機(jī)制。如Spring Cloud Retry和Hystrix,Ribbon、Feign、Zuul都可以配置Retry,服務(wù)消費(fèi)者訪(fǎng)問(wèn)某個(gè)已下線(xiàn)節(jié)點(diǎn)時(shí)一般報(bào)ConnectTimeout,這時(shí)可以通過(guò)Retry機(jī)制重試下一個(gè)節(jié)點(diǎn)。
- 服務(wù)消費(fèi)者縮短更新周期。Eureka Client和Ribbon二級(jí)緩存影響狀態(tài)更新,縮短這兩個(gè)定時(shí)任務(wù)周期可減少滯后時(shí)間,例如配置:
- eureka.client.registryFetchIntervalSeconds: 5 # Eureka Client更新周期
- ribbon.ServerListRefreshInterval: 2000
七、網(wǎng)關(guān)實(shí)現(xiàn)服務(wù)下線(xiàn)實(shí)時(shí)感知
在軟件工程中,沒(méi)有一個(gè)問(wèn)題是中間層解決不了的,而網(wǎng)關(guān)是服務(wù)提供者和服務(wù)消費(fèi)者的中間層。以Spring Cloud Zuul網(wǎng)關(guān)為例,網(wǎng)關(guān)作為Eureka Client保存了服務(wù)注冊(cè)信息,服務(wù)消費(fèi)者通過(guò)網(wǎng)關(guān)將請(qǐng)求轉(zhuǎn)發(fā)給服務(wù)提供者,只需要做到服務(wù)提供者下線(xiàn)時(shí)通知網(wǎng)關(guān)在自己保存的服務(wù)列表中使該服務(wù)失效。為了保持網(wǎng)關(guān)的獨(dú)立性,可實(shí)現(xiàn)一個(gè)獨(dú)立服務(wù)接收下線(xiàn)通知并協(xié)調(diào)網(wǎng)關(guān)集群。
【本文是51CTO專(zhuān)欄機(jī)構(gòu)宜信技術(shù)學(xué)院的原創(chuàng)文章,微信公眾號(hào)“宜信技術(shù)學(xué)院( id: CE_TECH)”】