天天看點

Spring Cloud 更新之路 - 2020.0.x - 4. 使用 Eureka 作為注冊中心

Eureka 目前的狀态:Eureka 目前 1.x 版本還在更新,但是應該不會更新新的功能了,隻是對現有功能進行維護,更新并相容所需的依賴。 Eureka 2.x 已經胎死腹中了。但是,這也不代表 Eureka 就是不能用了。如果你需要一個簡便易于部署的注冊中心,Eureka 還是一個很好的選擇。雲服務環境中,基本上所有執行個體位址和微服務名稱都在不斷變化,也并不太需要 Eureka 所缺少的持久化特性。當你的叢集屬于中小規模的時候(節點小于 1000 個), Eureka 依然是一個不錯的選擇。當你的叢集很大的時候,Eureka 的同步機制可能就限制了他的表現。

Eureka 的設計

Eureka 的設計比較小巧,沒有複雜的同步機制,也沒有複雜的持久化機制,叢集關系隻是簡單的将收到的用戶端請求轉發到叢集内的其他 Eureka 執行個體。Eureka 本身也隻有注冊中心的功能,不像其他種類的注冊中心那樣,将注冊中心和配置中心合在一起,例如 Consul 和 nacos。

Eureka 的互動流程如下:

Spring Cloud 更新之路 - 2020.0.x - 4. 使用 Eureka 作為注冊中心

首先,Service A 通過 Eureka Client 發送注冊請求(Register)到同一可用區的 Eureka Server 1。之後通過發送心跳請求(Renew)到這個 Eureka Server 1. Eureka Server 1 收到這些請求的時候,會處理這些請求并将這些請求轉發到其他的叢集内的 Eureka Server 2 和 Eureka Server 3. Eureka Server 2 和 Eureka Server 3 不會再轉發收到的 Eureka Server 1 轉發過來的請求。然後,Service B 還有 Service C 通過 Eureka 擷取到了 Service A 的位置,最後調用了 Service A。

對于本地沒有查詢到的微服務,Eureka Server 還會從遠端 Region 的 Eureka Server 去擷取,例如這裡對于 Service D,本地沒有查到,Eureka Server 會傳回遠端 Region 的 Service D 的執行個體。由于本地有 Service A,是以肯定不會傳回遠端 Region 的 Service A 的執行個體。并且,本地是定時拉取的遠端 Region 的 Service 清單,并不是每次查詢的時候現查詢的。

一般的,微服務之間的互相調用,并不經過 Eureka,也不會涉及到 Eureka 用戶端了,而是通過負載均衡器調用,這個我們後面就會提到。

Eureka 相關概念

這裡我們忽略所有的 AWS 相關的術語以及配置還有相關邏輯處理。

Eureka 中的術語:

  1. Eureka 執行個體:每個注冊到 Eureka 上面的執行個體就是 Eureka 執行個體。
  2. Eureka 執行個體狀态:包括 UP(可以處理請求),DOWN(健康檢查失敗,不能正常處理請求),STARTING(啟動中,不能處理請求),OUT_OF_SERVICE(人為下線,暫時不處理請求),UNKNOWN(未知狀态)。
  3. Eureka 伺服器:作為注冊中心運作,主要提供執行個體管理功能(處理執行個體注冊(register)請求、處理執行個體登出(cancel)請求、處理執行個體心跳(renew)請求、内部處理執行個體過期(evict))、執行個體查詢功能(各種查詢執行個體資訊的接口,例如通過 AppName 擷取執行個體清單,通過執行個體 id 擷取執行個體資訊等等)
  4. Eureka 伺服器叢集:Eureka 伺服器的叢集,每個 Eureka 伺服器都配置了區域以及可用區,Eureka 伺服器收到的用戶端請求會轉發到同一區域内的其他 Eureka 伺服器,可以配置優先發到同一可用區的 Eureka 伺服器。非同一區域内 Eureka 伺服器,通過定時拉取的方式進行同步。
  5. Eureka 用戶端:請求 Eureka 伺服器的用戶端。封裝發送執行個體注冊(register)請求、執行個體登出(cancel)請求和執行個體心跳(renew)請求。
  6. VIP(或者是 Virtual Hostname): Eureka 中可以通過兩種方式擷取執行個體,一個是通過服務名稱,另一種是通過 VIP。每個執行個體都有服務名稱,以及 VIP。Eureka 伺服器中的索引方式是以服務名稱為 key 的索引,我們也可以通過周遊所有執行個體資訊的方式通過 VIP 字元串比對擷取相關的執行個體。在 Spring Cloud 體系中,一個執行個體的 VIP、SVIP(其實就是 Secure VIP,即 https 的位址)以及服務名稱都是

    spring.application.name

    指定的服務名稱。

