• SpringBoot 使用 Feign 无废话 All-in-one 指南


    开篇

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

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

    一、使用 Feign 的示例

    1.1 添加依赖

    1. <dependencies>
    2.   <!--openfein的依赖-->
    3.   <dependency>
    4.       <groupId>org.springframework.cloud</groupId>
    5.       <artifactId>spring-cloud-starter-openfeign</artifactId>
    6.       <version>2.1.3.RELEASE</version>
    7.   </dependency>
    8. </dependencies>
    9. 复制代码

    1.2 启用 Feign

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

    1. @SpringBootApplication
    2. @EnableFeignClients
    3. @EnableWeb
    4. public class FeignApplication {
    5.     public static void main(String[] args) {
    6.         SpringApplication.run(FeignApplication.class,args);
    7.     }
    8. }
    9. 复制代码

    1.3 编写 FeignClient 接口

    1. @FeignClient(
    2.         name = "demo-service",
    3.         url = "http://localhost:8080/feign/server/",
    4.         configuration = FeignInterceptor.class,
    5.         fallback = TestService.DefaultFallback.class
    6. )
    7. public interface TestService {
    8.     @RequestMapping(value = "/getError/{id}", method = RequestMethod.GET)
    9.     public String getError(@RequestParam("id") Integer id);
    10.     @RequestMapping(value = "/get1", method = RequestMethod.GET)
    11.     public String get1();
    12.     @RequestMapping(value = "/get2/{param}", method = RequestMethod.GET)
    13.     public String get2(@RequestParam("param") String param);
    14.     @RequestMapping(value = "/post1", method = RequestMethod.POST)
    15.     public FeignDemo post1(@RequestBody FeignDemo demo);
    16. 复制代码

    1.4 编写对应的服务端

    1. @RestController
    2. @RequestMapping("/feign/server")
    3. public class FeignServerController {
    4.     @GetMapping("/get1")
    5.     public String get1() {
    6.         return "get1";
    7.     }
    8.     @GetMapping("/get2/{para}")
    9.     public String get2(@PathVariable("para"String para){
    10.         return para;
    11.     }
    12.     @PostMapping("/post1")
    13.     public FeignDemo  post1(@RequestBody FeignDemo demo) {
    14.         return demo;
    15.     }
    16. }
    17. 复制代码
    1. public class FeignDemo {
    2.     private String name;
    3.     private Integer age;
    4.     public String getName() {
    5.         return name;
    6.     }
    7.     public void setName(String name) {
    8.         this.name = name;
    9.     }
    10.     public Integer getAge() {
    11.         return age;
    12.     }
    13.     public void setAge(Integer age) {
    14.         this.age = age;
    15.     }
    16.     @Override
    17.     public String toString() {
    18.         return "FeignDemo{" +
    19.                 "name='" + name + ''' +
    20.                 ", age=" + age +
    21.                 '}';
    22.     }
    23. }
    24. 复制代码

    1.5 调用 FeignClient

    1. @RunWith(SpringJUnit4ClassRunner.class)
    2. @SpringBootTest(classes = {FeignApplication.class},webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
    3. @ActiveProfiles("dev,feign")
    4. public class FeignClientTest {
    5.     @Autowired
    6.     private TestService testService;
    7.     @Test
    8.     public void testFallback(){
    9.         testService.getError(1);
    10.     }
    11.     @Test
    12.     public void testGet1(){
    13.         System.out.println(testService.get1());
    14.         System.out.println(testService.get2("abc"));
    15.         System.out.printf("..");
    16.         FeignDemo feignDemo = new FeignDemo();
    17.         feignDemo.setName("name");
    18.         feignDemo.setAge(1);
    19.         System.out.println(testService.post1(feignDemo));
    20.     }
    21. @Component
    22.     public class DefaultFallback implements TestService {
    23.         @Override
    24.         public String getError(@RequestParam("id") Integer id){
    25.             return "";
    26.         }
    27.         @Override
    28.         public String get1() {
    29.             return null;
    30.         }
    31.         @Override
    32.         public String get2(String param) {
    33.             return null;
    34.         }
    35.         @Override
    36.         public FeignDemo post1(FeignDemo demo) {
    37.             return null;
    38.         }
    39.     }
    40. }
    41. 复制代码

    二、如何切换 Client

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

    2.1 使用 Apache 的 HTTP Client

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

    2.1.1 添加依赖

    1. <!--httpclient的依赖,因为选择了使用httpclient-->
    2. <dependency>
    3.     <groupId>org.apache.httpcomponents</groupId>
    4.     <artifactId>httpclient</artifactId>
    5. </dependency>
    6. <dependency>
    7.     <groupId>io.github.openfeign</groupId>
    8.     <artifactId>feign-httpclient</artifactId>
    9.     <version>10.4.0</version>
    10. </dependency>
    11. 复制代码

    2.1.2 配置启用

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

    1. feign:
    2.   httpclient:
    3.     enabled: true
    4. 复制代码

    2.2 使用 OkHttp

    2.2.1 添加依赖

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

    1. <dependency>
    2.     <groupId>io.github.openfeign</groupId>
    3.     <artifactId>feign-okhttp</artifactId>
    4.     <version>10.2.0</version>
    5. </dependency>
    6. 复制代码

    2.2.2 配置启用

    1. feign:
    2.   okhttp:
    3.     enabled: true
    4. 复制代码

    三、如何修改日志级别

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

    级别说明
    NONE不做任何记录
    BASIC仅记录请求方法和 URL 以及响应状态代码和执行时间
    HEADERS记录基本信息以及请求和响应标头
    FULL记录请求和响应的标题,正文和元数据

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

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

    1. logging:
    2.   level:
    3.     com.zto.titans.test.feign.service.TestService : DEBUG
    4. 复制代码

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

    1. @Configuration
    2. public class FooConfiguration {
    3.     @Bean
    4.     Logger.Level feignLoggerLevel() {
    5.         return Logger.Level.FULL;
    6.     }
    7. }
    8. 复制代码

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

    四、如何实现数据压缩

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

    1. feign:
    2.     compression:
    3.         request:
    4.             enabled: true
    5.             mime-types: text/xml,application/xml,application/json # 配置压缩支持的MIME TYPE
    6.             min-request-size: 2048  # 配置压缩数据大小的下限
    7.         response:
    8.             enabled: true # 配置响应GZIP压缩
    9. 复制代码

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

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

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

    实现一个配置类

    1. public class TestConfiguration {
    2.     @Bean
    3.     Logger.Level feignLoggerLevel() {
    4.         return Logger.Level.FULL;
    5.     }
    6. }
    7. 复制代码

    将配置类 TestConfiguration 指定给configuration

    1. @FeignClient(
    2.         name = "test-service",
    3.         configuration = {FeignInterceptor2.class,TestConfiguration.class}
    4. )
    5. 复制代码

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

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

    1. feign:
    2.   client:
    3.     config:
    4.       default:
    5.         connectTimeout: 5000
    6.         readTimeout: 5000
    7.         loggerLevel: basic
    8. 复制代码

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

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

    1. feign:
    2.   client:
    3.     config:
    4.       feignName:
    5.         connectTimeout: 5000
    6.         readTimeout: 5000
    7.         loggerLevel: full
    8.         errorDecoder: com.example.SimpleErrorDecoder
    9.         retryer: com.example.SimpleRetryer
    10.         requestInterceptors:
    11.           - com.example.FooRequestInterceptor
    12.           - com.example.BarRequestInterceptor
    13.         decode404false
    14.         encoder: com.example.SimpleEncoder
    15.         decoder: com.example.SimpleDecoder
    16. 复制代码

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

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

    1. configureUsingConfiguration(context, builder); // 1
    2. configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()),builder); //2
    3. configureUsingProperties(properties.getConfig().get(this.contextId),builder);//3
    4. 复制代码
    1. 第 1 类为通过自定义配置类来指定配置
    2. 第 2 类为在配置文件中的feign.client.config.default.xxx设置全局配置
    3. 第 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
    1. class FeignInterceptor implements RequestInterceptor {
    2.     @Override
    3.     public void apply(RequestTemplate requestTemplate) {
    4.         requestTemplate.header("user""myuser1");
    5.         requestTemplate.header("password""mypassword");
    6.     }
    7. }
    8. 复制代码
    1. class FeignInterceptor1 implements RequestInterceptor {
    2.     @Override
    3.     public void apply(RequestTemplate requestTemplate) {
    4.         requestTemplate.header("user1""myuser1");
    5.         requestTemplate.header("password1""mypassword1");
    6.     }
    7. }
    8. 复制代码
    1. class FeignInterceptor2 implements RequestInterceptor {
    2.     @Override
    3.     public void apply(RequestTemplate requestTemplate) {
    4.         requestTemplate.header("user2""myuser2");
    5.         requestTemplate.header("password2""mypassword2");
    6.     }
    7. }
    8. 复制代码
    • @FeignClient 中指定一个
    1. @FeignClient(
    2.         name = "test-service",
    3.         url = "http://localhost:8080/feign/server/",
    4.         configuration = {FeignInterceptor.class,TestConfiguration.class},
    5.         fallback = TestService.DefaultFallback.class
    6. )
    7. 复制代码
    • 配置中指定 2 个

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

    1. feign:
    2.   httpclient:
    3.     enabled: true
    4.   okhttp:
    5.     enabled: true
    6.   client:
    7.     config:
    8.       default:
    9.         connectTimeout: 5000
    10.         readTimeout: 5000
    11.         #loggerLevel: none
    12.         requestInterceptors:
    13.           - com.zto.titans.test.feign.service.FeignInterceptor1
    14.       test-service:
    15.         #loggerLevel: basic
    16.         requestInterceptors:
    17.           - com.zto.titans.test.feign.service.FeignInterceptor2
    18. logging:
    19.   level:
    20.     com.zto.titans.test.feign.service.TestService : DEBUG
    21. 复制代码

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

    1. FeignInterceptor
    2. FeignInterceptor1
    3. FeignInterceptor2

    总结

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

  • 相关阅读:
    win11的C/C++环境配置——基于MinGW-W64 GCC-8.1.0
    Java基础之IO流操作
    ROS工程实践1—创建工作空间和功能包
    千瓜重磅发布|2022年双十一母婴行业数据研报
    计算机视觉快速入门一 —— 图像基本操作(二)
    vue组件通信方式有哪些?
    UE5——动画混合
    南软复试真题
    快收下这份照片模糊变清晰方法攻略
    实习生跑路留了一个大坑,搞出2个线上问题,我被坑惨了
  • 原文地址:https://blog.csdn.net/m0_71777195/article/details/128034088