2.2 Feign远程调用

一、基于Feign远程调用

  1. RestTemplate请求中存在的问题

    1. 代码可读性差,编程体验不统一

    2. 参数复杂,URL难以维护

  2. Feign的介绍

    1. Feign是一个声明式的http客户端,官方地址是https://github.com/OpenFeign/feign,所用是帮助我们优雅的实现http请求的发送,解决RestTemplate存在的问题

  3. 使用Feign的步骤:

    1. 引入Feign依赖

              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-openfeign</artifactId>
              </dependency>
    2. 在调用方(消费方)的启动类中添加注解,开启Feign的功能

      @SpringBootApplication
      @MapperScan("cn.learn.order.mapper")
      @EnableFeignClients   // 开启Feign远程调用
      public class OrderApplication {
      
          public static void main(String[] args) {
              SpringApplication.run(OrderApplication.class, args);
          }
      
      }
    3. 使用编写Feign客户端

      @FeignClient("user-service")  // 声明远程调用的服务
      public interface UserServiceClient { // 这是一个接口
      
          @GetMapping("/user/{id}")  // 声明请求方式以及请求全路径 
          User findById(@PathVariable("id") Long id);  // 请求方法以及参数
      
      }

      备注:这是所有对user-service发起的远程调用

      ​ Feign自动实现负载均衡

    4. 使用:

      @Service
      public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
      
          @Autowired
          private RestTemplate restTemplate;
          @Autowired
          private UserServiceClient userServiceClient;
      
          @Override
          public OrderVO getByOrderId(Long id) {
              Order order = this.getById(id);
              OrderVO orderVO = BeanHelper.copyProperties(order, OrderVO.class);
              // 使用Feign远程调用
              User user = userServiceClient.findById(order.getUserId());
              orderVO.setUser(user);
              return orderVO;
          }
      }
    5. 注意:

      1. SpringCloud的Feign在Hoxton.M2 RELEASED版本后不再使用Ribbon进行负载均衡,而是使用spring-cloud-loadbalancer

      2. 因此,如果使用Hoxton.M2 RELEASED版本周的Feign,就需要直接调用方中假如坐标

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-loadbalancer</artifactId>
        </dependency>
      3. 由于Nacos的discovery中使用了Ribbon,并且这个Ribbon会使loadbalancer失效,因此,使用了Nacos,需要将Ribbon提出

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

二、自定义配置

  1. Feign运行自定义配置来覆盖默认配置,可修改配置如下:

    类型
    作用
    说明

    feign.Logger.Level

    修改日志级别

    包含四种不同的级别:NONE、BASIC、HEADERS、FULL

    feign.codec.Decoder

    相应结果的解析器

    http远程调用的结果做解析,例如解析jaon字符串为Java对象

    frign.cloudec.Encoder

    请求参数编码

    将请求参数编码,便于通过http请求发送

    feign.Contract

    支持的注解格式

    默认是SpringMVC的注解

    feign.Retryer

    失败重试机制

    请求失败的重试机制,默认是没有,比不过会使用Ribbon的重试

    备注:一般配置的就是日志级别

    ​ 日志级别解释:NONE:默认、BASIC:基本请求、HEADERS基本请求+请求头、FULL:所有日志信息

  2. 修改日志级别的方式:

    1. 基于配置文件修改:

      # 全局生效
      feign:
      	client:
      		config:
      			default: # default就是全局配置,如果是服务名就是针对某个服务配置,局部
      				loggerLevel: FULL
      
      # 局部生效
      feign:
      	client:
      		config:
      			user-service: # 针对user-service服务的配置
      				loggerLevel: FULL
    2. 基于Java代码的配置

      // 声明一个Bean
      public class FeignClientConfiguration {
          @Bean
          public Logger.Level feignLogLevel() {
              return Logger.Level.BASIC;
          }
      }
      
      // 全局配置
      @EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)
      
      // 局部配置
      @FeignClient(vlaue = "user-service", configuration = FeignClientConfiguration.class)

三、Feign的性能优化

  1. 底层客户端实现

    1. URLConnection:默认实现,不支持连接池

    2. Appache HttpClient:支持连接池

    3. OkHttp:支持连接池

  2. 优化Feign的性能主要包括

    1. 使用连接池代替默认的URLConnect

    2. 日志级别,最好使用basic活none

  3. 修改连接池为HttpClient

    1. 添加依赖:

              <dependency>
                  <groupId>io.github.openfeign</groupId>
                  <artifactId>feign-httpclient</artifactId>
              </dependency>
    2. 配置连接池

      feign:
        client:
          config:
            default: # 全局配置
              loggerLevel: BASIC # 日志级别,基本响应信息
        httpclient:
          enabled: true # 开启feign对HttpClient的支持
          max-connections: 200 # 最大连接数
          max-connections-per-route: 50 # 每个路径最大连接数量

      备注:连接数与每个路径的最大连接数需要压力测试后才可以给出最优量

      ​ 如果使用OkHttp则需要将httpclient修改为okhttp,并添加OkHttp的坐标依赖

四、Feign最佳实践

  1. 方式一:继承

    1. 给消费者的FeignClient和提供者的Controller定义统一的父接口作为标准

    2. 定义统一接口声明,Controller以及FeignClient同时实现这个接口

    3. 官方不推荐,因为耦合性太高;父接口参数列表中的映射不被继承

  2. 方式二:抽取

    1. 将FeignClient抽取为独立模块,并且吧接口有关的pojo、默认的Feign配置都放在这个模块中,提供给所有的消费者

    2. 缺点:每个服务都要引入所有的服务

    3. 会出现的问题:指定的连接类找不到

      1. 指定FeignClient所在包

        @EnableFeignClients(basePackages = "包地址")
      2. 指定FeignClient字节码(推荐)

        @EnableFeignClients(clients = {文件名.class})

最后更新于

这有帮助吗?