Eureka 相關配置

  1. Eureka 執行個體配置:Eureka 執行個體,每個注冊到 Eureka 上面的執行個體就是 Eureka 執行個體。Eureka 執行個體包含以下元素,以及相關配置:
  2. 基本資訊:包括 IP,端口等通路這個 Eureka 執行個體所需的資訊:
eureka:
  instance:
    #一般不用我們自己設定,EurekaInstanceConfigBean 的構造器會通過 InetUtils 擷取 ip 位址
    #ip-address:
    #一般不用我們自己設定,EurekaInstanceConfigBean 的構造器會通過 InetUtils 擷取 hostname
    #hostname:
    #注冊到 eureka 上面供其他執行個體通路的位址使用 ip 進行注冊,其他執行個體會通過 ip 進行通路
    prefer-ip-address: true
    #不用設定 non-secure-port,自動使用 server.port 作為 non-secure-port
    #non-secure-port:
    #如果 secure-port-enabled 是 true,則會自動使用 server.port 作為 secure-port;我們一般内部調用不用 ssl,是以不需要配置 secure-port
    #secure-port:
    #預設是啟用 non-secure-port 的
    non-secure-port-enabled: true
    #預設是不啟用 secure-port 的,我們一般内部調用不用 ssl
    secure-port-enabled: false
    #個性化的執行個體id,包括 ip:微服務名稱:端口
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
    # app名稱,不填寫在 Spring-cloud-netflix 體系下預設就是 spring.application.name
    appname: ${spring.application.name}
    #app組名稱歸類用的,目前也沒什麼用
    app-group-name: common
    #執行個體命名空間,目前也沒什麼用
    namespace: public
           
  1. 基本連結資訊:包括首頁路徑位址以及健康檢查路徑位址:
eureka:
  instance:
    # 健康檢查位址,預設是 /actuator/health
    health-check-url-path: /actuator/health
    # 執行個體狀态位址,預設是 /actuator/info
    status-page-url-path: /actuator/info
    # 首頁位址,預設是 /
    home-page-url-path: /
           
  1. 執行個體注冊行為,即執行個體注冊後的行為,以及心跳間隔等配置:
eureka:
  instance:
    # 服務過期時間配置,超過這個時間沒有接收到心跳EurekaServer就會将這個執行個體剔除
    # 注意,EurekaServer一定要設定eureka.server.eviction-interval-timer-in-ms否則這個配置無效
    # 這個配置一般為服務重新整理時間配置的三倍
    # 預設90s
    lease-expiration-duration-in-seconds: 15
    #服務重新整理時間配置,每隔這個時間會主動心跳一次
    #預設30s
    lease-renewal-interval-in-seconds: 5
    registry:
      #請參考 wait-time-in-ms-when-sync-empty 配置說明
      default-open-for-traffic-count: 1
      #初始期望發送心跳請求的執行個體個數,預設為1,在有新執行個體注冊的時候,會 +1,有登出的時候會 -1,初始預設為 1 一般因為自己也注冊到 eureka 上
      expected-number-of-clients-sending-renews: 1
    #執行個體注冊後是否立刻開始服務,預設為 false,一般注冊後還需要做一些操作,是以注冊執行個體的狀态是 STARTING。後面改變狀态後會更新為 UP
    instance-enabled-onit: false
           
  1. 執行個體中繼資料:
