此版本仍在开发中,尚未达到稳定状态。要使用最新稳定版本,请使用 Spring Cloud OpenFeign 5.0.1!spring-doc.cadn.net.cn

Spring Cloud OpenFeign 特性

声明式 REST 客户端: Feign

Feign 是一个声明式的 web 服务客户端。它使编写 web 服务客户端变得更加容易。要使用 Feign,请创建一个接口并添加注解。它具有可插拔的注释支持,包括 Feign 注释和 JAX-RS 注释。Feign 还支持可插拔编码器和解码器。Spring Cloud 为 Spring MVC 注解以及默认情况下与 Spring Web 兼容的同一 HttpMessageConverters 添加了对 Feign 的支持。Spring Cloud 集成了 Eureka、Spring Cloud CircuitBreaker、以及 Spring Cloud LoadBalancer,当使用 Feign 时,可以提供一个负载均衡的 http 客户端。spring-doc.cadn.net.cn

如何包含Feign

要将 Feign 包含在您的项目中,请使用组为 org.springframework.cloud 且工件 ID 为 spring-cloud-starter-openfeign 的Starters。有关使用当前 Spring Cloud 发布列车设置构建系统的详细信息,请参阅 Spring Cloud 项目页面spring-doc.cadn.net.cn

示例 spring boot 应用spring-doc.cadn.net.cn

@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);
}

在注解中,上面的String值(“stores”)是一个任意的客户端名称,它用于创建一个Spring Cloud LoadBalancer客户端。您还可以使用url属性指定URL(绝对值或仅主机名)。应用程序上下文中的bean的名称是接口的完全限定名。要指定自己的别名值,可以使用@FeignClient注解的qualifiers值。spring-doc.cadn.net.cn

负载均衡器客户端上面想要发现“商店”服务的物理地址。如果您的应用程序是Eureka客户端,那么它将在Eureka服务注册表中解析该服务。如果您不想使用Eureka,您可以在外部配置中为服务器列表配置一个列表,如SimpleDiscoveryClient所示。spring-doc.cadn.net.cn

Spring Cloud OpenFeign 支持 Spring Cloud LoadBalancer 阻塞模式下所有可用的功能。有关更多信息,请参阅 项目文档spring-doc.cadn.net.cn

使用@EnableFeignClients注解在@Configuration-注解的类上,确保指定客户端的位置,例如:@EnableFeignClients(basePackages = "com.example.clients")或者显式列出它们: @EnableFeignClients(clients = InventoryServiceFeignClient.class)

为了在多模块设置中加载 Spring Feign 客户端 beans,你需要直接指定包路径。spring-doc.cadn.net.cn

在初始上下文刷新应该发生之前,可能有FactoryBean个对象在被实例化。由于Spring Cloud OpenFeign客户端的实例化会触发上下文刷新,因此不应该在FactoryBean类中声明它们。

属性解析模式

在创建 0 客户端 bean 时,我们会通过1 注解传递的值进行解析。自2 以来,这些值是被急于解决的。这对于大多数用例来说是一个很好的解决方案,它还允许对AOT进行支持。spring-doc.cadn.net.cn

如果需要将属性以惰性方式解析,请将spring.cloud.openfeign.lazy-attributes-resolution属性值设置为truespring-doc.cadn.net.cn

对于 Spring Cloud Contract 测试集成,应使用延迟属性解析。

覆盖Feign默认值

一个概念在Spring Cloud的Feign支持中是命名的客户端。每个Feign 客户端是一组组件的一部分,这些组件一起工作来按需接触远程服务器,并且这组组件有一个名称,您可以使用0 注解作为应用程序开发人员给它。每当需要创建一个新的命名集时,Spring Cloud都会使用2 创建一个新的1 。其中包括(但不限于)一个3 、一个4 和一个5 。 可以使用6 注解的7 属性来覆盖该集合的名称。spring-doc.cadn.net.cn

Spring Cloud 让您通过声明附加配置(在 0 的基础上)来完全控制 Feign 客户端,使用 1。例如:spring-doc.cadn.net.cn

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

在这种情况下,客户机由 0 中已经存在的组件与 1 中的任何组件(后者将覆盖前者)组成。spring-doc.cadn.net.cn

