Feign远程调用


一、先看在此之前,我们服务之间需要进行调用的时候使用的restTemplate,代码示例如下:

String url = "http://userservice/user/"+userId; User user = restTemplate.getForObject(url , User.class);

这种方法需要先定义一个url,再使用restTemplate的api向这个路径去发送请求

思考这种方式的缺陷:在实际开发中,一个url会有很复杂的情况出现,参数可能多达几十个,此时要维护一个url将是一件很恐怖的事情。其次,代码的可读性也比较差

因此,有一种新的方式去发起远程调用,也就是Feign,feign是一种声明式的http客户端,其作用就是帮助开发者优雅的实现远程调用,极其优雅

 

二、使用方式

  1、引入Feign的依赖

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

  2、在服务消费者启动类上添加@EnableFeignClient注解,开启Feign的功能

  3、编写Feign的客户端(在服务消费者中新建一个接口,内容如下)

@FeignClient("userservice") //这个注解的默认属性是name/value,值是服务提供者的服务名称 public interface UserClient { @GetMapping("/user/{id}") //这里是要调用服务提供者的接口的地址 User findById(@PathVariable("id") Long id); //这里是标识服务提供者的接口的方法,方法名可以不一样,返回值、参数列表必须一样 }

     这个客户端主要是基于springMVC的注解来声明远程调用的信息,比如:

服务名称:userservice

* 请求方式:GET

* 请求路径:/user/{id}

* 请求参数:Long id

* 返回值类型:User

  声明了这些信息,Feign就可以帮助我们发送http请求了,无需自己使用restTemplate去发送了

     注:FeignClient注解属性的说明

          

 

 

   4、发起调用

    在需要调用的地方注入定义浩的接口(这里是UserClient),再用注入的接口对象去调用对应的 "方法"

@Resource
private UserClient userClient;

@GetMapping("{orderId}") public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) { // 根据id查询订单并返回 Order order = orderService.queryOrderById(orderId);
//restTemplate方式远程调用 //定义请求的路径 //String url = "http://userservice/user/"+order.getUserId(); //发送get请求 //User user = restTemplate.getForObject(url, User.class); //Feign方式远程调用 User user1 = userClient.getById(order.getUserId());
order.setUser(user1);
return order; }

 

三、总结使用方法:

  ①引入依赖

  ②再服务消费者的启动类上添加@EnableFeignClients注解

  ③编写FeignClient接口

  ④使用FeignClient接口中定义的 ”方法“ 远程调用

 

四、Feign的自定义配置

  1、Feign可以支持很多的自定义配置,大概有如下这些:

类型 作用 说明
feign.Logger.Level 修改日志级别 包含四种不同的级别:NONE、BASIC、HEADERS、FULL
feign.codec.Decoder 响应结果的解析器 http远程调用的结果做解析,例如解析json字符串为java对象
feign.codec.Encoder 请求参数编码 将请求参数编码,便于通过http请求发送
feign. Contract 支持的注解格式 默认是SpringMVC的注解
feign. Retryer 失败重试机制 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试

   一般情况下,默认值就能满足我们使用,如果要自定义时,只需要创建自定义的@Bean覆盖默认Bean即可

  

  2、以日志为例,实现自定义配置(两种方式)

    ①基于配置文件修改feign的日志级别(可针对某个服务,也可针对所有的服务):

feign: client: config: userservice: # 如果针对某个服务,这里就写对应的服务名,如果针对所有服务,就写default loggerLevel: FULL # 日志的级别(有NONE、BASIC、HEADERS、FULL)

    日志的级别分为四种:

NONE:不记录任何日志信息,这是默认值

BASIC:仅记录请求的方法,URL以及响应状态码和执行时间

HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息

FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据

 

    ②基于Java代码,在项目中声明一个类,将Logger.Level对象注册到spring容器中

public class DefaultFeignConfiguration { @Bean public Logger.Level feignLogLevel(){ return Logger.Level.BASIC; // 日志级别为BASIC(枚举类)  } }

      Ⅰ、如果想要自定义配置全局生效,则在启动类的@EnableFeignClients注解中放入这个类

        @EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class

      

      Ⅱ、如果想要自定义配置局部生效,则在对应的@FeignClient注解中放入这个类

         @FeignClient(value = "userservice", configuration = DefaultFeignConfiguration .class

 

五、Feign的优化建议:

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

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

Apache HttpClient :支持连接池

OKHttp:支持连接池

   因此,想要提升Feign的性能主要手段就是使用连接池代替默认的URLConnection

  2、实例使用Apache HttpClient

    ①引入HttpClient的依赖

<!--httpClient的依赖 --> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>

    ②在配置文件中配置连接池的一些配置:

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

    ③总结Feign的优化建议:

      Ⅰ、日志级别尽量使用BASIC或者使用NONE

      Ⅱ、使用带连接池的Feign客户端

 

 

六、Feign的最佳实践(所谓最近实践,就是使用过程中总结的经验,最好的一种使用方式)

  ①使用过程中我们可以发现,Feign的客户端与服务提供者的controller代码非常相似,所以我们利用继承的方式来抽取共同的部分:

    Ⅰ、定义一个API接口,利用定义方法,基于springMVC注解做声明

    Ⅱ、Feign客户端和Controller都继承该接口

                 

 

     Ⅲ、不足之处:

        服务提供方、服务消费方紧耦合

        参数列表中的注解映射并不会继承,因此Controller中必须再次声明方法、参数列表、注解

 

  ②抽取方式

    将Feign的Client抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用

                

 

   ③抽取步骤

    Ⅰ、创建一个module,命名为feign-api

    Ⅱ、在feign-api的pom中引入依赖(包括geign的starter依赖和Apache HttpClient依赖)

    Ⅲ、将之前服务消费者中编写的UserClient、User、DefaultFeignConfiguration都提取到feign-api子项目中

    Ⅳ、在原先的服务消费者中使用抽取的feign-api

      1、首先,删除order-service中的UserClient、User、DefaultFeignConfiguration等类或接口

      2、在服务的消费者中引入feign-api的依赖

      3、修改服务中所有与上述三个组件有关的部分,改成feign-api包中引入

      4、重启,(发现会报错)

        报错原因:UserClient在另一个项目的包中,所以服务的消费者无法扫描到UserClient组件

        解决方式:(两种)

          ①指定Feign应该扫描的包(不建议使用,这样把没用到的也引入了,造成浪费)

            @EnableFeignClients(basePackages = "cn.itcast.feign.clients")

          ②指定需要加载的Client的接口:(推荐使用,按需引用)

            @EnableFeignClients(clients = {UserClient.class})

      

  

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM