Spring cloud alibaba--Feign微服务调用组件

﹏ヽ暗。殇╰゛Y 2023-10-03 11:44 91阅读 0赞

目录

1.1Feign优势

2.spring cloud alibaba整合Feign

3.Spring Cloud Feign日志配置

4.Feign契约配置

5.Feign超时时间配置

6.Open Feign自定义拦截器

7.Feign远程调用原理


1.什么是Feign

Feign是Netflix开发的声明式、模板化的HTTP客户端,Feign支持多种注解,例如JAX-RS注解。

spring cloud openfeign对feign进行了增强,使其支持spring mvc注解,另外还整合了Ribbon和Nacos,从而使得feign的使用更加方便。

1.1Feign优势

Feign可以做到使用http请求远程服务时就像调用本地方法一样的体验,开发者完全感知不到这是远程调用,更感知不到这是http请求。它像dubbo一样,consumer直接调用接口调用provider,而不需要通过常规的Http client构造请求再解析返回数据。它解决了让开发者调用远程接口跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布式环境开发。

2.spring cloud alibaba整合Feign

(1)pom.xml中引入依赖

  1. <!-- 添加springcloud 的openfeign-->
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-openfeign</artifactId>
  5. <!-- 排除冲突的jar包文件-->
  6. <exclusions>
  7. <exclusion>
  8. <groupId>org.springframework</groupId>
  9. <artifactId>spring-web</artifactId>
  10. </exclusion>
  11. <exclusion>
  12. <groupId>org.springframework.cloud</groupId>
  13. <artifactId>spring-cloud-commons</artifactId>
  14. </exclusion>
  15. </exclusions>
  16. </dependency>

(2)application.properties中配置链接nacos的信息

  1. server.port=8084
  2. #应用名称,nacos会将该名称当做服务名称
  3. spring.application.name=order-service
  4. #nacos服务连接地址
  5. spring.cloud.nacos.server-addr=127.0.0.1:8848
  6. #nacos discovery连接用户名
  7. spring.cloud.nacos.discovery.username=nacos
  8. #nacos discovery连接密码
  9. spring.cloud.nacos.discovery.password=nacos
  10. #nacos discovery工作空间
  11. spring.cloud.nacos.discovery.workspace=public

(3)编写Feign调用接口

  1. 使用@FeignClient来定义feign的调用方式,value为调用的服务名,path对应被调用服务处理的controller层,按相同的写法把提供者的方法在此接口中实现,使用spring mvc注解请求的方式定义。
  2. /**
  3. * 使用@FeignClient来定义feign的调用方式,value为调用的服务名,path对应被调用服务处理的controller层
  4. */
  5. @FeignClient(value = "stock-service",path ="/stock" )
  6. public interface StockOpenFeign {
  7. @RequestMapping("/reduct")
  8. String reduct();
  9. }
  10. /**
  11. * @RequestMapping("/stock")
  12. * public class StockController {
  13. *
  14. * @Value("${server.port}")
  15. * String port;
  16. *
  17. * @RequestMapping("/reduct")
  18. * public String reduct(){
  19. * System.out.println("扣减库存");
  20. * return "扣减库存成功,端口号为:"+port+"的服务提供调用";
  21. * }
  22. * }
  23. */

(4)调用类在启动类代码中添加@EnableFeignClients注解

  1. /**
  2. * 程序启动类
  3. */
  4. @SpringBootApplication
  5. @EnableFeignClients
  6. public class OrderApplication {
  7. public static void main(String[] args) {
  8. SpringApplication.run(OrderApplication.class,args);
  9. }
  10. }

(5)像调用本地一样发起远程调用,使用接口调用

之前使用spring boot 的RestTemplate调用方式:

①先创建RestTemplate

  1. //程序启动时创建RestTemplate
  2. //使用注解LoadBalanced标识负载均衡,默认轮询的方式
  3. @Bean
  4. @LoadBalanced
  5. public RestTemplate restTemplate(RestTemplateBuilder builder){
  6. RestTemplate build = builder.build();
  7. return build;
  8. }