FooConfiguration 不需要使用 @Configuration 注解。然而,如果使用了,则要确保从任何会包含此配置的 @ComponentScan 中排除,否则当指定时,它将成为 feign.Decoderfeign.Encoderfeign.Contract 等的默认来源。可以通过将其放在与任何 @ComponentScan@SpringBootApplication 不重叠的包中,或在 @ComponentScan 中显式排除来避免此问题。
使用注解的contextId属性,以及更改@FeignClient集合中ApplicationContext的名称,它将覆盖客户端名称的别名,并将其用作创建该客户端配置 bean 名称的一部分。
之前,使用 url 属性,并不需要 name 属性。现在,使用 name 是必须的。

占位符支持在属性中使用 nameurlspring-doc.cadn.net.cn

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

Spring Cloud OpenFeign 默认为 feign (BeanType beanName: ClassName):spring-doc.cadn.net.cn

  • 0 feign 解码器: 1 (这包装了一个 2 spring-doc.cadn.net.cn

  • Encoder feignEncoder: SpringEncoderspring-doc.cadn.net.cn

  • (代码) 0 日志记录器:(代码) 1spring-doc.cadn.net.cn

  • MicrometerObservationCapability micrometerObservationCapability: 如果 feign-micrometer 在类路径上且 ObservationRegistry 可用spring-doc.cadn.net.cn

  • MicrometerCapability micrometerCapability: 如果classpath中存在feign-micrometer,则MeterRegistry可用,而ObservationRegistry不可用spring-doc.cadn.net.cn

  • CachingCapability 缓存能力:如果使用了@EnableCaching注释。可以通过spring.cloud.openfeign.cache.enabled禁用。spring-doc.cadn.net.cn

  • Contract feignContract: SpringMvcContractspring-doc.cadn.net.cn

  • Feign.Builder feignBuilder: FeignCircuitBreaker.Builderspring-doc.cadn.net.cn

  • Client feignClient: 如果Spring Cloud LoadBalancer在类路径上,FeignBlockingLoadBalancerClient使用。如果它们都不在类路径上,则默认使用Feign客户端。spring-doc.cadn.net.cn

spring-cloud-starter-openfeign 支持 spring-cloud-starter-loadbalancer。然而,作为可选依赖,如果您想使用它,请确保已将其添加到项目中。

当涉及到基于 Apache HttpClient 5 的 Feign 客户端时,只需确保 HttpClient 5 在类路径中,但你也可以通过将值从 1 设置为 0 来禁用其在 Feign 客户端中的使用。 你可以通过提供一个 org.apache.hc.client5.http.impl.classic.CloseableHttpClient 的 bean 来自定义所使用的 HTTP 客户端,当使用 Apache HC5 时。spring-doc.cadn.net.cn

您还可以通过设置spring.cloud.openfeign.httpclient.xxx属性来自定义HTTP客户端。以httpclient开头的属性适用于所有客户端,以httpclient.hc5开头的适用于Apache HttpClient 5,以httpclient.http2开头的适用于Http2Client。您可以在附录中找到可以自定义的完整属性列表。
如果您无法通过属性配置Apache HttpClient 5,则有HttpClient5FeignConfiguration.HttpClientBuilderCustomizer接口可以进行程序化配置。同样地,要配置HttpClientConnectionManager,您可以使用HttpClient5FeignConfiguration.HttpClientConnectionManagerBuilderCustomizer。下面的示例展示了这两种用法。spring-doc.cadn.net.cn

Apache HTTP Components 5.4 已更改 HttpClient 关于 HTTP/1.1 TLS 升级的默认设置。大多数代理服务器可以毫无问题地处理升级,但是,您可能会遇到 Envoy 或 Istio 的问题。如果需要恢复以前的行为,可以使用HttpClient5FeignConfiguration.HttpClientBuilderCustomizer来实现,如下面的示例所示。
public class HttpClientConfiguration {

	@Bean
	public HttpClient5FeignConfiguration.HttpClientBuilderCustomizer httpClientBuilder() {
		return (httpClientBuilder) -> {
			RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
			requestConfigBuilder.setProtocolUpgradeEnabled(false);
			httpClientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
		};
	}
}
Apache HTTP 组件5.5.1在HttpClient中更改了与TLS握手中的超时时间相关的默认值。未设置的TLS握手超时时间现在隐式地由connectTimeout绑定,而以前它是无界的。要恢复以前的默认值,可以使用2 来执行此操作,如下例所示。
public class HttpClientConnectionManagerConfiguration {

	@Bean
	public HttpClient5FeignConfiguration.HttpClientConnectionManagerBuilderCustomizer httpClientConnectionManagerBuilder() {
		return (httpClientConnectionManagerBuilder) -> {
			TlsConfig.Builder tlsConfigBuilder = TlsConfig.custom();
			// 0 seconds timeout means infinite
			tlsConfigBuilder.setHandshakeTimeout(Timeout.of(0, TimeUnit.SECONDS));
			httpClientConnectionManagerBuilder.setDefaultTlsConfig(tlsConfigBuilder.build());
		};
	}
}
从Spring Cloud OpenFeign 4开始,不再支持Feign Apache HttpClient 4。我们建议使用Apache HttpClient 5。

Spring Cloud OpenFeign 并不默认提供以下 beans,但仍会从应用程序上下文中查找这些类型的 beans 以创建 Feign 客户端:spring-doc.cadn.net.cn

默认情况下,会创建一个类型为 RetryerRetryer.NEVER_RETRY bean,这将会禁用重试。 请注意,这种重试行为与 Feign 的默认行为不同,Feign 会自动重试 IOExceptions, 将其视为短暂的网络相关异常,以及任何由 ErrorDecoder 抛出的 RetryableException。spring-doc.cadn.net.cn

创建那些类型之一的 bean 并将其放在 @FeignClient 配置(如上例中的 FooConfiguration)中,允许您重写每个所述 bean。例如:spring-doc.cadn.net.cn

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

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

This replaces the SpringMvcContract with feign.Contract.Default and adds a RequestInterceptor to the collection of RequestInterceptor.spring-doc.cadn.net.cn

@FeignClient 还可以使用配置属性进行配置。spring-doc.cadn.net.cn

application.ymlspring-doc.cadn.net.cn

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

在此示例中,代码 0 对应代码 1 代码 2,它还与代码 3 代码 4 和代码 5 代码 6 别名。在负载平衡方案中,它还对应用于检索实例的应用服务器的代码 7。指定的解码器、重试程序和其他类必须在 Spring 上下文中有一个 bean 或有一个默认构造函数。spring-doc.cadn.net.cn

默认配置可以在属性@EnableFeignClients中指定,方法与上文所述类似。其区别在于,此配置将应用于所有Feign客户端。spring-doc.cadn.net.cn

如果您更喜欢使用配置属性来配置所有@FeignClient,您可以创建带有default feign 名称的配置属性。spring-doc.cadn.net.cn

你可以使用 spring.cloud.openfeign.client.config.feignName.defaultQueryParametersspring.cloud.openfeign.client.config.feignName.defaultRequestHeaders 来指定客户端名为 feignName 的每个请求将发送的查询参数和头信息。spring-doc.cadn.net.cn

application.ymlspring-doc.cadn.net.cn

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

如果我们在 0 中创建了 Bean 和配置属性,配置属性将获胜。它会覆盖 1 值。但如果你想改变优先级为 2 ,您可以将 3 更改为 4 spring-doc.cadn.net.cn

如果我们想要创建多个 Feign 客户端,它们具有相同的名称或 URL 以便指向同一台服务器,但每个都有不同的自定义配置 那么就需要使用 contextId 属性 of @FeignClient,以避免这些配置 Bean 的名称冲突。spring-doc.cadn.net.cn

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

也有可能配置 FeignClient 不继承父上下文中的 beans。 可以通过在 1 中重写 0 来返回 2 spring-doc.cadn.net.cn

@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的值设置为2。
将在下一个主要版本中将从请求路径删除尾部斜杠作为默认行为。

SpringEncoder配置

在我们提供的SpringEncoder中,我们为二进制内容类型设置null字符集,为所有其他内容类型设置2。
spring-doc.cadn.net.cn

您可以修改此行为,通过设置值spring.cloud.openfeign.encoder.charset-from-content-typetrue,从Content-Type标头charset派生字符集。spring-doc.cadn.net.cn

超时处理

我们可以在默认的和命名的客户端上都配置超时。OpenFeign 与两个超时参数一起工作:spring-doc.cadn.net.cn

In case the server is not running or available a packet results in connection refused. The communication ends either with an error message or in a fallback. This can happen before the connectTimeout if it is set very low. The time taken to perform a lookup and to receive such a packet causes a significant part of this delay. It is subject to change based on the remote host that involves a DNS lookup.

创建 Feign 客户端手动

In some cases it might be necessary to customize your Feign Clients in a way that is not possible using the methods above. In this case you can create Clients using the Feign Builder API. Below is an example which creates two Feign Clients with the same interface but configures each one with a separate request interceptor.spring-doc.cadn.net.cn

@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");
	}
}
In the above example FeignClientsConfiguration.class is the default configuration provided by Spring Cloud OpenFeign.
PROD-SVC 是客户端将要发出请求的服务的名称。
Feign Contract 对象定义了接口上有效的注解和值。自动装配的 Contract bean 提供了对 SpringMVC 注解的支持,而不是默认的 Feign 原生注解。不建议将 Spring MVC 注解与原生 Feign 注解混合使用。

