Skip to content
On this page

Feign

feign是Netflix开发的声明式、模板化的HTTP客户端,其灵感来自于Retrofit、JAXRS-2.0以及WebSocket。Feign可以帮助我们更加便捷、优雅的调用HTTP API

Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解等

Spring Cloud OpenFeign

对Feign进行了增强,使其支持Spring MVC注解,另外还整合了Ribbon和Nacos,从而使得Feign的使用更加方便

优势

feign可以做到使用http请求远程服务时就像调用本地方法一样的体验,开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求,它像适Dubbo一样,consumer直接调用接口方法调用provider,而不需要通过常规的HttpClient构造请求在解析返回数据,它解决了让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布式环境开发

Spring Cloud Alibaba整合OpenFeign

  1. order服务引入openfeign依赖
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
  1. 编写调用接口
java
package com.cloud.order.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * value:注册到nacos的服务名称
 * path:路径的前缀
 */
@FeignClient(value = "alibaba-stock", path = "/stock")
public interface StockFeignService {


    @GetMapping("/openFeign/{goodsId}")
    String openFeign(@PathVariable("goodsId") int goodsId);

}
  1. order服务控制层测试feign接口
java
@Autowired
    private StockFeignService stockFeignService;

    /**
     * 测试open调用
     * @param goodsId
     * @return
     */
    @GetMapping("openFeign/{goodsId}")
    public String openFeign(@PathVariable("goodsId") int goodsId) {

        String returnMsg = stockFeignService.openFeign(goodsId);

        System.out.println("stock=>restTemplateGet=>"+goodsId);

        return "stock=>restTemplateGet=>"+goodsId ;
    }
  1. stock服务开放feign测试接口
java
/**
     * 测试open调用
     * @param goodsId
     * @return
     */
    @GetMapping("openFeign/{goodsId}")
    public String openFeign(@PathVariable("goodsId") int goodsId) {
        System.out.println("stock=>restTemplateGet=>"+goodsId);

        return "stock=>restTemplateGet=>"+goodsId ;
    }
  1. order服务启动类增加启动feign客户端功能注解
java
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApplication {

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

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

}
  1. 接口测试

访问:http://localhost:9501/order/openFeign/100

Spring Cloud Feign自定义配置及使用

日志配置

  1. 定义一个配置类,指定日志级别
java
package com.cloud.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * 全局配置:当使用@Configuration会将配置作用所有的服务提供方
 * 局部配置:如果只想针对某一个服务进行配置,就不要加@Configurat
 */
//@Configuration
public class FeignConfig {

    @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

日志级别

  • NONE【性能最佳,适用于生产】:不记录任何日志(默认值)
  • BASIC【适用生产环境追踪问题】:仅记录请求方法,URL、响应状态代码以及执行时间
  • HEADERS:记录BASIC级别的基础上,记录请求和响应的header
  • FULL【比较适用于开发及测试环境的定位问题】:记录请求和响应的header、body和元数据
  1. 局部配置,让调用微服务生效,在@FeignClient注解中指定使用的配置类【注意:这个配置类,不能加@Configuration,否则全局生效】
java
package com.cloud.order.feign;

import com.cloud.config.FeignConfig;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * value:注册到nacos的服务名称
 * path:路径的前缀
 */
@FeignClient(value = "alibaba-stock", path = "/stock", configuration = FeignConfig.class)
public interface StockFeignService {

    @GetMapping("/openFeign/{goodsId}")
    String openFeign(@PathVariable("goodsId") int goodsId);

}
  1. yml配置指定@FeignClient使用的日志级别
  • 因为feign调试日志是debug级别输出,springboot默认的日志级别是info,所以feign的debug日志级别就不会输出
  • logging.level=debug这样配置是对所有的日志级别进行配置
  • 该场景只需要对feign接口进行debug配置,所以是这样配置logging.level.com.cloud.order.feign=debug
yaml
logging:
  level:
    com.cloud.order.feign: debug

补充:局部配置可以在yml中配置

对应的属性配置类:org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration

yaml
feign:
  client:
    config:
      # default是全局所有模块
      alibaba-stock:
        loggerLevel: FULL

自定义拦截器实现认证逻辑

  1. 自定义拦截器类
java
package com.cloud.interceptor;


import feign.RequestInterceptor;
import feign.RequestTemplate;

import java.util.UUID;

public class FeignAuthRequestInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {

        // 业务逻辑
        String access_token = UUID.randomUUID().toString();
        requestTemplate.header("Authorization",access_token);

    }
}
  1. 拦截器配置,增加FeignAuthRequestInterceptor类【方式一】
  2. yml配置拦截器【方式二】
yaml
feign:
  client:
    config:
      alibaba-stock:
        loggerLevel: FULL
        requestInterceptors[0]: com.cloud.interceptor.FeignAuthRequestInterceptor
  1. 被调用方可以使用@RequestHeader注解获取请求头数据
java
/**
     * 测试open调用
     * @param goodsId
     * @return
     */
    @GetMapping("openFeign/{goodsId}")
    public String openFeign(@PathVariable("goodsId") int goodsId, @RequestHeader("Authorization") String authorization) {
        System.out.println("stock=>restTemplateGet=>"+goodsId);

        return "stock=>restTemplateGet=>"+goodsId ;
    }

超时时间配置

通过 Options 可以配置连接超时时间和读取超时时间,Options 的第一个参数是连接的超时时间(ms),默认值是 2s;第二个是请求处理的超时时间(ms),默认值是 5s。

客户端组件配置

Feign 中默认使用 JDK 原生的 URLConnection 发送 HTTP 请求,我们可以集成别的组件来替换掉 URLConnection,比如 Apache HttpClient,OkHttp。

Feign发起调用真正执行逻辑:feign.Client#execute

GZIP压缩配置

开启压缩可以有效节约网络资源,提升接口性能,我们可以配置 GZIP 来压缩数据