Spring Cloud OpenFeign 功能

宣告式 REST Client: Feign

Feign 是一個宣告式的網路服務客戶端。它讓撰寫網路服務客戶端變得更容易。要使用 Feign,請建立一個介面並加上註解。它具有可插拔的註解支援,包括 Feign 註解和 JAX-RS 註解。Feign 也支援可插拔的編碼器和解碼器。Spring Cloud 新增了對 Spring MVC 註解的支援,以及使用與 Spring Web 中預設相同的 HttpMessageConverters。Spring Cloud 整合了 Eureka、Spring Cloud CircuitBreaker 以及 Spring Cloud LoadBalancer,在使用 Feign 時提供負載平衡的 http 客戶端。

如何包含 Feign

若要在您的專案中包含 Feign,請使用群組 org.springframework.cloud 和 artifact id spring-cloud-starter-openfeign 的 starter。請參閱 Spring Cloud 專案頁面,以瞭解如何使用目前的 Spring Cloud Release Train 設定您的建置系統的詳細資訊。

Spring Boot 應用程式範例

@SpringBootApplication
@EnableFeignClients
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}

}
StoreClient.java
@FeignClient("stores")
public interface StoreClient {
	@RequestMapping(method = RequestMethod.GET, value = "/stores")
	List<Store> getStores();

	@GetMapping("/stores")
	Page<Store> getStores(Pageable pageable);

	@PostMapping(value = "/stores/{storeId}", consumes = "application/json",
				params = "mode=upsert")
	Store update(@PathVariable("storeId") Long storeId, Store store);

	@DeleteMapping("/stores/{storeId:\\d+}")
	void delete(@PathVariable Long storeId);
}

@FeignClient 註解中,字串值(上面的 "stores")是任意的客戶端名稱,用於建立 Spring Cloud LoadBalancer client。您也可以使用 url 屬性(絕對值或僅主機名稱)指定 URL。應用程式內容中 bean 的名稱是介面的完整限定名稱。若要指定您自己的別名值,您可以使用 @FeignClient 註解的 qualifiers 值。

上面的負載平衡器客戶端會想要探索 "stores" 服務的實體位址。如果您的應用程式是 Eureka 客戶端,則它將在 Eureka 服務註冊表中解析服務。如果您不想使用 Eureka,您可以使用 SimpleDiscoveryClient 在您的外部組態中設定伺服器清單。

Spring Cloud OpenFeign 支援 Spring Cloud LoadBalancer 封鎖模式的所有可用功能。您可以在專案文件中閱讀有關它們的更多資訊。

若要在 @Configuration 註解類別上使用 @EnableFeignClients 註解,請確定指定客戶端的位置,例如:@EnableFeignClients(basePackages = "com.example.clients") 或明確列出它們:@EnableFeignClients(clients = InventoryServiceFeignClient.class)
由於 FactoryBean 物件可能會在應該發生的初始內容重新整理之前被實例化,而且 Spring Cloud OpenFeign Clients 的實例化會觸發內容重新整理,因此它們不應在 FactoryBean 類別中宣告。

屬性解析模式

在建立 Feign 客戶端 bean 時,我們會解析透過 @FeignClient 註解傳遞的值。從 4.x 開始,這些值會被積極地解析。對於大多數使用案例來說,這是一個好的解決方案,它也允許 AOT 支援。

如果您需要延遲解析屬性,請將 spring.cloud.openfeign.lazy-attributes-resolution 屬性值設定為 true

對於 Spring Cloud Contract 測試整合,應使用延遲屬性解析。

覆寫 Feign 預設值

Spring Cloud 的 Feign 支援中的核心概念是具名客戶端。每個 feign 客戶端都是組件集合的一部分,這些組件協同工作以按需聯絡遠端伺服器,並且該集合有一個名稱,您作為應用程式開發人員使用 @FeignClient 註解為其命名。Spring Cloud 使用 FeignClientsConfiguration 為每個具名客戶端按需建立一個新的集合作為 ApplicationContext。這包含(除其他外)feign.Decoderfeign.Encoderfeign.Contract。可以使用 @FeignClient 註解的 contextId 屬性來覆寫該集合的名稱。

Spring Cloud 讓您透過宣告額外組態(在 FeignClientsConfiguration 之上)並使用 @FeignClient 來完全控制 feign 客戶端。範例

@FeignClient(name = "stores", configuration = FooConfiguration.class)
public interface StoreClient {
	//..
}

在這種情況下,客戶端由 FeignClientsConfiguration 中已有的組件以及 FooConfiguration 中的任何組件組成(後者將覆寫前者)。

