Spring Cloud OpenFeign 功能

宣告式 REST 客戶端:Feign

Feign 是一個宣告式的 Web 服務客戶端。它使得編寫 Web 服務客戶端變得更容易。要使用 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,請使用組 ID 為 org.springframework.cloud、artifact ID 為 spring-cloud-starter-openfeign 的啟動器。有關如何使用當前 Spring Cloud Release Train 設定構建系統的詳細資訊,請參閱 Spring Cloud 專案頁面

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 客戶端。你也可以使用 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)

為了在多模組設定中載入 Spring Feign 客戶端 bean,你需要直接指定包。

由於 FactoryBean 物件可能在初始上下文刷新發生之前例項化,並且 Spring Cloud OpenFeign 客戶端的例項化會觸發上下文重新整理,因此不應在 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.Decoder、一個 feign.Encoder 和一個 feign.Contract。可以使用 @FeignClient 註解的 contextId 屬性來覆蓋該元件集合的名稱。

Spring Cloud 允許你透過使用 @FeignClient 宣告附加配置(在 FeignClientsConfiguration 之上)來完全控制 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 bean名稱: 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 客戶端的使用。在使用 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,可以使用 HttpClient5FeignConfiguration.HttpClientBuilderCustomizer 介面進行程式化配置。

Apache HTTP Components 5.4 版本更改了 HttpClient 中與 HTTP/1.1 TLS 升級相關的預設設定。大多數代理伺服器可以順利處理升級,但是你可能會遇到 Envoy 或 Istio 的問題。如果需要恢復以前的行為,可以使用 HttpClient5FeignConfiguration.HttpClientBuilderCustomizer 來實現,如下面的示例所示。
@Configuration
public class FooConfiguration {

	@Bean
	public HttpClient5FeignConfiguration.HttpClientBuilderCustomizer httpClientBuilder() {
		return (httpClientBuilder) -> {
			RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
			requestConfigBuilder.setProtocolUpgradeEnabled(false);
			httpClientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
		};
	}
}
從 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 的預設行為不同,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");
	}
}

這將用 feign.Contract.Default 替換 SpringMvcContract,並將 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 指的是 @FeignClientvalue,它也透過 @FeignClientname@FeignClientcontextId 進行別名。在負載均衡場景中,它也對應於將用於檢索例項的伺服器應用的 serviceId。為解碼器、重試器及其他元件指定的類必須在 Spring 上下文中有 bean 或具有預設建構函式。

可以透過 @EnableFeignClientsdefaultConfiguration 屬性指定預設配置,其方式與上述類似。不同之處在於此配置將應用於所有 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.decode-slash 的值設定為 false 來更改此行為。
預設情況下,Feign 客戶端不會從請求路徑中刪除尾部斜槓 / 字元。你可以透過將 spring.cloud.openfeign.client.remove-trailing-slash 的值設定為 true 來更改此行為。在下一個主要版本中,從請求路徑中刪除尾部斜槓將成為預設行為。

SpringEncoder 配置

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

你可以透過將 spring.cloud.openfeign.encoder.charset-from-content-type 的值設定為 true 來修改此行為,改為從 Content-Type 頭部的字元集中派生字元集。

超時處理

我們可以在預設客戶端和命名客戶端上配置超時。OpenFeign 使用兩個超時引數

  • connectTimeout 防止因伺服器長時間處理而阻塞呼叫者。

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

如果伺服器未執行或不可用,資料包會導致連線被拒絕。通訊要麼以錯誤訊息結束,要麼進入回退。如果 connectTimeout 設定得非常低,這可能在 connectTimeout 之前發生。執行查詢和接收此類資料包所需的時間是此延遲的重要組成部分。它會根據涉及 DNS 查詢的遠端主機而發生變化。

手動建立 Feign 客戶端

在某些情況下,可能需要以一種無法使用上述方法實現的方式定製你的 Feign 客戶端。在這種情況下,你可以使用 Feign Builder API 建立客戶端。下面是一個示例,它建立了兩個具有相同介面的 Feign 客戶端,但為每個客戶端配置了單獨的請求攔截器。

@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 是客戶端將要請求的服務名稱。
Feign Contract 物件定義了介面上哪些註解和值是有效的。自動注入的 Contract bean 提供對 SpringMVC 註解的支援,而不是預設的 Feign 原生註解。不建議將 Spring MVC 註解和 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”的普通 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)。