②程序中使用RestTemplate调用

  1. //注入RestTemplate
  2. @Autowired
  3. RestTemplate restTemplate;
  4. @RequestMapping("/add")
  5. public String add(){
  6. System.out.println("下单成功");
  7. // String forObject = restTemplate.getForObject("http://localhost:8083/stock/reduct", String.class);
  8. String forObject = restTemplate.getForObject("http://stock-service/stock/reduct", String.class);
  9. return "add order "+forObject;
  10. }

现在使用OpenFeign调用:直接使用刚才创建的接口定义的方法

  1. @Autowired
  2. StockOpenFeign stockOpenFeign;
  3. @RequestMapping("/add")
  4. public String add(){
  5. System.out.println("下单成功");
  6. String reduct = stockOpenFeign.reduct();
  7. return "add order "+reduct;
  8. }

3.Spring Cloud Feign日志配置

当我们遇到Bug,比如接口调用失败,参数没收到等问题,或者想看看调用性能问题,就需要配置Feign的性能日志,以此让Feign把请求信息输出来。

(1)Feign的日志级别有四种

①NONE【性能最佳,适用于生产】:不记录任何日志(默认)

②BASIC【适用于生产环境追踪问题】:仅记录请求方法,URL,响应状态代码及执行时间

③HEADERS:记录BASIC的基础上,记录请求和响应的header、

④FULL【比较适用于开发和测试环境定位问题】:记录请求和响应的header、body和元数据

(2)定义一个配置类,指定日志级别

  1. /**
  2. * Feign配置类
  3. */
  4. public class FeignConfig {
  5. //指定日志级别
  6. @Bean
  7. public Logger.Level feignLoggerLever(){
  8. return Logger.Level.FULL;
  9. }
  10. }

(3)application中配置日志级别

  1. spring boot默认级别是infofeign需要配置为debug,所以默认级别情况下不输出feign日志,debug模式,日志太多,可以指定某个包下的日志配置成dubug

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIQU5HTElaRU5H_size_16_color_FFFFFF_t_70

(4)系统中再创建一个项目ProductNacos(商品管理系统),提供一个带参数@PathVariable 方式的访问服务。为了后面配置整体日志、指定某个日志方式做准备。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIQU5HTElaRU5H_size_16_color_FFFFFF_t_70 1

(5)使用Feign访问订单OrderNacos和商品ProductNacos项目的配置接口

  1. /**
  2. * 使用@FeignClient来定义feign的调用方式,value为调用的服务名,path对应被调用服务处理的controller层
  3. */
  4. @FeignClient(value = "stock-service",path ="/stock" )
  5. public interface StockOpenFeign {
  6. @RequestMapping("/reduct")
  7. String reduct();
  8. }
  9. /**
  10. * 使用feign方式访问ProductNacos项目
  11. */
  12. @FeignClient(value = "product-service",path = "product")
  13. public interface ProductOpenFeign {
  14. @RequestMapping("/{id}")
  15. String get(@PathVariable("id") Integer id);
  16. }

******当我们有参数时,使用@PathVariable时,需要把具体的参数名带上@PathVariable(“id”),否则feign会报错,spring mvc请求时可以不带上具体的参数名。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIQU5HTElaRU5H_size_16_color_FFFFFF_t_70 2

(6)配置Feign的日志级别为全局方式,所有的服务都使用这个日志级别,在Feign日志配置类中加注解@Configuration

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIQU5HTElaRU5H_size_16_color_FFFFFF_t_70 3

(7)我们的订单控制层,使用Feign的方式,调用库存Stock和商品Product的方式

  1. @RestController
  2. @RequestMapping("/order")
  3. public class OrderController {
  4. @Autowired
  5. StockOpenFeign stockOpenFeign;
  6. @Autowired
  7. ProductOpenFeign productOpenFeign;
  8. @RequestMapping("/add")
  9. public String add(){
  10. System.out.println("下单成功");
  11. String reduct = stockOpenFeign.reduct();
  12. String s = productOpenFeign.get(1);
  13. return "add order "+reduct+s;
  14. }
  15. }