FooConfiguration 不需要使用 @Configuration 進行註解。但是,如果它是,請注意將其從任何 @ComponentScan 中排除,否則該組態將成為指定時 feign.Decoderfeign.Encoderfeign.Contract 等的預設來源。這可以透過將其放在與任何 @ComponentScan@SpringBootApplication 分開且不重疊的套件中來避免,或者可以在 @ComponentScan 中明確排除它。
除了變更 ApplicationContext 集合的名稱外,使用 @FeignClient 註解的 contextId 屬性也會覆寫客戶端名稱的別名,並將其用作為該客戶端建立的組態 bean 的名稱的一部分。
先前,使用 url 屬性不需要 name 屬性。現在需要使用 name

nameurl 屬性中支援佔位符。

@FeignClient(name = "${feign.name}", url = "${feign.url}")
public interface StoreClient {
	//..
}

Spring Cloud OpenFeign 預設為 feign 提供以下 bean (BeanType beanName: ClassName)

  • Decoder feignDecoder: ResponseEntityDecoder(它包裝了 SpringDecoder

  • Encoder feignEncoder: SpringEncoder

  • Logger feignLogger: Slf4jLogger

  • MicrometerObservationCapability micrometerObservationCapability: 如果 feign-micrometer 在類別路徑上且 ObservationRegistry 可用

  • MicrometerCapability micrometerCapability: 如果 feign-micrometer 在類別路徑上,MeterRegistry 可用且 ObservationRegistry 不可用

  • CachingCapability cachingCapability: 如果使用了 @EnableCaching 註解。可以透過 spring.cloud.openfeign.cache.enabled 停用。

  • Contract feignContract: SpringMvcContract

  • Feign.Builder feignBuilder: FeignCircuitBreaker.Builder

  • Client feignClient: 如果 Spring Cloud LoadBalancer 在類別路徑上,則使用 FeignBlockingLoadBalancerClient。如果它們都不在類別路徑上,則使用預設的 feign 客戶端。

spring-cloud-starter-openfeign 支援 spring-cloud-starter-loadbalancer。但是,由於它是可選的依賴項,如果您想使用它,則需要確保已將其新增到您的專案中。

若要使用 OkHttpClient 支援的 Feign 客戶端和 Http2Client Feign 客戶端,請確保您想要使用的客戶端在類別路徑上,並將 spring.cloud.openfeign.okhttp.enabledspring.cloud.openfeign.http2client.enabled 分別設定為 true

當涉及到 Apache HttpClient 5 支援的 Feign 客戶端時,只需確保 HttpClient 5 在類別路徑上就足夠了,但您仍然可以透過將 spring.cloud.openfeign.httpclient.hc5.enabled 設定為 false 來停用其用於 Feign Clients。您可以透過在使用 Apache HC5 時提供 org.apache.hc.client5.http.impl.classic.CloseableHttpClient 的 bean 來客製化 HTTP 客戶端。

您可以透過在 spring.cloud.openfeign.httpclient.xxx 屬性中設定值來進一步客製化 http 客戶端。僅以 httpclient 為前綴的那些屬性將適用於所有客戶端,以 httpclient.hc5 為前綴的那些屬性適用於 Apache HttpClient 5,以 httpclient.okhttp 為前綴的那些屬性適用於 OkHttpClient,以 httpclient.http2 為前綴的那些屬性適用於 Http2Client。您可以在附錄中找到您可以客製化的屬性的完整清單。如果您無法透過使用屬性來組態 Apache HttpClient 5,則有一個 HttpClientBuilderCustomizer 介面用於程式化組態。

從 Spring Cloud OpenFeign 4 開始,不再支援 Feign Apache HttpClient 4。我們建議改用 Apache HttpClient 5。

Spring Cloud OpenFeign 為 feign 提供以下 bean 作為預設值,但仍會從應用程式內容中尋找這些類型的 bean 以建立 feign 客戶端

  • Logger.Level

  • Retryer

  • ErrorDecoder

  • Request.Options

  • Collection<RequestInterceptor>

  • SetterFactory

  • QueryMapEncoder

  • Capability (預設提供 MicrometerObservationCapabilityCachingCapability)

預設情況下,會建立類型為 RetryerRetryer.NEVER_RETRY 的 bean,這將停用重試。請注意,此重試行為與 Feign 預設行為不同,後者會自動重試 IOExceptions,將其視為暫時性網路相關例外,以及從 ErrorDecoder 擲回的任何 RetryableException。

建立其中一種型別的 bean 並將其放在 @FeignClient 組態中(例如上面的 FooConfiguration)可讓您覆寫每個描述的 bean。範例

@Configuration
public class FooConfiguration {
	@Bean
	public Contract feignContract() {
		return new feign.Contract.Default();
	}

	@Bean
	public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
		return new BasicAuthRequestInterceptor("user", "password");
	}
}