使用配置屬性配置 CircuitBreakers

你可以透過配置屬性配置 CircuitBreakers。

例如,如果你有這個 Feign 客戶端

@FeignClient(url = "https://: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 = "https://:${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 = "https://:${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。為了解決這個問題,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 請求/響應壓縮

你可以考慮為你的 Feign 請求啟用請求或響應的 GZIP 壓縮。你可以透過啟用以下屬性之一來實現這一點

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

Feign 請求壓縮為你提供了類似於你在 Web 伺服器上設定的選項

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

這些屬性允許你選擇性地指定壓縮的媒體型別和最小請求閾值長度。

當請求匹配 spring.cloud.openfeign.compression.request.mime-types 中設定的 MIME 型別以及 spring.cloud.openfeign.compression.request.min-request-size 中設定的大小,並且 spring.cloud.openfeign.compression.request.enabled=true 時,會將壓縮相關的頭部新增到請求中。這些頭部的功能是向伺服器發出訊號,表明客戶端期望一個壓縮的請求體。伺服器端應用的責任是根據客戶端提供的頭部資訊提供壓縮的請求體。

由於 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 Capability 支援

Feign capability 暴露了核心 Feign 元件,以便可以修改這些元件。例如,capability 可以獲取 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);
	}
}

仍然可以在 Feign 中使用 MicrometerCapability(僅度量支援),你需要停用 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 啟動器或 org.springframework.boot:spring-boot-starter-data-rest 啟動器,則預設啟用 Feign HATEOAS 支援。

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

@FeignClient("demo")
public interface DemoTemplate {

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

Spring @MatrixVariable 支援

Spring Cloud OpenFeign 支援 Spring 的 @MatrixVariable 註解。

如果將 Map 作為方法引數傳遞,則 @MatrixVariable 路徑段是透過將 Map 中的鍵值對用 = 連線來建立的。

如果傳遞的是不同的物件,則使用 =@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);

}

Reactive 支援

在 Spring Cloud OpenFeign 積極開發期間,OpenFeign 專案 不支援響應式客戶端,例如 Spring WebClient,因此 Spring Cloud OpenFeign 也無法新增此類支援。

由於 Spring Cloud OpenFeign 專案現在被認為是功能完善的,即使上游專案提供了支援,我們也不打算新增。我們建議遷移到 Spring Interface Clients。那裡支援阻塞和響應式兩種堆疊。

早期初始化錯誤

我們不鼓勵在應用程式生命週期的早期階段使用 Feign 客戶端,例如在處理配置和初始化 bean 時。在 bean 初始化期間使用客戶端是不支援的。

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

@Autowired
ObjectProvider<TestFeignClient> testFeignClient;

Spring Data 支援

如果 Jackson Databind 和 Spring Data Commons 在 classpath 中,則會自動新增 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 作為 refresh-scoped bean。這意味著 connectTimeoutreadTimeout 等屬性可以在任何 Feign 客戶端例項上重新整理。

  • 包裹在 org.springframework.cloud.openfeign.RefreshableUrl 下的 URL。這意味著如果使用 spring.cloud.openfeign.client.config.{feignName}.url 屬性定義了 Feign 客戶端的 URL,則可以在任何 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 客戶端註冊 ID 對於負載均衡的 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 頭支援

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

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

為 Feign 客戶端提供 URL 的支援方式

您可以透過以下任何一種方式為 Feign 客戶端提供 URL

情況 示例 詳情

@FeignClient 註解中提供 URL。

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

URL 從註解的 url 屬性解析,不進行負載均衡。

@FeignClient 註解和配置屬性中提供 URL。

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

URL 從註解的 url 屬性解析,不進行負載均衡。配置屬性中提供的 URL 未被使用。

未在 @FeignClient 註解中提供 URL,但在配置屬性中提供。

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

URL 從配置屬性解析,不進行負載均衡。如果 spring.cloud.openfeign.client.refresh-enabled=true,則配置屬性中定義的 URL 可以按照 Spring RefreshScope 支援 中所述進行重新整理。

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

@FeignClient(name="testClient")

URL 從註解的 name 屬性解析,進行負載均衡。

AOT 和 Native Image 支援

Spring Cloud OpenFeign 支援 Spring AOT 轉換和 Native Image,但僅限於停用重新整理模式、停用 Feign 客戶端重新整理(預設設定)和停用 延遲 @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 相關的配置屬性列表,請檢視 附錄頁