Feign可以做到使用 HTTP 请求远程服务时就像调用本地方法一样的体验,开发者完全感知不到这是远程方法,更感知不到这是个 HTTP 请求。它像 Dubbo 一样,consumer 直接调用接口方法调用 provider,而不需要通过常规的 Http Client 构造请求再解析返回数据。它解决了让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布式环境开发。
添加feign 的依赖
<!-- 引入open feign 依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
使用,在接口上添加注解 @FeignClient("") 进行配置
@FeignClient(value = "order-server", path = "order") public interface OrderFeignService { @GetMapping("/findOrderByUserId/{id}") R findOrderByUserId(@PathVariable("id") Integer id); }
在启动类中加入注解 @EnableFeignClients
@SpringBootApplication @EnableFeignClients public class StudyuserApplication { public static void main(String[] args) { SpringApplication.run(StudyuserApplication.class, args); } }
在UserController 调用 , 跟普通接口一样的写法。
@Autowired private OrderFeignService orderFeignService; @GetMapping("/findOrderByUserIdWithFeign/{id}") public R findOrderByUserIdWithFeign(@PathVariable("id") Integer id) { // feign调用 R result = orderFeignService.findOrderByUserId(id); return result; }
测试,验证
spring cloud feign 的自定义配置及使用
日志配置
有时候我们遇到 Bug,比如接口调用失败、参数没收到等问题,或者想看看调用性能,就需要配置 Feign 的日志了,以此让 Feign 把请求信息输出来。
日志等级有 4 种,分别是:
NONE【性能最佳,适用于生产】:不记录任何日志(默认值)。
BASIC【适用于生产环境追踪问题】:仅记录请求方法、URL、响应状态代码以及执行时间。
HEADERS:记录BASIC级别的基础上,记录请求和响应的header。
FULL【比较适用于开发及测试环境定位问题】:记录请求和响应的header、body和元数据
全局配置
加入配置类 FeignConfig
package com.jiuge.user.config; import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author jiuge * @version 1.0 * @date 2021/8/5 11:16 */ @Configuration public class FeignConfig { @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
配置好后在yml 文件加入对应feign目录的日志级别
logging: level: com.jiuge.user.feign: debug
请求接口 http://localhost:8010/user/findOrderByUserIdWithFeign/1 测试,验证, 把日志请求信息打印出来了
2021-08-05 11:22:08.205 DEBUG 14128 --- [nio-8010-exec-1] com.jiuge.user.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] ---> GET http://order-server/order/findOrderByUserId/1 HTTP/1.1 2021-08-05 11:22:08.205 DEBUG 14128 --- [nio-8010-exec-1] com.jiuge.user.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] ---> END HTTP (0-byte body) 2021-08-05 11:22:08.562 DEBUG 14128 --- [nio-8010-exec-1] com.jiuge.user.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] <--- HTTP/1.1 200 (356ms) 2021-08-05 11:22:08.562 DEBUG 14128 --- [nio-8010-exec-1] com.jiuge.user.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] connection: keep-alive 2021-08-05 11:22:08.563 DEBUG 14128 --- [nio-8010-exec-1] com.jiuge.user.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] content-type: application/json 2021-08-05 11:22:08.563 DEBUG 14128 --- [nio-8010-exec-1] com.jiuge.user.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] date: Thu, 05 Aug 2021 03:22:08 GMT 2021-08-05 11:22:08.563 DEBUG 14128 --- [nio-8010-exec-1] com.jiuge.user.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] keep-alive: timeout=60 2021-08-05 11:22:08.563 DEBUG 14128 --- [nio-8010-exec-1] com.jiuge.user.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] transfer-encoding: chunked 2021-08-05 11:22:08.563 DEBUG 14128 --- [nio-8010-exec-1] com.jiuge.user.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] 2021-08-05 11:22:08.563 DEBUG 14128 --- [nio-8010-exec-1] com.jiuge.user.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] {"msg":"success","code":0,"orders":[{"id":1,"userId":"1","commodityCode":"UT2191929213903","count":1,"amount":2}]} 2021-08-05 11:22:08.563 DEBUG 14128 --- [nio-8010-exec-1] com.jiuge.user.feign.OrderFeignService : [OrderFeignService#findOrderByUserId] <--- END HTTP (114-byte body)
// @Configuration public class FeignConfig { @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
修改OrderFeignService 接口的注解,接口头加入 configuration 注解
// feignConfig 的局部配置 @FeignClient(value = "order-server", path = "order",configuration = FeignConfig.class) public interface OrderFeignService { @RequestMapping("/findOrderByUserId/{id}") R findOrderByUserId(@PathVariable("id") Integer id); }
请求接口http://localhost:8010/user/findOrderByUserIdWithFeign/1测试,验证
局部配置2,可以在yml 进行配置
logging: level: com.jiuge.user.feign: debug feign: client: config: order-server: loggerLevel: FULL
OrderFeignService 类上去除 configuration 配置
@FeignClient(value = "order-server", path = "order",configuration = FeignC
换成
@FeignClient(value = "order-server", path = "order")
测试验证
契约配置
Spring Cloud 在 Feign 的基础上做了扩展,可以让 Feign 支持 Spring MVC 的注解来调用。原生的 Feign 是不支持 Spring MVC 注解的,如果你想在 Spring Cloud 中使用原生的注解方式来定义客户端也是可以的,通过配置契约来改变这个配置,Spring Cloud 中默认的是 SpringMvcContract。
修改契约配置,支持原生的配置
在FeignConfig 上添加如下内容
/** * 修改契约配置,支持Feign原生的注解 * @return */ @Bean public Contract feignContract() { return new Contract.Default(); }
修改OrderFeignService 类内容
使用原生的feign 注解 @RequestLine
// feignConfig 的局部配置 @FeignClient(value = "order-server", path = "order") public interface OrderFeignService { // @RequestMapping("/findOrderByUserId/{id}") @RequestLine("GET /findOrderByUserId/{userId}") R findOrderByUserId(@PathVariable("userId") Integer userId); }
局部配置 上面配置需要加这个
修改application.yml 文件 添加contract
feign: client: config: order-server: loggerLevel: FULL contract: feign.Contract.Default #指定Feign原生注解契约配置
通过拦截器实现认证
通常我们调用的接口都是有权限控制的,很多时候可能认证的值是通过参数去传递的,还有就是通过请求头去传递认证信息,比如 Basic 认证方式。
添加bean 实例
@Bean public BasicAuthRequestInterceptor basicAuthRequestInterceptor(){ return new BasicAuthRequestInterceptor("jiuge","123456"); }
扩展点 feign.RequestInterceptor
每次 feign 发起http调用之前,会去执行拦截器中的逻辑。
public interface RequestInterceptor { /** * Called for every request. Add data using methods on the supplied {@link}. */ void apply(RequestTemplate template); }
使用场景
统一添加 header 信息;
对 body 中的信息做修改或替换;
自定义拦截实现逻辑
public class FeignAuthRequestInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { // 业务逻辑 String access_token = UUID.randomUUID().toString(); template.header("Authorization",access_token); } }
order-server 端可以通过 @RequestHeader获取请求参数
全局配置
@Configuration // 全局配置 public class FeignConfig { @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } /** * 自定义拦截器 * @return */ @Bean public FeignAuthRequestInterceptor feignAuthRequestInterceptor(){ return new FeignAuthRequestInterceptor(); } }
局部配置
feign: client: config: order-server: loggerLevel: FULL requestInterceptors[0]: com.jiuge.user.interceptor.FeignAuthRequestInterceptor
超时时间配置
通过 Options 可以配置连接超时时间和读取超时时间,Options 的第一个参数是连接的超时时间(ms),默认值是 2s;第二个是请求处理的超时时间(ms),默认值是 5s。
@Bean public Request.Options options() { return new Request.Options(5000, 10000); }
局部配置
feign: client: config: order-server: loggerLevel: FULL # 连接超时时间,默认2s connectTimeout: 2000 # 请求处理超时时间,默认5s readTimeout: 3000
补充说明:Feign的底层用的是Ribbon,但超时时间以Feign配置为准
客户端组件配置
配置Apache HttpClient
引入依赖
<!-- Apache HttpClient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.7</version> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> <version>10.1.0</version> </dependency>
修改yml 文件 ,启用 apache httpclient
feign: #feign 使用 Apache HttpClient 可以忽略,默认开启 httpclient: enabled: true
测试,验证 会进入ApacheHttpClient 中的execute 方法
配置OkHttp
引入依赖
<!-- 引入OkHttp --> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> </dependency>
修改yml,将Feign的HttpClient 禁用, 启用OkHttp
feign: #feign 使用 okhttp okhttp: enabled: true
测试,验证 会进入OkHttpClient 类的 execute 方法
GZIP 接口压缩配置
开启压缩可以有效节约网络资源,提升接口性能,可以配置GZIP 来压缩数据
修改yml 文件,为如下内容
feign: httpclient: enabled: true compression: request: enabled: true # 配置压缩的类型 mime-types: text/xml,application/xml,application/json #最小压缩值 min-request-size: 2048 response: enabled: true
注意:只有当Feign 的Http client 不是okhttp3的时候,压缩才会生效, 配置源码在 FeignAcceptGzipEncodingAutoConfiguration 上
测试,验证
编码器解码器配置
引入jackson 依赖
<!-- 引入jackson 依赖 --> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-jackson</artifactId> </dependency>
java 配置方式
@Bean public Decoder decoder() { return new JacksonDecoder(); } @Bean public Encoder encoder() { return new JacksonEncoder(); }
yml 配置方式
feign: client: config: order-server: #对应微服务 # 配置编解码器 encoder: feign.jackson.JacksonEncoder decoder: feign.jackson.JacksonDecoder
以上两种,任选一种进行配置。