Spring Cloud OpenFeign Features

Spring Cloud OpenFeign Features

官方原版:docs.spring.io/spring-clou...

Declarative REST Client:Feign

Feign是一个声明式的web服务客户端,创建一个interface并添加注解就可以创建一个web服务客户端。提供了可插拔的注解包含Feign的注解和JAX-RS的注解,Feign同样支持可插拔的编码解码器。SpringCloud为Spring MVC的注解提供了支持并且默认使用同样的HttpMessageConverters。当使用Feign时,SpringCloud集成了Eureka,Spring Cloud CircuitBreaker以及Spring Cloud LoadBalancer来为Feign提供负载均衡方面的支持。

How to Include Feign

添加group为org.springframework.cloud,artifact id为spring-cloud-starter-openfeign的starter依赖。Spring Cloud Project page。查看Spring Cloud Project page以查看构建细节。

SpringBoot app 示例:

java 复制代码
@SpringBootApplication
@EnableFeignClients
public class Application {

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

}

StoreClient.java

java 复制代码
@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。这个bean在application context的名称是这个interface的全限定类名。为了指定你自己的别名,你可以使用@FeignClient的qualifiers值。

load-balancer client会根据"stores"来找到物理的地址。如果你的应用是一个Eureka client,那么就会用过Eureka注册中心来解析"stores"。如果你不想使用Eureka,你可以通过SimpleDiscoveryClient在你的外部配置中定义一系列servers。

SpringCloud OpenFeign支持Spring Cloud LoadBanlancer的阻塞模式的所有Feature。更多信息请参考project documentation

Tip

要在@Configuration上使用注解 @EnableFeignClients ,请确保指定client的位置,例如 @EnableFeignClients(basePackages = "com.example.clients")或者把他们详细列出来: @EnableFeignClients(clients = InventoryServiceFeignClient.class)

Attribute resolution mode

当创建Feign client bean的时候,我们处理通过@FeignClient注解传入的value。对于4.x,values会被立即处理,这对于大多数的使用场景都是好的解决方式,而且还支持AOT。

如果你希望这些参数被懒加载式处理,设置spring.cloud.openfeign.lazy-attributes-resolution=true

Overriding Feign Default

SpringCloud feign的一个核心概念就是具名客户端,每个feign client都作为一组组件的一部分来发送请求,并且这一组组件的名字由@FeignClient注解所给。Spring Cloud会使用FeignClientsConfiguration来创建出一组组件作为ApplicationContext,其中包含一个feign.Decoder,一个feign.Encoder和一个feign.Contract。可以使用@FeignClient注解的contextId参数来覆盖这一组组件的名字。

SpringCloud可以让你完全控制feign客户端,通过声明额外的配置(其在FeignClientsConfiguration之上),如下所示

java 复制代码
@FeignClient(name = "stores", configuration = FooConfiguration.class)
public interface StoreClient {
	//..
}

在这个事例中 FooConfiguration的配置会覆盖FeignClientsConfiguration的配置。

Note

FooConfiguration不需要被@Configuration注解。然而,如果被@Configuration注解了,要注意不要被@ComponentScan扫描到,否则它将会作为feign.Decoder, feign.Encoder, feign.Contract等,默认的配置。你可以将他们放在与@ComponentScan@SpringBootApplication分离的包下,或者显式地从@ComponentScan排除掉。

Note

使用@FeignClient注解的contextId参数除了改变ApplicationContext的名字外,还会覆盖client的别名,还会作为client所创建的configuration bean名称的一部分。

Warning

之前,使用url参数时,不必要有name参数,但是现在是必要的。

nameurl参数是支持占位符的

java 复制代码
@FeignClient(name = "${feign.name}", url = "${feign.url}")
public interface StoreClient {
	//..
}

