声明式HTTP客户端-Feign 使用入门详解

什么是 OpenFeign

OpenFeign (以下统一简称为 Feign) 是 Netflix 开源的声明式 HTTP 客户端,集成了 Ribbon 的负载均衡、轮询算法和 RestTemplate 的 HTTP 调用等特性,并对其进行封装,使用者只需要在此基础上,定义一个接口,并在接口上标注一个 FeignClient ,便可以实现 HTTP 远程调用,上面的声明式 HTTP 如何理解,可以理解为

​ 只需要声明一个接口,Feign 就会通过你定义的接口,自动给你构造请求的目标地址并请求。

下面介绍下如何在项目中集成 Feign 组件,只需要遵循 SpringBoot 开发三板斧(1、加依赖,2、加注解,3、加配置)即可

环境准备

  1. 加依赖 openfeign

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
  2. 加注解 @EnableFeignClients

    @SpringBootApplication
    @EnableFeignClients
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    
  3. 加配置(由于 feign 不需要额外在 application.yml 或者 application.properties)中配置,只需要配置好调用微服务的名称和端口即可,下面举个例子,user-center 就是我们调用的微服务

    server:
      port: 8081
    spring:
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
      # 微服务名称
      application:
        name: user
    

新建 OpenFeign 接口

@FeignClient(name = "user")
public interface UserFeignClient {
    @GetMapping("/users/{id}")
    UserDto findById(@PathVariable Integer id);
}

新建 Controller 层

@Controller
public class UserController {
​
    @Resource
    private UserFeignClient userFeignClient;
​
    public UserDto findByUserId(Integer userId) {
​
        UserDto userDto = this.userFeignClient.findById(userId);
        return userDto;
​
    }
​
}

以上,就是 openFeign 的基本使用入门了

Feign的日志配置

由于 Feign 在没有配置的情况下是不会打印任何日志,如果想要看到 Feign 的日志,需要额外的配置;但是在此之前,我们先了解下 Feign 的自定义日志级别。

Feign 的自定义日志级别

级别 打印内容
NONE(默认值) 不记录任何日志
BASIC 仅记录请求方法、URL、响应状态码以及执行时间
HEADERS 记录 BASIC 级别的基础上,记录请求和响应的 header
FULL 记录请求和响应的 header、body 和元数据

Feign 的自定义日志级别可以通过 Java 代码方式或配置属性方式来实现,下面我们先介绍下代码的实现方式