(8)访问服务,查看使用@Configuration全局配置日志的方式,控制台的输出情况:两个服务的feign调用日志都输出来了。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIQU5HTElaRU5H_size_16_color_FFFFFF_t_70 4(9)feign日志指定某个服务调用时使用配置方式

需要先去除@Configuration配置

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIQU5HTElaRU5H_size_16_color_FFFFFF_t_70 5

方式一:在Feign的配置接口中添加configuration指定

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIQU5HTElaRU5H_size_16_color_FFFFFF_t_70 6

只有商品的日志信息打印出来了

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIQU5HTElaRU5H_size_16_color_FFFFFF_t_70 7

方式二:在application.properties配置文件中指定

  1. #feign日志局部配置:feign.client.config.服务名.logger-level=日志级别
  2. feign.client.config.stock-service.logger-level=BASIC

此时只有stock服务调用有日志

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIQU5HTElaRU5H_size_16_color_FFFFFF_t_70 8

4.Feign契约配置

Spring cloud在Feign的基础上做了扩展,使用Spring MVC注解的方式来完成Feign的功能。原生的Feign是不支持Spring mvc的,如果想在spring cloud客户端中使用原生的注解方式定义客户端也是可以的,通过配置契约来改变这个配置。

(1)修改契约配置,支持Feign原生的注解

  1. //修改契约配置,支持Feign原生的注解
  2. @Bean
  3. public Contract feignContract(){
  4. return new Contract.Default();
  5. }

注意:修改Feign的契约后,使用Feign连接的接口不在支持spring mvc的注解,需要使用Feign原生的注解

(2)Feign连接的接口使用原生的注解

  1. RequestMapping需要改成RequestLine@PathVariable需要改成@Param
  2. @FeignClient(value = "product-service",path = "/product")
  3. public interface ProductOpenFeign {
  4. @RequestLine("GET /{id}")
  5. String get(@Param("id") Integer id);
  6. }

(3)可以在application.properties配置文件中配置,局部指定某个服务使用原生feign配置

  1. #feign契约局部配置:feign.client.config.服务名.contract=feign.Contract.Default
  2. feign.client.config.product-service.contract=feign.Contract.Default

此时只是product-service这个配置了原生的feign,stock-service还是使用spring mvc的注解

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIQU5HTElaRU5H_size_16_color_FFFFFF_t_70 9

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIQU5HTElaRU5H_size_16_color_FFFFFF_t_70 10

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIQU5HTElaRU5H_size_16_color_FFFFFF_t_70 11

5.Feign超时时间配置

通过Options可以设置连接超时时间和读取超时时间,Options的第一个参数是连接的超时时间(ms),模式2s;第二个参数是请求处理的超时时间(ms),默认5s。

(1)全局配置

  1. @Bean
  2. public Request.Options options(){
  3. return new Request.Options(5000,10000);
  4. }

(2)application.properties配置文件中进行局部配置

  1. #feign连接超时时间局部配置:feign.client.config.服务名.connect-timeout=
  2. feign.client.config.product-service.connect-timeout=5000
  3. #feign请求处理超时时间局部配置:feign.client.config.服务名.connect-timeout=
  4. feign.client.config.product-service.read-timeout=10000

6.Open Feign自定义拦截器

(1)定义一个拦截器来,实现RequestInterceptor接口

  1. /**
  2. * 自定义feign拦截器
  3. */
  4. public class FeignAuthRequestInterceptor implements RequestInterceptor {
  5. //重写拦截方法
  6. @Override
  7. public void apply(RequestTemplate requestTemplate) {
  8. System.out.println("Feign拦截器拦截到信息");
  9. requestTemplate.uri("/8"); //改变请求的结尾链接
  10. }
  11. }

(2)在配置类中使用@Configuration全局配置

  1. //配置自定义拦截器
  2. @Bean
  3. public FeignAuthRequestInterceptor feignAuthRequestInterceptor(){
  4. return new FeignAuthRequestInterceptor();
  5. }

