5.Feign的介绍与使用

340 阅读4分钟

5.1 Feign介绍

Feign是一个HTTP客户端,为了使服务消费者更加方便、优雅的调用服务提供者。就想调用本地service一样简单,使用者完全感觉不到实在远程http调用服务。

Feign特性:

  • Spring MVC注解

  • 整合了Ribbon负载均衡组件,实现调用服务实例的负载均衡

  • 支持HTTP请求和响应的压缩

5.2 Feign基本使用

主要流程是:

1.在服务消费端添加open-feign依赖

 <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
 </dependency>

2.在服务消费端启动类标记@EnableFeignClients注解

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

标记此注解的目的是扫描所有带@FeignClient注解的类

3.在服务消费端编写Feign客户端接口

/**
 * @description: 定义一个Feign接口,用于调用device服务
 * @author:xg
 * @date: 2024/12/13
 * @Copyright:
 */
@FeignClient("smart-device")
public interface FeignDeviceService {


    @GetMapping("/device/hello")
    String hello(@RequestParam("param") String param);
}

在服务提供端的device服务接口

@RestController
@RequestMapping("/device")
public class DeviceController {

    @GetMapping("/hello")
    public String hello(@RequestParam("param") String param, HttpServletRequest request) {
        return "hello: " +  param + " 服务实例端口:" + request.getServerPort();// 当前实例端口号
    }

}

4.在服务消费端编写接口调用Feign客户端接口

@RestController
@RequestMapping("/deviceData")
@Slf4j
public class DeviceDataController {

    @Resource
    private FeignDeviceService feignDeviceService;
   
    @GetMapping("/hello")
    public String hello(@RequestParam("param") String param) {
        return feignDeviceService.hello(param);
    }
}

这些流程在前面Nacos注册中心章节,有说明。

5.3 Feign的运行原理

  • 启动类添加@EnableFeignClients注解,会扫描添加了@FeignClient注解的Feign接口类
  • Feign接口类注入IoC容器,Feign接口类的方法被调用时,通过JDK代理的方式生产RequestTemplate对象,此对象封装了Http请求的参数、请求方法等信息
  • 由RequestTemplate生成Request对象,然后交给client处理,这个client可以时JDK原生的URLConnection、HttpClient、Okhttp。client会封装到LoadBalanceClient中,结合Ribbon负载均衡调用服务实例

5.4 Feign的各种配置

5.4.1 Feign开启GZIP压缩

Feign对请求和响应开启GZIP压缩,可以提高通信效率。

feign:
  compression:
    request:
      enabled: true # 请求开启压缩
      mime-types: text/xml,application/xml,application/json # 配置压缩支持的类型
      min-request-size: 2048 # 压缩数据大小的下限
    response:
      enabled: true # 响应开启压缩

business业务服务

  @GetMapping("/hello")
    public String hello(@RequestParam("param") String param) {
        String hello = feignDeviceService.hello(param);
        log.info("收到hello:{}",hello);
        return hello;
    }

调用device设备服务的hello接口


    @GetMapping("/hello")
    public String hello(@RequestParam("param") String param, HttpServletRequest request) {
        String hello = "hello: " +  param + " 服务实例端口:" + request.getServerPort();// 当前实例端口号
        log.info("hello:{}", hello);
        return hello;
    }

Feign开启GZIP压缩之后调用返回

看起来,跟没有开启压缩没什么两样,实际上通信效率是有提升的。

5.4.2 配置超时时间

feign: 
  client:
    config:
      default:
        connectTimeout: 5000 # 连接超时
        readTimeout: 5000 # 读取超时
        loggerLevel: basic

5.4.3 Feign中的Ribbon超时配置

ribbon:
  ReadTimeout: 5000 # 请求处理超时
  ConnectTimeout: 5000 # 连接超时

5.5 Feign使用问题处理

5.5.1 替换默认的client

Feign原始的Http客户端没有连接池,对每个调用建立一个连接,存在较大的创建新连接的开销。通过替换Feign默认的连接池,我们可以复用连接,配置连接池大小,配置超时时间,使得Feign调用服务性能达到最优。

接下来,我们使用okhttp替换Feign默认的client。okhttp客户端真的很棒,它的牛x之处在于:

  • 支持连接池,可以复用连接,减少创建连接开销,减少调用延迟

  • 支持GZIP压缩,减少传输的数据量

  • 合并多个到同一主机的请求

  • 缓存响应,避免重复请求

1.首先我们引入okhttp依赖

在business业务服务工程中添加okhttp依赖

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

2.在配置文件中开启okhttp

feign:
  okhttp:
    enabled: true

3.在配置类中配置OkHttpClient对象并配置超时、连接池等参数

/**
 * @description: 配置OkHttpClient,并设置超时参数、连接池参数
 * @author:xg
 * @date: 2024/12/26
 * @Copyright:
 */
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignOkHttpConfig {

    @Bean
    public okhttp3.OkHttpClient okHttpClient() {
        return new okhttp3.OkHttpClient.Builder()
                // 连接超时
                .connectTimeout(10, TimeUnit.SECONDS)
                // 读超时
                .readTimeout(10, TimeUnit.SECONDS)
                // 写超时
                .writeTimeout(10,TimeUnit.SECONDS)
                // 自动重连
                .retryOnConnectionFailure(true)
                // 连接池
                .connectionPool(new ConnectionPool())
                .build();
        
    }
}

4.最后启动business服务,device服务,然后进行Feign调用