你也可以在 Builder 上使用 Builder`to configure FeignClient not to inherit beans from the parent context. You can do this by overriding calling `inheritParentContext(false)spring-doc.cadn.net.cn

Feign Spring Cloud 电路断路器支持

如果Spring Cloud CircuitBreaker在类路径上且值为spring.cloud.openfeign.circuitbreaker.enabled=true,Feign将使用断路器包装所有方法。spring-doc.cadn.net.cn

要按客户端禁用Spring Cloud CircuitBreaker支持,请使用“prototype”范围创建一个vanilla Feign.Builder,例如:spring-doc.cadn.net.cn

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

遵循此模式命名断路器名称<feignClientClassName>#<calledMethod>(<parameterTypes>)。调用带有FooClient接口的@FeignClient时,具有无参数的被调用接口方法是bar,则断路器名称将为FooClient#bar()spring-doc.cadn.net.cn

从2020.0.2开始,电路断路器名称模式已更改为<feignClientName>_<calledMethod>。使用在2020.0.4中引入的CircuitBreakerNameResolver,电路断路器名称可以保留旧模式。

提供值为CircuitBreakerNameResolver的bean,你可以改变熔断器名称模式。spring-doc.cadn.net.cn

@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)。spring-doc.cadn.net.cn

使用配置属性配置断路器

