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 相关的配置属性列表,请查看 附录页