1. 简介

1.1 了解Feign

Feign 是 Netflix 开发的声明式、模板化的 HTTP 客户端,让编写 web 服务客户端变得非常容易。Feign 集成了 Ribbon、RestTemplate 实现了负载均衡的执行 Http 调用,只不过对原有的方式(Ribbon+RestTemplate)进行了封装,开发者不必手动使用 RestTemplate 调服务,而是使用 Feign 的注解定义接口,调用这个接口,就可以调用服务注册中心的服务,这样更加符合面向接口编程的宗旨,简化了开发。但遗憾的是 Feign 现在停止迭代了。

1.2 了解Open Feign

OpenFeign 是 springcloud 在 Feign 的基础上支持了 SpringMVC 的注解,如 @RequestMapping 等等。OpenFeign的 @FeignClient 可以解析 SpringMVC 的@RequestMapping 注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

2. 快速上手

2.1 引入依赖

  1. <!-- openfeign 远程调用 -->
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-openfeign</artifactId>
  5. </dependency>

2.2 服务创建

1、创建两个服务 openfeign-provider8082(服务提供者)、openFeign-consumer8083(服务消费者)。
2、在 openFeign-consumer8083 启动类添加 @EnableFeignClients开启 openfeign 功能。
3、修改两个服务配置文件,指定服务名称和端口号、nacos 地址等。
4、在 openfeign-provider8082 服务中定义控制器方法。

  1. @RestController
  2. @RequestMapping("/openfeign/provider")
  3. public class OpenFeignProviderController {
  4. @PostMapping("/order1")
  5. public Order createOrder1(Order order){
  6. System.out.println("表单传输 order");
  7. return order;
  8. }
  9. @PostMapping("/order2")
  10. public Order createOrder2(@RequestBody Order order){
  11. System.out.println("请求体传输:order");
  12. return order;
  13. }
  14. @GetMapping("/test/{id}")
  15. public String test(@PathVariable("id")Integer id){
  16. System.out.println("请求url带参数:" + id);
  17. return "accept one msg id="+id;
  18. }
  19. @PostMapping("/test2")
  20. public String test2(String id, String name){
  21. System.out.println("请求参数:" + id + name);
  22. return MessageFormat.format("accept on msg id={0},name={1}",id,name);
  23. }
  24. }

2.3 服务调用

1、服务消费者想要调用服务生产者中的控制器方法,需要定义对应的接口,指明访问路径。

  1. @Service
  2. @FeignClient(value = "openFeign-provider")
  3. public interface OpenFeignService {
  4. /**
  5. * 参数默认是@RequestBody标注的,如果通过POJO表单传参的,使用@SpringQueryMap标注
  6. */
  7. @PostMapping("/openfeign/provider/order1")
  8. Order createOrder1(@SpringQueryMap Order order);
  9. /**
  10. * 参数默认是@RequestBody标注的,这里的@RequestBody可以不填
  11. * 方法名称任意
  12. */
  13. @PostMapping("/openfeign/provider/order2")
  14. Order createOrder2(@RequestBody Order order);
  15. @GetMapping("/openfeign/provider/test/{id}")
  16. String test1(@PathVariable("id")Integer id);
  17. /**
  18. * 必须要@RequestParam注解标注,且value属性必须填上参数名
  19. * 方法参数名可以任意,但是@RequestParam注解中的value属性必须和provider中的参数名相同
  20. */
  21. @PostMapping("/openfeign/provider/test2")
  22. String test2(@RequestParam("id") String arg1, @RequestParam("name") String arg2);
  23. }

2、在 openFeign-consumer8083 中的控制器中引入 OpenFeignService,就可以实现像调用本地方法一样调用远程服务方法。

  1. @RestController
  2. @RequestMapping("/openfeign")
  3. public class OpenFeignConsumerController {
  4. @Autowired
  5. private OpenFeignService openFeignService;
  6. @PostMapping("/order1")
  7. public Order createOrder1(Order order){
  8. return openFeignService.createOrder1(order);
  9. }
  10. @PostMapping("/order2")
  11. public Order createOrder2(@RequestBody Order order){
  12. return openFeignService.createOrder2(order);
  13. }
  14. @GetMapping("/test/{id}")
  15. public String test(@PathVariable("id")Integer id){
  16. return openFeignService.test1(id);
  17. }
  18. @PostMapping("/test2")
  19. public String test2(@RequestParam String id, @RequestParam String name){
  20. return openFeignService.test2(id, name);
  21. }
  22. }

3. 自定义配置

3.1 配置超时时间

open feign 其实是有默认的超时时间的,默认分别是连接超时时间10秒、读超时时间60秒,源码在 feign.Request.Options#Options() 这个方法中:

  1. public Options() {
  2. this(10L, TimeUnit.SECONDS, 60L, TimeUnit.SECONDS, true);
  3. }

但是又因为 open feign 集成了 ribbon,Ribbon的默认超时连接时间、读超时时间都是是1秒,源码在org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer#execute() 方法中:

  1. // 如果openFeign没有设置对应得超时时间,那么将会采用Ribbon的默认超时时间。
  2. public FeignLoadBalancer.RibbonResponse execute(FeignLoadBalancer.RibbonRequest request, IClientConfig configOverride) throws IOException {
  3. Options options;
  4. if (configOverride != null) {
  5. RibbonProperties override = RibbonProperties.from(configOverride);
  6. options = new Options((long)override.connectTimeout(this.connectTimeout), TimeUnit.MILLISECONDS, (long)override.readTimeout(this.readTimeout), TimeUnit.MILLISECONDS, override.isFollowRedirects(this.followRedirects));
  7. } else {
  8. options = new Options((long)this.connectTimeout, TimeUnit.MILLISECONDS, (long)this.readTimeout, TimeUnit.MILLISECONDS, this.followRedirects);
  9. }
  10. Response response = request.client().execute(request.toRequest(), options);
  11. return new FeignLoadBalancer.RibbonResponse(request.getUri(), response);
  12. }