您可以通过配置属性来配置断路器。spring-doc.cadn.net.cn

例如,如果您有此 Feign 客户端spring-doc.cadn.net.cn

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

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

你也可以通过执行以下操作使用配置属性对其进行配置spring-doc.cadn.net.cn

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.enabledfalse

Feign Spring Cloud CircuitBreaker Fallbacks

Spring Cloud CircuitBreaker 支持回退的概念:当电路打开或出现错误时执行的默认代码路径。要为某个@FeignClient启用回退,请将fallback属性设置为实现回退的类名。您还需要将其实现声明为 Spring bean。spring-doc.cadn.net.cn

@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";
	}

}

如果需要访问导致回退触发的原因,可以使用 fallbackFactory 属性 inside @FeignClientspring-doc.cadn.net.cn

@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 框架会知道注入哪一个 bean。在某些情况下,这种行为可能并不 desirable。要关闭此行为,请将 @FeignClientprimary 属性设置为 false。spring-doc.cadn.net.cn

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

Feign 继承支持

Feign 通过单继承接口支持样板式 API。 这允许将常见的操作分组到方便的基接口中。spring-doc.cadn.net.cn

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 {

}
`0` 接口不应该在服务器端和客户端之间共享,而且在接口上标注 `1` 时不再支持在类级别上使用 `2` 。

Feign 请求/响应压缩

你可能需要为你的 Feign 请求启用请求或响应 GZIP 压缩。 你可以通过启用其中一个属性来实现:spring-doc.cadn.net.cn

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

Feign request compression gives you settings similar to what you may set for your web server:spring-doc.cadn.net.cn

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-doc.cadn.net.cn

当请求在spring.cloud.openfeign.compression.request.mime-types中设置的 mime type和在spring.cloud.openfeign.compression.request.min-request-size中设置的大小匹配时,spring.cloud.openfeign.compression.request.enabled=true会导致压缩标头被添加到请求中。功能是向服务器表明客户端期望压缩正文。基于客户端提供的标题,提供压缩正文是服务器端应用程序的责任。spring-doc.cadn.net.cn