eureka:
  instance:
    #中繼資料map,我們可以自己使用,放一些個性化的中繼資料,目前隻有 configPath 和 zone 比較有用。 configPath 是使用 spring-cloud-config 的時候會設定 
    metadata-map:
      # spring cloud 體系中,可用區的配置放入中繼資料中,key 為 zone
      zone: zone1
           
  1. Eureka 用戶端配置:
  2. Eureka 伺服器位址配置,可以直接指定連結,也可以通過 region 和 zone 進行配置,也可以通過 DNS 配置:
eureka:
  instance:
    # 可用區清單,key 為 region,value 為 zone
    availability-zones:
      region1: zone1, zone2
      region2: zone3
    # 所在區域,通過這個讀取 availability-zones 擷取 zone,然後通過 zone 讀取 service-url 擷取對應的 eureka url
    # 這裡的邏輯對應的類是 ConfigClusterResolver 和 ZoneAffinityClusterResolver
    region: region1
    # key 為 zone,value 為 eureka 連結,以逗号分隔
    service-url:
      # 預設eureka叢集,這裡必須是defaultZone,不能用-替換大寫,與其他的配置不一樣,因為實在EurekaClientConfigBean裡面寫死的
      defaultZone: http://127.0.0.1:8211/eureka/
      zone1: http://127.0.0.1:8212/eureka/
      zone2: http://127.0.0.1:8213/eureka/
      zone3: http://127.0.0.1:8214/eureka/
    # 如果上面 eureka server 位址相關配置更新了,多久之後會重新讀取感覺到
    eureka-service-url-poll-interval-seconds: 300
    # 是否使用 dns 擷取,如果指定了則通過下面的 dns 配置擷取,而不是上面的 service-url
    use-dns-for-fetching-service-urls: false
    # dns 配置
    # eureka-server-d-n-s-name:
    # dns 配置的 eureka server 的 port
    # eureka-server-port:
    # dns 配置的 eureka server 的 port 後面的 uri 字首 context
    # eureka-server-u-r-l-context:
    # 如果設定為 true,則同一個 zone 下的 eureka 會跑到前面優先通路。預設為 true
    prefer-same-zone-eureka: true
           
  1. 拉取服務執行個體資訊相關配置:
eureka:
  instance:
    # 是否從 eureka 上面拉取執行個體
    fetch-registry: true
    # 如果隻想擷取一個特定 virtual host name 的執行個體清單,就配置 registry-refresh-single-vip-address
    #registry-refresh-single-vip-address:
    # 用戶端請求頭指定服務端傳回的執行個體資訊是壓縮的資訊還是完整資訊,預設是完整資訊
    # full, compact
    client-data-accept: full
    # eureka client 重新整理本地緩存時間
    # 預設30s
    registry-fetch-interval-seconds: 5
    # eureka client 重新整理本地緩存(定時拉取 eureka 執行個體清單)線程池大小,預設為 2
    cache-refresh-executor-thread-pool-size: 2
    # eureka client 重新整理本地緩存(定時拉取 eureka 執行個體清單)線程池任務最大延遲時間,這個配置是定時拉取任務延遲(registry-fetch-interval-seconds)的倍數,預設 10 倍
    cache-refresh-executor-exponential-back-off-bound: 10
    # 是否禁用增量拉取,如果網絡條件不好,可以禁用,每次都會拉取全量
    disable-delta: false
    # 隻保留狀态為 UP 的執行個體,預設為 true
    filter-only-up-instances: true
    #可以指定也從某些 region 拉取服務執行個體
    #fetch-remote-regions-registry:
    # 是否打日志記錄每次拉取執行個體資訊與目前緩存内的執行個體資訊變化
    log-delta-diff: true
    
    #在spring cloud 環境中,DiscoveryClient 用的其實都是 CompositeDiscoveryClient,這個 CompositeDiscoveryClient 邏輯其實就是多個 DiscoveryClient 共存,先通路一個,沒找到就通過下一個尋找
    #這個order決定了順序,預設為 0
    order: 0
           
  1. 目前執行個體注冊相關配置:
eureka:
  instance:
    # 是否将自己注冊到 eureka 上面
    register-with-eureka: true
    # 是否在初始化的時候就注冊到 eureka,一般設定為 false,因為執行個體還不能正常提供服務
    should-enforce-registration-at-init: false
    # 是否在關閉的時候登出執行個體,預設為 true
    should-unregister-on-shutdown: true
    # 是否對于執行個體狀态改變更新進行限流,預設為 true
    on-demand-update-status-change: true
    # 執行個體資訊同定時同步到 Eureka Server 的間隔時間。每隔這麼長時間,檢查執行個體資訊(即eureka.instance配置資訊)是否發生變化,如果發生變化,則同步到 Eureka Server,預設 30s
    # 主要檢查兩類資訊,分别是服務位址相關資訊,以及服務過期時間與重新整理時間配置資訊
    instance-info-replication-interval-seconds: 30
    # 執行個體資訊同定時同步到 Eureka Server 的初始延遲時間,預設 40s
    initial-instance-info-replication-interval-seconds: 40
           
  1. http連接配接相關配置:
eureka:
  instance:
    # 代理相關配置
    # proxy-host:
    # proxy-port:
    # proxy-user-name:
    # proxy-password:
    # 是否對于發往 Eureka Server 的 http 請求啟用 gzip,目前已經過期了,隻要 Eureka Server 啟用了 gzip,請求就是 gzip 壓縮的
    g-zip-content: true
    # httpclient 的連結逾時,預設 5s
    eureka-server-connect-timeout-seconds: 5
    # httpclient 的讀取逾時,預設 5s
    eureka-server-read-timeout-seconds: 8
    # httpclient 的空閑連接配接逾時,預設 30s
    eureka-connection-idle-timeout-seconds: 30
    # httpclient 的總連接配接數量,預設 200
    eureka-server-total-connections: 200
    # httpclient 的每個 host 的連接配接數量
    eureka-server-total-connections-per-host: 50
    # tls 相關配置,預設沒有啟用
#      tls:
#        enabled: false
#        key-password:
#        key-store:
#        key-store-password:
#        key-store-type:
#        trust-store:
#        trust-store-password:
#        trust-store-type:
           
  1. Eureka 伺服器配置:
  2. 定時檢查執行個體過期相關配置:執行個體注冊後需要發送心跳證明這個執行個體是活着的, Eureka 伺服器中也有定時任務檢查執行個體是否已經過期:
eureka:
  server:
    #主動檢查服務執行個體是否失效的任務執行間隔,預設是 60s
    eviction-interval-timer-in-ms: 3000
    #這個配置在兩個地方被使用:
    #如果啟用用了自我保護,則會 renewal-threshold-update-interval-ms 指定的時間内,收到的心跳請求個數是否小于執行個體個數乘以這個 renewal-percent-threshold
    #定時任務檢查過期執行個體,每次最多過期 1 - renewal-percent-threshold 這麼多比例的執行個體
    renewal-percent-threshold: 0.85
           
  1. 自我保護相關配置:Eureka 伺服器中有定時過期的任務,檢查遲遲沒有心跳的執行個體,并登出他們。自我保護主要針對叢集中網絡出現問題,導緻有很多執行個體無法發送心跳導緻很多執行個體狀态異常,但是實際執行個體還在正常工作的情況,不要讓這些執行個體不參與負載均衡:
