雲原生是一種應用程式開發風格,鼓勵輕鬆採用持續交付和價值驅動開發領域的最佳實踐。一個相關的原則是構建12 要素應用程式,其中開發實踐與交付和運營目標保持一致——例如,透過使用宣告式程式設計以及管理和監控。Spring Cloud 透過多種特定方式促進了這些開發風格。起點是分散式系統中所有元件都需要輕鬆訪問的一組功能。

其中許多功能由 Spring Cloud 構建於其上的 Spring Boot 涵蓋。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 Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files。有關更多資訊,請參閱以下連結

將檔案解壓到您使用的任何 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),從而使引導和主上下文的外部配置清晰地分開。以下清單顯示了一個示例

示例 1. bootstrap.yml
spring:
  application:
    name: foo
  cloud:
    config:
      uri: ${SPRING_CONFIG_URI:https://:8888}

如果您的應用程式需要來自伺服器的任何應用程式特定配置,最好設定 spring.application.name(在 bootstrap.ymlapplication.yml 中)。要將屬性 spring.application.name 用作應用程式的上下文 ID,您必須在 bootstrap.[properties | yml] 中設定它。

如果要檢索特定配置檔案配置,還應該在 bootstrap.[properties | yml] 中設定 spring.profiles.active

您可以透過設定 spring.cloud.bootstrap.enabled=false(例如,在系統屬性中)完全停用引導過程。

1.2. 應用程式上下文層次結構

如果您從 SpringApplicationSpringApplicationBuilder 構建應用程式上下文,引導上下文將作為父上下文新增到該上下文。Spring 的一個特性是子上下文從其父上下文繼承屬性源和配置檔案,因此“主”應用程式上下文包含額外的屬性源,與不使用 Spring Cloud Config 構建相同上下文相比。額外的屬性源是

  • “bootstrap”:如果在引導上下文中找到任何 PropertySourceLocators 並且它們具有非空屬性,則會出現一個可選的 CompositePropertySource,優先順序很高。一個示例是來自 Spring Cloud Config Server 的屬性。有關如何自定義此屬性源內容的資訊,請參閱“自定義引導屬性源”。

在 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.yml 中的任何資料,bootstrap.yml 的優先順序非常低,但可用於設定預設值。

您可以透過設定建立的任何 ApplicationContext 的父上下文來擴充套件上下文層次結構——例如,透過使用其自己的介面或 SpringApplicationBuilder 方便方法(parent()child()sibling())。引導上下文是您自己建立的最資深祖先的父上下文。層次結構中的每個上下文都有自己的“引導”(可能為空)屬性源,以避免意外地將值從父級提升到其後代。如果存在配置伺服器,則層次結構中的每個上下文也可以(原則上)具有不同的 spring.application.name,因此具有不同的遠端屬性源。正常的 Spring 應用程式上下文行為規則適用於屬性解析:子上下文中的屬性按名稱和屬性源名稱覆蓋父上下文中的屬性。(如果子上下文具有與父上下文同名的屬性源,則父上下文中的值不包含在子上下文)。

請注意,SpringApplicationBuilder 允許您在整個層次結構中共享一個 Environment,但這並非預設設定。因此,兄弟上下文(特別是)不需要具有相同的配置檔案或屬性源,即使它們可能與其父上下文共享共同值。

1.3. 更改引導屬性的位置

bootstrap.yml(或 .properties)的位置可以透過設定 spring.cloud.bootstrap.name(預設:bootstrap)、spring.cloud.bootstrap.location(預設:空)或 spring.cloud.bootstrap.additional-location(預設:空)來指定——例如,在系統屬性中。

這些屬性的行為與同名的 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 Server)。預設情況下,它們不能在本地被覆蓋。如果您希望應用程式使用自己的系統屬性或配置檔案覆蓋遠端屬性,則遠端屬性源必須透過設定 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 都將新增到其中。

1.6. 自定義引導屬性源

引導過程新增的外部配置的預設屬性源是 Spring Cloud Config Server,但您可以透過向引導上下文(透過 spring.factories)新增 PropertySourceLocator 型別的 bean 來新增其他源。例如,您可以從不同的伺服器或資料庫插入其他屬性。

例如,考慮以下自定義定位器

@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 Server 屬性源定位器中所做的那樣)。

如果您建立了一個包含此類的 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 中。具體來說,任何依賴於這些 bean 的東西都不能依賴於它們在啟動重新整理時得到更新,除非它本身在 @RefreshScope 中。在這種情況下,它會在重新整理時重建,並且其依賴項會被重新注入。屆時,它們將從重新整理的 @Configuration 重新初始化。
刪除配置值然後執行重新整理不會更新配置值的存在。必須存在配置屬性才能在重新整理後更新值。如果您依賴於應用程式中某個值的存在,您可能希望將邏輯切換為依賴其不存在。另一種選擇是依賴值的更改而不是應用程式配置中不存在。
Spring AOT 轉換和本地映象不支援上下文重新整理。對於 AOT 和本地映象,需要將 spring.cloud.refresh.enabled 設定為 false

1.10. 加密和解密

Spring Cloud 有一個 Environment 預處理器,用於在本地解密屬性值。它遵循與 Spring Cloud Config Server 相同的規則,並透過 encrypt.* 進行相同的外部配置。因此,您可以使用 {cipher}* 形式的加密值,只要存在有效金鑰,它們就會在主應用程式上下文獲取 Environment 設定之前解密。要在應用程式中使用加密功能,您需要在類路徑中包含 Spring Security RSA(Maven 座標:org.springframework.security:spring-security-rsa),並且您的 JVM 中還需要完整的 JCE 擴充套件。

如果您由於“Illegal key size”而收到異常,並且您使用的是 Sun 的 JDK,則需要安裝 Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files。有關更多資訊,請參閱以下連結

將檔案解壓到您使用的任何 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 中查詢 DiscoveryClientReactiveDiscoveryClient 介面的實現。發現客戶端的實現將配置類新增到 spring.factories 中,鍵名為 org.springframework.cloud.client.discovery.EnableDiscoveryClientDiscoveryClient 實現的示例包括 Spring Cloud Netflix EurekaSpring Cloud Consul DiscoverySpring Cloud Zookeeper Discovery

Spring Cloud 預設將提供阻塞式和響應式服務發現客戶端。您可以透過設定 spring.cloud.discovery.blocking.enabled=falsespring.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。否則,它可能會作為彙總的 HealthIndicatordescription 冒泡。

  • 要停用服務檢索,請設定 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 實現的順序,其中包括 ConsulDiscoveryClientEurekaDiscoveryClientZookeeperDiscoveryClient。為此,您只需將 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 實現。

  • ZookeeperRegistrationZookeeperServiceRegistry 一起使用

  • EurekaRegistrationEurekaServiceRegistry 一起使用

  • ConsulRegistrationConsulServiceRegistry 一起使用

如果您正在使用 ServiceRegistry 介面,則需要為您使用的 ServiceRegistry 實現傳遞正確的 Registry 實現。

2.2.1. ServiceRegistry 自動註冊

預設情況下,ServiceRegistry 實現會自動註冊正在執行的服務。要停用此行為,您可以設定:* @EnableDiscoveryClient(autoRegister=false) 以永久停用自動註冊。* spring.cloud.service-registry.auto-registration.enabled=false 以透過配置停用此行為。

ServiceRegistry 自動註冊事件

服務自動註冊時將觸發兩個事件。第一個事件名為 InstancePreRegisteredEvent,在服務註冊之前觸發。第二個事件名為 InstanceRegisteredEvent,在服務註冊之後觸發。您可以註冊一個或多個 ApplicationListener 來監聽和響應這些事件。

如果將 spring.cloud.service-registry.auto-registration.enabled 屬性設定為 false,則不會觸發這些事件。

2.2.2. 服務登錄檔 Actuator 端點

Spring Cloud Commons 提供了一個 /serviceregistry Actuator 端點。此端點依賴於 Spring 應用程式上下文中的 Registration bean。使用 GET 呼叫 /serviceregistry 返回 Registration 的狀態。使用 POST 到相同端點並帶有 JSON 主體將當前 Registration 的狀態更改為新值。JSON 主體必須包含帶有首選值的 status 欄位。請參閱您使用的 ServiceRegistry 實現的文件,瞭解更新狀態時允許的值以及返回的狀態值。例如,Eureka 支援的狀態包括 UPDOWNOUT_OF_SERVICEUNKNOWN

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 啟動器 新增到您的專案中即可使用它。

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 啟動器 新增到您的專案中。然後,底層使用 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 新增到您提供的異常列表中,以便我們還重試可重試的狀態碼。如果您沒有透過屬性指定任何異常,我們預設使用的異常是 IOExceptionTimeoutExceptionRetryableStatusCodeException。您還可以透過將 spring.cloud.loadbalancer.[serviceId].retry.retry-on-all-exceptions 設定為 true 來啟用對所有異常的重試。