Feign 日志记录

A logger is created for each Feign client created. By default, the name of the logger is the full class name of the interface used to create the Feign client. Feign logging only responds to the 0 level.spring-doc.cadn.net.cn

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

Logger.Level对象,您可以为每个客户端进行配置,告诉Feign记录多少日志。选项包括:spring-doc.cadn.net.cn

例如,以下代码将设置为Logger.LevelFULLspring-doc.cadn.net.cn

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

Feign 功能支持

Feign功能公开了核心Feign组件,以便可以修改这些组件。例如,功能可以对Client进行修饰,并将修饰后的实例返回给Feign。Micrometer支持是此方面的良好实际示例。请参阅Micrometer支持spring-doc.cadn.net.cn

创建一个或多个Capability个 bean 并将其放入@FeignClient配置,可以让您注册它们并修改涉及的客户端的行为。spring-doc.cadn.net.cn

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

Micrometer 支持

If all of the following conditions are true, a MicrometerObservationCapability bean is created and registered so that your Feign client is observable by Micrometer:spring-doc.cadn.net.cn

如果您的应用程序已经使用了 Micrometer,启用此功能只需将feign-micrometer放到类路径中即可。

您还可以通过以下方式禁用此功能:spring-doc.cadn.net.cn

spring.cloud.openfeign.micrometer.enabled=false 禁用 Micrometer 对所有 Feign 客户端的支持,无论客户端级别的标志值是多少:spring.cloud.openfeign.client.config.feignName.micrometer.enabled。如果希望按客户端启用或禁用 Micrometer 支持,请不要设置 spring.cloud.openfeign.micrometer.enabled 并使用 spring.cloud.openfeign.client.config.feignName.micrometer.enabled

您还可以通过注册自己的bean来自定义MicrometerObservationCapabilityspring-doc.cadn.net.cn

@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:spring-doc.cadn.net.cn

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

Feign 缓存

如果使用@EnableCaching注解,则会创建并注册一个CachingCapabilitybean,以便您的Feign客户端能够识别其接口上的@Cache*注解:spring-doc.cadn.net.cn

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-doc.cadn.net.cn

Spring @RequestMapping 支持

Spring Cloud OpenFeign 提供对 Spring @RequestMapping 注解及其派生注解(如 @GetMapping@PostMapping 等)的支持。@RequestMapping 注解上的属性(包括 valuemethodparamsheadersconsumesproduces)由 SpringMvcContract 解析为请求的内容。spring-doc.cadn.net.cn

考虑以下示例:spring-doc.cadn.net.cn

使用params属性定义一个接口。spring-doc.cadn.net.cn

@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.
参数属性还支持使用多个key=value或仅一个key:
spring-doc.cadn.net.cn

  • params = { "key1=v1", "key2=v2" } 时,请求 URL 被解析为 /stores/storeId?key1=v1&key2=v2spring-doc.cadn.net.cn

  • params = "key" 时,请求 URL 被解析为 /stores/storeId?keyspring-doc.cadn.net.cn

Feign @QueryMap 支持

Spring Cloud OpenFeign 提供了一个等效的 @SpringQueryMap 注解,该注解用于将 POJO 或 Map 参数标记为查询参数映射。spring-doc.cadn.net.cn

例如,Params类定义了参数param1param2spring-doc.cadn.net.cn

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

	// [Getters and setters omitted for brevity]
}

以下 Feign 客户端通过使用 Params 类并利用 @SpringQueryMap 注解来实现:spring-doc.cadn.net.cn