SpringCloud OpenFeign默认提供了系列的bean (BeanType beanName: ClassName

  • Decoder feignDecoder: ResponseEntityDecoder (which wraps a SpringDecoder)
  • Encoder feignEncoder: SpringEncoder
  • Logger feignLogger: Slf4jLogger
  • MicrometerObservationCapability micrometerObservationCapability: If feign-micrometer is on the classpath and ObservationRegistry is available
  • MicrometerCapability micrometerCapability: If feign-micrometer is on the classpath, MeterRegistry is available and ObservationRegistry is not available
  • CachingCapability cachingCapability: If @EnableCaching annotation is used. Can be disabled via spring.cloud.openfeign.cache.enabled.
  • Contract feignContract: SpringMvcContract
  • Feign.Builder feignBuilder: FeignCircuitBreaker.Builder
  • Client feignClient: 如果SpringCloud LoadBanlancer在classpath上,会使用FeignBlockingLoadBalancerClient,如果不在classpath上,就会使用默认的feign client。

Note

spring-cloud-starter-openfeign支持 spring-cloud-starter-loadbalancer,然而,作为一个可选的依赖,如果想使用它请确保该依赖在你的classpath下。

OkHttpClient, Apache HttpClient 5 和Http2Client, Feign clients可以通过设置相应配置并添加对应依赖来启用相应HttpClient, spring.cloud.openfeign.okhttp.enabledspring.cloud.openfeign.httpclient.hc5.enabledspring.cloud.openfeign.http2client.enabled

你可以进一步通过配置项来进一步对http clients进行自定义。例如spring.cloud.openfeign.httpclient.xxxhttpclient为前缀的配置项将会应用于所有客户端,以httpclient.hc5为前缀的将应用于Apache HttpClient5。以httpclient.okhttp为前缀的将应用于OkHttpClient 。以httpclient.http2为前缀的将应用于Http2Client。可以在附录中查询完整的配置列表。对于HttpClient 5 ,你还可以实现HttpClientBuilderCustomizer接口来实现编码式配置。

Tip

从Spring Cloud OpenFeign4 开始,Feign Apache HttpClient 4 不再被支持。所以我们建议以使用Apache HttpClient 5。

Spring Cloud OpenFeign默认并没有提供下面的bean,但是在创建feign client的时候会在application context中查找这些类型的bean。

  • Logger.Level
  • Retryer
  • ErrorDecoder
  • Request.Options
  • Collection<RequestInterceptor>
  • SetterFactory
  • QueryMapEncoder
  • Capability (MicrometerObservationCapability and CachingCapability are provided by default)

Retryer 类型的 Retryer.NEVER_RETRY bean会被默认创建,它会禁用掉重试。需要注意的是,这种重试行为与Feign默认行为是不一样的。默认的将会自动重试IOExceptions,将它们看做短暂的网络相关的异常,还会重试那些从ErrorDecoder中抛出的任何RetryableException 。

创建这些类型的bean,并将它们放置在@FeignClient配置中(像上面的 FooConfiguration 一样)。这样你就可以覆盖每一个上述的bean。例如

java 复制代码
@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,并添加一个RequestInterceptorRequestInterceptor的集合中。

@FeignClient还可以使用configuration properties进行配置

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。在负载均衡场景下,它相当于server app的serviceId,用于拿取对应的实例。在其中所配置的哪些类必须要在Spring Context中有一个bean或者有一个默认的构造函数。

可以在@EnableFeignClients注解的defaultConfiguration参数中指定默认的配置,它会对所有的feign client生效。

如果你倾向于使用配置项来配置所有的@FeignClient,那么你可以以default作为feign 名称进行配置。

你可以使用spring.cloud.openfeign.client.config.feignName.defaultQueryParametersspring.cloud.openfeign.client.config.feignName.defaultRequestHeaders来指定名为feignName的client每次请求所携带的query paramters和headers。

application.yml

yml 复制代码
spring:
	cloud:
		openfeign:
			client:
				config:
					default:
						connectTimeout: 5000
						readTimeout: 5000
						loggerLevel: basic

如果同时创建了 @Configuration bean和配置文件,那么会使用配置文件进行配置。但是你如果想改成 @Configuration优先的形式,你可以设置 spring.cloud.openfeign.client.default-to-properties=false

如果你想创建具有相同name或url的feign client,让他们指向同一server,但是又具有不同的自定义配置,你可以使用@FeignClientcontextId参数来避免这些configuration bean的冲突。

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

你可以配置FeignClient 不从父Context中继承beans。你可以覆盖FeignClientConfigurerbean中的inheritParentConfiguration()为false。

java 复制代码
@Configuration
public class CustomConfiguration {
	@Bean
	public FeignClientConfigurer feignClientConfigurer() {
		return new FeignClientConfigurer() {
			@Override
			public boolean inheritParentConfiguration() {
				 return false;
			}
		};
	}
}

Tip

默认情况下,Feign client不会对/进行编码,你可以通过设置spring.cloud.openfeign.client.decodeSlash=false来改变该行为。

SpringEncoder configuration

在我们提供的SpringEncoder中,我们为二进制内容设置了空字符集,对于其他设置了UTF-8字符集。

Timeout Handling

我们可以为默认和named client上配置超时时间。OpenFeign可以使用两种超时参数

  • connectTimeout防止由于服务器处理时间过长而阻塞调用者
  • readTimeout是从连接建立之后,返回response时间过长是被触发

Creating Feign Clients Manually

你可以通过Feign Builder API来创建Client。下面示例展示了使用相同接口却配置了不同请求拦截器的Feign Client。

java 复制代码
@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");
	}
}