這會將 SpringMvcContract 替換為 feign.Contract.Default,並將 RequestInterceptor 新增至 RequestInterceptor 的集合。

@FeignClient 也可以使用組態屬性進行組態。

application.yml

spring:
	cloud:
		openfeign:
			client:
				config:
					feignName:
                        url: http://remote-service.com
						connectTimeout: 5000
						readTimeout: 5000
						loggerLevel: full
						errorDecoder: com.example.SimpleErrorDecoder
						retryer: com.example.SimpleRetryer
						defaultQueryParameters:
							query: queryValue
						defaultRequestHeaders:
							header: headerValue
						requestInterceptors:
							- com.example.FooRequestInterceptor
							- com.example.BarRequestInterceptor
						responseInterceptor: com.example.BazResponseInterceptor
						dismiss404: false
						encoder: com.example.SimpleEncoder
						decoder: com.example.SimpleDecoder
						contract: com.example.SimpleContract
						capabilities:
							- com.example.FooCapability
							- com.example.BarCapability
						queryMapEncoder: com.example.SimpleQueryMapEncoder
						micrometer.enabled: false

在此範例中,feignName 指的是 @FeignClient value,它也與 @FeignClient name@FeignClient contextId 別名。在負載平衡情境中,它也對應於將用於檢索實例的伺服器應用程式的 serviceId。解碼器、重試器和其他指定的類別必須在 Spring 內容中具有 bean 或具有預設建構子。

預設組態可以在 @EnableFeignClients 屬性 defaultConfiguration 中指定,方式與上面描述的類似。不同之處在於此組態將應用於所有 feign 客戶端。

如果您偏好使用組態屬性來組態所有 @FeignClient,您可以建立具有 default feign 名稱的組態屬性。

您可以使用 spring.cloud.openfeign.client.config.feignName.defaultQueryParametersspring.cloud.openfeign.client.config.feignName.defaultRequestHeaders 來指定將與名為 feignName 的客戶端的每個請求一起傳送的查詢參數和標頭。

application.yml

spring:
	cloud:
		openfeign:
			client:
				config:
					default:
						connectTimeout: 5000
						readTimeout: 5000
						loggerLevel: basic

如果我們同時建立 @Configuration bean 和組態屬性,則組態屬性將獲勝。它將覆寫 @Configuration 值。但是,如果您想將優先順序變更為 @Configuration,您可以將 spring.cloud.openfeign.client.default-to-properties 變更為 false

如果我們想要建立多個具有相同名稱或 url 的 feign 客戶端,以便它們指向相同的伺服器,但每個客戶端都具有不同的自訂組態,那麼我們必須使用 @FeignClientcontextId 屬性,以避免這些組態 bean 的名稱衝突。

@FeignClient(contextId = "fooClient", name = "stores", configuration = FooConfiguration.class)
public interface FooClient {
	//..
}
@FeignClient(contextId = "barClient", name = "stores", configuration = BarConfiguration.class)
public interface BarClient {
	//..
}

也可以將 FeignClient 組態為不繼承父內容中的 bean。您可以透過覆寫 FeignClientConfigurer bean 中的 inheritParentConfiguration() 以傳回 false 來執行此操作

@Configuration
public class CustomConfiguration {
	@Bean
	public FeignClientConfigurer feignClientConfigurer() {
		return new FeignClientConfigurer() {
			@Override
			public boolean inheritParentConfiguration() {
				 return false;
			}
		};
	}
}
預設情況下,Feign 客戶端不會編碼斜線 / 字元。您可以透過將 spring.cloud.openfeign.client.decodeSlash 的值設定為 false 來變更此行為。

SpringEncoder 組態

在我們提供的 SpringEncoder 中,我們為二進位內容類型設定 null 字元集,為所有其他類型設定 UTF-8 字元集。

您可以修改此行為,改為從 Content-Type 標頭字元集衍生字元集,方法是將 spring.cloud.openfeign.encoder.charset-from-content-type 的值設定為 true

逾時處理

我們可以在預設客戶端和具名客戶端上組態逾時。OpenFeign 使用兩個逾時參數

  • connectTimeout 可防止呼叫端因伺服器處理時間過長而封鎖。

  • readTimeout 從連線建立時開始應用,並在傳回回應花費時間過長時觸發。

如果伺服器未執行或不可用,則封包會導致連線被拒絕。通訊以錯誤訊息或回退結束。如果 connectTimeout 設定得非常低,則可能會在之前發生這種情況。執行查找和接收此類封包所花費的時間會導致此延遲的很大一部分。它可能會根據涉及 DNS 查找的遠端主機而變化。

手動建立 Feign 客戶端