eureka:
  server: 
    #注意,最好所有的用戶端執行個體配置的心跳時間相關的配置,是相同的。這樣使用自我保護的特性最準确。
    #關閉自我保護
    #我們這裡不使用自我保護,因為:
    #自我保護主要針對叢集中網絡出現問題,導緻有很多執行個體無法發送心跳導緻很多執行個體狀态異常,但是實際執行個體還在正常工作的情況,不要讓這些執行個體不參與負載均衡
    #啟用自我保護的情況下,就會停止對于執行個體的過期
    #但是,如果出現這種情況,其實也代表很多執行個體無法讀取注冊中心了。
    #并且還有一種情況就是,Eureka 重新開機。雖然不常見,但是對于鏡像中其他的元件更新我們還是很頻繁的
    #我傾向于從用戶端對于執行個體緩存機制來解決這個問題,如果傳回執行個體清單為空,則使用上次的執行個體清單進行負載均衡,這樣既能解決 Eureka 重新開機的情況,又能處理一些 Eureka 網絡隔離的情況
    #自我保護模式基于每分鐘需要收到 renew (執行個體心跳)請求個數,如果啟用了自我保護模式,隻有上一分鐘接收到的 renew 個數,大于這個值,執行個體過期才會被登出
    enable-self-preservation: false
    # 每分鐘需要收到 renew (執行個體心跳)請求個數是需要動态重新整理的,這個重新整理間隔就是 renewal-threshold-update-interval-ms
    #更新流程大概是:計算目前一共有多少執行個體,如果大于之前期望的執行個體量 * renewal-percent-threshold(或者沒開啟自我保護模式),則更新期望的執行個體數量為目前一共有多少執行個體
    #之後根據期望的執行個體數量,計算期望需要收到的執行個體心跳請求個數 = 期望的執行個體數量 * (60 / expected-client-renewal-interval-seconds) * renewal-percent-threshold
    #公式中 60 代表一分鐘,因為公式用到了 expected-client-renewal-interval-seconds,也就是執行個體平均心跳間隔,為了使這個公式準确,最好每個執行個體配置一樣的心跳時間
    #預設 900000ms = 900s = 15min
    renewal-threshold-update-interval-ms: 900000
    #上面提到的執行個體平均心跳間隔,或者說是期望的心跳間隔,為了使這個公式準确,最好每個執行個體配置一樣的心跳時間
    #預設 30s
    expected-client-renewal-interval-seconds: 30
    #這個配置在兩個地方被使用:
    #如果啟用用了自我保護,則會 renewal-threshold-update-interval-ms 指定的時間内,收到的心跳請求個數是否小于執行個體個數乘以這個 renewal-percent-threshold
    #定時任務檢查過期執行個體,每次最多過期 1 - renewal-percent-threshold 這麼多比例的執行個體
    renewal-percent-threshold: 0.85
           
  1. 同一區域内叢集配置相關:上面我們提到了,同一區域内的 Eureka 伺服器執行個體,收到的用戶端請求,會轉發到同一區域内的的其他 Eureka 伺服器執行個體。同時,在某一 Eureka 伺服器執行個體啟動的時候,會從同一區域内其他 Eureka 伺服器同步執行個體清單。并且,轉發到其他 Eureka 伺服器執行個體是異步轉發的,這就有專門的線程池進行轉發。同時,轉發的也是 HTTP 請求,這就需要 HTTP 連接配接池:
eureka:
  server: 
    #Eureka Server 從配置中更新同一區域内的其他 Eureka Server 執行個體清單間隔,預設10分鐘
    peer-eureka-nodes-update-interval-ms: 600000
    #啟動時從其他 Eureka Server 同步服務執行個體資訊的最大重試次數,直到執行個體個數不為 0,預設為 0,這樣其實就是不同步
    registry-sync-retries: 0
    #啟動時從其他 Eureka Server 同步服務執行個體資訊重試間隔
    registry-sync-retry-wait-ms: 30000
    #叢集内至少有多少個 UP 的 Eureka Server 執行個體數量,目前 Eureka Server 狀态為 UP。預設 -1,也就是 Eureka Server 狀态不考慮 UP 的叢集内其他 Eureka Server 數量。
    min-available-instances-for-peer-replication: -1
    #請求其他執行個體任務的最大逾時時間,預設 30 秒
    max-time-for-replication: 30000
    #用來處理同步任務的線程數量,有兩個線程池,一個處理批量同步任務,預設大小為20
    max-threads-for-peer-replication: 20
    #另一個處理非批量任務(如果沒用 AWS Autoscaling 對接相關特性則沒有啥用),預設大小為20
    max-threads-for-status-replication: 20
    #處理批量任務的線程池隊列長度,預設為 10000
    max-elements-in-peer-replication-pool: 10000
    #處理非批量任務的線程池隊列長度,預設為 10000
    max-elements-in-status-replication-pool: 10000
    #Eureka Server 通過 httpclient 通路其他 Eureka Server 同步執行個體,httpclient 的連接配接逾時,預設 200ms
    peer-node-connect-timeout-ms: 200
    #httpclient 的讀取逾時,預設 200ms,一般不用太長
    peer-node-read-timeout-ms: 200
    #httpclient 的最大總連接配接數量,預設 1000
    peer-node-total-connections: 1000
    #httpclient 的對于某一 host 最大總連接配接數量,預設 500
    peer-node-total-connections-per-host: 500
    #httpclient 的連接配接空閑保持時間,預設 30s
    peer-node-connection-idle-timeout-seconds: 30
           
  1. 跨區域相關配置。Eureka 伺服器會定時拉取其他區域的服務執行個體清單緩存在本地。在查詢本地查詢不到某個微服務的時候,就會查詢這個遠端區域服務執行個體的緩存。相關配置如下:
eureka:
  server: 
    #請求其他 Region 的 httpclient 的連接配接逾時,預設 1000ms
    remote-region-connect-timeout-ms: 1000
    #請求其他 Region 的 httpclient 的讀取逾時,預設 1000ms
    remote-region-read-timeout-ms: 1000
    #請求其他 Region 的 httpclient 的最大總連接配接數量,預設 1000
    remote-region-total-connections: 1000
    #請求其他 Region 的 httpclient 的對于某一 host 最大總連接配接數量,預設 500
    remote-region-total-connections-per-host: 500
    #請求其他 Region 的 httpclient 的連接配接空閑保持時間,預設 30s
    remote-region-connection-idle-timeout-seconds: 30
    #請求其他 Region 的 http 請求是否開啟 gzip,對于其他 Region 我們認為網絡連接配接是比較慢的,是以預設開啟壓縮
    g-zip-content-from-remote-region: true
    #    remote-region-urls-with-name:
    #      region2eureka1: http://127:0:0:1:8212/eureka/
    #      region2eureka2: http://127:0:0:1:8213/eureka/
    #    remote-region-app-whitelist:
    #如果需要從其他 Region 擷取執行個體資訊,這個擷取間隔,預設為 30s
    remote-region-registry-fetch-interval: 30
    #如果需要從其他 Region 擷取執行個體資訊,這個任務的線程池,預設為 20個
    remote-region-fetch-thread-pool-size: 20
           

啟動一個 Eureka Server

啟動一個 Eureka 注冊中心伺服器非常簡單,我們這裡使用的是 Spring Cloud 封裝好的啟動包。Eureka 1.x 的 Eureka Server 是純基于 servlet 的應用。為了與 Spring Cloud 結合使用,需要粘合子產品,這就是 spring-cloud-netflix-eureka-server。在 spring-cloud-netflix-eureka-server 中,也有一個和

com.netflix.eureka.EurekaBootStrap

代碼很類似的啟動類,即

org.springframework.cloud.netflix.eureka.server.EurekaServerBootstrap

。在我們啟動 EurekaServer 執行個體的時候,隻用加入對于 spring-cloud-starter-eureka-server 的依賴即可。之後通過 @EnableEurekaServer 注解即可啟動一個 Eureka 伺服器執行個體。

Eureka Server 的依賴:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-cloud-iiford</artifactId>
        <groupId>com.github.hashjang</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-cloud-iiford-eureka-server</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.github.hashjang</groupId>
            <artifactId>spring-cloud-iiford-service-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>
</project>
           

Eureka Server 的配置:

參考我們上面的配置即可:

application.yml

Eureka Server 的啟動類:

EurekaServerApplication.java

package com.github.hashjang.iiford.eureka.server;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

           

繼續閱讀