@FeignClient("demo")
public interface DemoTemplate {

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

如果您需要对生成的查询参数映射进行更多控制,可以实现一个自定义的 QueryMapEncoder Bean。spring-doc.cadn.net.cn

支持 HATEOAS

Spring 提供了一些 API 来创建遵循 HATEOAS 原则的 REST 表示,Spring HateoasSpring Data RESTspring-doc.cadn.net.cn

如果您的项目使用org.springframework.boot:spring-boot-starter-hateoasStarters或org.springframework.boot:spring-boot-starter-data-restStarters,Feign HATEOAS 支持将默认启用。spring-doc.cadn.net.cn

当启用 HATEOAS 支持时,Feign 客户端被允许序列化和反序列化 HATEOAS 表示模型:EntityModelCollectionModelPagedModelspring-doc.cadn.net.cn

@FeignClient("demo")
public interface DemoTemplate {

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

Spring @MatrixVariable 支持

Spring Cloud OpenFeign 提供了对 Spring @MatrixVariable 注解的支持。spring-doc.cadn.net.cn

如果将映射传递为方法参数,则使用=连接映射中的键值对来创建@MatrixVariable路径段。spring-doc.cadn.net.cn

如果传递了不同的对象,则使用 = 将提供的 @MatrixVariable 注解中的 name(如果已定义)或带注解的变量名与提供的方法参数连接。spring-doc.cadn.net.cn

重要

即使在服务器端,Spring 不要求用户将路径段占位符的名称与矩阵变量名称相同(因为这在客户端会过于模糊),但 Spring Cloud OpenFeign 要求您添加一个路径段占位符,并且其名称必须匹配 name 注解中提供的名称(如果已定义)或标注的变量名。
spring-doc.cadn.net.cn

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

请注意,变量名称和路径段占位符都称为matrixVarsspring-doc.cadn.net.cn

@FeignClient("demo")
public interface DemoTemplate {

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

FeignCollectionFormat支持

我们通过提供 feign.CollectionFormat 注解来支持 @CollectionFormat。 您可以通过传递所需的 feign.CollectionFormat 作为注解值,将该注解放在 Feign 客户端方法上(或整个类上以影响所有方法)。spring-doc.cadn.net.cn

在下面的例子中,使用了CSV格式而不是默认的EXPLODED来处理该方法。spring-doc.cadn.net.cn

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

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

}

响应式支持

在 Spring Cloud OpenFeign 积极开发期间,OpenFeign 项目 不支持响应式客户端,例如 Spring WebClient,也无法向 Spring Cloud OpenFeign 添加此支持。spring-doc.cadn.net.cn

由于 Spring Cloud OpenFeign 项目现在被认为是功能完备的,我们计划不再添加支持,即使它在上游项目中可用。我们建议改用Spring HTTP Service Clients。两者都支持阻塞和响应式堆栈。spring-doc.cadn.net.cn

早期初始化错误

我们不建议在应用程序生命周期的早期阶段使用Feign客户端,尤其是在处理配置和初始化bean时。在bean初始化期间使用这些客户端是不受支持的。spring-doc.cadn.net.cn

同样地,根据您使用Feign客户端的方式,在启动应用程序时可能会看到初始化错误。要解决此问题,可以在自动连接您的客户端时使用ObjectProviderspring-doc.cadn.net.cn

@Autowired
ObjectProvider<TestFeignClient> testFeignClient;

Spring 数据支持

如果 Jackson 数据绑定和 spring-boot-data-commons 在类路径上,org.springframework.data.domain.Pageorg.springframework.data.domain.Sort 的转换器将自动添加。spring-doc.cadn.net.cn

取消他的脉子,请设置spring-doc.cadn.net.cn

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

参见 org.springframework.cloud.openfeign.FeignAutoConfiguration.FeignJacksonConfiguration 了解详细信息。spring-doc.cadn.net.cn

Spring@RefreshScope支持

如果启用了 Feign 客户端刷新,则每个 Feign 客户端都是使用以下方式创建的:spring-doc.cadn.net.cn

  • feign.Request.Options作为刷新范围的bean。这意味着可以针对任何Feign客户端实例刷新connectTimeoutreadTimeout等属性。spring-doc.cadn.net.cn