在某些情況下,可能需要以使用上述方法不可能的方式自訂您的 Feign Clients。在這種情況下,您可以使用 Feign Builder API 建立 Clients。以下範例建立兩個具有相同介面的 Feign Clients,但每個都使用單獨的請求攔截器進行組態。

@Import(FeignClientsConfiguration.class)
class FooController {

	private FooClient fooClient;

	private FooClient adminClient;

	@Autowired
	public FooController(Client client, Encoder encoder, Decoder decoder, Contract contract, MicrometerObservationCapability micrometerObservationCapability) {
		this.fooClient = Feign.builder().client(client)
				.encoder(encoder)
				.decoder(decoder)
				.contract(contract)
				.addCapability(micrometerObservationCapability)
				.requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
				.target(FooClient.class, "https://PROD-SVC");

		this.adminClient = Feign.builder().client(client)
				.encoder(encoder)
				.decoder(decoder)
				.contract(contract)
				.addCapability(micrometerObservationCapability)
				.requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
				.target(FooClient.class, "https://PROD-SVC");
	}
}
在上面的範例中,FeignClientsConfiguration.class 是 Spring Cloud OpenFeign 提供的預設組態。
PROD-SVC 是 Clients 將向其發出請求的服務的名稱。
Feign Contract 物件定義了介面上哪些註解和值是有效的。自動裝配的 Contract bean 提供了對 SpringMVC 註解的支援,而不是預設的 Feign 原生註解。

您也可以使用 Builder 來組態 FeignClient 不繼承父內容中的 bean。您可以透過在 Builder 上覆寫呼叫 inheritParentContext(false) 來執行此操作。

Feign Spring Cloud CircuitBreaker 支援

如果 Spring Cloud CircuitBreaker 在類別路徑上且 spring.cloud.openfeign.circuitbreaker.enabled=true,則 Feign 將使用斷路器包裝所有方法。

若要針對每個客戶端停用 Spring Cloud CircuitBreaker 支援,請使用 "prototype" 範圍建立 vanilla Feign.Builder,例如

@Configuration
public class FooConfiguration {
	@Bean
	@Scope("prototype")
	public Feign.Builder feignBuilder() {
		return Feign.builder();
	}
}

斷路器名稱遵循此模式 <feignClientClassName>#<calledMethod>(<parameterTypes>)。當使用 FooClient 介面呼叫 @FeignClient 且呼叫的介面方法沒有參數時,bar 則斷路器名稱將為 FooClient#bar()

從 2020.0.2 開始,斷路器名稱模式已從 <feignClientName>_<calledMethod> 變更。使用 2020.0.4 中引入的 CircuitBreakerNameResolver,斷路器名稱可以保留舊模式。

提供 CircuitBreakerNameResolver 的 bean,您可以變更斷路器名稱模式。

@Configuration
public class FooConfiguration {
	@Bean
	public CircuitBreakerNameResolver circuitBreakerNameResolver() {
		return (String feignClientName, Target<?> target, Method method) -> feignClientName + "_" + method.getName();
	}
}

若要啟用 Spring Cloud CircuitBreaker 群組,請將 spring.cloud.openfeign.circuitbreaker.group.enabled 屬性設定為 true(預設為 false)。

使用組態屬性組態斷路器

您可以透過組態屬性組態斷路器。

例如,如果您有此 Feign 客戶端

@FeignClient(url = "http://localhost:8080")
public interface DemoClient {

    @GetMapping("demo")
    String getDemo();
}

您可以使用組態屬性透過執行以下操作來組態它

spring:
  cloud:
    openfeign:
      circuitbreaker:
        enabled: true
        alphanumeric-ids:
          enabled: true
resilience4j:
  circuitbreaker:
    instances:
      DemoClientgetDemo:
        minimumNumberOfCalls: 69
  timelimiter:
    instances:
      DemoClientgetDemo:
        timeoutDuration: 10s
如果您想要切換回 Spring Cloud 2022.0.0 之前使用的斷路器名稱,您可以將 spring.cloud.openfeign.circuitbreaker.alphanumeric-ids.enabled 設定為 false

Feign Spring Cloud CircuitBreaker 回退

Spring Cloud CircuitBreaker 支援回退的概念:當斷路器開啟或發生錯誤時執行的預設程式碼路徑。若要為給定的 @FeignClient 啟用回退,請將 fallback 屬性設定為實作回退的類別名稱。您還需要將您的實作宣告為 Spring bean。

@FeignClient(name = "test", url = "http://localhost:${server.port}/", fallback = Fallback.class)
protected interface TestClient {

	@GetMapping("/hello")
	Hello getHello();

	@GetMapping("/hellonotfound")
	String getException();

}

@Component
static class Fallback implements TestClient {

	@Override
	public Hello getHello() {
		throw new NoFallbackAvailableException("Boom!", new RuntimeException());
	}

