一次 Eureka 服務(wù)下線太慢的慘痛經(jīng)歷!
這一天,忽然發(fā)現(xiàn)大量的告警,經(jīng)過多番調(diào)查研究考察,發(fā)現(xiàn)是由于 Eureka 服務(wù)下線太慢,而仍然有大量的請(qǐng)求打進(jìn)來導(dǎo)致的報(bào)錯(cuò)。
于是,又經(jīng)過了大量詳細(xì)周密的考察和研究,終于找到了問題并且解決了。
為啥服務(wù)都下線了還會(huì)有那么多的請(qǐng)求一直進(jìn)來呢?
吶,我們都知道 Eureka 是 AP 模型,其實(shí)根本原因在于 Eureka 使用了三級(jí)緩存來保存服務(wù)的實(shí)例信息。
如下圖所示:

我們的服務(wù)注冊(cè)的時(shí)候會(huì)和 server 保持一個(gè)心跳,這個(gè)心跳的時(shí)間是 30 秒,服務(wù)注冊(cè)之后,客戶端的實(shí)例信息保存到 Registry 服務(wù)注冊(cè)表當(dāng)中,注冊(cè)表中的信息會(huì)立刻同步到 readWriteCacheMap 之中。
而客戶端如果感知到這個(gè)服務(wù),要從 readOnlyCacheMap 去讀取,這個(gè)只讀緩存需要 30 秒的時(shí)間去從 readWriteCacheMap 中同步。
客戶端和 Ribbon 負(fù)載均衡 都保持一個(gè)本地緩存,都是 30 秒定時(shí)同步。
按照上面所說,我們來計(jì)算一下客戶端感知到一個(gè)服務(wù)下線極端的情況需要多久。
- 客戶端每隔 30 秒會(huì)發(fā)送心跳到服務(wù)端
- registry 保存了所有服務(wù)注冊(cè)的實(shí)例信息,他會(huì)和 readWriteCacheMap 保持一個(gè)實(shí)時(shí)的同步,而 readWriteCacheMap 和 readOnlyCacheMap 會(huì)每隔 30 秒同步一次。
- 客戶端每隔 30 秒去同步一次 readOnlyCacheMap 的注冊(cè)實(shí)例信息
- 考慮到如果使用 ribbon 做負(fù)載均衡的話,他還有一層緩存每隔 30 秒同步一次
如果說一個(gè)服務(wù)的正常下線,極端的情況這個(gè)時(shí)間應(yīng)該就是 30+30+30+30 差不多 120 秒的時(shí)間了。
如果服務(wù)非正常下線,還需要靠每 60 秒執(zhí)行一次的清理線程去剔除超過 90 秒沒有心跳的服務(wù),那么這里的極端情況可能需要 3 次 60秒才能檢測(cè)出來,就是 180 秒的時(shí)間。
累計(jì)可能最長的感知時(shí)間就是:180 + 120 = 300 秒,5分鐘的時(shí)間,這個(gè)時(shí)間屬實(shí)有點(diǎn)夸張了,如果考慮到可能有些中間件畫蛇添足加了點(diǎn)啥清理的工作,這個(gè)時(shí)間簡直就是災(zāi)難性的。
那有人就問了,我在 Eureka 控制臺(tái)看見服務(wù)上下線非常快啊,你這不跟我扯犢子嗎?
大哥啊,控制臺(tái)的顯示是直接獲取的 Registry 的信息,那肯定快啊,所以我們不能這樣來判斷。
那怎么解決呢,解決方案當(dāng)然就是改這些時(shí)間了,這個(gè)時(shí)間需要根據(jù)實(shí)際生產(chǎn)的情況來判斷修改,這里僅提供一個(gè)示例。

修改 ribbon 同步緩存的時(shí)間為 3 秒:ribbon.ServerListRefreshInterval = 3000
修改客戶端同步緩存時(shí)間為 3 秒 :eureka.client.registry-fetch-interval-seconds = 3
心跳間隔時(shí)間修改為 3 秒:eureka.instance.lease-renewal-interval-in-seconds = 3
超時(shí)剔除的時(shí)間改為 9 秒:eureka.instance.lease-expiration-duration-in-seconds = 9
清理線程定時(shí)時(shí)間改為 5 秒執(zhí)行一次:eureka.server.eviction-interval-timer-in-ms = 5000
同步到只讀緩存的時(shí)間修改為 3 秒一次:eureka.server.response-cache-update-interval-ms = 3000
需要注意的是這里的只讀緩存其實(shí)是可以關(guān)閉的,通過修改參數(shù)??eureka.server.use-read-only-response-cache = false??可以做到,
但是建議不要沒有太大必要不要這樣做,Eureka 本身就是 AP 模型,用它你就應(yīng)該有這個(gè)覺悟了,另外這個(gè)配置只針對(duì)原生的 Eureka 生效,SpringCloud Eureka 是沒有的,必須一定會(huì)從 readOnlyCacheMap 去讀。
如果按照這個(gè)時(shí)間參數(shù)設(shè)置讓我們重新計(jì)算可能感知到服務(wù)下線的最大時(shí)間:
正常下線就是 3+3+3+3=12 秒,非正常下線再加 15 秒為 27 秒。
