(3)可以在application.properties配置文件中配置,局部配置

  1. #feign请求处理超时时间局部配置:feign.client.config.服务名.request-interceptors[数组长度,支持多个拦截器]=自定义拦截器目录
  2. feign.client.config.product-service.request-interceptors[0]=com.qingyun.inter.FeignAuthRequestInterceptor

(4)Feign发送请求前拦截控制台打印数据,修改uri的参数成功

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIQU5HTElaRU5H_size_16_color_FFFFFF_t_70 1220210724215535389.png

7.Feign远程调用原理

1ce0e9c148dc39e8fba41ce719b3f6a2.png

Feign远程调用流程图

watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA5riF5LqR6Z2S5LqR_size_20_color_FFFFFF_t_70_g_se_x_16

(1) 基于面向接口的动态代理方式生成实现类

在使用feign 时,会定义对应的接口类,在接口类上使用Http相关的注解,@FeignClient,标识HTTP请求参数信息,在Feign 底层,通过基于面向接口的动态代理方式生成实现类,将请求调用委托到动态代理实现类。

(2) 根据Contract协议规则,解析接口类的注解信息,解析分为默认的契约方式或者spring mvc的方式。

watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA5riF5LqR6Z2S5LqR_size_20_color_FFFFFF_t_70_g_se_x_16 1

(3) 基于 RequestBean,动态生成Request

根据传入的Bean对象和注解信息,从中提取出相应的值,来构造Http Request 对象。

(4) 使用Encoder 将Bean转换成 Http报文正文(消息解析和转码逻辑)

Feign 最终会将请求转换成Http 消息发送出去,传入的请求对象最终会解析成消息体。

(5) 拦截器负责对请求和返回进行装饰处理

在请求转换的过程中,Feign 抽象出来了拦截器接口,用于用户自定义对请求的操作,比如,如果希望Http消息传递过程中被压缩,可以定义一个请求拦截器。

(6) 日志记录

(7) 基于重试器发送HTTP请求

Feign 内置了一个重试器,当HTTP请求出现IO异常时,Feign会有一个最大尝试次数发送请求

(8) 发送Http请求

Feign 真正发送HTTP请求是委托给 feign.Client 来做的。

Feign 默认底层通过JDK 的 java.net.HttpURLConnection 实现了feign.Client接口类,在每次发送请求的时候,都会创建新的HttpURLConnection 链接

(9)Feign优化

①GZIP压缩:当Gzip压缩到一个纯文本数据时,可以减少70%以上的数据大小。

②替换为HttpClient客户端(使用HTTP连接池提供性能)

Feign的HTTP客户端支持3种框架,分别是;HttpURLConnection、HttpClient、OKHttp。Feign中默认使用HttpURLConnection。

  • HttpURLConnection是JDK自带的HTTP客户端技术,并不支持连接池,如果要实现连接池的机制,还需要自己来管理连接对象。对于网络请求这种底层相对复杂的操作,如果有可用的其他方案,也没有必要自己去管理连接对象。
  • Apache提供的HttpClient框架相比传统JDK自带的HttpURLConnection,它封装了访问http的请求头,参数,内容体,响应等等;它不仅使客户端发送HTTP请求变得容易,而且也方便了开发人员测试接口(基于Http协议的),即提高了开发的效率,也方便提高代码的健壮性;另外高并发大量的请求网络的时候,还是用“HTTP连接池”提升吞吐量。
  • OKHttp是一个处理网络请求的开源项目,是安卓端最火热的轻量级框架。OKHttp拥有共享Socket,减少对服务器的请求次数,通过连接池,减少了请求延迟等技术特点。
  • feign.httpclient.enabled=true

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIQU5HTElaRU5H_size_16_color_FFFFFF_t_70 13

发表评论

表情:
评论列表 (有 0 条评论,91人围观)

还没有评论,来说两句吧...

相关阅读