SpringBoot Feign使用教程超全面讲解

Java
294
0
0
2023-06-25
标签   SpringBoot
目录
  • 开篇
  • 一、使用 Feign 的示例
  • 1.1 添加依赖
  • 1.2 启用 Feign
  • 1.3 编写 FeignClient 接口
  • 1.4 编写对应的服务端
  • 1.5 调用 FeignClient
  • 二、如何切换 Client
  • 2.1 使用 Apache 的 HTTP Client
  • 2.1.1 添加依赖
  • 2.1.2 配置启用
  • 2.2 使用 OkHttp
  • 2.2.1 添加依赖
  • 2.2.2 配置启用
  • 三、如何修改日志级别
  • 3.1 通过配置文件修改日志级别
  • 3.2 通过配置类修改日志级别
  • 四、如何实现数据压缩
  • 五、FeignClient 的配置以及配置的优先级机制
  • 5.1 通过自定义配置类来定制配置
  • 5.2 在配置文件中设置全局配置
  • 5.3 在配置文件中设置专属配置
  • 5.4 理解配置的优先级与拦截器的追加原则
  • 5.4.1 优先级的效果
  • 5.4.2 追加的原则
  • 5.4.3 拦截器的效果验证
  • 总结

开篇

Feign 是声明式、模板化的 HTTP 客户端, 可以帮助我们更快捷、优雅地调用 HTTP API;Spring Cloud 为 Feign 添加了 Spring MVC 的注解支持,并整合了 Ribbon 和 Eureka 来为使用 Feign 时提供负载均衡;在 Spring Cloud 中使用 Feign 是非常容易的。

本篇主要介绍 SpringBoot 中要玩转 Feign 需要掌握的如添加 pom 依赖、客户端注解启用、切换底层 HttpClient、配置数据压缩、调整日志级别、定制配置、配置的优先级机制、增加拦截器以及拦截器的追加机制等知识。

一、使用 Feign 的示例

1.1 添加依赖

<dependencies>
  <!--openfein的依赖-->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
      <version>.1.3.RELEASE</version>
  </dependency>
</dependencies>

1.2 启用 Feign

在 SpringBoot 的启用类上添加注解@EnableFeignClients,@EnableFeignClients用于开启 Feign,会自动扫描@FeignClient标注的 FeignClient 接口。

@SpringBootApplication
@EnableFeignClients
@EnableWeb
public class FeignApplication {
    public static void main(String[] args) {
        SpringApplication.run(FeignApplication.class,args);
    }
}

1.3 编写 FeignClient 接口

