一,OpenFeign

1.1 介绍

我们利用 Nacos 实现了服务的治理,利用 RestTemplate 实现了服务的远程调用。但是远程调用的代码太复杂了:

2953321-20250424110511364-2909976.png

而且这种调用方式,与原本的本地方法调用差异太大,编程时的体验也不统一,一会儿远程调用,一会儿本地调用。

因此,我们必须想办法改变远程调用的开发模式,让远程调用像本地方法调用一样简单。而这就要用到 OpenFeign 组件了。

其实远程调用的关键点就在于四个:

  • 请求方式
  • 请求路径
  • 请求参数
  • 返回值类型

所以,OpenFeign 就利用 SpringMVC 的相关注解来声明上述 4 个参数,然后基于动态代理帮我们生成远程调用的代码,而无需我们手动再编写,非常方便。

1.2 快速入门

  1. 引入依赖

      <!--openFeign-->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-openfeign</artifactId>
      </dependency>
      <!--负载均衡器-->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-loadbalancer</artifactId>
      </dependency>
    
  2. 启用 OpenFeign

    在启动类上添加注解,启动 OpenFeign 功能
    2953321-20250424110759477-734027794.png

  3. 编写 OpenFeign 客户端

    package com.hmall.cart.client;
    
    import com.hmall.cart.domain.dto.ItemDTO;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import java.util.List;
    
    @FeignClient("item-service")
    public interface ItemClient {
    
        @GetMapping("/items")
        List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
    }
    

    这里只需要声明接口,无需实现方法。接口中的几个关键信息:

    • @FeignClient("item-service") :声明服务名称
    • @GetMapping :声明请求方式
    • @GetMapping("/items") :声明请求路径
    • @RequestParam("ids") Collection<Long> ids :声明请求参数
    • List<ItemDTO> :返回值类型

    有了上述信息,OpenFeign 就可以利用动态代理帮我们实现这个方法,并且向http://item-service/items发送一个GET请求,携带 ids 为请求参数,并自动将返回值处理为List<ItemDTO>

    我们只需要直接调用这个方法,即可实现远程调用了。

  4. 使用 FeignClient

    2953321-20250424110932091-1692527026.png
    feign 替我们完成了服务拉取、负载均衡、发送 http 请求的所有工作,而且,这里我们不再需要 RestTemplate 了,还省去了 RestTemplate 的注册。

1.3 连接池

Feign 底层发起 http 请求,依赖于其它的框架。其底层支持的 http 客户端实现包括:

  • HttpURLConnection:默认实现,不支持连接池
  • Apache HttpClient :支持连接池
  • OKHttp:支持连接池

因此我们通常会使用带有连接池的客户端来代替默认的HttpURLConnection。比如,我们使用 OK Http.

  1. 引入依赖

    <!--OK http 的依赖 -->
    <dependency>
      <groupId>io.github.openfeign</groupId>
      <artifactId>feign-okhttp</artifactId>
    </dependency>
    
  2. 开启连接池

    feign:
      okhttp:
        enabled: true # 开启OKHttp功能
    
  3. 验证

    我们可以打断点验证连接池是否生效,在org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient中的execute方法中打断点:

2953321-20250424112120033-458801951.png

 Debug方式启动cart-service,请求一次查询我的购物车方法,进入断点:

2953321-20250424112132066-143434741.png

 可以发现这里底层的实现已经改为`OkHttpClient`

注意:对于 Feign 客户端,业务上不会在单独一个服务里面创建,而是抽取成一个api-service模块,然后其他服务需要调用直接引入这个模块,从而降低重复的创建 Feign 客户端。

1.4 日志输出

OpenFeign 只会在 FeignClient 所在包的日志级别为DEBUG时,才会输出日志。而且其日志级别有 4 级:

  • NONE:不记录任何日志信息,这是默认值。
  • BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
  • HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
  • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。

Feign 默认的日志级别就是 NONE,所以默认我们看不到请求日志。

  1. 定义日志级别

    api-service模块下新建一个配置类,定义 Feign 的日志级别:

    package com.hmall.api.config;
    
    import feign.Logger;
    import org.springframework.context.annotation.Bean;
    
    public class DefaultFeignConfig {
        @Bean
        public Logger.Level feignLogLevel(){
            return Logger.Level.FULL;
        }
    }
    
  2. 配置

    接下来,要让日志级别生效,还需要配置这个类。有两种方式:

    • 局部生效:在某个FeignClient中配置,只对当前FeignClient生效

      @FeignClient(value = "item-service", configuration = DefaultFeignConfig.class)
      
    • 全局生效:在@EnableFeignClients中配置,针对所有FeignClient生效。

      @EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)