Spring Cloud Netflix 功能
服務發現:Eureka 用戶端
服務發現是基於微服務架構的關鍵原則之一。嘗試手動組態每個用戶端或某種形式的慣例可能難以做到且可能很脆弱。Eureka 是 Netflix 服務發現伺服器和用戶端。伺服器可以組態和部署為高可用性,每個伺服器將關於已註冊服務的狀態複製到其他伺服器。
如何包含 Eureka 用戶端
若要在您的專案中包含 Eureka 用戶端,請使用群組 ID 為 org.springframework.cloud
和 Artifact ID 為 spring-cloud-starter-netflix-eureka-client
的 Starter。請參閱 Spring Cloud 專案頁面,以取得使用目前 Spring Cloud Release Train 設定您的建置系統的詳細資訊。
向 Eureka 註冊
當用戶端向 Eureka 註冊時,它會提供關於自身的元數據 — 例如主機、埠、健康指示器 URL、首頁和其他詳細資訊。Eureka 從屬於服務的每個實例接收心跳訊息。如果心跳在可組態的時限內失敗,則實例通常會從登錄檔中移除。
以下範例顯示了最小的 Eureka 用戶端應用程式
@SpringBootApplication
@RestController
public class Application {
@RequestMapping("/")
public String home() {
return "Hello world";
}
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
請注意,上述範例顯示了正常的 Spring Boot 應用程式。透過在類別路徑中加入 spring-cloud-starter-netflix-eureka-client
,您的應用程式會自動向 Eureka 伺服器註冊。需要組態才能找到 Eureka 伺服器,如下列範例所示
eureka: client: serviceUrl: defaultZone: https://127.0.0.1:8761/eureka/
在上述範例中,defaultZone
是一個神奇的字串回退值,為任何未表達偏好的用戶端提供服務 URL(換句話說,它是一個有用的預設值)。
defaultZone 屬性區分大小寫,並且需要駝峰式命名,因為 serviceUrl 屬性是 Map<String, String> 。因此,defaultZone 屬性不遵循正常的 Spring Boot 蛇形命名慣例 default-zone 。 |
預設應用程式名稱(即服務 ID)、虛擬主機和非安全埠(取自 Environment
)分別為 ${spring.application.name}
、${spring.application.name}
和 ${server.port}
。
在類別路徑中加入 spring-cloud-starter-netflix-eureka-client
會使應用程式同時成為 Eureka「實例」(即,它會註冊自身)和「用戶端」(它可以查詢登錄檔以找到其他服務)。實例行為由 eureka.instance.*
組態金鑰驅動,但是如果您確保您的應用程式具有 spring.application.name
的值(這是 Eureka 服務 ID 或 VIP 的預設值),則預設值就很好。
有關可組態選項的更多詳細資訊,請參閱 EurekaInstanceConfigBean 和 EurekaClientConfigBean。
若要停用 Eureka Discovery Client,您可以將 eureka.client.enabled
設定為 false
。當 spring.cloud.discovery.enabled
設定為 false
時,Eureka Discovery Client 也會被停用。
目前不支援將 Spring Cloud Netflix Eureka 伺服器的版本指定為路徑參數。這表示您無法在內容路徑 (eurekaServerURLContext ) 中設定版本。相反地,您可以將版本包含在伺服器 URL 中(例如,您可以設定 defaultZone: localhost:8761/eureka/v2 )。 |
使用 Eureka 伺服器進行驗證
如果 eureka.client.serviceUrl.defaultZone
URL 之一具有內嵌的憑證(curl 樣式,如下所示:user:password@localhost:8761/eureka
),則 HTTP 基本驗證會自動新增至您的 eureka 用戶端。對於更複雜的需求,您可以建立類型為 DiscoveryClientOptionalArgs
的 @Bean
並將 ClientFilter
實例注入其中,所有這些都會應用於從用戶端到伺服器的呼叫。
當 Eureka 伺服器需要用戶端憑證進行驗證時,可以透過屬性組態用戶端憑證和信任儲存區,如下列範例所示
eureka:
client:
tls:
enabled: true
key-store: <path-of-key-store>
key-store-type: PKCS12
key-store-password: <key-store-password>
key-password: <key-password>
trust-store: <path-of-trust-store>
trust-store-type: PKCS12
trust-store-password: <trust-store-password>
eureka.client.tls.enabled
需要為 true 才能啟用 Eureka 用戶端 TLS。當省略 eureka.client.tls.trust-store
時,會使用 JVM 預設信任儲存區。eureka.client.tls.key-store-type
和 eureka.client.tls.trust-store-type
的預設值為 PKCS12。當省略密碼屬性時,會假設為空密碼。
由於 Eureka 的限制,無法支援每個伺服器的基本驗證憑證,因此僅使用找到的第一組憑證。 |
如果您想要自訂 Eureka HTTP 用戶端使用的 RestTemplate,您可能想要建立 EurekaClientHttpRequestFactorySupplier
的 bean,並為產生 ClientHttpRequestFactory
實例提供您自己的邏輯。
Eureka HTTP 用戶端使用的 RestTemplate 的所有預設逾時相關屬性都設定為 3 分鐘(與 Apache HC5 預設 RequestConfig
和 SocketConfig
保持一致)。因此,若要指定逾時值,您必須直接使用 eureka.client.rest-template-timeout
中的屬性指定值。(所有逾時屬性均以毫秒為單位。)
eureka:
client:
rest-template-timeout:
connect-timeout: 5000
connect-request-timeout: 8000
socket-timeout: 10000
狀態頁面和健康指示器
Eureka 實例的狀態頁面和健康指示器預設分別為 /info
和 /health
,它們是 Spring Boot Actuator 應用程式中有用端點的預設位置。如果您使用非預設內容路徑或 servlet 路徑(例如 server.servletPath=/custom
),即使對於 Actuator 應用程式,您也需要變更這些設定。以下範例顯示了兩個設定的預設值
eureka: instance: statusPageUrlPath: ${server.servletPath}/info healthCheckUrlPath: ${server.servletPath}/health
這些連結會顯示在用戶端使用的元數據中,並且在某些情況下用於決定是否將請求傳送至您的應用程式,因此如果它們準確,則會很有幫助。
在 Dalston 中,當變更該管理內容路徑時,也需要設定狀態和健康檢查 URL。從 Edgware 開始,已移除此需求。 |
註冊安全應用程式
如果您的應用程式想要透過 HTTPS 聯絡,您可以在 EurekaInstanceConfigBean
中設定兩個旗標
-
eureka.instance.[nonSecurePortEnabled]=[false]
-
eureka.instance.[securePortEnabled]=[true]
這樣做會使 Eureka 發布實例資訊,這些資訊顯示對安全通訊的明確偏好。Spring Cloud DiscoveryClient
始終為以此方式組態的服務傳回以 https
開頭的 URI。同樣地,當服務以此方式組態時,Eureka(原生)實例資訊具有安全健康檢查 URL。
由於 Eureka 內部的運作方式,它仍然會發布狀態和首頁的非安全 URL,除非您也明確覆寫這些 URL。您可以使用佔位符組態 eureka 實例 URL,如下列範例所示
eureka: instance: statusPageUrl: https://${eureka.hostname}/info healthCheckUrl: https://${eureka.hostname}/health homePageUrl: https://${eureka.hostname}/
(請注意,${eureka.hostname}
是一個原生佔位符,僅在較新版本的 Eureka 中可用。您也可以使用 Spring 佔位符來達到相同的目的 — 例如,透過使用 ${eureka.instance.hostName}
。)
如果您的應用程式在 Proxy 後方執行,並且 SSL 終止在 Proxy 中(例如,如果您在 Cloud Foundry 或其他平台即服務中執行),則您需要確保 Proxy「轉發」標頭被應用程式攔截和處理。如果 Spring Boot 應用程式中內嵌的 Tomcat 容器具有針對 'X-Forwarded-\*' 標頭的明確組態,則會自動發生這種情況。您的應用程式呈現給自身的連結錯誤(錯誤的主機、埠或協定)表示您的組態錯誤。 |
Eureka 的健康檢查
預設情況下,Eureka 使用用戶端心跳來判斷用戶端是否啟動。除非另有指定,否則 Discovery Client 不會傳播應用程式的目前健康檢查狀態,根據 Spring Boot Actuator。因此,在成功註冊後,Eureka 始終宣告應用程式處於「UP」狀態。可以透過啟用 Eureka 健康檢查來變更此行為,這會導致將應用程式狀態傳播到 Eureka。因此,每個其他應用程式都不會將流量傳送到狀態不是「UP」的應用程式。以下範例顯示如何為用戶端啟用健康檢查
eureka: client: healthcheck: enabled: true
eureka.client.healthcheck.enabled=true 應僅在 application.yml 中設定。在 bootstrap.yml 中設定值會導致不必要的副作用,例如以 UNKNOWN 狀態在 Eureka 中註冊。 |
如果您需要更多地控制健康檢查,請考慮實作您自己的 com.netflix.appinfo.HealthCheckHandler
。
Eureka 實例和用戶端的元數據
值得花一些時間了解 Eureka 元數據如何運作,以便您可以在您的平台上以有意義的方式使用它。有標準元數據用於諸如主機名稱、IP 位址、埠號、狀態頁面和健康檢查等資訊。這些發布在服務登錄檔中,並由用戶端使用,以直接的方式聯絡服務。可以將其他元數據新增至 eureka.instance.metadataMap
中的實例註冊,並且此元數據可在遠端用戶端中存取。一般而言,除非用戶端知道元數據的含義,否則其他元數據不會變更用戶端的行為。稍後在本文檔中描述了幾個特殊情況,其中 Spring Cloud 已將含義指派給元數據對應。
在 Cloud Foundry 上使用 Eureka
Cloud Foundry 具有全域路由器,因此同一應用程式的所有實例都具有相同的主機名稱(具有類似架構的其他 PaaS 解決方案也具有相同的配置)。這不一定是使用 Eureka 的障礙。但是,如果您使用路由器(建議或甚至是強制性的,取決於您的平台設定方式),則需要明確設定主機名稱和埠號(安全或非安全),以便它們使用路由器。您可能還想要使用實例元數據,以便您可以在用戶端上區分實例(例如,在自訂負載平衡器中)。預設情況下,eureka.instance.instanceId
為 vcap.application.instance_id
,如下列範例所示
eureka: instance: hostname: ${vcap.application.uris[0]} nonSecurePort: 80
根據您的 Cloud Foundry 實例中設定的安全規則方式,您可能可以註冊並使用主機 VM 的 IP 位址進行直接服務對服務呼叫。此功能在 Pivotal Web Services (PWS) 上尚不可用。
在 AWS 上使用 Eureka
如果應用程式計劃部署到 AWS 雲端,則必須將 Eureka 實例組態為 AWS 感知。您可以透過自訂 EurekaInstanceConfigBean 來執行此操作,如下所示
@Bean
@Profile("!default")
public EurekaInstanceConfigBean eurekaInstanceConfig(InetUtils inetUtils) {
EurekaInstanceConfigBean bean = new EurekaInstanceConfigBean(inetUtils);
AmazonInfo info = AmazonInfo.Builder.newBuilder().autoBuild("eureka");
bean.setDataCenterInfo(info);
return bean;
}
變更 Eureka 實例 ID
普通的 Netflix Eureka 實例以等於其主機名稱的 ID 註冊(也就是說,每個主機只有一個服務)。Spring Cloud Eureka 提供了一個合理的預設值,定義如下
${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}
範例為 myhost:myappname:8080
。
透過使用 Spring Cloud,您可以透過在 eureka.instance.instanceId
中提供唯一識別碼來覆寫此值,如下列範例所示
eureka: instance: instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}
使用上述範例中顯示的元數據以及部署在 localhost 上的多個服務實例,隨機值會插入到那裡以使實例唯一。在 Cloud Foundry 中,vcap.application.instance_id
會在 Spring Boot 應用程式中自動填入,因此不需要隨機值。
使用 EurekaClient
一旦您擁有作為 Discovery Client 的應用程式,您就可以使用它從 Eureka 伺服器探索服務實例。一種方法是使用原生 com.netflix.discovery.EurekaClient
(而不是 Spring Cloud DiscoveryClient
),如下列範例所示
@Autowired private EurekaClient discoveryClient; public String serviceUrl() { InstanceInfo instance = discoveryClient.getNextServerFromEureka("STORES", false); return instance.getHomePageUrl(); }
請勿在 |
底層 HTTP 用戶端
EurekaClient
在底層使用 RestTemplate
、WebClient
或 JerseyClient
。為了使用 EurekaClient
,您需要在您的類別路徑中具有其中一個受支援的 HTTP 用戶端。
若要使用 RestTemplate
,請將 spring-boot-starter-web
新增至您的相依性。若要使用 WebClient
,請將 spring-boot-starter-webflux
新增至您的相依性。如果 RestTemplate
和 WebClient
都在類別路徑中,並且 eureka.client.webclient.enabled
設定為 true
,則會使用 WebClient
。否則,會使用 RestTemplate
。
如果您希望改用 Jersey,則需要將 Jersey 相依性新增至您的類別路徑。以下範例顯示您需要新增的相依性
<dependencies>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
</dependency>
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-apache-client4</artifactId>
</dependency>
</dependencies>
如果您在類別路徑中有 JerseyClient
,但不希望在您的 EuerekaClient
中使用它,請確保將 eureka.client.jersey.enabled
設定為 false
。
原生 Netflix EurekaClient 的替代方案
您不需要使用原始 Netflix EurekaClient
。此外,通常更方便在某種包裝器後面使用它。Spring Cloud 支援 Feign(REST 用戶端建置器)和 Spring Cloud LoadBalancer,透過邏輯 Eureka 服務識別碼 (VIP) 而不是實體 URL。
您也可以使用 org.springframework.cloud.client.discovery.DiscoveryClient
,它為 Discovery Client 提供了一個簡單的 API(非 Netflix 特定),如下列範例所示
@Autowired private DiscoveryClient discoveryClient; public String serviceUrl() { List<ServiceInstance> list = discoveryClient.getInstances("STORES"); if (list != null && list.size() > 0 ) { return list.get(0).getUri(); } return null; }
為何服務註冊如此緩慢?
作為實例也涉及定期向登錄檔發送心跳(透過用戶端的 serviceUrl
),預設持續時間為 30 秒。在實例、伺服器和用戶端在其本機快取中都具有相同的元數據之前,服務無法供用戶端探索(因此可能需要 3 次心跳)。您可以透過設定 eureka.instance.leaseRenewalIntervalInSeconds
來變更週期。將其設定為小於 30 的值會加快用戶端連線到其他服務的過程。在生產環境中,由於伺服器中的內部計算會對租用續約週期做出假設,因此最好堅持使用預設值。
區域
如果您已將 Eureka 用戶端部署到多個區域,您可能希望這些用戶端先使用同一區域內的服務,然後再嘗試其他區域中的服務。若要設定它,您需要正確組態您的 Eureka 用戶端。
首先,您需要確保您已將 Eureka 伺服器部署到每個區域,並且它們彼此互為對等點。請參閱關於 區域和地區 的章節,以取得更多資訊。
接下來,您需要告訴 Eureka 您的服務位於哪個區域。您可以透過使用 metadataMap
屬性來執行此操作。例如,如果 service 1
同時部署到 zone 1
和 zone 2
,則您需要在 service 1
中設定以下 Eureka 屬性
Zone 1 中的服務 1
eureka.instance.metadataMap.zone = zone1
eureka.client.preferSameZoneEureka = true
Zone 2 中的服務 1
eureka.instance.metadataMap.zone = zone2
eureka.client.preferSameZoneEureka = true
重新整理 Eureka 用戶端
預設情況下,EurekaClient
bean 是可重新整理的,這表示 Eureka 用戶端屬性可以變更和重新整理。當發生重新整理時,用戶端將從 Eureka 伺服器取消註冊,並且可能會有短暫的時間所有給定服務的實例都不可用。避免發生這種情況的一種方法是停用重新整理 Eureka 用戶端的能力。若要執行此操作,請設定 eureka.client.refresh.enable=false
。
將 Eureka 與 Spring Cloud LoadBalancer 搭配使用
我們提供對 Spring Cloud LoadBalancer ZonePreferenceServiceInstanceListSupplier
的支援。來自 Eureka 實例元數據 (eureka.instance.metadataMap.zone
) 的 zone
值用於設定 spring-cloud-loadbalancer-zone
屬性的值,該屬性用於依區域篩選服務實例。
如果缺少該值,並且 spring.cloud.loadbalancer.eureka.approximateZoneFromHostname
旗標設定為 true
,則它可以使用伺服器主機名稱中的網域名稱作為區域的 Proxy。
如果沒有其他區域資料來源,則會根據用戶端組態(而不是實例組態)進行猜測。我們採用 eureka.client.availabilityZones
,它是從地區名稱到區域清單的對應,並為實例自己的地區(即 eureka.client.region
,預設為 "us-east-1",為了與原生 Netflix 相容)提取第一個區域。
服務發現:Eureka 伺服器
本節說明如何設定 Eureka 伺服器。
如何包含 Eureka 伺服器
若要在您的專案中包含 Eureka 伺服器,請使用群組 ID 為 org.springframework.cloud
和 Artifact ID 為 spring-cloud-starter-netflix-eureka-server
的 Starter。請參閱 Spring Cloud 專案頁面,以取得使用目前 Spring Cloud Release Train 設定您的建置系統的詳細資訊。
如果您的專案已將 Thymeleaf 用作其範本引擎,則 Eureka 伺服器的 Freemarker 範本可能無法正確載入。在這種情況下,必須手動組態範本載入器 |
spring: freemarker: template-loader-path: classpath:/templates/ prefer-file-system-access: false
如何執行 Eureka 伺服器
以下範例顯示了最小的 Eureka 伺服器
@SpringBootApplication
@EnableEurekaServer
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
伺服器有一個首頁,其中包含 UI 和 HTTP API 端點,用於 /eureka/*
下的正常 Eureka 功能。
以下連結有一些 Eureka 背景閱讀資料:flux capacitor 和 google group discussion。
由於 Gradle 的相依性解析規則以及缺少父系 bom 功能,因此取決於 build.gradle
|
defaultOpenForTrafficCount
及其對 EurekaServer 暖機時間的影響
Netflix Eureka 的 waitTimeInMsWhenSyncEmpty
設定在一開始未在 Spring Cloud Eureka 伺服器中考慮。為了啟用暖機時間,請設定 eureka.server.defaultOpenForTrafficCount=0
。
高可用性、區域和地區
Eureka 伺服器沒有後端儲存區,但是登錄檔中的服務實例都必須傳送心跳,以保持其註冊為最新狀態(因此可以在記憶體中完成)。用戶端也具有 Eureka 註冊的記憶體快取(因此它們不必為每個服務請求都訪問登錄檔)。
預設情況下,每個 Eureka 伺服器也是一個 Eureka 用戶端,並且需要(至少一個)服務 URL 來找到對等點。如果您未提供它,則服務會執行並運作,但是它會在您的記錄中填滿大量關於無法向對等點註冊的雜訊。
獨立模式
只要有某種監視器或彈性執行階段(例如 Cloud Foundry)使其保持運作,兩個快取(用戶端和伺服器)和心跳的組合使獨立 Eureka 伺服器具有相當的彈性來應對故障。在獨立模式下,您可能更喜歡關閉用戶端行為,以便它不會一直嘗試且無法連線到其對等點。以下範例顯示如何關閉用戶端行為
server: port: 8761 eureka: instance: hostname: localhost client: registerWithEureka: false fetchRegistry: false serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
請注意,serviceUrl
指向與本機實例相同的主機。
對等點感知
可以透過執行多個實例並要求它們彼此註冊,使 Eureka 變得更具彈性和可用性。實際上,這是預設行為,因此您使其運作所需做的就是向對等點新增有效的 serviceUrl
,如下列範例所示
--- spring: profiles: peer1 eureka: instance: hostname: peer1 client: serviceUrl: defaultZone: https://peer2/eureka/ --- spring: profiles: peer2 eureka: instance: hostname: peer2 client: serviceUrl: defaultZone: https://peer1/eureka/
在上述範例中,我們有一個 YAML 檔案,可用於透過在不同的 Spring 設定檔中執行相同的伺服器,在兩個主機(peer1
和 peer2
)上執行相同的伺服器。您可以透過操作 /etc/hosts
來解析主機名稱,來使用此組態在單一主機上測試對等點感知(在生產環境中執行此操作沒有太多價值)。實際上,如果您在知道自身主機名稱的機器上執行(預設情況下,它會使用 java.net.InetAddress
查閱),則不需要 eureka.instance.hostname
。
您可以向系統新增多個對等點,並且只要它們都透過至少一條邊緣彼此連線,它們就會在彼此之間同步註冊。如果對等點在物理上是分開的(在資料中心內或多個資料中心之間),則原則上系統可以在「裂腦」類型故障中倖存下來。您可以向系統新增多個對等點,並且只要它們都直接彼此連線,它們就會在彼此之間同步註冊。
eureka: client: serviceUrl: defaultZone: https://peer1/eureka/,http://peer2/eureka/,http://peer3/eureka/ --- spring: profiles: peer1 eureka: instance: hostname: peer1 --- spring: profiles: peer2 eureka: instance: hostname: peer2 --- spring: profiles: peer3 eureka: instance: hostname: peer3
何時偏好 IP 位址
在某些情況下,Eureka 最好宣傳服務的 IP 位址而不是主機名稱。將 eureka.instance.preferIpAddress
設定為 true
,並且當應用程式向 eureka 註冊時,它會使用其 IP 位址而不是其主機名稱。
如果 Java 無法判斷主機名稱,則會將 IP 位址傳送至 Eureka。設定主機名稱的唯一明確方法是透過設定 |
保護 Eureka 伺服器
您可以透過透過 spring-boot-starter-security
將 Spring Security 新增至伺服器的類別路徑來輕鬆保護您的 Eureka 伺服器。預設情況下,當 Spring Security 在類別路徑中時,它會要求每個對應用程式的請求都傳送有效的 CSRF 權杖。Eureka 用戶端通常不會擁有有效的跨網站請求偽造 (CSRF) 權杖,您需要針對 /eureka/**
端點停用此需求。例如
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated())
.httpBasic(withDefaults());
http.csrf().ignoringRequestMatchers("/eureka/**");
return http.build();
}
有關 CSRF 的更多資訊,請參閱 Spring Security 文件。
示範 Eureka 伺服器可以在 Spring Cloud Samples repo 中找到。
JDK 11 支援
Eureka 伺服器所依賴的 JAXB 模組已在 JDK 11 中移除。如果您打算在使用 Eureka 伺服器時使用 JDK 11,則必須在您的 POM 或 Gradle 檔案中包含這些相依性。
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
指標
EurekaInstanceMonitor
監聽與 Eureka 實例註冊相關的事件,並在 Micrometer 的 MeterRegistry
中為 Eureka 實例資訊建立/更新 Gauge
。預設情況下,此行為已停用。如果您想要啟用它,則需要將 eureka.server.metrics.enabled
設定為 true
。
預設情況下,Gauge
名為 eureka.server.instances
,並具有以下標籤
-
application
:應用程式名稱 -
status
:實例狀態 (UP
、DOWN
、STARTING
、OUT_OF_SERVICE
、UNKNOWN
,請參閱:com.netflix.appinfo.InstanceInfo.InstanceStatus
)
您可以透過注入您自己實作的 EurekaInstanceTagsProvider
來新增額外的標籤。
組態屬性
若要查看所有與 Spring Cloud Netflix 相關的組態屬性列表,請查看附錄頁面。