代码配置方式

  1. 编写一个 Configuration 的类

    public class UserFeignConfiguration {
        @Bean
        public Logger.Level level() {
            //	生产上不建议使用FULL,这样会产生大量的日志,影响性能的同时还不好定位问题。
            //	建议使用 BASIC
            return Logger.Level.FULL;
        }
    }
    
  2. 然后在对应的 Feign 接口设置 Configuration 类

    @FeignClient(name = "user", configuration = UserFeignConfiguration.class)
    public interface UserFeignClient {
        @GetMapping("/users/{id}")
        UserDto findById(@PathVariable Integer id);
    }
    
  3. 最后还要将 Feign接口类的全路径设置在 yml 文件里面

    logging:
      level:
    	# Feign接口类的全路径 
    	# Feign的日志级别是建立在Feign的接口 debug 级别之上的
        com.example.article.feignclient.UserFeignClient: debug
    

    当调用接口时,输出日志结果如下

    [UserFeignClient#findById] <--- HTTP/1.1 200 (794ms)
    [UserFeignClient#findById] content-type: application/json;charset=UTF-8
    [UserFeignClient#findById] date: Mon, 29 Aug 2022 10:56:27 GMT
    [UserFeignClient#findById] transfer-encoding: chunked
    [UserFeignClient#findById] 
    [UserFeignClient#findById] {"id":1,"username":"张三","createTime":"2022-08-25T17:17:04.000+0000","updateTime":"2022-08-25T17:17:04.000+0000"}
    [UserFeignClient#findById] <--- END HTTP (180-byte body)
    

    这样,通过代码来设置 Feign 的日志自定义级别方式就配置好了。但是这样,并不是完美的解决方案,就相当于每次新建一个 Feign 接口,就要编写一个对应的 xxxFeignConfiguration 类,然后在 Feign 接口上指定,这样下来太繁琐了,下面看下全局的代码配置方式:

    不需要在 每个 Feign 接口上面都要配置下 configuration = UserFeignConfiguration.class,我们只需要在 Application 启动类上面,指定 @EnableFeignClients(defaultConfiguration = GlobalFeignConfiguration.class),注:这里将 UserFeignConfiguration 已经更名为 GlobalFeignConfiguration

    @SpringBootApplication
    @EnableFeignClients(defaultConfiguration = GlobalFeignConfiguration.class)
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    

属性配置方式

在 application.yml 加入只需要配置以下的属性既可以实现 Feign 的自定义级别

feign:
  client:
    config:
      # 微服务名称
      user:
        loggerLevel: full

同理,yml 文件也支持 全局的属性配置方式

feign:
  client:
    config:
      # 全局配置(default 默认就是适用于全部微服务)
      default:
        loggerLevel: full

Feign的多参数请求构造

GET 请求

请求多参数的URL,如请求地址为 http://www.xxx.me/admin/user/get?username=张三&school=阳光小学&birthDay=2012-08-01,SpringCloud 为 Feign 整合了 SpringMVC 的注解支持

  1. @SpringQueryMap 注解

    @FeignClient("user")
    public interface UserFeignClient {
      @RequestMapping(value = "/get", method = RequestMethod.GET)
      public User get(@SpringQueryMap User user);
    }
    
  2. @RequestParam注解(表单传参)

    @FeignClient("user")
    public interface UserFeignClient {
      @RequestMapping(value = "/get", method = RequestMethod.GET)
      public User get(@RequestParam("username") String username, @RequestParam("school") String school, @RequestParam("birthDayDay") String birthDay);
    }
    
  3. @PathVariable注解(URL携带参数)

    @FeignClient("user")
    public interface UserFeignClient {
      @RequestMapping(value = "/get", method = RequestMethod.GET)
      public User get(@PathVariable("username") String username, @PathVariable("school") String school, @PathVariable("birthDayDay") String birthDay);
    }
    

POST 请求

Feign 默认的传参方式就是 JSON 传参(@RequestBody),因此定义接口的时候可以不用@RequestBody注解标注,但为了开发规范,建议加上

  1. @RequestBody 注解

    @FeignClient("user")
    public interface UserFeignClient {
      @RequestMapping(value = "/post", method = RequestMethod.POST)
      public User post(@RequestBody User user);
    }
    

超时设置

我们在通过 Feign 去调用接口,难免会遇到超时的问题,我们可以在 yml 文件设置超时属性,防止系统抛出超时异常

feign:
    client:
        config:
		   # 全局配置(default 默认就是适用于全部微服务)
            default:
                connectTimeout: 100000
                readTimeout: 100000
            # 单独配置
            user:
                connectTimeout: 300000
                readTimeout: 300000

Feign 性能优化

默认情况下,Feign 使用的是 UrlConnetcion 去请求,这种原生的请求方式一旦遇到高并发的情况下,响应会变得很慢,所以我们可以考虑加入连接池技术来优化性能,下面介绍下如何集成 Apache 下的 HttpClient 的连接池

  1. 加入 httpclient 依赖

    <dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-httpclient</artifactId>
    </dependency>
    
  2. 设置 yml 文件属性

    feign:
    	# 这样就设置好了 feign 请求方式是 httpclient,而不是 UrlConnetcion 
        httpclient:
            enable: true
    	    # feign的最大连接数
            max-connection: 200
     	    # feign 单个路径的最大连接数
            max-connections-per-route: 50
    
posted @ 2022-09-06 08:59  追风少年潇歌  阅读(705)  评论(3编辑  收藏  举报