文章目录
Feign原意为伪装,是一个声明式的伪Java Http客户端,Feign不做任何的请求处理,Feign通过处理注解生成Request模板,从而简化了Http API的开发。开发人员可以使用注解的方式定制Request API模板,在发送Http Request请求之前,Feign通过处理注解的方式替换掉Request模板中的参数,生成真正的Request,并交给Java Http客户端去处理。利用这种方式,使得开发者只需关注Feign注解模板的开发,而不用关注Http请求本身,简化了Http请求的过程,使得程序中的Http请求变得简单和容易理解。Feign提供Feign提供可插拔的配置注解和encoder、decoder。也可以通过集成Ribbon 和 Eureka、Spring Cloud CircuitBreaker 以及 Spring Cloud LoadBalancer 来使 Feign 可成为负载均衡的 http 客户端。
一、引入启用OpenFeign
1、在使用了SpringBoot和SpringCloud的项目中:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、在主启动类上添加@EnableFeignClients注解
@SpringBootApplication
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
二、OpenFeign的简单使用
@FeignClient(value="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);
}
1、@FeignClient
在上例的@FeignClient 注释中,字符串值(上面的“stores”)是一个任意的客户端名称(微服务中的服务名),它用于创建 Ribbon 负载平衡器或 Spring Cloud LoadBalancer。你还可以使用 url 属性(绝对值或仅主机名)指定真实接口的 URL。
Spring Cloud Netflix Ribbon 现在处于维护模式,因此建议改用 Spring Cloud LoadBalancer。为此,需将 spring.cloud.loadbalancer.ribbon.enabled 的值设置为 false。
一个FeignClient下的具体接口和SpringMVC中编写接口的方式是一致的。当调用这些伪装的接口时,OpenFeign就会通过在@FeignClient中配置的服务名或者URL去寻找调用真实的接口。
2、使用StoreClient
@Service
public class TestService {
@Autowired
StoreClient storeClient;
public String stores(){
return storeClient.getStores();
}
}
当调用storeClient的getStores()方法时,就会去请求stores服务下的/stores接口。
三、覆盖Feign的默认配置
Spring Cloud 使用 FeignClientsConfiguration 为每个命名的客户端按需创建一个的ensemble 作为 ApplicationContext。ensemble 包含(除其他外)一个 feign.Decoder、一个 feign.Encoder 和一个 feign.Contract。可以使用 @FeignClient 注解的 contextId 属性来覆盖ensemble 的名称。
Spring Cloud 允许通过使用 @FeignClient 声明额外的配置(在 FeignClientsConfiguration 之上)来完全控制 feign 客户端。例如:
@FeignClient(name = "stores", configuration = FooConfiguration.class)
public interface StoreClient {
//..
}
在上例中,客户端由 FeignClientsConfiguration 中已有的组件和 FooConfiguration 中的任何组件组成(后者将覆盖前者)。FooConfiguration 不需要用@Configuration 注解。但如果使用了@Configuration 注解,那么请注意将其从任何包含此配置的@ComponentScan 中排除,因为在指定时它将成为 feign.Decoder、feign.Encoder、feign.Contract 等的默认源。可以以与上述类似的方式在 @EnableFeignClients 属性 defaultConfiguration 中指定默认配置。不同之处在于此配置将适用于所有 feign 客户端。
以前使用 url 属性时不需要 name 属性。现在需要。
Spring Cloud OpenFeign默认提供以下bean:
BeanType | beanName | ClassName |
---|---|---|
Decoder | feignDecoder | ResponseEntityDecoder |
Encoder | feignEncoder | SpringEncoder |
Logger | feignLogger | Slf4jLogger |
Contract | feignContract | SpringMvcContract |
Feign.Builder | feignBuilder | HystrixFeign.Builder |
Feign.Builder | feignBuilder | FeignCircuitBreaker.Builder |
Client | feignClient | LoadBalancerFeignClient(有Ribbon时)、FeignBlockingLoadBalancerClient (有Spring Cloud LoadBalancer时),都没有的话就是默认Client |
OkHttpClient 和 ApacheHttpClient 以及 ApacheHC5 feign 客户端可以通过分别将 feign.okhttp.enabled 或 feign.httpclient.enabled 或 feign.httpclient.hc5.enabled 设置为 true并将导入其依赖包来使用。也可以通过提供 org.apache.http.impl.client.CloseableHttpClient 的 bean 来自定义使用的 HTTP 客户端。
@FeignClient 也可以使用配置属性进行配置。
feign:
client:
config:
feignName:
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
decode404: false
encoder: com.example.SimpleEncoder
decoder: com.example.SimpleDecoder
contract: com.example.SimpleContract
如果我们同时创建@Configuration bean 和配置属性,配置属性将获胜。它将覆盖@Configuration 值。但是如果你想把优先级改成@Configuration,你可以把feign.client.default-to-properties改成false。默认情况下,Feign 客户端不编码斜杠/字符。可以通过将 feign.client.decodeSlash 的值设置为 false 来更改此行为。
配置失败重试
OpenFeign默认请求失败后重试次数为0,即不重试,如果想要配置重试,就需要在FeignClient指定的配置类中注入Retryer的bean,覆盖掉默认的Retryer:
@Configuration
public class FeignConfig {
@Bean
public Retryer feignRetryer(){
// 重试间隔为100毫秒,最大重试时间为2秒,重试次数为5次
return new Retryer.Default(100,2000,5);
}
}
四、超时处理
我们可以对每个Feign客户端进行超时配置,主要有connectTimeout和readTimeout两个配置参数。connectTimeout 可防止由于服务器处理时间过长而阻塞调用者。而readTimeout则适用于读取响应时间过长的情况。启用 Hystrix 时,其超时配置默认为 1000 毫秒。可以在配置文件中增加超时时间:
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
hystrix:
enabled: true
hystrix:
command:
default:
execution:
timeout:
enabled: true
isolation:
thread:
timeoutInMilliseconds: 60000
仅配置feign的超时时间而不增加hystrix的超时时间,总的超时时间仍然还是是1000毫秒。
五、Feign对Hystrix的支持
如果项目中使用了Hystrix并且配置了feign.hystrix.enabled=true,Feign会将所有的方法包装为断路器。Hystrix支持fallback的概念:当熔断降级链路打开或出现错误时会执行指定路径的代码。要为给定的 @FeignClient 启用fallback,请将fallback属性设置为实现了指定FeignClient的类名,例如:
@FeignClient(name = "hello", fallback = HystrixClientFallback.class)
protected interface HystrixClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello iFailSometimes();
}
static class HystrixClientFallback implements HystrixClient {
@Override
public Hello iFailSometimes() {
return new Hello("fallback");
}
}
如果需要访问触发回退的原因,可以在@FeignClient 中使用 fallbackFactory 属性。
@FeignClient(name = "hello", fallbackFactory = HystrixClientFallbackFactory.class)
protected interface HystrixClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello iFailSometimes();
}
@Component
static class HystrixClientFallbackFactory implements FallbackFactory<HystrixClient> {
@Override
public HystrixClient create(Throwable cause) {
return new HystrixClient() {
@Override
public Hello iFailSometimes() {
return new Hello("fallback; reason was: " + cause.getMessage());
}
};
}
}
六、Feign对Spring Cloud CircuitBreaker 的支持
与Hystrix一样,当项目中使用了Spring Cloud CircuitBreaker并且配置了feign.circuitbreaker.enabled=true时,Feign会将所有的方法包装为断路器。同样也可以使用fallback机制。
七、请求响应压缩
可以考虑为 Feign 启用请求和响应的 GZIP 压缩。只需通过启用以下属性来执行此操作:
feign.compression.request.enabled=true
feign.compression.response.enabled=true
也可以通过以下配置来设置对哪些类型的请求响应才进行压缩,以及多大的请求才压缩:
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048
对于除了 OkHttpClient 之外的 http 客户端,可以启用默认的 gzip 解码器来解码 UTF-8 编码的 gzip 响应:
feign.compression.response.useGzipDecoder=true
八、Feign的日志
每个 Feign 客户端会创建一个日志记录器,默认情况下,记录器的名称是用于创建 Feign 客户端的接口的完整类名。 Feign logging 只响应 DEBUG 级别。可以在配置文件中进行配置:
logging:
level.xyz.ymtao.feign.TestFeign: DEBUG
可以通过为每个客户端配置 Logger.Level 对象告诉 Feign 要记录多少日志内容。可选的Level有:
- NONE:不记录;
- BASIC:仅记录请求方法和 URL 以及响应状态代码和执行时间;
- HEADERS:记录请求和响应头的基本信息;
- FULL:记录请求和响应的标头、正文和元数据。
例如:
@Configuration
public class FooConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
九、@QueryMap
OpenFeign 的@QueryMap 注解支持将 POJO 转换为 GET请求的 参数映射。不幸的是,默认的 OpenFeign QueryMap 注释与 Spring 不兼容,因为它缺少 value 属性。Spring Cloud OpenFeign 提供了一个等效的 @SpringQueryMap 注解,用于将 POJO 或 Map 参数注解为查询参数映射。例如,Params 类定义了参数 param1 和 param2:
@FeignClient("demo")
public interface DemoTemplate {
@GetMapping(path = "/demo")
String demoEndpoint(@SpringQueryMap Params params);
}
这样,调用时就会是/demo?param1=value1¶m2=value2
十、OpenFeign工作流程
1、首先通过@EnableFeignClients注解开启FeignClient的功能,只有这个注解存在,才会在程序启动时进行包扫描,对使用了@FeignClient注解的类作相应处理;
2、当接口方法被调用时,通过JDK的代理来生成具体的RequestTemplate模板对象;
3、根据RequestTemplate再生成Http请求的Request对象;
4、将Request对象交给Client去处理,其中Client可以是HttpClient,OKHttp,HttpURLConnection;
5、最后Client被封装到LoadBalanceClient类中,这个类结合了Ribbon或Spring Cloud LoadBalance做负载均衡。