	@Override
	public String getException() {
		return "Fixed response";
	}

}

如果需要存取導致回退觸發的原因,可以使用 @FeignClient 內的 fallbackFactory 屬性。

@FeignClient(name = "testClientWithFactory", url = "http://localhost:${server.port}/",
			fallbackFactory = TestFallbackFactory.class)
protected interface TestClientWithFactory {

	@GetMapping("/hello")
	Hello getHello();

	@GetMapping("/hellonotfound")
	String getException();

}

@Component
static class TestFallbackFactory implements FallbackFactory<FallbackWithFactory> {

	@Override
	public FallbackWithFactory create(Throwable cause) {
		return new FallbackWithFactory();
	}

}

static class FallbackWithFactory implements TestClientWithFactory {

	@Override
	public Hello getHello() {
		throw new NoFallbackAvailableException("Boom!", new RuntimeException());
	}

	@Override
	public String getException() {
		return "Fixed response";
	}

}

Feign 和 @Primary

將 Feign 與 Spring Cloud CircuitBreaker 回退一起使用時,ApplicationContext 中有多個相同類型的 bean。這將導致 @Autowired 無法運作,因為沒有正好一個 bean,或一個標記為 primary 的 bean。為了解決這個問題,Spring Cloud OpenFeign 將所有 Feign 實例標記為 @Primary,因此 Spring Framework 將知道要注入哪個 bean。在某些情況下,這可能不是理想的。若要關閉此行為,請將 @FeignClientprimary 屬性設定為 false。

@FeignClient(name = "hello", primary = false)
public interface HelloClient {
	// methods here
}

Feign 繼承支援

Feign 透過單一繼承介面支援樣板 api。這允許將常見操作分組到方便的基礎介面中。

UserService.java
public interface UserService {

	@GetMapping("/users/{id}")
	User getUser(@PathVariable("id") long id);
}
UserResource.java
@RestController
public class UserResource implements UserService {

}
UserClient.java
@FeignClient("users")
public interface UserClient extends UserService {

}
@FeignClient 介面不應在伺服器和客戶端之間共用,並且不再支援在類別層級使用 @RequestMapping 註解 @FeignClient 介面。

[[feign-request/response-compression]] === Feign 請求/回應壓縮

您可以考慮為您的 Feign 請求啟用請求或回應 GZIP 壓縮。您可以透過啟用其中一個屬性來執行此操作

spring.cloud.openfeign.compression.request.enabled=true
spring.cloud.openfeign.compression.response.enabled=true

Feign 請求壓縮為您提供了類似於您可以為您的網路伺服器設定的設定

spring.cloud.openfeign.compression.request.enabled=true
spring.cloud.openfeign.compression.request.mime-types=text/xml,application/xml,application/json
spring.cloud.openfeign.compression.request.min-request-size=2048

這些屬性允許您有選擇性地選擇壓縮的媒體類型和最小請求閾值長度。

由於 OkHttpClient 使用 "透明" 壓縮,如果存在 content-encodingaccept-encoding 標頭,則會停用該壓縮,因此當 feign.okhttp.OkHttpClient 存在於類別路徑上且 spring.cloud.openfeign.okhttp.enabled 設定為 true 時,我們不會啟用壓縮。

Feign 日誌記錄

為每個建立的 Feign 客戶端建立一個記錄器。預設情況下,記錄器的名稱是用於建立 Feign 客戶端的介面的完整類別名稱。Feign 日誌記錄僅回應 DEBUG 層級。

application.yml
logging.level.project.user.UserClient: DEBUG

您可以針對每個客戶端組態的 Logger.Level 物件,告知 Feign 要記錄多少內容。選項為

  • NONE,無記錄 (預設)。

  • BASIC,僅記錄請求方法和 URL 以及回應狀態碼和執行時間。

  • HEADERS,記錄基本資訊以及請求和回應標頭。

  • FULL,記錄請求和回應的標頭、主體和中繼資料。

例如,以下內容會將 Logger.Level 設定為 FULL

@Configuration
public class FooConfiguration {
	@Bean
	Logger.Level feignLoggerLevel() {
		return Logger.Level.FULL;
	}
}

Feign 功能支援

Feign 功能公開了核心 Feign 組件,以便可以修改這些組件。例如,功能可以取得 Client裝飾它,並將裝飾後的實例回傳給 Feign。對 Micrometer 的支援是這方面的一個很好的真實範例。請參閱 Micrometer 支援

建立一個或多個 Capability bean 並將它們放在 @FeignClient 組態中,可讓您註冊它們並修改所涉及的客戶端的行為。

@Configuration
public class FooConfiguration {
	@Bean
	Capability customCapability() {
		return new CustomCapability();
	}
}

Micrometer 支援