这里不建议配置 ribbon 的超时时间,可以修改 open feign 的超时时间(谁调用服务就配超时时间),有两种办法:
1、yaml 方式修改。

  1. feign:
  2. client:
  3. config:
  4. ## default 设置的全局超时时间,指定服务名称可以设置单个服务的超时时间
  5. default:
  6. connectTimeout: 5000
  7. readTimeout: 5000
  8. ## 如果一个服务调用时间超过给定的默认时间,可以专门定义调用该服务的超时时间。
  9. serviceC:
  10. connectTimeout: 30000
  11. readTimeout: 30000

2、配置类修改。

  1. @Configuration
  2. public class FeignConfig {
  3. @Bean
  4. public Request.Options options() {
  5. return new Request.Options(5000, 10000);
  6. }
  7. }

3.2 配置日志增强

openFeign 提供了日志增强功能,但是默认是不显示任何日志的,不过开发者在调试阶段可以自己配置日志的级别。
openFeign 的日志级别如下:

  • NONE:默认的,不显示任何日志;
  • BASIC:仅记录请求方法、URL、响应状态码及执行时间;
  • HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息;
  • FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。

1、全局配置,在配置类中设置日志级别:

  1. // 注意:此处配置@Configuration注解就会全局生效,如果想指定对应微服务生效,就不能配置
  2. public class FeignConfig {
  3. /**
  4. * 日志级别
  5. * @return
  6. */
  7. @Bean
  8. public Logger.Level feignLoggerLevel() {
  9. return Logger.Level.FULL;
  10. }
  11. }

2、局部配置,在 yaml 中配置:

  1. feign:
  2. client:
  3. config:
  4. mall-order: #对应微服务名称
  5. loggerLevel: FULL

3、设置接口日志级别。

  1. logging:
  2. level:
  3. com.xuwei.openfeignconsumer8083.service: debug

3.3 配置GZIP通讯压缩

gzip 是一种数据格式,采用用 deflate 算法压缩数据;gzip 是一种流行的数据压缩算法,应用十分广泛,尤其是在 Linux 平台。当 GZIP 压缩到一个纯文本数据时,效果是非常明显的,大约可以减少70%以上的数据大小。网络数据经过压缩后实际上降低了网络传输的字节数,最明显的好处就是可以加快网页加载的速度。网页加载速度加快的好处不言而喻,除了节省流量,改善用户的浏览体验外,另一个潜在的好处是 GZIP 与搜索引擎的抓取工具有着更好的关系。例如 Google 就可以通过直接读取 GZIP 文件来比普通手工抓取更快地检索网页。

压缩流程:

  • 客户端向服务器请求头中带有:Accept-Encoding:gzip,deflate 字段,向服务器表示,客户端支持的压缩格式(gzip 或者 deflate),如果不发送该消息头,服务器是不会压缩的。
  • 服务端在收到请求之后,如果发现请求头中含有Accept-Encoding字段,并且支持该类型的压缩,就对响应报文压缩之后返回给客户端,并且携带 Content-Encoding:gzip 消息头,表示响应报文是根据该格式压缩过的。
  • 客户端接收到响应之后,先判断是否有 Content-Encoding 消息头,如果有,按该格式解压报文。否则按正常报文处理。

注意:openFeign 支持的 GZIP 仅仅是在 openFeign 接口的请求和响应,即是 openFeign 消费者调用服务提供者的接口。

openFeign开启GZIP步骤也是很简单,只需要在配置文件中开启如下配置:

  1. feign:
  2. # 配置 GZIP 来压缩数据
  3. compression:
  4. request:
  5. enabled: true
  6. # 配置压缩的类型
  7. mime-types: text/xml,application/xml,application/json
  8. # 最小压缩值
  9. min-request-size: 2048
  10. response:
  11. enabled: true

3.4 配置自定义拦截器

1、自定义拦截器,需实现 RequestInterceptor 接口,重写 apply 方法。

  1. public class FeignAuthRequestInterceptor implements RequestInterceptor {
  2. @Override
  3. public void apply(RequestTemplate requestTemplate) {
  4. // 业务逻辑
  5. String access_token = UUID.randomUUID().toString();
  6. requestTemplate.header("Authorization", access_token);
  7. }
  8. }

2、配置类中引入或者 yaml 文件中直接配置。

  1. # 配置类
  2. public class FeignConfig {
  3. /**
  4. * 自定义拦截器
  5. * @return
  6. */
  7. @Bean
  8. public FeignAuthRequestInterceptor feignAuthRequestInterceptor(){
  9. return new FeignAuthRequestInterceptor();
  10. }
  11. }
  12. # yaml文件引入
  13. feign:
  14. client:
  15. config:
  16. order-service: #对应微服务
  17. requestInterceptors[0]: #配置拦截器
  18. com.xuwei.order.Interceptor.FeignAuthRequestInterceptor

3、测试,查看认证是否生效。
image.png