Spring Cloud OpenFeign是一种基于Spring Cloud的声明式REST客户端,它简化了与HTTP服务交互的过程。它将REST客户端的定义转化为Java接口,并且可以通过注解的方式来声明请求参数、请求方式、请求头等信息,从而使得客户端的使用更加方便和简洁。同时,它还提供了负载均衡和服务发现等功能,可以与Eureka、Consul等注册中心集成使用。Spring Cloud OpenFeign能够提高应用程序的可靠性、可扩展性和可维护性,是构建微服务架构的重要工具之一。
本站(springdoc.cn)中的内容来源于 spring.io ,原始版权归属于 spring.io。由 springdoc.cn 进行翻译,整理。可供个人学习、研究,未经许可,不得进行任何转载、商用或与之相关的行为。 商标声明:Spring 是 Pivotal Software, Inc. 在美国以及其他国家的商标。 |
4.0.4-SNAPSHOT
该项目通过自动配置和与 Spring Environment 及其他 Spring 编程模型习语的绑定,为 Spring Boot 应用程序提供 OpenFeign 集成。
1. 声明式 REST 客户端: Feign
Feign 是一个声明式的 Web Service 客户端。它使编写 Web Service 客户端更容易。要使用 Feign,需要创建一个接口并对其进行注解。它有可插拔的注解支持,包括 Feign 注解和 JAX-RS 注解。Feign 还支持可插拔的编码器和解码器。Spring Cloud 增加了对 Spring MVC 注解的支持,并支持使用 Spring Web 中默认使用的 HttpMessageConverters
。Spring Cloud 集成了 Eureka、Spring Cloud CircuitBreaker以及Spring Cloud LoadBalancer,以便在使用Feign时提供一个负载均衡的http客户端。
1.1. 如何“包含” Feign
要在你的项目中包含 Feign,请使用 group 为 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);
}
}
@FeignClient("stores")
public interface StoreClient {
@RequestMapping(method = RequestMethod.GET, value = "/stores")
List<Store> getStores();
@RequestMapping(method = RequestMethod.GET, value = "/stores")
Page<Store> getStores(Pageable pageable);
@RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
Store update(@PathVariable("storeId") Long storeId, Store store);
@RequestMapping(method = RequestMethod.DELETE, value = "/stores/{storeId:\\d+}")
void delete(@PathVariable Long storeId);
}
在 @FeignClient
注解中,字符串值(上面的 "stores")是一个任意的客户端名称,它被用来创建一个 Spring Cloud LoadBalancer client.。你也可以使用 url
属性指定一个 URL
(绝对值或只是一个hostname)。application context 中的 bean 名称是接口的完全限定名称。要指定你自己的别名值,你可以使用 @FeignClient
注解的 qualifiers
值。
上面的负载均衡器客户端将想发现 "stores" service 的物理地址。如果你的应用程序是一个 Eureka 客户端,那么它将在 Eureka 服务注册中心中解析该服务。如果你不想使用Eureka,你可以使用 SimpleDiscoveryClient
在你的外部配置中配置一个服务列表。
Spring Cloud OpenFeign 支持 Spring Cloud LoadBalancer 阻塞模式的所有功能。你可以在 项目文档 中阅读更多关于它们的信息。
要在 @Configuration 注解的类上使用 @EnableFeignClients 注解,请确保指定客户端的位置,例如: @EnableFeignClients(basePackages = "com.example.clients") 或明确列出它们: @EnableFeignClients(clients = InventoryServiceFeignClient.class)
|
1.1.1. 属性解析模式
在创建 Feign
客户端 Bean 时,我们通过 @FeignClient
注解来解析传递的值。从 4.x
开始,这些值被急切地解析。这对于大多数的使用情况来说是一个很好的解决方案,而且它也允许对 AOT 的支持。
如果你需要延迟地解析属性,请将 spring.cloud.openfeign.lazy-attributes-resolution
属性值设置为 true
。
对于 Spring Cloud Contract 测试集成,应该使用延迟的属性解析。 |
1.2. 覆盖 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 {
//..
}
在这种情况下,client 由 FeignClientsConfiguration
中已有的组件和 FooConfiguration
中的任何组件组成(后者将覆盖前者)。
FooConfiguration 不需要用 @Configuration 来注解。然而,如果它是,那么请注意将它从任何 @ComponentScan 中排除,否则会包括这个配置,因为它将成为 feign.Decoder 、feign.Encoder 、feign.Contract 等的默认来源。这可以通过把它放在与任何 @ComponentScan 或 @SpringBootApplication 单独的、不重叠的包中来避免,或者在 @ComponentScan 中明确排除它。
|
使用 @FeignClient 注解的 contextId 属性,除了改变 ApplicationContext 集合的名称外,它还将覆盖客户端名称的别名,它将被用作为该客户端创建的配置Bean名称的一部分。
|
以前,使用 url 属性,不需要使用 name 属性。现在,使用 name 是必须的。
|
在 name
和 url
属性中支持占位符。
@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
在 classpath 并且ObservationRegistry
是可用的 -
MicrometerCapability
micrometerCapability: 如果feign-micrometer
在 classpath,MeterRegistry
可用,并且ObservationRegistry
不可用 -
CachingCapability
cachingCapability: 如果使用了@EnableCaching
注解,可以通过spring.cloud.openfeign.cache.enabled
来禁用。 -
Contract
feignContract:SpringMvcContract
-
Feign.Builder
feignBuilder:FeignCircuitBreaker.Builder
-
Client
feignClient: 如果 Spring Cloud LoadBalancer 在 classpath 上,就会使用FeignBlockingLoadBalancerClient
。如果它们都不在 classpath 上,则使用默认的 feign 客户端。
spring-cloud-starter-openfeign 支持 spring-cloud-starter-loadbalancer 。然而,由于它是一个可选的依赖项,如果你想使用它,你需要确保它已经被添加到你的项目中。
|
通过将 spring.cloud.openfeign.okhttp.enabled
或 spring.cloud.openfeign.httpclient.hc5.enabled
分别设置为 true
并将其放在 classpath 上,就可以使用 OkHttpClient 和 Apache HttpClient 5 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
。你可以在附录中找到你可以定制的属性的完整列表。
从Spring Cloud OpenFeign 4开始,Feign Apache HttpClient 4不再被支持。我们建议使用Apache HttpClient 5代替。 |
Spring Cloud OpenFeign 默认不为 feign 提供以下Bean,但仍然从 application context 查找这些类型的 Bean 来创建 feign 客户端:
-
Logger.Level
-
Retryer
-
ErrorDecoder
-
Request.Options
-
Collection<RequestInterceptor>
-
SetterFactory
-
QueryMapEncoder
-
Capability
(MicrometerObservationCapability
和CachingCapability
默认提供) 默认情况下,会创建一个Retryer.NEVER_RETRY
类型的Retryer
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");
}
}
这就用 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
指的是 @FeignClient
value
,它也被别名为 @FeignClient
name
和 @FeignClient
contextId
。在负载均衡的情况下,它也对应于将被用来检索实例的服务器应用程序的 serviceId
。
默认配置可以在 @EnableFeignClients
属性 defaultConfiguration
中指定,其方式与上面描述的类似。不同的是,这个配置将适用于所有 feign 客户端。
如果你喜欢用配置属性来配置所有的 @FeignClient
,你可以用 default
feign name 创建配置属性。
你可以使用 spring.cloud.openfeign.client.config.feignName.defaultQueryParameters
和 spring.cloud.openfeign.client.config.feignName.defaultRequestHeaders
来指定查询参数和 header 信息,它们将与名为 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
。
如果我们想创建多个具有相同名称或网址的feign客户端,使它们指向同一台服务器,但每个都有不同的自定义配置,那么我们必须使用 @FeignClient
的 contextId
属性,以避免这些配置 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 来改变这种行为。
|
1.2.1. SpringEncoder
配置
在我们提供的 SpringEncoder
中,我们为二进制内容类型设置 null
字符集,为所有其他类型设置 UTF-8
。
你可以通过将 spring.cloud.openfeign.encoder.charset-from-content-type
的值设置为 true
来修改此行为,以便从 Content-Type
头的字符推导出字符集。
1.3. 超时处理
我们可以在默认客户端和命名客户端上配置超时。OpenFeign
使用两个超时参数:
-
connectTimeout
防止因服务器处理时间过长而阻塞调用者。 -
readTimeout
从连接建立时开始应用,当返回响应的时间过长时就会被触发。
如果服务器没有运行或可用,则数据包会导致连接被拒绝。通信以错误信息或 fallback 的方式结束。如果 connectTimeout 设置得很低,这可能在它之前发生。执行查找和接收这种数据包所需的时间导致了这一延迟的重要部分。它可能根据涉及DNS查询的远程主机而改变。
|
1.4. 手动创建 Feign Client
在某些情况下,可能需要对你的 Feign 客户进行定制,而使用上述方法是不可能的。在这种情况下,你可以使用 Feign Builder API 来创建客户端。下面是一个例子,它创建了两个具有相同接口的 Feign 客户端,但每个客户端都配置了一个单独的请求拦截器(request interceptor)。
@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 原生注解。
|
你也可以使用 Builder
来配置 FeignClient
,使其不从父级上下文中继承bean。你可以通过覆盖调用 Builder
的 inheritParentContext(false)
来做到这一点。
1.5. Feign Spring Cloud CircuitBreaker 的支持
如果 Spring Cloud CircuitBreaker 在 classpath 上,并且 spring.cloud.openfeign.circuitbreaker.enabled=true
,Feign 将用 circuit breaker 来包装所有方法。
为了在每个客户端的基础上禁用 Spring Cloud CircuitBreaker
的支持,创建一个具有 "prototype" scope 的 vanilla Feign.Builder
,例如:
@Configuration
public class FooConfiguration {
@Bean
@Scope("prototype")
public Feign.Builder feignBuilder() {
return Feign.builder();
}
}
circuit breaker 的名称遵循这种模式 <feignClientClassName>#<calledMethod>(<parameterTypes>)
。当调用一个带有 FooClient
接口的 @FeignClient
,并且被调用的接口方法 bar
没有参数,那么 circuit breaker 的名称将是 FooClient#bar()
。
从2020.0.2开始,circuit breaker 名称模式已经从 <feignClientName>_<calledMethod> 改变。使用2020.0.4中引入的 CircuitBreakerNameResolver ,circuit breaker 名称可以保留旧模式。
|
提供一个 CircuitBreakerNameResolver
的bean,你可以改变 circuit breaker 名称模式。
@Configuration
public class FooConfiguration {
@Bean
public CircuitBreakerNameResolver circuitBreakerNameResolver() {
return (String feignClientName, Target<?> target, Method method) -> feignClientName + "_" + method.getName();
}
}
要启用 Spring Cloud CircuitBreaker group,请将 spring.cloud.openfeign.circuitbreaker.group.enabled
属性设置为 true
(默认为 false
)。
1.6. 使用配置属性配置 CircuitBreaker
你可以通过配置属性来配置 CircuitBreaker。
例如,如果你有这个 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 之前使用的 circuit breaker name,你可以将 spring.cloud.openfeign.circuitbreaker.alphanumeric-ids.enabled 设置为 false 。
|
1.7. Feign Spring Cloud CircuitBreaker Fallback
Spring Cloud CircuitBreaker 支持 fallback 的概念:一个默认的代码路径,在 circuit 打开或出现错误时执行。要为一个给定的 @FeignClient
启用 fallback,将 fallback
属性设置为实现 fallback 的类名。你还需要将你的实现声明为一个 Spring Bean。
@FeignClient(name = "test", url = "http://localhost:${server.port}/", fallback = Fallback.class)
protected interface TestClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
@RequestMapping(method = RequestMethod.GET, value = "/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";
}
}
如果需要访问使 fallback 触发的原因,可以使用 @FeignClient
里面的 fallbackFactory
属性。
@FeignClient(name = "testClientWithFactory", url = "http://localhost:${server.port}/",
fallbackFactory = TestFallbackFactory.class)
protected interface TestClientWithFactory {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
@RequestMapping(method = RequestMethod.GET, value = "/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";
}
}
1.8. Feign 和 @Primary
当使用 Feign 与 Spring Cloud CircuitBreaker fallback 时,ApplicationContext
中存在多个相同类型的Bean。这将导致 @Autowired
不起作用,因为没有确切的一个 bean,或一个被标记为 primary 的 bean。为了解决这个问题,Spring Cloud OpenFeign 将所有Feign实例标记为 @Primary
,因此 Spring Framework 将知道要注入哪个Bean。在某些情况下,这可能是不可取的。要关闭这种行为,请将 @FeignClient
的 primary
属性设置为 false
。
@FeignClient(name = "hello", primary = false)
public interface HelloClient {
// methods here
}
1.9. Feign 继承的支持
Feign 通过单继承接口支持模板式的api。这允许将常见的操作分组到方便的基础接口中。
public interface UserService {
@RequestMapping(method = RequestMethod.GET, value ="/users/{id}")
User getUser(@PathVariable("id") long id);
}
@RestController
public class UserResource implements UserService {
}
package project.user;
@FeignClient("users")
public interface UserClient extends UserService {
}
@FeignClient 接口不应该在服务器和客户端之间共享,并且不再支持在类级别上用 @RequestMapping 注解 @FeignClient 接口。
|
1.10. Feign request/response 压缩
你可以考虑为你的 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
这些属性允许你对压缩媒体类型和最小请求阈值长度进行选择。
由于 OkHttpClient 使用 "transparent"(透明) 压缩,即在存在 content-encoding 或 accept-encoding 头的情况下禁用,所以当 feign.okhttp.OkHttpClient 出现在 classpath 上并且 spring.cloud.openfeign.okhttp.enabled 被设置为 true 时,我们不会启用压缩。
|
1.11. Feign 日志
每个创建的 Feign 客户端都会创建一个logger。默认情况下,logger 的名字是用于创建Feign客户端的接口的全类名称。Feign 的日志只响应 DEBUG
级别。
logging.level.project.user.UserClient: DEBUG
你可以为每个客户端配置 Logger.Level
对象,告诉 Feign 要记录多少内容。选择是:
-
NONE
, 没日志(默认)。 -
BASIC
, 只记录请求方法和URL以及响应状态代码和执行时间。 -
HEADERS
, 记录基本信息以及请求和响应头。 -
FULL
, 记录请求和响应的header、正文和元数据。
例如,下面将设置 Logger.Level
为 FULL
:
@Configuration
public class FooConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
1.12. Feign Capability 的支持
Feign Capability 暴露了Feign的核心组件,因此这些组件可以被修改。例如,这些功能可以接受客户端,对其进行装饰,并将装饰后的实例反馈给 Feign。对 Micrometer
的支持就是一个很好的现实生活中的例子。参见 [micrometer-support]。
创建一个或多个 Capability
Bean并将其置于 @FeignClient
配置中,可以让你注册它们并修改相关客户端的行为。
@Configuration
public class FooConfiguration {
@Bean
Capability customCapability() {
return new CustomCapability();
}
}
1.13. Micrometer 的支持
如果以下所有条件为 true
,就会创建并注册一个 MicrometerObservationCapability
Bean,这样你的 Feign 客户端就可以被 Micrometer 观察到:
-
feign-micrometer
在 classpath 上。 -
ObservationRegistry
bean 可用。 -
feign micrometer 属性设置为
true
(默认)-
spring.cloud.openfeign.micrometer.enabled=true
(针对所有客户) -
spring.cloud.openfeign.client.config.feignName.micrometer.enabled=true
(针对单个客户端)
-
如果你的应用程序已经使用了 Micrometer,启用这个功能就像把 feign-micrometer 放到你的classpath上一样简单。
|
你也可以通过以下两种方式禁用该功能:
-
从你的 classpath 中排除
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);
}
}
1.14. 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
来禁用该功能。
1.15. Feign @QueryMap 的支持
Spring Cloud OpenFeign 提供了一个等价的 @SpringQueryMap
注解,用于将POJO或Map参数注解为查询参数map。
例如,Params
类定义了参数 param1
和 param2
:
// 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);
}
如果你需要对生成的查询参数 Map 有更多控制,你可以实现一个自定义的 QueryMapEncoder
Bean。
1.16. HATEOAS 的支持
Spring提供了一些API来创建遵循 HATEOAS 原则的REST表示, Spring Hateoas 和 Spring Data REST。
如果你的项目使用 org.springframework.boot:spring-boot-starter-hateoas
starter 或 org.springframework.boot:spring-boot-starter-data-rest
starter,Feign HATEOAS 支持被默认启用。
当HATEOAS支持被启用时,Feign 客户端被允许序列化和反序列化 HATEOAS 表示模型: EntityModel、 CollectionModel 和 PagedModel.。
@FeignClient("demo")
public interface DemoTemplate {
@GetMapping(path = "/stores")
CollectionModel<Store> getStores();
}
1.17. Spring @MatrixVariable 的支持
Spring Cloud OpenFeign提供对Spring @MatrixVariable
注解的支持。
如果一个 map 被作为方法参数传递,@MatrixVariable
path 段是通过用 =
连接 map 中的键值对而创建的。
如果传递了一个不同的对象,那么在 @MatrixVariable
注解中提供的 name
(如果定义了的话)或者注解的变量名称将使用 =
与提供的方法参数结合起来。
- IMPORTANT
-
尽管在服务器端,Spring 并不要求用户将路径段占位符的名称与 matrix variable 的名称相同,因为这在客户端太模糊了,Spring Cloud OpenFeign要求你添加一个路径段占位符,其名称要与
@MatrixVariable
注解(如果定义了)中提供的name
或注解的变量名称相符。
例如:
@GetMapping("/objects/links/{matrixVars}")
Map<String, List<String>> getObjects(@MatrixVariable Map<String, List<String>> matrixVars);
注意,变量名和 path 段占位符都被称为 matrixVars
。
@FeignClient("demo")
public interface DemoTemplate {
@GetMapping(path = "/stores")
CollectionModel<Store> getStores();
}
1.18. 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);
}
1.19. 响应式的支持
由于 OpenFeign项目 目前不支持响应式客户端,如 Spring WebClient,Spring Cloud OpenFeign也不支持。一旦核心项目中可用,我们将在这里添加对它的支持。
在这之前,我们建议使用 feign-reactive 来支持 Spring WebClient。
1.19.1. 早期的初始化错误
根据你使用 Feign 客户端的方式,你可能会在启动你的应用程序时看到初始化错误。为了解决这个问题,你可以在自动连接客户端时使用一个 ObjectProvider
。
@Autowired
ObjectProvider<TestFeignClient> testFeignClient;
1.20. Spring Data 的支持
如果 Jackson Databind 和 Spring Data Commons 在classpath上,org.springframework.data.domain.Page
和 org.springframework.data.domain.Sort
的 converter 将被自动添加。
要禁用这种行为,请设置:
spring.cloud.openfeign.autoconfiguration.jackson.enabled=false
详见 org.springframework.cloud.openfeign.FeignAutoConfiguration.FeignJacksonConfiguration
。
1.21. Spring @RefreshScope
的支持
如果启用了Feign客户端刷新,每个Feign客户端的创建都有:
-
feign.Request.Options
作为一个 refresh scope 的bean。这意味着诸如connectTimeout
和readTimeout
等属性可以针对任何Feign客户端实例进行刷新。 -
在
org.springframework.cloud.openfeign.RefreshableUrl
下包装的url。这意味着如果用spring.cloud.openfeign.client.config.{feignName}.url
属性定义 Feign 客户端的URL,可以针对任何 Feign 客户端实例进行刷新。
你可以通过 POST /actuator/refresh
刷新这些属性。
默认情况下,Feign 客户端的刷新行为是禁用的。使用以下属性来启用刷新行为:
spring.cloud.openfeign.client.refresh-enabled=true
不要用 @RefreshScope 注解来注解 @FeignClient 接口。
|
1.22. OAuth2 的支持
可以通过设置以下标志启用OAuth2支持:
spring.cloud.openfeign.oauth2.enabled=true
当该标志被设置为 "true",且oauth2客户端 context resource detail 存在时,就会创建一个 OAuth2AccessTokenInterceptor
类的 bean。在每次请求之前,拦截器都会解析所需的访问令牌,并将其作为一个header。OAuth2AccessTokenInterceptor
使用 OAuth2AuthorizedClientManager
来获取持有 OAuth2AccessToken
的 OAuth2AuthorizedClient
。如果用户使用 spring.cloud.openfeign.oauth2.clientRegistrationId
属性指定了一个 OAuth2 clientRegistrationId
,它将被用来检索令牌。如果没有检索到令牌或没有指定 clientRegistrationId
,将使用从 url
host 段检索的 serviceId
。
- TIP
-
使用
serviceId
作为 OAuth2 客户端的 registrationId 对于负载均衡的Feign客户端是很方便的。对于非负载均衡的客户端,基于属性的clientRegistrationId
是一个合适的方法。 - TIP
-
如果你不想使用
OAuth2AuthorizedClientManager
的默认设置,你可以直接在你的配置中实例化一个这种类型的bean。
1.23. 转换负载均衡的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
来指定这个顺序。
1.24. X-Forwarded Headers 的支持
X-Forwarded-Host
和 X-Forwarded-Proto
支持可以通过设置以下标志启用:
spring.cloud.loadbalancer.x-forwarded.enabled=true
1.25. 支持向 Feign 客户端提供URL的方法
你可以通过以下任何一种方式向Feign客户端提供一个URL:
场景 | 例子 | 细节 |
---|---|---|
URL是在 |
|
URL是从注解的 |
URL是在 |
|
URL是从注解的 |
URL没有在 |
|
URL 从配置属性中解析,没有负载均衡。如果 |
在 |
|
URL是从注解的 |
1.26. AOT 和原生镜像的支持
Spring Cloud OpenFeign 支持 Spring AOT 转换和原生镜像,但是,只有在禁用刷新模式、禁用Feign客户端刷新(默认设置)和禁用 延迟的 @FeignClient
属性解析(默认设置)的情况下,才能支持。
如果你想在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] 标志的镜像,就有可能覆盖 @FeignClient 的 url 值。为了实现覆盖,在构建时也必须通过属性而不是 @FeignClient 属性来设置一个 url 值。
|