@FeignClient(
        name = "demo-service",
        url = "http://localhost:/feign/server/",
        configuration = FeignInterceptor.class,
        fallback = TestService.DefaultFallback.class
)
public interface TestService {
    @RequestMapping(value = "/getError/{id}", method = RequestMethod.GET)
    public String getError(@RequestParam("id") Integer id);
    @RequestMapping(value = "/get", method = RequestMethod.GET)
    public String get();
    @RequestMapping(value = "/get/{param}", method = RequestMethod.GET)
    public String get(@RequestParam("param") String param);
    @RequestMapping(value = "/post", method = RequestMethod.POST)
    public FeignDemo post(@RequestBody FeignDemo demo);

1.4 编写对应的服务端

@RestController
@RequestMapping("/feign/server")
public class FeignServerController {
    @GetMapping("/get")
    public String get() {
        return "get";
    }
    @GetMapping("/get/{para}")
    public String get(@PathVariable("para") String para){
        return para;
    }
    @PostMapping("/post")
    public FeignDemo  post(@RequestBody FeignDemo demo) {
        return demo;
    }
}
public class FeignDemo {
    private String name;
    private Integer age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "FeignDemo{" +
                "name='" + name + ''' +
                ", age=" + age +
                '}';
    }
}

1.5 调用 FeignClient

@RunWith(SpringJUnitClassRunner.class)
@SpringBootTest(classes = {FeignApplication.class},webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@ActiveProfiles("dev,feign")
public class FeignClientTest {
    @Autowired
    private TestService testService;
    @Test
    public void testFallback(){
        testService.getError();
    }
    @Test
    public void testGet(){
        System.out.println(testService.get());
        System.out.println(testService.get("abc"));
        System.out.printf("..");
        FeignDemo feignDemo = new FeignDemo();
        feignDemo.setName("name");
        feignDemo.setAge();
        System.out.println(testService.post(feignDemo));
    }
@Component
    public class DefaultFallback implements TestService {
        @Override
        public String getError(@RequestParam("id") Integer id){
            return "";
        }
        @Override
        public String get() {
            return null;
        }
        @Override
        public String get(String param) {
            return null;
        }
        @Override
        public FeignDemo post(FeignDemo demo) {
            return null;
        }
    }
}

二、如何切换 Client

Feign 中自带的是 HttpURLConnection,这个 client 健壮性差,可替换为成熟的 Apache HttpClient 或 OkHttp 来进行网络请求。

2.1 使用 Apache 的 HTTP Client

使用 Apache 的httpclient替换 Feign 中默认的 client。

2.1.1 添加依赖

<!--httpclient的依赖,因为选择了使用httpclient-->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
    <version>.4.0</version>
</dependency>

2.1.2 配置启用

配置中添加如下信息,表示启用httpclient。

feign:
 httpclient:
  enabled: true

2.2 使用 OkHttp

2.2.1 添加依赖

在 Feign 中使用OkHttp作为网络请求框架,则只需要在 pom 文件中加上feign-okhttp的依赖,代码如下:

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
    <version>.2.0</version>
</dependency>

2.2.2 配置启用

feign:
 okhttp:
  enabled: true

三、如何修改日志级别

在发送和接收请求的时候,其内部将日志的打印输出定义成了四个等级,对应的详情如下:

级别

说明

NONE

不做任何记录

BASIC

仅记录请求方法和 URL 以及响应状态代码和执行时间

HEADERS

记录基本信息以及请求和响应标头

FULL

记录请求和响应的标题,正文和元数据

3.1 通过配置文件修改日志级别

注意需要指定接口的全限定名

logging:
 level:
  com.zto.titans.test.feign.service.TestService : DEBUG

3.2 通过配置类修改日志级别

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

这个一看即懂,不再废话。

四、如何实现数据压缩

可以分别对 HTTP 通信的request和response设置是否启用 GZIP 压缩,配置方法如下:

feign:
  compression:
    request:
      enabled: true
      mime-types: text/xml,application/xml,application/json # 配置压缩支持的MIME TYPE
      min-request-size: 2048 # 配置压缩数据大小的下限
    response:
      enabled: true # 配置响应GZIP压缩

五、FeignClient 的配置以及配置的优先级机制

有 2 种途径设置 FeignClient 的配置,通过自定义配置类来设置配置和在配置文件中设置,其中配置文件方式有点特殊,它里边可以指定全局配置对所有 FeignClient 有效,也可以为特定名称的 FeignClient 设置专属的配置。

5.1 通过自定义配置类来定制配置

实现一个配置类

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

将配置类TestConfiguration指定给configuration。

@FeignClient(
        name = "test-service",
        configuration = {FeignInterceptor.class,TestConfiguration.class}
)

5.2 在配置文件中设置全局配置

feign.client.config.default.xxx ,这个default意为全局的配置属性。

feign:
 client:
  config:
   default:
    connectTimeout: 5000
    readTimeout: 5000
    loggerLevel: basic

5.3 在配置文件中设置专属配置

feign.client.config.feignName.xxx , 给名字为feignName的FeignClient指定专属的配置。

feign:
 client:
  config:
   feignName:
    connectTimeout: 5000
    readTimeout: 5000
    loggerLevel: full
    errorDecoder: com.example.SimpleErrorDecoder
    retryer: com.example.SimpleRetryer
    requestInterceptors:
     - com.example.FooRequestInterceptor
     - com.example.BarRequestInterceptor
    decode404: false
    encoder: com.example.SimpleEncoder
    decoder: com.example.SimpleDecoder

5.4 理解配置的优先级与拦截器的追加原则

从org.springframework.cloud.openfeign.FeignClientFactoryBean#configureFeign中可以确认以上 3 种配置的优先级:

configureUsingConfiguration(context, builder); //
configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()),builder); //
configureUsingProperties(properties.getConfig().get(this.contextId),builder);//
  • 第 1 类为通过自定义配置类来指定配置
  • 第 2 类为在配置文件中的feign.client.config.default.xxx设置全局配置
  • 第 3 类为在配置文件中的feign.client.config.feignName.xxx设置专属配置

5.4.1 优先级的效果

配置文件里的专属配置-覆盖->配置文件里的全局配置-覆盖->配置类的配置

5.4.2 追加的原则

RequestInterceptor是拦截器,可以在发送前做一些处理,比如统一添加header信息。每一类中的requestInterceptors可以存储多个拦截器,拦截器并非覆盖的效果,而是链式追加的效果;从执行顺序来看优先级是:1 > 2 > 3,即先执行配置类中指定的拦截器,然后是配置文件中指定的全局拦截器,最后是配置文件中指定的专属拦截器。

需特别注意:RequestInterceptor的实现类(例如 RI-A,RI-B)上如果添加了@Component注解,就都会被扫描识别到,并被追加到第一类的requestInterceptors列表中;倘若不小心 RI-A 还在第 2 类中又被指定了,则还会将拦截器 RI-A 追加在第二类的requestInterceptors列表中,结果是会 RI-A 总计会执行 2 次;若也在第三类中指定 RI-A,则 RI-A 也在其列表中追加,结果是 RI-A 总计会执行 3 次。

5.4.3 拦截器的效果验证

以一个实例来验证说明效果

自定义三个 RequestInterceptor

class FeignInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        requestTemplate.header("user", "myuser");
        requestTemplate.header("password", "mypassword");
    }
}
class FeignInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        requestTemplate.header("user", "myuser1");
        requestTemplate.header("password", "mypassword1");
    }
}
class FeignInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        requestTemplate.header("user", "myuser2");
        requestTemplate.header("password", "mypassword2");
    }
}

@FeignClient 中指定一个

@FeignClient(
        name = "test-service",
        url = "http://localhost:/feign/server/",
        configuration = {FeignInterceptor.class,TestConfiguration.class},
        fallback = TestService.DefaultFallback.class
)

配置中指定 2 个

default指定了一个,test-service里指定一个

feign:
 httpclient:
  enabled: true
 okhttp:
  enabled: true
 client:
  config:
   default:
    connectTimeout: 5000
    readTimeout: 5000
    #loggerLevel: none
    requestInterceptors:
     - com.zto.titans.test.feign.service.FeignInterceptor1
   test-service:
    #loggerLevel: basic
    requestInterceptors:
     - com.zto.titans.test.feign.service.FeignInterceptor2
logging:
 level:
  com.zto.titans.test.feign.service.TestService : DEBUG

根据追加逻辑,最终执行的顺序是:

  • FeignInterceptor
  • FeignInterceptor1
  • FeignInterceptor2

总结

本篇主要介绍 SpringBoot 中要玩转 Feign 需要掌握的如添加 pom 依赖、客户端注解启用、切换底层 HttpClient、配置数据压缩、调整日志级别、定制配置、配置的优先级机制、增加拦截器以及拦截器的追加机制等知识,以实例 + 效果的方式帮读者高效全面并深入的理解它们。