雲原生是一種應用程式開發風格,鼓勵輕鬆採用持續交付和價值驅動開發領域的最佳實務。一個相關的學科是建構 十二要素應用程式,其中開發實務與交付和營運目標一致 — 例如,透過使用宣告式程式設計以及管理與監控。Spring Cloud 以多種特定方式促進這些開發風格。起點是一組分散式系統中所有組件都需要輕鬆存取的功能。
許多這些功能都涵蓋在 Spring Boot 中,Spring Cloud 建構於其之上。Spring Cloud 以兩個程式庫的形式交付更多功能:Spring Cloud Context 和 Spring Cloud Commons。Spring Cloud Context 為 Spring Cloud 應用程式的 ApplicationContext
提供公用程式和特殊服務(啟動內容、加密、重新整理範圍和環境端點)。Spring Cloud Commons 是一組抽象和通用類別,用於不同的 Spring Cloud 實作(例如 Spring Cloud Netflix 和 Spring Cloud Consul)。
如果您因為「Illegal key size」而收到例外,並且您使用 Sun 的 JDK,則您需要安裝 Java 密碼術擴展 (JCE) 無限制強度管轄策略檔案。請參閱以下連結以取得更多資訊
將檔案解壓縮到您使用的任何 JRE/JDK x64/x86 版本的 JDK/jre/lib/security 資料夾中。
Spring Cloud 是在非限制性的 Apache 2.0 授權下發布的。如果您想為文件的這個章節貢獻,或者如果您發現錯誤,您可以在 {docslink}[github] 找到專案的原始碼和問題追蹤器。 |
1. Spring Cloud Context:應用程式內容服務
Spring Boot 對於如何使用 Spring 建構應用程式有其主觀觀點。例如,它針對常見的配置檔案具有慣例位置,並且針對常見的管理與監控任務具有端點。Spring Cloud 建構於此之上,並新增了一些系統中許多組件會使用或偶爾需要的功能。
1.1. 啟動應用程式內容
Spring Cloud 應用程式透過建立「啟動」內容來運作,該內容是主要應用程式的父內容。此內容負責從外部來源載入配置屬性,以及解密本機外部配置檔案中的屬性。這兩個內容共用一個 Environment
,它是任何 Spring 應用程式的外部屬性來源。依預設,啟動屬性(不是 bootstrap.properties
,而是啟動階段期間載入的屬性)以高優先順序新增,因此它們無法被本機配置覆寫。
啟動內容使用與主要應用程式內容不同的慣例來尋找外部配置。您可以改為使用 bootstrap.yml
,而不是 application.yml
(或 .properties
),使啟動和主要內容的外部配置保持良好分離。以下清單顯示一個範例
spring: application: name: foo cloud: config: uri: ${SPRING_CONFIG_URI:http://localhost:8888}
如果您的應用程式需要來自伺服器的任何應用程式特定配置,最好設定 spring.application.name
(在 bootstrap.yml
或 application.yml
中)。為了使屬性 spring.application.name
用作應用程式的內容 ID,您必須在 bootstrap.[properties | yml]
中設定它。
如果您想要擷取特定的設定檔配置,您也應該在 bootstrap.[properties | yml]
中設定 spring.profiles.active
。
您可以透過設定 spring.cloud.bootstrap.enabled=false
(例如,在系統屬性中)完全停用啟動程序。
1.2. 應用程式內容階層
如果您從 SpringApplication
或 SpringApplicationBuilder
建構應用程式內容,則啟動內容會新增為該內容的父內容。Spring 的一項功能是子內容從其父內容繼承屬性來源和設定檔,因此與在沒有 Spring Cloud Config 的情況下建構相同內容相比,「主要」應用程式內容包含其他屬性來源。其他屬性來源為
-
「bootstrap」:如果在啟動內容中找到任何
PropertySourceLocators
,並且它們具有非空屬性,則會出現具有高優先順序的可選CompositePropertySource
。Spring Cloud Config 伺服器的屬性就是一個範例。請參閱「自訂啟動屬性來源」,以瞭解如何自訂此屬性來源的內容。
在 Spring Cloud 2022.0.3 之前,PropertySourceLocators (包括 Spring Cloud Config 的那些)是在主要應用程式內容期間而非在啟動內容中執行的。您可以透過在 bootstrap.[properties | yaml] 中設定 spring.cloud.config.initialize-on-context-refresh=true ,強制 PropertySourceLocators 在啟動內容期間執行。 |
-
「applicationConfig: [classpath:bootstrap.yml]」(以及相關檔案,如果 Spring 設定檔處於作用中狀態):如果您有
bootstrap.yml
(或.properties
),則這些屬性會用於配置啟動內容。然後,當設定其父內容時,它們會新增至子內容。它們的優先順序低於application.yml
(或.properties
)以及新增至子內容的任何其他屬性來源,作為建立 Spring Boot 應用程式的正常程序的一部分。請參閱「變更啟動屬性的位置」,以瞭解如何自訂這些屬性來源的內容。
由於屬性來源的排序規則,「bootstrap」項目優先。但是,請注意,這些項目不包含來自 bootstrap.yml
的任何資料,後者的優先順序非常低,但可用於設定預設值。
您可以透過設定您建立的任何 ApplicationContext
的父內容來擴展內容階層 — 例如,透過使用其自己的介面或使用 SpringApplicationBuilder
便利方法(parent()
、child()
和 sibling()
)。啟動內容是您自己建立的最資深祖先的父內容。階層中的每個內容都有自己的「bootstrap」(可能為空)屬性來源,以避免無意中將值從父內容向下提升到其後代。如果存在配置伺服器,則階層中的每個內容原則上也可以具有不同的 spring.application.name
,因此也具有不同的遠端屬性來源。正常的 Spring 應用程式內容行為規則適用於屬性解析:來自子內容的屬性會依名稱以及屬性來源名稱覆寫父內容中的屬性。(如果子內容具有與父內容同名的屬性來源,則父內容中的值不會包含在子內容中)。
請注意,SpringApplicationBuilder
可讓您在整個階層中共用 Environment
,但這不是預設設定。因此,同級內容(特別是)不需要具有相同的設定檔或屬性來源,即使它們可能與其父內容共用通用值。
1.3. 變更啟動屬性的位置
可以透過設定 spring.cloud.bootstrap.name
(預設值:bootstrap
)、spring.cloud.bootstrap.location
(預設值:空)或 spring.cloud.bootstrap.additional-location
(預設值:空) — 例如,在系統屬性中,來指定 bootstrap.yml
(或 .properties
)的位置。
這些屬性的行為與具有相同名稱的 spring.config.*
變體類似。使用 spring.cloud.bootstrap.location
時,預設位置會被取代,並且僅使用指定的位置。若要將位置新增至預設位置清單,可以使用 spring.cloud.bootstrap.additional-location
。實際上,它們用於透過在其 Environment
中設定這些屬性來設定啟動 ApplicationContext
。如果存在作用中的設定檔(來自 spring.profiles.active
或透過您正在建構的內容中的 Environment
API),則也會載入該設定檔中的屬性,與在一般 Spring Boot 應用程式中相同 — 例如,對於 development
設定檔,從 bootstrap-development.properties
載入。
1.4. 覆寫遠端屬性的值
啟動內容新增至應用程式的屬性來源通常是「遠端」的(例如,來自 Spring Cloud Config 伺服器)。依預設,它們無法在本機覆寫。如果您想要讓您的應用程式使用其自己的系統屬性或配置檔案覆寫遠端屬性,則遠端屬性來源必須透過設定 spring.cloud.config.allowOverride=true
來授予其權限(在本機設定此項無效)。一旦設定了該旗標,兩個更精細的設定會控制遠端屬性相對於系統屬性和應用程式本機配置的位置
-
spring.cloud.config.overrideNone=true
:從任何本機屬性來源覆寫。 -
spring.cloud.config.overrideSystemProperties=false
:只有系統屬性、命令列引數和環境變數(但不包括本機配置檔案)應該覆寫遠端設定。
1.5. 自訂啟動配置
可以透過將項目新增到 /META-INF/spring.factories
中名為 org.springframework.cloud.bootstrap.BootstrapConfiguration
的金鑰下,將啟動內容設定為執行您想要的任何操作。這會保留用於建立內容的 Spring @Configuration
類別的逗號分隔清單。您想要在主要應用程式內容中可供自動裝配的任何 bean 都可以在此處建立。ApplicationContextInitializer
類型的 @Beans
有一個特殊合約。如果您想要控制啟動順序,則可以使用 @Order
註解標記類別(預設順序為 last
)。
新增自訂 BootstrapConfiguration 時,請注意您新增的類別不會錯誤地透過 @ComponentScanned 掃描到您的「主要」應用程式內容中,因為它們可能不是必要的。為啟動配置類別使用單獨的套件名稱,並確保該名稱尚未包含在您的 @ComponentScan 或 @SpringBootApplication 註解配置類別中。 |
啟動程序透過將初始化器注入到主要 SpringApplication
實例中來結束(這是正常的 Spring Boot 啟動順序,無論它是作為獨立應用程式執行還是在應用程式伺服器中部署)。首先,從 spring.factories
中找到的類別建立啟動內容。然後,在主要 SpringApplication
啟動之前,將所有 ApplicationContextInitializer
類型的 @Beans
新增到主要 SpringApplication
。
1.6. 自訂啟動屬性來源
啟動程序新增的外部配置的預設屬性來源是 Spring Cloud Config 伺服器,但您可以透過將 PropertySourceLocator
類型的 bean 新增到啟動內容(透過 spring.factories
)來新增其他來源。例如,您可以從不同的伺服器或資料庫插入其他屬性。
作為範例,請考慮以下自訂定位器
@Configuration
public class CustomPropertySourceLocator implements PropertySourceLocator {
@Override
public PropertySource<?> locate(Environment environment) {
return new MapPropertySource("customProperty",
Collections.<String, Object>singletonMap("property.from.sample.custom.source", "worked as intended"));
}
}
傳入的 Environment
是即將建立的 ApplicationContext
的環境 — 換句話說,我們為其提供其他屬性來源的環境。它已經具有其正常的 Spring Boot 提供的屬性來源,因此您可以使用這些來源來尋找特定於此 Environment
的屬性來源(例如,透過將其鍵入 spring.application.name
,就像在預設 Spring Cloud Config 伺服器屬性來源定位器中所做的那樣)。
如果您建立一個 jar,其中包含此類別,然後新增一個包含以下設定的 META-INF/spring.factories
,則 customProperty
PropertySource
會出現在包含該 jar 在其類別路徑中的任何應用程式中
org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomPropertySourceLocator
從 Spring Cloud 2022.0.3 開始,Spring Cloud 現在將呼叫 PropertySourceLocators
兩次。第一次擷取將擷取任何沒有設定檔的屬性來源。這些屬性來源將有機會使用 spring.profiles.active
啟動設定檔。在主要應用程式內容啟動後,將第二次呼叫 PropertySourceLocators
,這次使用任何作用中的設定檔,允許 PropertySourceLocators
尋找任何具有設定檔的其他 PropertySources
。
1.7. 日誌配置
如果您使用 Spring Boot 來配置日誌設定,如果您希望它適用於所有事件,則應將此配置放在 bootstrap.[yml | properties]
中。
為了讓 Spring Cloud 正確初始化日誌配置,您不能使用自訂前置詞。例如,在初始化日誌系統時,Spring Cloud 無法辨識使用 custom.loggin.logpath 。 |
1.8. 環境變更
應用程式會監聽 EnvironmentChangeEvent
,並以幾種標準方式對變更做出反應(可以以正常方式將其他 ApplicationListeners
新增為 @Beans
)。當觀察到 EnvironmentChangeEvent
時,它會具有已變更的金鑰值清單,並且應用程式會使用這些金鑰值來
-
重新繫結內容中的任何
@ConfigurationProperties
bean。 -
設定
logging.level.*
中任何屬性的記錄器層級。
請注意,依預設,Spring Cloud Config Client 不會輪詢 Environment
中的變更。一般來說,我們不建議使用這種方法來偵測變更(儘管您可以使用 @Scheduled
註解來設定它)。如果您有擴展型客戶端應用程式,則最好將 EnvironmentChangeEvent
廣播到所有實例,而不是讓它們輪詢變更(例如,透過使用 Spring Cloud Bus)。
EnvironmentChangeEvent
涵蓋了很大一類重新整理用例,只要您實際上可以變更 Environment
並發布事件即可。請注意,這些 API 是公開的,並且是核心 Spring 的一部分。您可以透過造訪 /configprops
端點(標準 Spring Boot Actuator 功能)來驗證變更是否繫結到 @ConfigurationProperties
bean。例如,DataSource
可以在執行階段變更其 maxPoolSize
(Spring Boot 建立的預設 DataSource
是 @ConfigurationProperties
bean),並動態增加容量。重新繫結 @ConfigurationProperties
不涵蓋另一大類用例,在這些用例中,您需要更多地控制重新整理,並且您需要變更在整個 ApplicationContext
上是原子性的。為了解決這些問題,我們有 @RefreshScope
。
1.9. 重新整理範圍
當配置變更時,標記為 @RefreshScope
的 Spring @Bean
會獲得特殊處理。此功能解決了有狀態 bean 的問題,這些 bean 的配置僅在初始化時注入。例如,如果 DataSource
在透過 Environment
變更資料庫 URL 時具有開啟的連線,您可能希望這些連線的持有者能夠完成他們正在執行的操作。然後,下次有東西從集區借用連線時,它會獲得一個具有新 URL 的連線。
有時,甚至可能必須在某些只能初始化一次的 bean 上套用 @RefreshScope
註解。如果 bean 是「不可變的」,則您必須使用 @RefreshScope
註解 bean,或在屬性金鑰下指定類別名稱:spring.cloud.refresh.extra-refreshable
。
如果您有一個 DataSource bean 是 HikariDataSource ,則無法重新整理它。它是 spring.cloud.refresh.never-refreshable 的預設值。如果您需要重新整理它,請選擇不同的 DataSource 實作。 |
重新整理範圍 bean 是延遲代理,它們在使用時(也就是說,當呼叫方法時)初始化,並且範圍充當已初始化值的快取。若要強制 bean 在下一個方法呼叫時重新初始化,您必須使其快取項目失效。
RefreshScope
是內容中的一個 bean,並且具有公用的 refreshAll()
方法,可透過清除目標快取來重新整理範圍中的所有 bean。/refresh
端點公開此功能(透過 HTTP 或 JMX)。若要依名稱重新整理個別 bean,還有一個 refresh(String)
方法。
若要公開 /refresh
端點,您需要將以下配置新增到您的應用程式
management:
endpoints:
web:
exposure:
include: refresh
@RefreshScope (技術上)適用於 @Configuration 類別,但它可能會導致令人驚訝的行為。例如,這並不表示在該類別中定義的所有 @Beans 本身都在 @RefreshScope 中。具體來說,除非它本身在 @RefreshScope 中,否則任何依賴於這些 bean 的事物都無法依賴它們在啟動重新整理時更新。在這種情況下,它會在重新整理時重建,並且其依賴項會重新注入。屆時,它們會從重新整理的 @Configuration 重新初始化)。 |
移除配置值,然後執行重新整理將不會更新配置值的存在。配置屬性必須存在,才能在重新整理後更新值。如果您依賴於應用程式中值的存在,您可能想要切換您的邏輯以依賴於它的不存在。另一種選擇是依賴於值變更,而不是不存在於應用程式的配置中。 |
Spring AOT 轉換和原生映像檔不支援內容重新整理。對於 AOT 和原生映像檔,需要將 spring.cloud.refresh.enabled 設定為 false 。 |
1.10. 加密與解密
Spring Cloud 具有 Environment
預處理器,用於在本機解密屬性值。它遵循與 Spring Cloud Config 伺服器相同的規則,並透過 encrypt.*
具有相同的外部配置。因此,您可以使用 {cipher}*
形式的加密值,並且只要存在有效的金鑰,它們就會在主要應用程式內容取得 Environment
設定之前解密。若要在應用程式中使用加密功能,您需要在您的類別路徑中包含 Spring Security RSA(Maven 座標:org.springframework.security:spring-security-rsa
),並且您還需要在您的 JVM 中使用完整強度 JCE 擴展。
如果您因為「Illegal key size」而收到例外,並且您使用 Sun 的 JDK,則您需要安裝 Java 密碼術擴展 (JCE) 無限制強度管轄策略檔案。請參閱以下連結以取得更多資訊
將檔案解壓縮到您使用的任何 JRE/JDK x64/x86 版本的 JDK/jre/lib/security 資料夾中。
1.11. 端點
對於 Spring Boot Actuator 應用程式,可以使用一些其他管理端點。您可以使用
-
POST
到/actuator/env
以更新Environment
並重新繫結@ConfigurationProperties
和記錄器層級。若要啟用此端點,您必須設定management.endpoint.env.post.enabled=true
。 -
/actuator/refresh
以重新載入啟動內容並重新整理@RefreshScope
bean。 -
/actuator/restart
以關閉ApplicationContext
並重新啟動它(依預設停用)。 -
/actuator/pause
和/actuator/resume
用於呼叫Lifecycle
方法(ApplicationContext
上的stop()
和start()
)。
雖然為 /actuator/env 端點啟用 POST 方法可以在管理您的應用程式環境變數時提供彈性和便利性,但確保端點受到保護和監控以防止潛在的安全風險至關重要。新增 spring-boot-starter-security 依賴項以配置 Actuator 端點的存取控制。 |
如果您停用 /actuator/restart 端點,則 /actuator/pause 和 /actuator/resume 端點也會停用,因為它們只是 /actuator/restart 的特殊情況。 |
2. Spring Cloud Commons:通用抽象
服務發現、負載平衡和斷路器等模式適用於通用抽象層,所有 Spring Cloud 客戶端都可以使用該抽象層,而與實作無關(例如,使用 Eureka 或 Consul 進行發現)。
2.1. @EnableDiscoveryClient
註解
Spring Cloud Commons 提供了 @EnableDiscoveryClient
註解。這會在 META-INF/spring.factories
中尋找 DiscoveryClient
和 ReactiveDiscoveryClient
介面的實作。探索用戶端的實作會將配置類別新增到 org.springframework.cloud.client.discovery.EnableDiscoveryClient
金鑰下的 spring.factories
。DiscoveryClient
實作的範例包括 Spring Cloud Netflix Eureka、Spring Cloud Consul Discovery 和 Spring Cloud Zookeeper Discovery。
依預設,Spring Cloud 將同時提供阻塞式和反應式服務發現用戶端。您可以透過設定 spring.cloud.discovery.blocking.enabled=false
或 spring.cloud.discovery.reactive.enabled=false
輕鬆停用阻塞式和/或反應式用戶端。若要完全停用服務發現,您只需要設定 spring.cloud.discovery.enabled=false
。
依預設,DiscoveryClient
的實作會使用遠端探索伺服器自動註冊本機 Spring Boot 伺服器。可以透過在 @EnableDiscoveryClient
中設定 autoRegister=false
來停用此行為。
不再需要 @EnableDiscoveryClient 。您可以將 DiscoveryClient 實作放在類別路徑中,以使 Spring Boot 應用程式向服務發現伺服器註冊。 |
2.1.1. 健康指示器
Commons 自動配置以下 Spring Boot 健康指示器。
DiscoveryClientHealthIndicator
此健康指示器基於目前註冊的 DiscoveryClient
實作。
-
若要完全停用,請設定
spring.cloud.discovery.client.health-indicator.enabled=false
。 -
若要停用描述欄位,請設定
spring.cloud.discovery.client.health-indicator.include-description=false
。否則,它可能會像彙總的HealthIndicator
的description
一樣浮出水面。 -
若要停用服務擷取,請設定
spring.cloud.discovery.client.health-indicator.use-services-query=false
。依預設,指示器會叫用用戶端的getServices
方法。在具有許多已註冊服務的部署中,在每次檢查期間擷取所有服務可能太過昂貴。這將跳過服務擷取,而是使用用戶端的probe
方法。
DiscoveryCompositeHealthContributor
此複合健康指示器基於所有已註冊的 DiscoveryHealthIndicator
bean。若要停用,請設定 spring.cloud.discovery.client.composite-indicator.enabled=false
。
2.1.2. 排序 DiscoveryClient
實例
DiscoveryClient
介面擴展了 Ordered
。當使用多個探索用戶端時,這很有用,因為它允許您定義傳回的探索用戶端的順序,類似於您可以排序 Spring 應用程式載入的 bean 的方式。依預設,任何 DiscoveryClient
的順序都設定為 0
。如果您想要為您的自訂 DiscoveryClient
實作設定不同的順序,您只需要覆寫 getOrder()
方法,使其傳回適合您設定的值。除此之外,您可以使用屬性來設定 Spring Cloud 提供的 DiscoveryClient
實作的順序,其中包括 ConsulDiscoveryClient
、EurekaDiscoveryClient
和 ZookeeperDiscoveryClient
。為了做到這一點,您只需要將 spring.cloud.{clientIdentifier}.discovery.order
(對於 Eureka,則為 eureka.client.order
)屬性設定為所需的值。
2.1.3. SimpleDiscoveryClient
如果類別路徑中沒有服務註冊中心支援的 DiscoveryClient
,則將使用 SimpleDiscoveryClient
實例,該實例使用屬性來取得有關服務和實例的資訊。
有關可用實例的資訊應透過以下格式的屬性傳遞:spring.cloud.discovery.client.simple.instances.service1[0].uri=http://s11:8080
,其中 spring.cloud.discovery.client.simple.instances
是通用前置詞,然後 service1
代表所討論服務的 ID,而 [0]
指示實例的索引編號(如範例中所示,索引從 0
開始),然後 uri
的值是實例可用的實際 URI。
2.2. ServiceRegistry
Commons 現在提供了一個 ServiceRegistry
介面,該介面提供諸如 register(Registration)
和 deregister(Registration)
之類的方法,這些方法可讓您提供自訂註冊的服務。Registration
是一個標記介面。
以下範例顯示了正在使用的 ServiceRegistry
@Configuration
@EnableDiscoveryClient(autoRegister=false)
public class MyConfiguration {
private ServiceRegistry registry;
public MyConfiguration(ServiceRegistry registry) {
this.registry = registry;
}
// called through some external process, such as an event or a custom actuator endpoint
public void register() {
Registration registration = constructRegistration();
this.registry.register(registration);
}
}
每個 ServiceRegistry
實作都有其自己的 Registry
實作。
-
ZookeeperRegistration
與ZookeeperServiceRegistry
一起使用 -
EurekaRegistration
與EurekaServiceRegistry
一起使用 -
ConsulRegistration
與ConsulServiceRegistry
一起使用
如果您正在使用 ServiceRegistry
介面,您將需要為您正在使用的 ServiceRegistry
實作傳遞正確的 Registry
實作。
2.2.1. ServiceRegistry 自動註冊
預設情況下,ServiceRegistry
實作會自動註冊正在執行的服務。若要停用此行為,您可以設定: * @EnableDiscoveryClient(autoRegister=false)
以永久停用自動註冊。 * spring.cloud.service-registry.auto-registration.enabled=false
以透過組態停用此行為。
ServiceRegistry 自動註冊事件
當服務自動註冊時,會觸發兩個事件。第一個事件稱為 InstancePreRegisteredEvent
,會在服務註冊前觸發。第二個事件稱為 InstanceRegisteredEvent
,會在服務註冊後觸發。您可以註冊 ApplicationListener
(s) 來監聽這些事件並做出反應。
如果 spring.cloud.service-registry.auto-registration.enabled 屬性設定為 false ,則不會觸發這些事件。 |
2.2.2. Service Registry Actuator 端點
Spring Cloud Commons 提供了 /serviceregistry
Actuator 端點。此端點依賴 Spring 應用程式上下文中的 Registration
Bean。使用 GET 呼叫 /serviceregistry
會傳回 Registration
的狀態。使用 POST 和包含 JSON 主體的相同端點會將目前 Registration
的狀態變更為新值。JSON 主體必須包含具有偏好值的 status
欄位。請參閱您使用的 ServiceRegistry
實作文件,以了解更新狀態時允許的值以及傳回的狀態值。例如,Eureka 支援的狀態為 UP
、DOWN
、OUT_OF_SERVICE
和 UNKNOWN
。
2.3. Spring RestTemplate 作為負載平衡器用戶端
您可以設定 RestTemplate
以使用負載平衡器用戶端。若要建立負載平衡的 RestTemplate
,請建立 RestTemplate
@Bean
並使用 @LoadBalanced
限定詞,如下列範例所示
@Configuration
public class MyConfiguration {
@LoadBalanced
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
public class MyClass {
@Autowired
private RestTemplate restTemplate;
public String doOtherStuff() {
String results = restTemplate.getForObject("http://stores/stores", String.class);
return results;
}
}
不再透過自動組態建立 RestTemplate Bean。個別應用程式必須自行建立。 |
URI 需要使用虛擬主機名稱(即服務名稱,而非主機名稱)。BlockingLoadBalancerClient
用於建立完整的實體位址。
若要使用負載平衡的 RestTemplate ,您的類別路徑中需要有負載平衡器實作。將 Spring Cloud LoadBalancer Starter 新增至您的專案以使用它。 |
2.4. Spring WebClient 作為負載平衡器用戶端
您可以設定 WebClient
以自動使用負載平衡器用戶端。若要建立負載平衡的 WebClient
,請建立 WebClient.Builder
@Bean
並使用 @LoadBalanced
限定詞,如下所示
@Configuration
public class MyConfiguration {
@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}
}
public class MyClass {
@Autowired
private WebClient.Builder webClientBuilder;
public Mono<String> doOtherStuff() {
return webClientBuilder.build().get().uri("http://stores/stores")
.retrieve().bodyToMono(String.class);
}
}
URI 需要使用虛擬主機名稱(即服務名稱,而非主機名稱)。Spring Cloud LoadBalancer 用於建立完整的實體位址。
如果您想要使用 @LoadBalanced WebClient.Builder ,您的類別路徑中需要有負載平衡器實作。我們建議您將 Spring Cloud LoadBalancer Starter 新增至您的專案。然後,底層會使用 ReactiveLoadBalancer 。 |
2.4.1. 重試失敗的請求
可以將負載平衡的 RestTemplate
設定為重試失敗的請求。預設情況下,此邏輯已停用。對於非反應式版本(使用 RestTemplate
),您可以將 Spring Retry 新增至應用程式的類別路徑來啟用它。對於反應式版本(使用 WebTestClient
),您需要設定 spring.cloud.loadbalancer.retry.enabled=true
。
如果您想要停用類別路徑中 Spring Retry 或 Reactive Retry 的重試邏輯,您可以設定 spring.cloud.loadbalancer.retry.enabled=false
。
對於非反應式實作,如果您想要在重試中實作 BackOffPolicy
,您需要建立 LoadBalancedRetryFactory
類型的 Bean 並覆寫 createBackOffPolicy()
方法。
對於反應式實作,您只需將 spring.cloud.loadbalancer.retry.backoff.enabled
設定為 false
即可停用它。
您可以設定
-
spring.cloud.loadbalancer.retry.maxRetriesOnSameServiceInstance
- 指示在相同的ServiceInstance
上應重試請求的次數(針對每個選取的執行個體分別計算) -
spring.cloud.loadbalancer.retry.maxRetriesOnNextServiceInstance
- 指示在新選取的ServiceInstance
上應重試請求的次數 -
spring.cloud.loadbalancer.retry.retryableStatusCodes
- 永遠重試失敗請求的狀態碼。
對於反應式實作,您可以額外設定:- spring.cloud.loadbalancer.retry.backoff.minBackoff
- 設定最小退避持續時間(預設為 5 毫秒)- spring.cloud.loadbalancer.retry.backoff.maxBackoff
- 設定最大退避持續時間(預設為毫秒的最大長整數值)- spring.cloud.loadbalancer.retry.backoff.jitter
- 設定用於計算每次呼叫的實際退避持續時間的抖動值(預設為 0.5)。
對於反應式實作,您也可以實作自己的 LoadBalancerRetryPolicy
,以便更詳細地控制負載平衡呼叫重試。
對於兩種實作,您也可以透過在 spring.cloud.loadbalancer.[serviceId].retry.retryable-exceptions
屬性下新增值清單來設定觸發回覆的例外狀況。如果您這樣做,我們會確保將 RetryableStatusCodeExceptions
新增至您提供的例外狀況清單,以便我們也可以針對可重試的狀態碼進行重試。如果您未透過屬性指定任何例外狀況,我們預設使用的例外狀況為 IOException
、TimeoutException
和 RetryableStatusCodeException
。您也可以將 spring.cloud.loadbalancer.[serviceId].retry.retry-on-all-exceptions
設定為 true
,以啟用在所有例外狀況上重試。
如果您將區塊實作與 Spring Retries 搭配使用,如果您想要保留先前版本的行為,請將 spring.cloud.loadbalancer.[serviceId].retry.retry-on-all-exceptions 設定為 true ,因為這曾經是區塊實作的預設模式。 |
可以個別設定個別的 Loadbalancer 用戶端,使用的屬性與上述相同,但前置詞為 spring.cloud.loadbalancer.clients.<clientId>.* ,其中 clientId 是負載平衡器的名稱。 |
對於負載平衡重試,預設情況下,我們會使用 RetryAwareServiceInstanceListSupplier 包裝 ServiceInstanceListSupplier Bean,以便從先前選取的執行個體中選取不同的執行個體(如果有的話)。您可以將 spring.cloud.loadbalancer.retry.avoidPreviousInstance 的值設定為 false 來停用此行為。 |
@Configuration
public class MyConfiguration {
@Bean
LoadBalancedRetryFactory retryFactory() {
return new LoadBalancedRetryFactory() {
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return new ExponentialBackOffPolicy();
}
};
}
}
如果您想要將一個或多個 RetryListener
實作新增至重試功能,您需要建立 LoadBalancedRetryListenerFactory
類型的 Bean,並傳回您想要用於指定服務的 RetryListener
陣列,如下列範例所示
@Configuration
public class MyConfiguration {
@Bean
LoadBalancedRetryListenerFactory retryListenerFactory() {
return new LoadBalancedRetryListenerFactory() {
@Override
public RetryListener[] createRetryListeners(String service) {
return new RetryListener[]{new RetryListener() {
@Override
public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
//TODO Do you business...
return true;
}
@Override
public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
//TODO Do you business...
}
@Override
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
//TODO Do you business...
}
}};
}
};
}
}
2.5. 多個 RestTemplate
物件
如果您想要不進行負載平衡的 RestTemplate
,請建立 RestTemplate
Bean 並注入它。若要存取負載平衡的 RestTemplate
,請在建立 @Bean
時使用 @LoadBalanced
限定詞,如下列範例所示
@Configuration
public class MyConfiguration {
@LoadBalanced
@Bean
RestTemplate loadBalanced() {
return new RestTemplate();
}
@Primary
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
public class MyClass {
@Autowired
private RestTemplate restTemplate;
@Autowired
@LoadBalanced
private RestTemplate loadBalanced;
public String doOtherStuff() {
return loadBalanced.getForObject("http://stores/stores", String.class);
}
public String doStuff() {
return restTemplate.getForObject("http://example.com", String.class);
}
}
請注意,在先前的範例中,在純 RestTemplate 宣告上使用 @Primary 註解,以消除非限定的 @Autowired 注入的歧義。 |
如果您看到類似 java.lang.IllegalArgumentException: Can not set org.springframework.web.client.RestTemplate field com.my.app.Foo.restTemplate to com.sun.proxy.$Proxy89 的錯誤,請嘗試注入 RestOperations 或設定 spring.aop.proxyTargetClass=true 。 |
2.6. 多個 WebClient 物件
如果您想要不進行負載平衡的 WebClient
,請建立 WebClient
Bean 並注入它。若要存取負載平衡的 WebClient
,請在建立 @Bean
時使用 @LoadBalanced
限定詞,如下列範例所示
@Configuration
public class MyConfiguration {
@LoadBalanced
@Bean
WebClient.Builder loadBalanced() {
return WebClient.builder();
}
@Primary
@Bean
WebClient.Builder webClient() {
return WebClient.builder();
}
}
public class MyClass {
@Autowired
private WebClient.Builder webClientBuilder;
@Autowired
@LoadBalanced
private WebClient.Builder loadBalanced;
public Mono<String> doOtherStuff() {
return loadBalanced.build().get().uri("http://stores/stores")
.retrieve().bodyToMono(String.class);
}
public Mono<String> doStuff() {
return webClientBuilder.build().get().uri("http://example.com")
.retrieve().bodyToMono(String.class);
}
}
2.7. Spring WebFlux WebClient
作為負載平衡器用戶端
Spring WebFlux 可以與反應式和非反應式 WebClient
組態搭配使用,如主題所述
2.7.1. Spring WebFlux WebClient
與 ReactorLoadBalancerExchangeFilterFunction
您可以設定 WebClient
以使用 ReactiveLoadBalancer
。如果您將 Spring Cloud LoadBalancer Starter 新增至您的專案,且 spring-webflux
在類別路徑中,則會自動組態 ReactorLoadBalancerExchangeFilterFunction
。下列範例顯示如何設定 WebClient
以使用反應式負載平衡器
public class MyClass {
@Autowired
private ReactorLoadBalancerExchangeFilterFunction lbFunction;
public Mono<String> doOtherStuff() {
return WebClient.builder().baseUrl("http://stores")
.filter(lbFunction)
.build()
.get()
.uri("/stores")
.retrieve()
.bodyToMono(String.class);
}
}
URI 需要使用虛擬主機名稱(即服務名稱,而非主機名稱)。ReactorLoadBalancer
用於建立完整的實體位址。
2.7.2. Spring WebFlux WebClient
與非反應式負載平衡器用戶端
如果 spring-webflux
在類別路徑中,則會自動組態 LoadBalancerExchangeFilterFunction
。但是請注意,這在底層使用非反應式用戶端。下列範例顯示如何設定 WebClient
以使用負載平衡器
public class MyClass {
@Autowired
private LoadBalancerExchangeFilterFunction lbFunction;
public Mono<String> doOtherStuff() {
return WebClient.builder().baseUrl("http://stores")
.filter(lbFunction)
.build()
.get()
.uri("/stores")
.retrieve()
.bodyToMono(String.class);
}
}
URI 需要使用虛擬主機名稱(即服務名稱,而非主機名稱)。LoadBalancerClient
用於建立完整的實體位址。
警告:此方法現在已淘汰。我們建議您改用 WebFlux 與反應式負載平衡器。
2.8. 忽略網路介面
有時,忽略某些已命名的網路介面很有用,以便可以將它們從服務探索註冊中排除(例如,在 Docker 容器中執行時)。可以設定規則運算式清單,以導致忽略所需的網路介面。下列組態會忽略 docker0
介面和所有以 veth
開頭的介面
spring: cloud: inetutils: ignoredInterfaces: - docker0 - veth.*
您也可以強制僅使用指定的網路位址,方法是使用規則運算式清單,如下列範例所示
spring: cloud: inetutils: preferredNetworks: - 192.168 - 10.0
您也可以強制僅使用站點本機位址,如下列範例所示
spring: cloud: inetutils: useOnlySiteLocalInterfaces: true
請參閱 Inet4Address.html.isSiteLocalAddress(),以取得有關站點本機位址構成的更多詳細資訊。
2.9. HTTP 用戶端工廠
Spring Cloud Commons 提供了用於建立 Apache HTTP 用戶端 (ApacheHttpClientFactory
) 和 OK HTTP 用戶端 (OkHttpClientFactory
) 的 Bean。只有當 OK HTTP jar 在類別路徑中時,才會建立 OkHttpClientFactory
Bean。此外,Spring Cloud Commons 也提供了用於建立這兩個用戶端使用的連線管理器的 Bean:Apache HTTP 用戶端的 ApacheHttpClientConnectionManagerFactory
和 OK HTTP 用戶端的 OkHttpClientConnectionPoolFactory
。如果您想要自訂下游專案中 HTTP 用戶端的建立方式,您可以提供這些 Bean 的自訂實作。此外,如果您提供 HttpClientBuilder
或 OkHttpClient.Builder
類型的 Bean,預設工廠會使用這些建構器作為傳回給下游專案的建構器的基礎。您也可以將 spring.cloud.httpclientfactories.apache.enabled
或 spring.cloud.httpclientfactories.ok.enabled
設定為 false
,以停用這些 Bean 的建立。
2.10. 已啟用的功能
Spring Cloud Commons 提供了 /features
Actuator 端點。此端點會傳回類別路徑上可用的功能,以及它們是否已啟用。傳回的資訊包括功能類型、名稱、版本和供應商。
2.10.1. 功能類型
有兩種「功能」類型:抽象功能和具名功能。
抽象功能是指定義了介面或抽象類別,且實作會建立的功能,例如 DiscoveryClient
、LoadBalancerClient
或 LockService
。抽象類別或介面用於在上下文中尋找該類型的 Bean。顯示的版本為 bean.getClass().getPackage().getImplementationVersion()
。
具名功能是指沒有實作特定類別的功能。這些功能包括「斷路器」、「API 閘道」、「Spring Cloud Bus」等。這些功能需要名稱和 Bean 類型。
2.10.2. 宣告功能
任何模組都可以宣告任意數量的 HasFeature
Bean,如下列範例所示
@Bean
public HasFeatures commonsFeatures() {
return HasFeatures.abstractFeatures(DiscoveryClient.class, LoadBalancerClient.class);
}
@Bean
public HasFeatures consulFeatures() {
return HasFeatures.namedFeatures(
new NamedFeature("Spring Cloud Bus", ConsulBusAutoConfiguration.class),
new NamedFeature("Circuit Breaker", HystrixCommandAspect.class));
}
@Bean
HasFeatures localFeatures() {
return HasFeatures.builder()
.abstractFeature(Something.class)
.namedFeature(new NamedFeature("Some Other Feature", Someother.class))
.abstractFeature(Somethingelse.class)
.build();
}
這些 Bean 中的每一個都應放入適當保護的 @Configuration
中。
2.11. Spring Cloud 相容性驗證
由於某些使用者在設定 Spring Cloud 應用程式時遇到問題,我們決定新增相容性驗證機制。如果目前的設定與 Spring Cloud 需求不相容,它會中斷,並顯示報告,說明確切的問題。
目前,我們會驗證新增至類別路徑的 Spring Boot 版本。
報告範例
*************************** APPLICATION FAILED TO START *************************** Description: Your project setup is incompatible with our requirements due to following reasons: - Spring Boot [2.1.0.RELEASE] is not compatible with this Spring Cloud release train Action: Consider applying the following actions: - Change Spring Boot version to one of the following versions [1.2.x, 1.3.x] . You can find the latest Spring Boot versions here [https://spring.dev.org.tw/projects/spring-boot#learn]. If you want to learn more about the Spring Cloud Release train compatibility, you can visit this page [https://spring.dev.org.tw/projects/spring-cloud#overview] and check the [Release Trains] section.
為了停用此功能,請將 spring.cloud.compatibility-verifier.enabled
設定為 false
。如果您想要覆寫相容的 Spring Boot 版本,只需使用逗號分隔的相容 Spring Boot 版本清單設定 spring.cloud.compatibility-verifier.compatible-boot-versions
屬性即可。
3. Spring Cloud LoadBalancer
Spring Cloud 提供了自己的用戶端負載平衡器抽象和實作。對於負載平衡機制,已新增 ReactiveLoadBalancer
介面,並為其提供了循環配置資源 (Round-Robin) 和隨機實作。為了取得要選取的執行個體,使用了反應式 ServiceInstanceListSupplier
。目前,我們支援 ServiceInstanceListSupplier
的服務探索型實作,它使用類別路徑中可用的 Discovery Client 從服務探索擷取可用的執行個體。
可以將 spring.cloud.loadbalancer.enabled 的值設定為 false 來停用 Spring Cloud LoadBalancer。 |
3.1. 負載平衡器上下文的預先載入
Spring Cloud LoadBalancer 為每個服務 ID 建立個別的 Spring 子上下文。預設情況下,這些上下文會在第一次負載平衡服務 ID 的請求時延遲初始化。
您可以選擇預先載入這些上下文。為了做到這一點,請指定您想要使用 spring.cloud-loadbalancer.eager-load.clients
屬性進行預先載入的服務 ID。
3.2. 在負載平衡演算法之間切換
預設使用的 ReactiveLoadBalancer
實作是 RoundRobinLoadBalancer
。若要切換到不同的實作,無論是針對選定的服務還是所有服務,您都可以使用自訂 LoadBalancer 組態機制。
例如,可以透過 @LoadBalancerClient
註解傳遞下列組態,以切換為使用 RandomLoadBalancer
public class CustomLoadBalancerConfiguration {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(loadBalancerClientFactory
.getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
}
您作為 @LoadBalancerClient 或 @LoadBalancerClients 組態引數傳遞的類別不應使用 @Configuration 註解,也不應位於元件掃描範圍之外。 |
3.3. Spring Cloud LoadBalancer 整合
為了方便使用 Spring Cloud LoadBalancer,我們提供了可與 WebClient
搭配使用的 ReactorLoadBalancerExchangeFilterFunction
和可與 RestTemplate
搭配使用的 BlockingLoadBalancerClient
。您可以在以下章節中查看更多資訊和使用範例
3.4. Spring Cloud LoadBalancer 快取
除了每次必須選擇執行個體時都透過 DiscoveryClient
擷取執行個體的基本 ServiceInstanceListSupplier
實作之外,我們還提供了兩種快取實作。
3.4.1. Caffeine 支援的 LoadBalancer 快取實作
如果您的類別路徑中有 com.github.ben-manes.caffeine:caffeine
,則會使用基於 Caffeine 的實作。請參閱 LoadBalancerCacheConfiguration 章節,以了解如何設定它。
如果您使用 Caffeine,您也可以透過在 spring.cloud.loadbalancer.cache.caffeine.spec
屬性中傳遞您自己的 Caffeine Specification 來覆寫 LoadBalancer 的預設 Caffeine 快取設定。
警告:傳遞您自己的 Caffeine 規格將覆寫任何其他 LoadBalancerCache 設定,包括 一般 LoadBalancer 快取組態 欄位,例如 ttl
和 capacity
。
3.4.2. 預設 LoadBalancer 快取實作
如果您的類別路徑中沒有 Caffeine,則會使用 DefaultLoadBalancerCache
,它會自動隨附 spring-cloud-starter-loadbalancer
。請參閱 LoadBalancerCacheConfiguration 章節,以了解如何設定它。
若要使用 Caffeine 而不是預設快取,請將 com.github.ben-manes.caffeine:caffeine 相依性新增至類別路徑。 |
3.4.3. LoadBalancer 快取組態
您可以設定自己的 ttl
值(寫入後經過的時間,超過此時間後應使項目過期),表示為 Duration
,方法是傳遞符合 Spring Boot String
到 Duration
轉換器語法 的 String
作為 spring.cloud.loadbalancer.cache.ttl
屬性的值。您也可以透過設定 spring.cloud.loadbalancer.cache.capacity
屬性的值來設定自己的 LoadBalancer 快取初始容量。
預設設定包括 ttl
設定為 35 秒,預設 initialCapacity
為 256
。
您也可以將 spring.cloud.loadbalancer.cache.enabled
的值設定為 false
,完全停用 LoadBalancer 快取。
雖然基本的非快取實作對於原型設計和測試很有用,但它的效率遠低於快取版本,因此我們建議始終在生產中使用快取版本。如果快取已由 DiscoveryClient 實作完成,例如 EurekaDiscoveryClient ,則應停用負載平衡器快取以防止雙重快取。 |
當您建立自己的組態時,如果您使用 CachingServiceInstanceListSupplier ,請確保將其放置在階層中緊接在透過網路擷取執行個體的供應器之後,例如 DiscoveryClientServiceInstanceListSupplier ,在任何其他篩選供應器之前。 |
3.5. 加權負載平衡
若要啟用加權負載平衡,我們提供了 WeightedServiceInstanceListSupplier
。我們使用 WeightFunction
來計算每個執行個體的權重。預設情況下,我們會嘗試從中繼資料映射讀取和剖析權重(鍵為 weight
)。
如果未在中繼資料映射中指定權重,則我們會將此執行個體的權重預設為 1。
您可以透過將 spring.cloud.loadbalancer.configurations
的值設定為 weighted
或提供您自己的 ServiceInstanceListSupplier
Bean 來設定它,例如
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withWeighted()
.withCaching()
.build(context);
}
}
您也可以透過提供 WeightFunction 來客製化權重計算邏輯。 |
您可以使用此範例組態,使所有執行個體都具有隨機權重
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withWeighted(instance -> ThreadLocalRandom.current().nextInt(1, 101))
.withCaching()
.build(context);
}
}
3.6. 基於區域的負載平衡
若要啟用基於區域的負載平衡,我們提供了 ZonePreferenceServiceInstanceListSupplier
。我們使用 DiscoveryClient
特定的 zone
組態(例如,eureka.instance.metadata-map.zone
)來選取用戶端嘗試篩選可用服務執行個體的區域。
您也可以透過設定 spring.cloud.loadbalancer.zone 屬性的值來覆寫 DiscoveryClient 特定的區域設定。 |
目前,只有 Eureka Discovery Client 已檢測到可設定 LoadBalancer 區域。對於其他探索用戶端,請設定 spring.cloud.loadbalancer.zone 屬性。更多檢測即將推出。 |
若要判斷擷取的 ServiceInstance 的區域,我們會檢查其中繼資料映射中 "zone" 鍵下的值。 |
ZonePreferenceServiceInstanceListSupplier
會篩選擷取的執行個體,並且僅傳回相同區域內的執行個體。如果區域為 null
或相同區域內沒有執行個體,則會傳回所有擷取的執行個體。
為了使用基於區域的負載平衡方法,您必須在自訂組態中實例化 ZonePreferenceServiceInstanceListSupplier
Bean。
我們使用委派來處理 ServiceInstanceListSupplier
Bean。我們建議使用 DiscoveryClientServiceInstanceListSupplier
委派,使用 CachingServiceInstanceListSupplier
包裝它以利用LoadBalancer 快取機制,然後將產生的 Bean 傳遞到 ZonePreferenceServiceInstanceListSupplier
的建構函式中。
您可以使用此範例組態來設定它
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withCaching()
.withZonePreference()
.build(context);
}
}
3.7. LoadBalancer 的執行個體健康檢查
可以啟用 LoadBalancer 的排程健康檢查。為此提供了 HealthCheckServiceInstanceListSupplier
。它會定期驗證委派 ServiceInstanceListSupplier
提供的執行個體是否仍然存活,並且僅傳回健康的執行個體,除非沒有健康的執行個體 - 然後它會傳回所有擷取的執行個體。
當使用 SimpleDiscoveryClient 時,此機制特別有用。對於由實際服務登錄支援的用戶端,沒有必要使用它,因為在查詢外部 ServiceDiscovery 後,我們已經取得健康的執行個體。 |
對於每個服務的執行個體數量較少的設定,也建議使用此供應器,以避免在失敗的執行個體上重試呼叫。 |
如果使用任何服務探索支援的供應器,通常不需要新增此健康檢查機制,因為我們直接從服務登錄擷取執行個體的健康狀態。 |
HealthCheckServiceInstanceListSupplier 依賴於委派 Flux 提供的已更新執行個體。在極少數情況下,當您想要使用不重新整理執行個體的委派時,即使執行個體清單可能會變更(例如我們提供的 DiscoveryClientServiceInstanceListSupplier ),您可以將 spring.cloud.loadbalancer.health-check.refetch-instances 設定為 true ,以讓 HealthCheckServiceInstanceListSupplier 重新整理執行個體清單。然後,您也可以透過修改 spring.cloud.loadbalancer.health-check.refetch-instances-interval 的值來調整重新擷取間隔,並選擇將 spring.cloud.loadbalancer.health-check.repeat-health-check 設定為 false 來停用額外的健康檢查重複,因為每次執行個體重新擷取也會觸發健康檢查。 |
HealthCheckServiceInstanceListSupplier
使用前置詞為 spring.cloud.loadbalancer.health-check
的屬性。您可以設定排程器的 initialDelay
和 interval
。您可以透過設定 spring.cloud.loadbalancer.health-check.path.default
屬性的值來設定健康檢查 URL 的預設路徑。您也可以透過設定 spring.cloud.loadbalancer.health-check.path.[SERVICE_ID]
屬性的值來設定任何給定服務的特定值,並將 [SERVICE_ID]
替換為服務的正確 ID。如果未指定 [SERVICE_ID]
,則預設使用 /actuator/health
。如果將 [SERVICE_ID]
設定為 null
或空值,則不會執行健康檢查。您也可以透過設定 spring.cloud.loadbalancer.health-check.port
的值來設定健康檢查請求的自訂埠。如果未設定任何埠,則使用請求服務在服務執行個體上可用的埠。
如果您依賴預設路徑 (/actuator/health ),請確保將 spring-boot-starter-actuator 新增至協作者的相依性,除非您計劃自行新增此端點。 |
預設情況下,healthCheckFlux 將在每個已擷取的存活 ServiceInstance 上發出訊號。您可以透過將 spring.cloud.loadbalancer.health-check.update-results-list 的值設定為 false 來修改此行為。如果此屬性設定為 false ,則整個存活執行個體序列會先收集到清單中,然後才發出訊號,這可確保 Flux 不會在屬性中設定的健康檢查間隔之間發出值。 |
為了使用健康檢查排程器方法,您必須在自訂組態中實例化 HealthCheckServiceInstanceListSupplier
Bean。
我們使用委派來處理 ServiceInstanceListSupplier
Bean。我們建議在 HealthCheckServiceInstanceListSupplier
的建構函式中傳遞 DiscoveryClientServiceInstanceListSupplier
委派。
您可以使用此範例組態來設定它
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withHealthChecks()
.build(context);
}
}
對於非反應式堆疊,請使用 withBlockingHealthChecks() 建立此供應器。您也可以傳遞您自己的 WebClient 或 RestTemplate 執行個體,以用於檢查。 |
HealthCheckServiceInstanceListSupplier 具有基於 Reactor Flux replay() 的自己的快取機制。因此,如果正在使用它,您可能想要跳過使用 CachingServiceInstanceListSupplier 包裝該供應器。 |
當您建立自己的組態、HealthCheckServiceInstanceListSupplier 時,請確保將其放置在階層中緊接在透過網路擷取執行個體的供應器之後,例如 DiscoveryClientServiceInstanceListSupplier ,在任何其他篩選供應器之前。 |
3.8. LoadBalancer 的相同執行個體偏好
您可以設定 LoadBalancer,使其偏好先前選取的執行個體(如果該執行個體可用)。
為此,您需要使用 SameInstancePreferenceServiceInstanceListSupplier
。您可以透過將 spring.cloud.loadbalancer.configurations
的值設定為 same-instance-preference
或提供您自己的 ServiceInstanceListSupplier
Bean 來設定它 — 例如
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withSameInstancePreference()
.build(context);
}
}
這也是 Zookeeper StickyRule 的替代方案。 |
3.9. LoadBalancer 的基於請求的黏性工作階段
您可以設定 LoadBalancer,使其偏好在請求 Cookie 中提供 instanceId
的執行個體。如果請求是透過 ClientRequestContext
或 ServerHttpRequestContext
傳遞到 LoadBalancer 的,我們目前支援此功能,SC LoadBalancer 交換篩選器函式和篩選器會使用這些內容。
為此,您需要使用 RequestBasedStickySessionServiceInstanceListSupplier
。您可以透過將 spring.cloud.loadbalancer.configurations
的值設定為 request-based-sticky-session
或提供您自己的 ServiceInstanceListSupplier
Bean 來設定它 — 例如
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withRequestBasedStickySession()
.build(context);
}
}
對於此功能,最好在將請求轉發之前更新選取的服務執行個體(如果原始請求 Cookie 中的執行個體不可用,則它可能與該執行個體不同)。若要執行此操作,請將 spring.cloud.loadbalancer.sticky-session.add-service-instance-cookie
的值設定為 true
。
預設情況下,Cookie 的名稱為 sc-lb-instance-id
。您可以透過變更 spring.cloud.loadbalancer.instance-id-cookie-name
屬性的值來修改它。
目前,WebClient 支援負載平衡的此功能。 |
3.10. Spring Cloud LoadBalancer 提示
Spring Cloud LoadBalancer 可讓您設定 String
提示,這些提示會傳遞至 Request
物件中的 LoadBalancer,稍後可以在可以處理它們的 ReactiveLoadBalancer
實作中使用。
您可以透過設定 spring.cloud.loadbalancer.hint.default
屬性的值來設定所有服務的預設提示。您也可以透過設定 spring.cloud.loadbalancer.hint.[SERVICE_ID]
屬性的值來設定任何給定服務的特定值,並將 [SERVICE_ID]
替換為服務的正確 ID。如果使用者未設定提示,則使用 default
。
3.11. 基於提示的負載平衡
我們也提供了 HintBasedServiceInstanceListSupplier
,它是用於基於提示的執行個體選取的 ServiceInstanceListSupplier
實作。
HintBasedServiceInstanceListSupplier
會檢查提示請求標頭(預設標頭名稱為 X-SC-LB-Hint
,但您可以透過變更 spring.cloud.loadbalancer.hint-header-name
屬性的值來修改它),如果找到提示請求標頭,則會使用標頭中傳遞的提示值來篩選服務執行個體。
如果未新增任何提示標頭,HintBasedServiceInstanceListSupplier
會使用來自屬性的提示值來篩選服務執行個體。
如果未設定任何提示(無論是透過標頭還是透過屬性),則會傳回委派提供的所有服務執行個體。
篩選時,HintBasedServiceInstanceListSupplier
會尋找在其 metadataMap
中的 hint
鍵下設定了相符值的服務執行個體。如果找不到相符的執行個體,則會傳回委派提供的所有執行個體。
您可以使用下列範例組態來設定它
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withCaching()
.withHints()
.build(context);
}
}
3.12. 轉換負載平衡的 HTTP 請求
您可以使用選取的 ServiceInstance
來轉換負載平衡的 HTTP 請求。
對於 RestTemplate
,您需要實作和定義 LoadBalancerRequestTransformer
,如下所示
@Bean
public LoadBalancerRequestTransformer transformer() {
return new LoadBalancerRequestTransformer() {
@Override
public HttpRequest transformRequest(HttpRequest request, ServiceInstance instance) {
return new HttpRequestWrapper(request) {
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.putAll(super.getHeaders());
headers.add("X-InstanceId", instance.getInstanceId());
return headers;
}
};
}
};
}
對於 WebClient
,您需要實作和定義 LoadBalancerClientRequestTransformer
,如下所示
@Bean
public LoadBalancerClientRequestTransformer transformer() {
return new LoadBalancerClientRequestTransformer() {
@Override
public ClientRequest transformRequest(ClientRequest request, ServiceInstance instance) {
return ClientRequest.from(request)
.header("X-InstanceId", instance.getInstanceId())
.build();
}
};
}
如果定義了多個轉換器,則會按照 Bean 定義的順序套用它們。或者,您可以使用 LoadBalancerRequestTransformer.DEFAULT_ORDER
或 LoadBalancerClientRequestTransformer.DEFAULT_ORDER
來指定順序。
3.13. Spring Cloud LoadBalancer Starter
我們也提供了一個 Starter,可讓您輕鬆地在 Spring Boot 應用程式中新增 Spring Cloud LoadBalancer。若要使用它,只需將 org.springframework.cloud:spring-cloud-starter-loadbalancer
新增至組建檔案中的 Spring Cloud 相依性即可。
Spring Cloud LoadBalancer Starter 包括 Spring Boot 快取 和 Evictor。 |
3.14. 傳遞您自己的 Spring Cloud LoadBalancer 組態
您也可以使用 @LoadBalancerClient
註解來傳遞您自己的負載平衡器用戶端組態,方法是傳遞負載平衡器用戶端的名稱和組態類別,如下所示
@Configuration
@LoadBalancerClient(value = "stores", configuration = CustomLoadBalancerConfiguration.class)
public class MyConfiguration {
@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}
}
為了讓您更輕鬆地處理自己的 LoadBalancer 組態,我們已將 builder() 方法新增至 ServiceInstanceListSupplier 類別。 |
您也可以使用我們預定義的替代組態,取代預設組態。方法是將 spring.cloud.loadbalancer.configurations 屬性的值設定為 zone-preference ,以使用帶有快取的 ZonePreferenceServiceInstanceListSupplier ;或設定為 health-check ,以使用帶有快取的 HealthCheckServiceInstanceListSupplier 。 |
您可以使用此功能來實例化 ServiceInstanceListSupplier
或 ReactorLoadBalancer
的不同實作,無論是由您撰寫,或是由我們提供作為替代方案(例如 ZonePreferenceServiceInstanceListSupplier
),以覆寫預設設定。
您可以這裡看到自訂組態的範例。
註解 value 引數(在上述範例中為 stores )指定了我們應該使用給定的自訂組態將請求傳送到的服務的服務 ID。 |
您也可以透過 @LoadBalancerClients
註解傳遞多個組態(適用於多個負載平衡器用戶端),如下列範例所示
@Configuration
@LoadBalancerClients({@LoadBalancerClient(value = "stores", configuration = StoresLoadBalancerClientConfiguration.class), @LoadBalancerClient(value = "customers", configuration = CustomersLoadBalancerClientConfiguration.class)})
public class MyConfiguration {
@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}
}
您作為 @LoadBalancerClient 或 @LoadBalancerClients 組態引數傳遞的類別不應使用 @Configuration 註解,也不應位於元件掃描範圍之外。 |
當您建立自己的組態時,如果您使用 CachingServiceInstanceListSupplier 或 HealthCheckServiceInstanceListSupplier ,請務必使用其中一個,而不是兩者都使用,並確保將其放置在階層結構中,緊接在透過網路檢索實例的供應商之後,例如,DiscoveryClientServiceInstanceListSupplier ,在任何其他篩選供應商之前。 |
3.15. Spring Cloud LoadBalancer 生命周期
使用自訂 LoadBalancer 組態註冊可能有用的其中一種 Bean 類型是 LoadBalancerLifecycle
。
LoadBalancerLifecycle
Bean 提供了回呼方法,名為 onStart(Request<RC> request)
、onStartRequest(Request<RC> request, Response<T> lbResponse)
和 onComplete(CompletionContext<RES, T, RC> completionContext)
,您應該實作這些方法以指定在負載平衡之前和之後應採取的動作。
onStart(Request<RC> request)
接受 Request
物件作為參數。它包含用於選擇適當實例的資料,包括下游用戶端請求和提示。onStartRequest
也接受 Request
物件,並額外接受 Response<T>
物件作為參數。另一方面,CompletionContext
物件會提供給 onComplete(CompletionContext<RES, T, RC> completionContext)
方法。它包含 LoadBalancer Response
,包括選定的服務實例、針對該服務實例執行的請求的 Status
,以及(如果可用)傳回給下游用戶端的響應,以及(如果發生例外狀況)相應的 Throwable
。
supports(Class requestContextClass, Class responseClass, Class serverTypeClass)
方法可用於判斷所討論的處理器是否處理所提供類型的物件。如果使用者未覆寫,則傳回 true
。
在前面的方法呼叫中,RC 表示 RequestContext 類型,RES 表示用戶端響應類型,而 T 表示傳回的伺服器類型。 |
3.16. Spring Cloud LoadBalancer 統計資訊
我們提供一個名為 MicrometerStatsLoadBalancerLifecycle
的 LoadBalancerLifecycle
Bean,它使用 Micrometer 為負載平衡的呼叫提供統計資訊。
為了將此 Bean 新增至您的應用程式內容,請將 spring.cloud.loadbalancer.stats.micrometer.enabled
的值設定為 true
,並備妥 MeterRegistry
(例如,透過將 Spring Boot Actuator 新增至您的專案)。
MicrometerStatsLoadBalancerLifecycle
在 MeterRegistry
中註冊以下儀表
-
loadbalancer.requests.active
:一個量規,可讓您監控任何服務實例目前作用中的請求數量(服務實例資料可透過標籤取得); -
loadbalancer.requests.success
:一個計時器,用於測量任何已結束並將響應傳遞給底層用戶端的負載平衡請求的執行時間; -
loadbalancer.requests.failed
:一個計時器,用於測量任何以例外狀況結束的負載平衡請求的執行時間; -
loadbalancer.requests.discard
:一個計數器,用於測量已捨棄的負載平衡請求的數量,即負載平衡器未檢索到要在其上執行請求的服務實例的請求。
有關服務實例、請求資料和響應資料的其他資訊,會在可用時透過標籤新增至指標。
對於某些實作,例如 BlockingLoadBalancerClient ,請求和響應資料可能不可用,因為我們從引數建立泛型類型,可能無法判斷類型和讀取資料。 |
當至少為給定儀表新增一筆記錄時,儀表會在登錄檔中註冊。 |
您可以進一步組態這些指標的行為(例如,新增發布百分位數和直方圖),方法是新增 MeterFilters 。 |
3.17. 組態個別 LoadBalancerClient
可以針對個別 Loadbalancer 用戶端進行組態,使用不同的前綴 spring.cloud.loadbalancer.clients.<clientId>.
,其中 clientId
是負載平衡器的名稱。預設組態值可以在 spring.cloud.loadbalancer.
命名空間中設定,並將與用戶端特定值合併,用戶端特定值優先。
spring: cloud: loadbalancer: health-check: initial-delay: 1s clients: myclient: health-check: interval: 30s
上述範例將產生合併的 health-check @ConfigurationProperties
物件,其中 initial-delay=1s
和 interval=30s
。
每個用戶端組態屬性適用於大多數屬性,除了以下全域屬性外
-
spring.cloud.loadbalancer.enabled
- 全域啟用或停用負載平衡 -
spring.cloud.loadbalancer.retry.enabled
- 全域啟用或停用負載平衡重試。如果您全域啟用它,您仍然可以使用用戶端前綴的屬性停用特定用戶端的重試,但反之則不然 -
spring.cloud.loadbalancer.cache.enabled
- 全域啟用或停用 LoadBalancer 快取。如果您全域啟用它,您仍然可以透過建立不包含CachingServiceInstanceListSupplier
在ServiceInstanceListSupplier
委派階層中的自訂組態來停用特定用戶端的快取,但反之則不然。 -
spring.cloud.loadbalancer.stats.micrometer.enabled
- 全域啟用或停用 LoadBalancer Micrometer 指標
對於已經使用對應的屬性,您可以在不使用 clients 關鍵字的情況下,為每個用戶端指定不同的值(例如,hints 、health-check.path ),我們保留了該行為,以保持程式庫的向後相容性。它將在下一個主要版本中修改。 |
從 4.0.4 開始,我們在 LoadBalancerProperties 中引入了 callGetWithRequestOnDelegates 旗標。如果此旗標設定為 true ,ServiceInstanceListSupplier#get(Request request) 方法將被實作為在可從 DelegatingServiceInstanceListSupplier 指派的類別中呼叫 delegate.get(request) ,這些類別尚未實作該方法,但不包括 CachingServiceInstanceListSupplier 和 HealthCheckServiceInstanceListSupplier ,後者應直接放置在實例供應商階層中,緊接在透過網路執行實例檢索的供應商之後,在執行任何基於請求的篩選之前。對於 4.0.x ,旗標預設設定為 false ,但是,自 4.1.0 起,它將預設設定為 true 。 |
3.18. AOT 和原生映像檔支援
自 4.0.0
以來,Spring Cloud LoadBalancer 支援 Spring AOT 轉換和原生映像檔。但是,要使用此功能,您需要明確定義您的 LoadBalancerClient
服務 ID。您可以透過使用 @LoadBalancerClient
註解的 value
或 name
屬性,或作為 spring.cloud.loadbalancer.eager-load.clients
屬性的值來執行此操作。
4. Spring Cloud 斷路器
4.1. 簡介
Spring Cloud 斷路器提供了跨不同斷路器實作的抽象層。它提供了一致的 API,可在您的應用程式中使用,讓您(開發人員)選擇最適合您的應用程式需求的斷路器實作。
4.2. 核心概念
若要在您的程式碼中建立斷路器,您可以使用 CircuitBreakerFactory
API。當您在類別路徑中包含 Spring Cloud Circuit Breaker starter 時,會自動為您建立一個實作此 API 的 Bean。以下範例顯示如何使用此 API 的簡單範例
@Service
public static class DemoControllerService {
private RestTemplate rest;
private CircuitBreakerFactory cbFactory;
public DemoControllerService(RestTemplate rest, CircuitBreakerFactory cbFactory) {
this.rest = rest;
this.cbFactory = cbFactory;
}
public String slow() {
return cbFactory.create("slow").run(() -> rest.getForObject("/slow", String.class), throwable -> "fallback");
}
}
CircuitBreakerFactory.create
API 建立一個名為 CircuitBreaker
的類別的實例。run
方法接受 Supplier
和 Function
。Supplier
是您要包裝在斷路器中的程式碼。Function
是在斷路器跳脫時執行的回退。函數會傳遞導致回退被觸發的 Throwable
。如果您不想提供回退,您可以選擇性地排除回退。
4.2.1. 反應式程式碼中的斷路器
如果 Project Reactor 在類別路徑中,您也可以為您的反應式程式碼使用 ReactiveCircuitBreakerFactory
。以下範例顯示如何執行此操作
@Service
public static class DemoControllerService {
private ReactiveCircuitBreakerFactory cbFactory;
private WebClient webClient;
public DemoControllerService(WebClient webClient, ReactiveCircuitBreakerFactory cbFactory) {
this.webClient = webClient;
this.cbFactory = cbFactory;
}
public Mono<String> slow() {
return webClient.get().uri("/slow").retrieve().bodyToMono(String.class).transform(
it -> cbFactory.create("slow").run(it, throwable -> return Mono.just("fallback")));
}
}
ReactiveCircuitBreakerFactory.create
API 建立一個名為 ReactiveCircuitBreaker
的類別的實例。run
方法接受 Mono
或 Flux
並將其包裝在斷路器中。您可以選擇性地設定回退 Function
,如果斷路器跳脫,將會呼叫該函數,並傳遞導致失敗的 Throwable
。
4.3. 組態
您可以透過建立 Customizer
類型的 Bean 來組態您的斷路器。Customizer
介面具有一個單一方法(名為 customize
),該方法接受要自訂的 Object
。
如需有關如何自訂給定實作的詳細資訊,請參閱以下文件
某些 CircuitBreaker
實作(例如 Resilience4JCircuitBreaker
)每次呼叫 CircuitBreaker#run
時都會呼叫 customize
方法。這可能效率不高。在這種情況下,您可以使用 CircuitBreaker#once
方法。這在多次呼叫 customize
沒有意義的情況下很有用,例如,在使用 Resilience4j 的事件的情況下。
以下範例顯示每個 io.github.resilience4j.circuitbreaker.CircuitBreaker
使用事件的方式。
Customizer.once(circuitBreaker -> {
circuitBreaker.getEventPublisher()
.onStateTransition(event -> log.info("{}: {}", event.getCircuitBreakerName(), event.getStateTransition()));
}, CircuitBreaker::getName)
5. CachedRandomPropertySource
Spring Cloud Context 提供了一個 PropertySource
,它根據金鑰快取隨機值。除了快取功能之外,它的運作方式與 Spring Boot 的 RandomValuePropertySource
相同。如果您想要一個即使在 Spring 應用程式內容重新啟動後仍然一致的隨機值,則此隨機值可能很有用。屬性值的格式為 cachedrandom.[yourkey].[type]
,其中 yourkey
是快取中的金鑰。type
值可以是 Spring Boot 的 RandomValuePropertySource
支援的任何類型。
myrandom=${cachedrandom.appname.value}
6. 安全性
6.1. 單一登入
所有 OAuth2 SSO 和資源伺服器功能都在 1.3 版中移至 Spring Boot。您可以在Spring Boot 使用者指南中找到文件。 |
6.1.1. 用戶端權杖轉發
如果您的應用程式是面向使用者的 OAuth2 用戶端(即已宣告 @EnableOAuth2Sso
或 @EnableOAuth2Client
),則它在 Spring Boot 的請求範圍內具有 OAuth2ClientContext
。您可以從此內容和自動裝配的 OAuth2ProtectedResourceDetails
建立您自己的 OAuth2RestTemplate
,然後內容將始終向下游轉發存取權杖,如果存取權杖過期,也會自動重新整理存取權杖。(這些是 Spring Security 和 Spring Boot 的功能。)
6.1.2. 資源伺服器權杖轉發
如果您的應用程式具有 @EnableResourceServer
,您可能希望將傳入的權杖向下游轉發到其他服務。如果您使用 RestTemplate
聯絡下游服務,那麼這只是如何使用正確的內容建立範本的問題。
如果您的服務使用 UserInfoTokenServices
來驗證傳入的權杖(即它正在使用 security.oauth2.user-info-uri
組態),那麼您可以簡單地使用自動裝配的 OAuth2ClientContext
建立 OAuth2RestTemplate
(它會在到達後端程式碼之前由驗證程序填入)。等效地(使用 Spring Boot 1.4),您可以注入 UserInfoRestTemplateFactory
並在您的組態中取得其 OAuth2RestTemplate
。例如
@Bean
public OAuth2RestTemplate restTemplate(UserInfoRestTemplateFactory factory) {
return factory.getUserInfoRestTemplate();
}
然後,此 rest template 將具有與驗證篩選器使用的相同的 OAuth2ClientContext
(請求範圍),因此您可以使用它來傳送具有相同存取權杖的請求。
如果您的應用程式未使用 UserInfoTokenServices
,但仍然是用戶端(即它宣告 @EnableOAuth2Client
或 @EnableOAuth2Sso
),那麼使用 Spring Security Cloud,使用者從 @Autowired
OAuth2Context
建立的任何 OAuth2RestOperations
也將轉發權杖。此功能預設以 MVC 處理常式攔截器實作,因此僅適用於 Spring MVC。如果您未使用 MVC,您可以使用自訂篩選器或 AOP 攔截器包裝 AccessTokenContextRelay
以提供相同的功能。
以下是一個基本範例,顯示了在其他地方建立的自動裝配 rest template 的用法(「foo.com」是一個資源伺服器,接受與周圍應用程式相同的權杖)
@Autowired
private OAuth2RestOperations restTemplate;
@RequestMapping("/relay")
public String relay() {
ResponseEntity<String> response =
restTemplate.getForEntity("https://foo.com/bar", String.class);
return "Success! (" + response.getBody() + ")";
}
如果您不想轉發權杖(這是一個有效的選擇,因為您可能想要以您自己的身分而不是傳送權杖的用戶端身分行事),那麼您只需要建立您自己的 OAuth2Context
,而不是自動裝配預設的權杖。
Feign 用戶端也將擷取使用 OAuth2ClientContext
的攔截器(如果可用),因此它們也應該在 RestTemplate
會轉發權杖的任何地方執行權杖轉發。