一次 Eureka 服務下線太慢的慘痛經(jīng)歷!
這一天,忽然發(fā)現(xiàn)大量的告警,經(jīng)過多番調(diào)查研究考察,發(fā)現(xiàn)是由于 Eureka 服務下線太慢,而仍然有大量的請求打進來導致的報錯。
于是,又經(jīng)過了大量詳細周密的考察和研究,終于找到了問題并且解決了。
為啥服務都下線了還會有那么多的請求一直進來呢?
吶,我們都知道 Eureka 是 AP 模型,其實根本原因在于 Eureka 使用了三級緩存來保存服務的實例信息。
如下圖所示:
我們的服務注冊的時候會和 server 保持一個心跳,這個心跳的時間是 30 秒,服務注冊之后,客戶端的實例信息保存到 Registry 服務注冊表當中,注冊表中的信息會立刻同步到 readWriteCacheMap 之中。
而客戶端如果感知到這個服務,要從 readOnlyCacheMap 去讀取,這個只讀緩存需要 30 秒的時間去從 readWriteCacheMap 中同步。
客戶端和 Ribbon 負載均衡 都保持一個本地緩存,都是 30 秒定時同步。
按照上面所說,我們來計算一下客戶端感知到一個服務下線極端的情況需要多久。
- 客戶端每隔 30 秒會發(fā)送心跳到服務端
- registry 保存了所有服務注冊的實例信息,他會和 readWriteCacheMap 保持一個實時的同步,而 readWriteCacheMap 和 readOnlyCacheMap 會每隔 30 秒同步一次。
- 客戶端每隔 30 秒去同步一次 readOnlyCacheMap 的注冊實例信息
- 考慮到如果使用 ribbon 做負載均衡的話,他還有一層緩存每隔 30 秒同步一次
如果說一個服務的正常下線,極端的情況這個時間應該就是 30+30+30+30 差不多 120 秒的時間了。
如果服務非正常下線,還需要靠每 60 秒執(zhí)行一次的清理線程去剔除超過 90 秒沒有心跳的服務,那么這里的極端情況可能需要 3 次 60秒才能檢測出來,就是 180 秒的時間。
累計可能最長的感知時間就是:180 + 120 = 300 秒,5分鐘的時間,這個時間屬實有點夸張了,如果考慮到可能有些中間件畫蛇添足加了點啥清理的工作,這個時間簡直就是災難性的。
那有人就問了,我在 Eureka 控制臺看見服務上下線非??彀?,你這不跟我扯犢子嗎?
大哥啊,控制臺的顯示是直接獲取的 Registry 的信息,那肯定快啊,所以我們不能這樣來判斷。
那怎么解決呢,解決方案當然就是改這些時間了,這個時間需要根據(jù)實際生產(chǎn)的情況來判斷修改,這里僅提供一個示例。
修改 ribbon 同步緩存的時間為 3 秒:ribbon.ServerListRefreshInterval = 3000
修改客戶端同步緩存時間為 3 秒 :eureka.client.registry-fetch-interval-seconds = 3
心跳間隔時間修改為 3 秒:eureka.instance.lease-renewal-interval-in-seconds = 3
超時剔除的時間改為 9 秒:eureka.instance.lease-expiration-duration-in-seconds = 9
清理線程定時時間改為 5 秒執(zhí)行一次:eureka.server.eviction-interval-timer-in-ms = 5000
同步到只讀緩存的時間修改為 3 秒一次:eureka.server.response-cache-update-interval-ms = 3000
需要注意的是這里的只讀緩存其實是可以關閉的,通過修改參數(shù)??eureka.server.use-read-only-response-cache = false?
?可以做到,
但是建議不要沒有太大必要不要這樣做,Eureka 本身就是 AP 模型,用它你就應該有這個覺悟了,另外這個配置只針對原生的 Eureka 生效,SpringCloud Eureka 是沒有的,必須一定會從 readOnlyCacheMap 去讀。
如果按照這個時間參數(shù)設置讓我們重新計算可能感知到服務下線的最大時間:
正常下線就是 3+3+3+3=12 秒,非正常下線再加 15 秒為 27 秒。