如果您將阻塞實現與 Spring Retries 一起使用,如果您想保留以前版本的行為,請將 spring.cloud.loadbalancer.[serviceId].retry.retry-on-all-exceptions 設定為 true,因為這曾經是阻塞實現的預設模式。
可以單獨配置各個負載均衡器客戶端,其屬性與上述相同,但字首為 spring.cloud.loadbalancer.clients.<clientId>.*,其中 clientId 是負載均衡器的名稱。
對於負載均衡重試,預設情況下,我們將 ServiceInstanceListSupplier bean 包裝在 RetryAwareServiceInstanceListSupplier 中,以便在可用時選擇與之前選擇的例項不同的例項。您可以透過將 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. 帶有 ReactorLoadBalancerExchangeFilterFunction 的 Spring WebFlux WebClient

您可以配置 WebClient 以使用 ReactiveLoadBalancer。如果將 Spring Cloud LoadBalancer 啟動器 新增到您的專案中,並且 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 開頭的介面

示例 2. application.yml
spring:
  cloud:
    inetutils:
      ignoredInterfaces:
        - docker0
        - veth.*

您還可以透過使用正則表示式列表強制僅使用指定的網路地址,如下例所示

示例 3. bootstrap.yml
spring:
  cloud:
    inetutils:
      preferredNetworks:
        - 192.168
        - 10.0

您還可以強制僅使用站點本地地址,如下例所示

示例 4. application.yml
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 的實現。此外,如果您提供 HttpClientBuilderOkHttpClient.Builder 型別的 bean,則預設工廠將這些構建器用作返回給下游專案的構建器的基礎。您還可以透過將 spring.cloud.httpclientfactories.apache.enabledspring.cloud.httpclientfactories.ok.enabled 設定為 false 來停用這些 bean 的建立。

2.10. 啟用功能

Spring Cloud Commons 提供了一個 /features Actuator 端點。此端點返回類路徑中可用的功能以及它們是否已啟用。返回的資訊包括功能型別、名稱、版本和供應商。

2.10.1. 功能型別

“功能”有兩種型別:抽象功能和命名功能。