如果以下所有條件都成立,則會建立並註冊 MicrometerObservationCapability bean,以便您的 Feign 客戶端可以透過 Micrometer 觀察

  • feign-micrometer 在類別路徑上

  • ObservationRegistry bean 可用

  • feign micrometer 屬性設定為 true (預設)

    • spring.cloud.openfeign.micrometer.enabled=true (適用於所有客戶端)

    • spring.cloud.openfeign.client.config.feignName.micrometer.enabled=true (適用於單一客戶端)

如果您的應用程式已使用 Micrometer,則啟用此功能就像將 feign-micrometer 放到您的類別路徑上一樣簡單。

您也可以透過以下任一方式停用該功能

  • 從您的類別路徑中排除 feign-micrometer

  • 將其中一個 feign micrometer 屬性設定為 false

    • spring.cloud.openfeign.micrometer.enabled=false

    • spring.cloud.openfeign.client.config.feignName.micrometer.enabled=false

spring.cloud.openfeign.micrometer.enabled=false 停用所有 Feign 客戶端的 Micrometer 支援,而與客戶端層級標誌的值無關:spring.cloud.openfeign.client.config.feignName.micrometer.enabled。如果您想要針對每個客戶端啟用或停用 Micrometer 支援,請不要設定 spring.cloud.openfeign.micrometer.enabled,而是使用 spring.cloud.openfeign.client.config.feignName.micrometer.enabled

您也可以透過註冊您自己的 bean 來客製化 MicrometerObservationCapability

@Configuration
public class FooConfiguration {
	@Bean
	public MicrometerObservationCapability micrometerObservationCapability(ObservationRegistry registry) {
		return new MicrometerObservationCapability(registry);
	}
}

仍然可以使用 MicrometerCapability 與 Feign (僅指標支援),您需要停用 Micrometer 支援 (spring.cloud.openfeign.micrometer.enabled=false) 並建立 MicrometerCapability bean

@Configuration
public class FooConfiguration {
	@Bean
	public MicrometerCapability micrometerCapability(MeterRegistry meterRegistry) {
		return new MicrometerCapability(meterRegistry);
	}
}

Feign 快取

如果使用了 @EnableCaching 註解,則會建立並註冊 CachingCapability bean,以便您的 Feign 客戶端可以識別其介面上的 @Cache* 註解

public interface DemoClient {

	@GetMapping("/demo/{filterParam}")
    @Cacheable(cacheNames = "demo-cache", key = "#keyParam")
	String demoEndpoint(String keyParam, @PathVariable String filterParam);
}

您也可以透過屬性 spring.cloud.openfeign.cache.enabled=false 停用該功能。

Spring @RequestMapping 支援

Spring Cloud OpenFeign 提供了對 Spring @RequestMapping 註解及其衍生註解(例如 @GetMapping@PostMapping 和其他註解)的支援。@RequestMapping 註解上的屬性(包括 valuemethodparamsheadersconsumesproduces)由 SpringMvcContract 解析為請求的內容。

考慮以下範例

使用 params 屬性定義介面。

@FeignClient("demo")
public interface DemoTemplate {

        @PostMapping(value = "/stores/{storeId}", params = "mode=upsert")
        Store update(@PathVariable("storeId") Long storeId, Store store);
}

在上面的範例中,請求 url 解析為 /stores/storeId?mode=upsert
params 屬性也支援使用多個 key=value 或僅一個 key

  • params = { "key1=v1", "key2=v2" } 時,請求 url 解析為 /stores/storeId?key1=v1&key2=v2

  • params = "key" 時,請求 url 解析為 /stores/storeId?key

Feign @QueryMap 支援

Spring Cloud OpenFeign 提供了等效的 @SpringQueryMap 註解,用於將 POJO 或 Map 參數註解為查詢參數對應。

例如,Params 類別定義了參數 param1param2

// Params.java
public class Params {
	private String param1;
	private String param2;

	// [Getters and setters omitted for brevity]
}

以下 feign 客戶端透過使用 @SpringQueryMap 註解來使用 Params 類別

@FeignClient("demo")
public interface DemoTemplate {

	@GetMapping(path = "/demo")
	String demoEndpoint(@SpringQueryMap Params params);
}

如果您需要更精細地控制產生的查詢參數對應,您可以實作自訂 QueryMapEncoder bean。

HATEOAS 支援

Spring 提供了一些 API 來建立遵循 HATEOAS 原則的 REST 表示,Spring HateoasSpring Data REST

如果您的專案使用 org.springframework.boot:spring-boot-starter-hateoas starter 或 org.springframework.boot:spring-boot-starter-data-rest starter,則預設情況下會啟用 Feign HATEOAS 支援。