  • 一个被包裹在org.springframework.cloud.openfeign.RefreshableUrl下的url。这意味着,如果使用spring.cloud.openfeign.client.config.{feignName}.url属性定义了Feign客户端的URL,则可以针对任何Feign客户端实例进行刷新。spring-doc.cadn.net.cn

您可以使用POST /actuator/refresh刷新这些属性。spring-doc.cadn.net.cn

默认情况下,Feign 客户端中的刷新行为被禁用。使用以下属性启用刷新行为:spring-doc.cadn.net.cn

spring.cloud.openfeign.client.refresh-enabled=true
不要使用@RefreshScope注解标记@FeignClient接口。

OAuth2 支持

可以通过向项目中添加spring-boot-starter-oauth2-client依赖并设置以下标志来启用OAuth2支持:spring-doc.cadn.net.cn

spring.cloud.openfeign.oauth2.enabled=true

当标志设置为 true,且存在 oauth2 客户端上下文资源详情时,将创建一个 OAuth2AccessTokenInterceptor 类型的 bean。在每次请求之前,拦截器会解析所需的访问Tokens,并将其作为标头包含在内。OAuth2AccessTokenInterceptor 使用 OAuth2AuthorizedClientManager 获取 OAuth2AuthorizedClient,其中包含一个 OAuth2AccessToken。如果用户使用 spring.cloud.openfeign.oauth2.clientRegistrationId 属性指定了 OAuth2 clientRegistrationId,则将使用它来获取Tokens。如果未检索到Tokens或未指定 clientRegistrationId,则将使用从 url 主机段检索到的 serviceIdspring-doc.cadn.net.cn

TIP

使用serviceId作为OAuth2客户端注册ID对于负载均衡的Feign客户端来说非常方便。对于非负载均衡的情况,基于属性的clientRegistrationId是一种合适的方法。spring-doc.cadn.net.cn

TIP

如果您不想使用默认设置OAuth2AuthorizedClientManager,只需在配置中实例化此类型的bean即可。spring-doc.cadn.net.cn

转换负载均衡的HTTP请求

您可以使用选定的 ServiceInstance 来转换负载均衡的 HTTP 请求。spring-doc.cadn.net.cn

对于 Request,您需要实现并定义 LoadBalancerFeignRequestTransformer,如下所示:spring-doc.cadn.net.cn

@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 来指定顺序。spring-doc.cadn.net.cn

X-Forwarded 标头支持

X-Forwarded-HostX-Forwarded-Proto 的支持可以通过设置以下标志来启用:spring-doc.cadn.net.cn

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

提供URL给Feign客户端的支持方式

您可以使用以下任何一种方式为Feign客户端提供URL:spring-doc.cadn.net.cn

案例 例举 详细信息

URL 在 @FeignClient 注解中提供。spring-doc.cadn.net.cn

@FeignClient(name="testClient", url="http://localhost:8081")spring-doc.cadn.net.cn

URL从注解的url属性解析,不进行负载均衡。spring-doc.cadn.net.cn

URL 在 @FeignClient 注解和配置属性中提供。spring-doc.cadn.net.cn

@FeignClient(name="testClient", url="http://localhost:8081")和在application.yml中定义的属性作为spring.cloud.openfeign.client.config.testClient.url=http://localhost:8081spring-doc.cadn.net.cn

URL 从注解的 url 属性解析,不进行负载均衡。
配置属性中提供的 URL 保持未使用状态。spring-doc.cadn.net.cn

URL未在@FeignClient注解中提供,但在配置属性中提供了。spring-doc.cadn.net.cn

@FeignClient(name="testClient")和在application.yml中定义的属性作为spring.cloud.openfeign.client.config.testClient.url=http://localhost:8081spring-doc.cadn.net.cn

URL 根据配置属性解析,不进行负载均衡。如果spring.cloud.openfeign.client.refresh-enabled=true,则可以按照Spring RefreshScope 支持中的描述刷新配置属性中定义的 URL。spring-doc.cadn.net.cn

URL 在 @FeignClient 注解中未提供,且在配置属性中也未找到。spring-doc.cadn.net.cn

@FeignClient(name="testClient")spring-doc.cadn.net.cn

URL从注解的name属性解析,使用负载均衡。spring-doc.cadn.net.cn

AOT 和原生映像支持

Spring Cloud OpenFeign 支持 Spring AOT 转换和原生镜像,但需要关闭刷新模式、禁用 Feign 客户端刷新(默认设置)以及禁用懒加载 lazy @FeignClient 属性解析(默认设置)。spring-doc.cadn.net.cn

如果您希望在 AOT 或原生镜像模式下运行 Spring Cloud OpenFeign 客户端,请确保将 spring.cloud.refresh.enabled 设置为 false
如果您希望在AOT或原生镜像模式下运行Spring Cloud OpenFeign客户端,请确保spring.cloud.openfeign.client.refresh-enabled未被设置为true
如果您希望在AOT或原生镜像模式下运行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相关的配置属性列表。spring-doc.cadn.net.cn