抽象功能是指定義了介面或抽象類並由實現建立的功能,例如 DiscoveryClientLoadBalancerClientLockService。抽象類或介面用於在上下文中查詢該型別的 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://springframework.tw/projects/spring-boot#learn].
If you want to learn more about the Spring Cloud Release train compatibility, you can visit this page [https://springframework.tw/projects/spring-cloud#overview] and check the [Release Trains] section.

要停用此功能,請將 spring.cloud.compatibility-verifier.enabled 設定為 false。如果您想覆蓋相容的 Spring Boot 版本,只需將 spring.cloud.compatibility-verifier.compatible-boot-versions 屬性設定為以逗號分隔的相容 Spring Boot 版本列表。

3. Spring Cloud LoadBalancer

Spring Cloud 提供自己的客戶端負載均衡抽象和實現。對於負載均衡機制,已新增 ReactiveLoadBalancer 介面,併為其提供了基於輪詢隨機的實現。為了從響應式 ServiceInstanceListSupplier 中選擇例項。目前我們支援基於服務發現的 ServiceInstanceListSupplier 實現,該實現使用類路徑中可用的 Discovery Client 從服務發現中檢索可用例項。

可以透過將 spring.cloud.loadbalancer.enabled 的值設定為 false 來停用 Spring Cloud LoadBalancer。

3.1. LoadBalancer 上下文的預載入

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 規範 來覆蓋 LoadBalancer 的預設 Caffeine 快取設定。

警告:傳遞您自己的 Caffeine 規範將覆蓋任何其他 LoadBalancerCache 設定,包括 通用 LoadBalancer 快取配置 欄位,例如 ttlcapacity

3.4.2. 預設 LoadBalancer 快取實現

如果類路徑中沒有 Caffeine,則將使用 DefaultLoadBalancerCache,它會自動隨 spring-cloud-starter-loadbalancer 提供。有關如何配置它的資訊,請參閱 LoadBalancerCacheConfiguration 部分。

要使用 Caffeine 而不是預設快取,請將 com.github.ben-manes.caffeine:caffeine 依賴項新增到類路徑。

3.4.3. LoadBalancer 快取配置

您可以透過將符合 Spring Boot StringDuration 轉換器語法String 作為 spring.cloud.loadbalancer.cache.ttl 屬性的值來設定自己的 ttl 值(寫入後條目應過期的時間),以 Duration 表示。您還可以透過設定 spring.cloud.loadbalancer.cache.capacity 屬性的值來設定自己的 LoadBalancer 快取初始容量。

預設設定包括 ttl 設定為 35 秒,預設 initialCapacity256

您還可以透過將 spring.cloud.loadbalancer.cache.enabled 的值設定為 false 來完全停用負載均衡器快取。

儘管基本的、非快取的實現對於原型設計和測試很有用,但它的效率遠低於快取版本,因此我們建議在生產中始終使用快取版本。如果 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 為字首的屬性。您可以為排程程式設定 initialDelayinterval。您可以透過設定 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() 建立此供應商。您還可以傳遞自己的 WebClientRestTemplate 例項用於檢查。
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 的例項。我們目前支援此功能,前提是請求透過 ClientRequestContextServerHttpRequestContext 傳遞給 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 會查詢在其 metadataMaphint 鍵下設定了匹配值的服務例項。如果未找到匹配的例項,則返回委託提供的所有例項。

您可以使用以下示例配置進行設定

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_ORDERLoadBalancerClientRequestTransformer.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 CachingEvictor

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 配置,我們在 ServiceInstanceListSupplier 類中添加了一個 builder() 方法。
您還可以透過將 spring.cloud.loadbalancer.configurations 屬性的值設定為 zone-preference 以使用帶快取的 ZonePreferenceServiceInstanceListSupplier,或設定為 health-check 以使用帶快取的 HealthCheckServiceInstanceListSupplier,來使用我們預定義的替代配置,而不是預設配置。

您可以使用此功能例項化 ServiceInstanceListSupplierReactorLoadBalancer 的不同實現,無論是您編寫的還是我們提供的替代方案(例如 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 進行註解,或者應超出元件掃描範圍。
當您建立自己的配置時,如果您使用 CachingServiceInstanceListSupplierHealthCheckServiceInstanceListSupplier,請確保只使用其中一個,而不是兩者都用,並確保將其直接放置在從網路檢索例項的供應商(例如 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 統計資訊

我們提供了一個名為 MicrometerStatsLoadBalancerLifecycleLoadBalancerLifecycle bean,它使用 Micrometer 為負載均衡呼叫提供統計資訊。

為了將此 bean 新增到您的應用程式上下文中,請將 spring.cloud.loadbalancer.stats.micrometer.enabled 的值設定為 true 並提供一個可用的 MeterRegistry(例如,透過將Spring Boot Actuator 新增到您的專案中)。

MicrometerStatsLoadBalancerLifecycleMeterRegistry 中註冊以下計量器

  • loadbalancer.requests.active:一個儀表,允許您監控任何服務例項當前活動的請求數(服務例項資料可透過標籤獲取);

  • loadbalancer.requests.success:一個計時器,測量任何已成功向底層客戶端傳遞響應的負載均衡請求的執行時間;

  • loadbalancer.requests.failed:一個計時器,測量任何已因異常而結束的負載均衡請求的執行時間;

  • loadbalancer.requests.discard:一個計數器,測量被丟棄的負載均衡請求數,即 LoadBalancer 未檢索到要執行請求的服務例項的請求。

有關服務例項、請求資料和響應資料的附加資訊在可用時透過標籤新增到指標中。

對於某些實現,例如 BlockingLoadBalancerClient,請求和響應資料可能不可用,因為我們從引數中建立泛型型別,可能無法確定型別和讀取資料。
當為給定計量器新增至少一條記錄時,計量器會在登錄檔中註冊。
您可以透過新增 MeterFilters 來進一步配置這些指標的行為(例如,新增釋出百分位數和直方圖)。

3.17. 配置單個 LoadBalancerClient

單個 LoadBalancer 客戶端可以使用不同的字首 spring.cloud.loadbalancer.clients.<clientId>. 獨立配置,其中 clientId 是負載均衡器的名稱。預設配置值可以在 spring.cloud.loadbalancer. 名稱空間中設定,並將與客戶端特定值合併,客戶端特定值優先。

示例 5. application.yml
spring:
  cloud:
    loadbalancer:
      health-check:
        initial-delay: 1s
      clients:
        myclient:
          health-check:
            interval: 30s

上述示例將生成一個合併的健康檢查 @ConfigurationProperties 物件,其中 initial-delay=1sinterval=30s

按客戶端配置屬性適用於大多數屬性,除了以下全域性屬性:

  • spring.cloud.loadbalancer.enabled - 全域性啟用或停用負載均衡

  • spring.cloud.loadbalancer.retry.enabled - 全域性啟用或停用負載均衡重試。如果您全域性啟用它,您仍然可以使用 client 字首屬性為特定客戶端停用重試,但不能反過來。

  • spring.cloud.loadbalancer.cache.enabled - 全域性啟用或停用 LoadBalancer 快取。如果您全域性啟用它,您仍然可以透過建立不包含 CachingServiceInstanceListSupplierServiceInstanceListSupplier 委託層次結構中的自定義配置來為特定客戶端停用快取,但不能反過來。

  • 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),這些類尚未實現該方法,但排除 CachingServiceInstanceListSupplierHealthCheckServiceInstanceListSupplier,它們應該直接放置在執行網路例項檢索的供應商之後,在進行任何基於請求的過濾之前。對於 4.0.x,該標誌預設設定為 false,但是,從 4.1.0 開始,它將預設設定為 true

3.18. AOT 和原生映象支援

4.0.0 開始,Spring Cloud LoadBalancer 支援 Spring AOT 轉換和原生映象。但是,要使用此功能,您需要明確定義您的 LoadBalancerClient 服務 ID。您可以透過使用 @LoadBalancerClient 註解的 valuename 屬性,或作為 spring.cloud.loadbalancer.eager-load.clients 屬性的值來實現。

4. Spring Cloud 斷路器

4.1. 簡介

Spring Cloud 斷路器提供了跨不同斷路器實現的抽象。它提供了在您的應用程式中使用的統一 API,讓您(開發者)選擇最適合您應用程式需求的斷路器實現。

4.1.1. 支援的實現

Spring Cloud 支援以下斷路器實現

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 和一個 FunctionSupplier 是您將包裝在斷路器中的程式碼。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 方法接受一個 MonoFlux 並將其包裝在斷路器中。您可以選擇提供一個備用 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。例如

MyConfiguration.java
@Bean
public OAuth2RestTemplate restTemplate(UserInfoRestTemplateFactory factory) {
    return factory.getUserInfoRestTemplate();
}

這個 rest 模板將擁有與身份驗證過濾器使用的相同的 OAuth2ClientContext(請求範圍),因此您可以使用它來發送帶有相同訪問令牌的請求。

如果您的應用程式沒有使用 UserInfoTokenServices,但仍然是一個客戶端(即它聲明瞭 @EnableOAuth2Client@EnableOAuth2S2o),那麼藉助 Spring Security Cloud,使用者從 @Autowired OAuth2Context 建立的任何 OAuth2RestOperations 也會轉發令牌。此功能預設作為 MVC 處理程式攔截器實現,因此它僅適用於 Spring MVC。如果您不使用 MVC,可以使用自定義過濾器或 AOP 攔截器包裝 AccessTokenContextRelay 來提供相同的功能。

這是一個基本示例,展示了使用在其他地方建立的自動裝配的 rest 模板("foo.com" 是一個接受與周圍應用程式相同令牌的資源伺服器)

MyController.java
@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 一樣在任何地方進行令牌中繼。

7. 配置屬性

要檢視所有 Spring Cloud Commons 相關配置屬性的列表,請檢視附錄頁

© . This site is unofficial and not affiliated with VMware.