Note

在上面示例中,FeignClientsConfiguration.class是由 Spring Cloud OpenFeign提供的默认配置。

Note

PROD-SVC是client所请求服务的name

Note

Feign的 Contract 对象定义了什么注解是在接口中可用的。autowired Contractbean为SpringMVC注解提供了支持而不是使用Feign原生的注解。

你同样可以使用Builder来配置FeignClient不从父Context中继承beans,通过调用 inheritParentContext(false)

Feign Spring Cloud CircuitBreaker Support(对熔断器的支持)

如果Spring Cloud CircuitBreaker 在classpath中,并且设置了spring.cloud.openfeign.circuitbreaker.enabled=true,那么Feign会为所有的方法用熔断器包裹。

想要在每个client上禁用 Spring Cloud CircuitBreaker,可以创建一个普通的Feign.Builder,设置其为prototype

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

熔断器的命名遵循的规则是<feignClientClassName>#<calledMethod>(<parameterTypes>)。当调用FooClient中的为bar的方法且无参数时,那这个熔断器会命名为FooClient#bar()

Note

从2020.0.2开始,熔断器的名称就从<feignClientName>_<calledMethod>改变了。根据2020.0.4的介绍,可以使用CircuitBreakerNameResolver自定义命名模式。

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

为了启用SpringCloud CircuitBreaker group,设置spring.cloud.openfeign.circuitbreaker.group.enabled=true

Configuring CircuitBreakers With Configuration Properties

你可以通过configuration properties来对CircuitBreakers 进行配置

例如,你有个Feign client定义如下

java 复制代码
@FeignClient(url = "http://localhost:8080")
public interface DemoClient {

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

你可以对其使用configuration properties配置

yaml 复制代码
spring:
  cloud:
    openfeign:
      circuitbreaker:
        enabled: true
        alphanumeric-ids:
          enabled: true
resilience4j:
  circuitbreaker:
    instances:
      DemoClientgetDemo:
        minimumNumberOfCalls: 69
  timelimiter:
    instances:
      DemoClientgetDemo:
        timeoutDuration: 10s

Note

如果你想要回到Spring Cloud 2022.0.0之前熔断器命名的方式,你可以设置spring.cloud.openfeign.circuitbreaker.alphanumeric-ids.enabled=true

Feign Spring Cloud CircuitBreaker Fallbacks

Spring Cloud CircuitBreaker 支持fallback的模式,即当连接不通或者发生错误时,会执行特定的代码。为了对@FeignClient启用fallback,可以添加实现fallback的类,并将类名设置到fallback参数中。需要注意实现fallback的类应注册到Spring中作为bean。

java 复制代码
@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,可以使用 @FeignClientfallbackFactory 参数。

java 复制代码
@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";
	}

}

