1. 程式人生 > >spring eureka 服務例項實現快速下線快速感知快速重新整理配置解析

spring eureka 服務例項實現快速下線快速感知快速重新整理配置解析

背景

預設的Spring Eureka伺服器,服務提供者和服務呼叫者配置不夠靈敏,總是服務提供者在停掉很久之後,服務呼叫者很長時間並沒有感知到變化。或者是服務已經註冊上去了,但是服務呼叫方很長時間還是呼叫不到,發現不了這個服務。

原始碼解讀

通過對Spring Eureka原始碼的研究,發現如下機制(預設配置下):
image

描述如下:

  1. EurekaServer預設有兩個快取,一個是ReadWriteMap,另一個是ReadOnlyMap。有服務提供者註冊服務或者維持心跳時時,會修改ReadWriteMap。當有服務呼叫者查詢服務例項列表時,預設會從ReadOnlyMap讀取(這個在原生Eureka可以配置,SpringCloud Eureka中不能配置,一定會啟用ReadOnlyMap讀取),這樣可以減少ReadWriteMap讀寫鎖的爭用,增大吞吐量。EurekaServer定時把資料從ReadWriteMap更新到ReadOnlyMap中。
    ReadWriteMap是一個Guava Cache,過期時間是可以配置的
  2. 服務提供者註冊服務後,會定時心跳。這個根據服務提供者的Eureka配置中的服務重新整理時間決定。還有個配置是服務過期時間,這個配置在服務提供者配置但是在EurekaServer使用了,但是預設配置EurekaServer不會啟用這個欄位。需要配置好EurekaServer的掃描失效時間,才會啟用EurekaServer的主動失效機制。在這個機制啟用下:每個服務提供者會發送自己服務過期時間上去,EurekaServer會定時檢查每個服務過期時間和上次心跳時間,如果在過期時間內沒有收到過任何一次心跳,同時沒有處於保護模式下(參考第一篇的Eureka自我保護機制),則會將這個例項從ReadWriteMap中去掉。
  3. 在預設沒有啟用EurekaServer主動失效服務例項的情況下,服務過期是利用ReadWriteMap超時快取失效實現的,只有傳送心跳的例項快取不會失效。
  4. 服務呼叫者有本地快取,定時從Eureka伺服器上增量拉取所有服務例項列表

原因分析

服務提供者和服務呼叫者配置不夠靈敏,總是服務提供者在停掉很久之後,服務呼叫者很長時間並沒有感知到變化的原因:
EurekaServer自己的ReadWriteMap快取失效延遲,重新整理到ReadOnlyMap的延遲,服務呼叫者自己本地快取重新整理的延遲。

服務已經註冊上去了,但是服務呼叫方很長時間還是呼叫不到,發現不了這個服務:
重新整理到ReadOnlyMap的延遲,服務呼叫者自己本地快取重新整理的延遲

解決方案

EurekaServer修改如下配置:

#eureka server重新整理readCacheMap的時間,注意,client讀取的是readCacheMap,這個時間決定了多久會把readWriteCacheMap的快取更新到readCacheMap上
#預設30s
eureka.server.responseCacheUpdateIntervalMs=3000
#eureka server快取readWriteCacheMap失效時間,這個只有在這個時間過去後快取才會失效,失效前不會更新,過期後從registry重新讀取註冊服務資訊,registry是一個ConcurrentHashMap。
#由於啟用了evict其實就用不太上改這個配置了
#預設180s
eureka.server.responseCacheAutoExpirationInSeconds=180

#啟用主動失效,並且每次主動失效檢測間隔為3s
eureka.server.eviction-interval-timer-in-ms=3000

Eureka服務提供方修改如下配置:

#服務過期時間配置,超過這個時間沒有接收到心跳EurekaServer就會將這個例項剔除
#注意,EurekaServer一定要設定eureka.server.eviction-interval-timer-in-ms否則這個配置無效,這個配置一般為服務重新整理時間配置的三倍
#預設90s
eureka.instance.lease-expiration-duration-in-seconds=15
#服務重新整理時間配置,每隔這個時間會主動心跳一次
#預設30s
eureka.instance.lease-renewal-interval-in-seconds=5

Eureka服務呼叫方修改如下配置:

#eureka client重新整理本地快取時間
#預設30s
eureka.client.registryFetchIntervalSeconds=5
#eureka客戶端ribbon重新整理時間
#預設30s
ribbon.ServerListRefreshInterval=5000