啟用 HATEOAS 支援後,允許 Feign 客戶端序列化和反序列化 HATEOAS 表示模型:EntityModelCollectionModelPagedModel

@FeignClient("demo")
public interface DemoTemplate {

	@GetMapping(path = "/stores")
	CollectionModel<Store> getStores();
}

Spring @MatrixVariable 支援

Spring Cloud OpenFeign 提供了對 Spring @MatrixVariable 註解的支援。

如果將對應作為方法引數傳遞,則會透過將對應中的鍵值對與 = 連接來建立 @MatrixVariable 路徑段。

如果傳遞了不同的物件,則 @MatrixVariable 註解中提供的 name(如果已定義)或註解的變數名稱會與提供的方法引數一起使用 = 連接。

重要事項

即使在伺服器端,Spring 不要求使用者將路徑段佔位符命名為與矩陣變數名稱相同,但由於在客戶端會過於模糊,因此 Spring Cloud OpenFeign 要求您新增路徑段佔位符,其名稱與 @MatrixVariable 註解中提供的 name(如果已定義)或註解的變數名稱相符。

例如

@GetMapping("/objects/links/{matrixVars}")
Map<String, List<String>> getObjects(@MatrixVariable Map<String, List<String>> matrixVars);

請注意,變數名稱和路徑段佔位符都稱為 matrixVars

@FeignClient("demo")
public interface DemoTemplate {

	@GetMapping(path = "/stores")
	CollectionModel<Store> getStores();
}

Feign CollectionFormat 支援

我們透過提供 @CollectionFormat 註解來支援 feign.CollectionFormat。您可以透過傳遞所需的 feign.CollectionFormat 作為註解值,使用它來註解 Feign 客戶端方法(或整個類別以影響所有方法)。

在以下範例中,使用 CSV 格式而不是預設的 EXPLODED 來處理方法。

@FeignClient(name = "demo")
protected interface DemoFeignClient {

    @CollectionFormat(feign.CollectionFormat.CSV)
    @GetMapping(path = "/test")
    ResponseEntity performRequest(String test);

}

反應式支援

由於 OpenFeign 專案目前不支援反應式客戶端,例如 Spring WebClient,Spring Cloud OpenFeign 也不支援。

由於 Spring Cloud OpenFeign 專案現在被認為功能完整,因此即使上游專案中提供支援,我們也不計劃新增支援。我們建議遷移到 Spring Interface Clients。那裡同時支援封鎖和反應式堆疊。

在完成之前,我們建議使用 feign-reactive 以取得 Spring WebClient 支援。

早期初始化錯誤

我們不鼓勵在應用程式生命週期的早期階段(在處理組態和初始化 bean 時)使用 Feign 客戶端。不支援在 bean 初始化期間使用客戶端。

同樣地,根據您使用 Feign 客戶端的方式,您可能會在啟動應用程式時看到初始化錯誤。若要解決此問題,您可以在自動裝配您的客戶端時使用 ObjectProvider

@Autowired
ObjectProvider<TestFeignClient> testFeignClient;

Spring Data 支援

如果 Jackson Databind 和 Spring Data Commons 在類別路徑上,則會自動新增 org.springframework.data.domain.Pageorg.springframework.data.domain.Sort 的轉換器。

若要停用此行為,請設定

spring.cloud.openfeign.autoconfiguration.jackson.enabled=false

請參閱 org.springframework.cloud.openfeign.FeignAutoConfiguration.FeignJacksonConfiguration 以瞭解詳細資訊。

Spring @RefreshScope 支援

如果已啟用 Feign 客戶端重新整理,則每個 Feign 客戶端都會使用以下內容建立

  • feign.Request.Options 作為重新整理範圍的 bean。這表示可以針對任何 Feign 客戶端實例重新整理 connectTimeoutreadTimeout 等屬性。

  • 封裝在 org.springframework.cloud.openfeign.RefreshableUrl 下的 url。這表示如果使用 spring.cloud.openfeign.client.config.{feignName}.url 屬性定義,則可以針對任何 Feign 客戶端實例重新整理 Feign 客戶端的 URL。

您可以透過 POST /actuator/refresh 重新整理這些屬性。

預設情況下,Feign 客戶端中的重新整理行為已停用。使用以下屬性啟用重新整理行為

spring.cloud.openfeign.client.refresh-enabled=true
請勿使用 @RefreshScope 註解註解 @FeignClient 介面。

OAuth2 支援

可以透過將 spring-boot-starter-oauth2-client 依賴項新增至您的專案並設定以下標誌來啟用 OAuth2 支援

spring.cloud.openfeign.oauth2.enabled=true