Feign and @Primary

当在Feign中使用Spring Cloud CircuitBreaker fallback时,在ApplicationContext中对于同一类型会有多个bean。这会导致@Autowired工作不正常。因此SpringCloud OpenFeign会把所有Feign示例标记为@Primary,这样Spring框架就知道应该注入哪个bean。如果你不需要该特性,可以将@FeignClientprimary参数设置为false。

java 复制代码
@FeignClient(name = "hello", primary = false)
public interface HelloClient {
	// methods here
}

Feign Inheritance Support

Feign支持通过继承interface来实现模板api的功能,这样就可以方便地将常用的操作分别放在一些base interface中。

java 复制代码
public interface UserService {

	@RequestMapping(method = RequestMethod.GET, value ="/users/{id}")
	User getUser(@PathVariable("id") long id);
}
java 复制代码
@RestController
public class UserResource implements UserService {

}
java 复制代码
@FeignClient("users")
public interface UserClient extends UserService {

}

Warning

@FeignClientinterfaces不应该在server和client之间共享,并且在@FeignClient在类层面用@RequestMapping注解是不被支持的。

Feign request/response compression

你也许想对你的Feign请求响应开启GZIP压缩功能,可以进行如下配置

properties 复制代码
spring.cloud.openfeign.compression.request.enabled=true
spring.cloud.openfeign.compression.response.enabled=true

Feign请求的压缩设置与对web server的设置很相似

properties 复制代码
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

这些配置让你可以修改压缩的media type和最小请求大小阈值。

Feign logging

对于每一个FeignClient,都会有一个相应的logger,logger的名称是创建feign client的interface的全类名。Feign logging只对应DEBUGlevel。

properties 复制代码
logging.level.project.user.UserClient: DEBUG

Logger.Level对象用来告诉Feign怎么去log

  • NONE, 不打印日志(默认)
  • BASIC, 请求方法和URL,以及响应status code和执行时间
  • HEADERS, 打印basis日志,还有请求和响应headers。
  • FULL, 打印请求和响应的headers,body以及元数据

例如,如下设置Logger.LevelFULL

java 复制代码
@Configuration
public class FooConfiguration {
	@Bean
	Logger.Level feignLoggerLevel() {
		return Logger.Level.FULL;
	}
}
相关推荐
Wx-bishekaifayuan18 分钟前
django电商易购系统-计算机设计毕业源码61059
java·spring boot·spring·spring cloud·django·sqlite·guava
customer0823 分钟前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
Stringzhua2 小时前
【SpringCloud】Kafka消息中间件
spring·spring cloud·kafka
想进大厂的小王7 小时前
项目架构介绍以及Spring cloud、redis、mq 等组件的基本认识
redis·分布式·后端·spring cloud·微服务·架构
customer088 小时前
【开源免费】基于SpringBoot+Vue.JS医院管理系统(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·开源·intellij-idea
杨荧8 小时前
【JAVA毕业设计】基于Vue和SpringBoot的服装商城系统学科竞赛管理系统
java·开发语言·vue.js·spring boot·spring cloud·java-ee·kafka
aloha_7891 天前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
茶馆大橘1 天前
微服务系列五:避免雪崩问题的限流、隔离、熔断措施
java·jmeter·spring cloud·微服务·云原生·架构·sentinel
荆州克莱1 天前
[FE] React 初窥门径(四):React 组件的加载过程(render 阶段)
spring boot·spring·spring cloud·css3·技术