當標誌設定為 true,且 oauth2 客戶端內容資源詳細資訊存在時,會建立類別 OAuth2AccessTokenInterceptor 的 bean。在每個請求之前,攔截器會解析所需的存取權杖並將其作為標頭包含在內。OAuth2AccessTokenInterceptor 使用 OAuth2AuthorizedClientManager 來取得持有 OAuth2AccessTokenOAuth2AuthorizedClient。如果使用者已使用 spring.cloud.openfeign.oauth2.clientRegistrationId 屬性指定 OAuth2 clientRegistrationId,則將使用它來檢索權杖。如果未檢索到權杖或未指定 clientRegistrationId,則將使用從 url 主機段檢索的 serviceId

提示

使用 serviceId 作為 OAuth2 client registrationId 對於負載平衡的 Feign 客户端來說很方便。對於非負載平衡的客户端,基於屬性的 clientRegistrationId 是一個合適的方法。

提示

如果您不想使用 OAuth2AuthorizedClientManager 的預設設定,您可以直接在您的組態中實例化此類型的 bean。

轉換負載平衡的 HTTP 請求

您可以使用選定的 ServiceInstance 來轉換負載平衡的 HTTP 請求。

對於 Request,您需要實作和定義 LoadBalancerFeignRequestTransformer,如下所示

@Bean
public LoadBalancerFeignRequestTransformer transformer() {
	return new LoadBalancerFeignRequestTransformer() {

		@Override
		public Request transformRequest(Request request, ServiceInstance instance) {
			Map<String, Collection<String>> headers = new HashMap<>(request.headers());
			headers.put("X-ServiceId", Collections.singletonList(instance.getServiceId()));
			headers.put("X-InstanceId", Collections.singletonList(instance.getInstanceId()));
			return Request.create(request.httpMethod(), request.url(), headers, request.body(), request.charset(),
					request.requestTemplate());
		}
	};
}

如果定義了多個轉換器,它們將按照 bean 定義的順序應用。或者,您可以使用 LoadBalancerFeignRequestTransformer.DEFAULT_ORDER 來指定順序。

X-Forwarded Headers 支援

可以通過設定以下標誌來啟用 X-Forwarded-HostX-Forwarded-Proto 支援

spring.cloud.loadbalancer.x-forwarded.enabled=true

為 Feign Client 提供 URL 的支援方式

您可以使用以下任何方式為 Feign client 提供 URL

案例 範例 詳細資訊

URL 在 @FeignClient 註解中提供。

@FeignClient(name="testClient", url="http://localhost:8081")

URL 從註解的 url 屬性解析,不帶負載平衡。

URL 在 @FeignClient 註解和組態屬性中提供。

@FeignClient(name="testClient", url="http://localhost:8081") 和在 application.yml 中定義的屬性 spring.cloud.openfeign.client.config.testClient.url=http://localhost:8081

URL 從註解的 url 屬性解析,不帶負載平衡。組態屬性中提供的 URL 保持未使用狀態。

URL 未在 @FeignClient 註解中提供,但在組態屬性中提供。

@FeignClient(name="testClient") 和在 application.yml 中定義的屬性 spring.cloud.openfeign.client.config.testClient.url=http://localhost:8081

URL 從組態屬性解析,不帶負載平衡。如果 spring.cloud.openfeign.client.refresh-enabled=true,則組態屬性中定義的 URL 可以按照 Spring RefreshScope Support 中的描述進行刷新。

URL 既未在 @FeignClient 註解中提供,也未在組態屬性中提供。

@FeignClient(name="testClient")

URL 從註解的 name 屬性解析,帶負載平衡。

AOT 和 Native Image 支援

Spring Cloud OpenFeign 支援 Spring AOT 轉換和 Native Image,但是,僅在禁用刷新模式、禁用 Feign 客户端刷新(預設設定)和禁用 lazy `@FeignClient` 屬性解析(預設設定)的情況下。

如果您想在 AOT 或 Native Image 模式下運行 Spring Cloud OpenFeign 客户端,請確保將 spring.cloud.refresh.enabled 設定為 false
如果您想在 AOT 或 Native Image 模式下運行 Spring Cloud OpenFeign 客户端,請確保 spring.cloud.openfeign.client.refresh-enabled 未設定為 true
如果您想在 AOT 或 Native Image 模式下運行 Spring Cloud OpenFeign 客户端,請確保 spring.cloud.openfeign.lazy-attributes-resolution 未設定為 true
然而,如果您通過屬性設定 url 值,則可以通過使用 -Dspring.cloud.openfeign.client.config.[clientId].url=[url] 標誌運行映像來覆蓋 @FeignClienturl 值。為了啟用覆蓋,還必須通過屬性而不是在構建時通過 @FeignClient 屬性來設定 url 值。

組態屬性

要查看所有與 Spring Cloud OpenFeign 相關的組態屬性列表,請查看 附錄頁面