Open Feign

Open Feign

在前面的学习中,我们使用了Ribbon的负载均衡功能,简化了远程调用时的代码:

String user = this.restTemplate.getForObject("http://spring-provider/provider/" + id, String.class);

如果就学到这里,可能以后需要编写类似的大量重复代码,格式基本相同,无非参数不一样。有没有更优雅的方式,来对这些代码再次优化呢?这就是我们接下来要学的Feign的功能了。

简介

Netflix 提供了 Feign,并在 2016.7 月的最后一个版本 8.18.0 之后,将其捐赠给 spring cloud 社区,并更名为 OpenFeign 。OpenFeign 的第一个版本就是 9.0.0 ,OpenFeign 会完全代理 HTTP 的请求,在使用过程中我们只需要依赖注入 Bean,然后调用对应的方法传递参数即可。这对程序员而言屏蔽了 HTTP 的请求响应过程,让代码更趋近于『调用』的形式。

Feign是一个远程调用组件,集成了ribbon和hystrix。把Rest的请求进行隐藏,伪装成类似SpringMVC的Controller一样。不用再自己拼接url,拼接参数等等操作,一切都交给Feign去做。

在这里插入图片描述

入门

导入依赖

1、引入依赖

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

2、启动类增加@EnableFeignClients

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients // 开启feign客户端,无需配置熔断器和负载均衡注解
public class SpringConsumerApplication {public static void main(String[] args) {SpringApplication.run(SpringConsumerApplication.class, args);}
}

启动类增加了新的注解: @EnableFeignClients,如果我们要使用 Feign(声明式 HTTP 客户端),必须要在启动类加入这个注解开启 Feign 。

这样,我们的 Feign 就已经集成完成了,那么如何通过 Feign 去调用之前我们写的 HTTP 接口呢?

首先创建一个接口 ApiService(名字任意),并且通过注解配置要调用的服务地址。

Feign的客户端

@FeignClient(value = "spring-provider") // 标注该类是一个feign接口
public interface ProviderOpenfeign {@GetMapping("/sms/provider/{id}")String provider(@PathVariable("id") String id);
}
  • 这是一个接口,Feign会通过动态代理,帮我们生成实现类。这点跟mybatis的mapper很像;
  • @FeignClient,声明这是一个Feign客户端,类似@Mapper注解。同时通过value属性指定服务名称;
  • 接口中定义的方法,完全采用SpringMVC的注解,Feign会根据注解帮我们生成URL,并访问获取结果;
  • 一个服务只能被一个类绑定,不能让多个类绑定同一个远程服务,否则,会在启动项目是出现『已绑定』异常。

说明:

1、方法的返回值、参数以及requestmapping路径要和提供方的一模一样和消费方无关,消费方只需要调用该方法得到结果;
2、如果服务方controller类有@RequestMapping 定义命名空间,provider,在这里我们建议在方法上面拼接,不能在ProviderOpenfeign接口上去定义@requestMapping("/provider/");
3、@PathVariable("id") 这个括号里面的id不能省略,同理@Requestparam("id")里面的id也不能省略。

改造原来的调用逻辑,调用ProviderOpenfeign接口:

@Controller
public class ConsumerController {@Autowiredprivate ProviderOpenfeign providerOpenfeign;@RequestMapping("/consumerOpenfeign/{id}")public String consumerOpenfeign(@RequestParam("id") String id){String consumer = providerOpenfeign.provider(id);return "consumerOpenfeign " + consumer;}
}

Hystrix支持

OPen Feign默认也有对Hystrix的集成:

在这里插入图片描述

默认是关闭的,我们需要通过下面的参数来开启:

feign:hystrix:enabled: true # 开启Feign的熔断功能,默认超时1s

只需要开启hystrix的熔断功能即可,默认时间是1s中,如果要修改熔断的时间,要做如下的配置:

feign:hystrix:enabled: true # 开启Feign的熔断功能client:config:default:connectTimeout: 4000  #设置feign超时连接时长readTimeout: 4000  #设置feign请求的超时时长  4s之后 提供方没有响应 直接降级
hystrix:command:default:execution:isolation:thread:timeoutInMilliseconds: 6000  strategy: THREADtimeout:enabled: true

说明:

connectTimeout是feign连接某个服务的超时时间,

readTimeout是feign请求某个接口的超时时长,

timeoutInMilliseconds是hystrix熔断的时间,

Hystrix的超时时间是站在命令执行时间来看的和Feign设置的超时时间在设置上并没有关联关系。Hystrix不仅仅可以封装Http调用,还可以封装任意的代码执行片段。Hystrix是从命令对象的角度去定义,某个命令执行的超时时间,超过此此时间,命令将会直接熔断,假设hystrix 的默认超时时间设置了3000(3秒),而feign 设置的是4秒,那么Hystrix会在3秒到来时直接熔断返回,不会等到feign的4秒执行结束,如果hystrix的超时时间设置为6s,而feign 设置的是4秒,那么最终要以feign的时间4s为准,你可以理解为以时间短的为准,这两段都要配置,不能不配hystrix的配置,否则feign配置的readTimeout不生效。

Feign中的Fallback降级配置不像hystrix中那样了。

1)首先,我们要定义一个类ProviderOpenfeignFallback,实现刚才编写的ProviderOpenfeign,作为fallback的处理类

@Component
public class ProviderOpenfeignFallback implements ProviderOpenfeign {@Overridepublic User provider(String id) {return "服务器繁忙,请稍后再试!";}
}

2)然后在ProviderOpenfeign中,指定刚才编写的实现类

@FeignClient(value = "spring-provider",fallback = ProviderOpenfeignFallback.class) // 标注该类是一个feign接口
public interface ProviderOpenfeign {@GetMapping("/provider/{id}")String provider(@PathVariable("id") String id);
}

3)修改消费方controller的consumerOpenfeign方法,注释掉**@HystrixCommand注解和@DefaultProperties(defaultFallback=“fallback”)**进行测试。

@RestController
//@DefaultProperties(defaultFallback = "fallbackMethod")
public class ConsumerController {@Autowiredprivate ProviderOpenfeign providerOpenfeign;@RequestMapping(value = "/consumerOpenfeign/{id}")//@HystrixCommandpublic String consumerOpenfeign(@PathVariable String id){String consumer = providerOpenfeign.provider(id);return "consumerOpenfeign consumer " + consumer;}
}

4)重启测试:

在这里插入图片描述

超时和超时重试

OpenFeign 本身也具备重试能力,在早期的 Spring Cloud 中,OpenFeign 使用的是 feign.Retryer.Default#Default() ,重试 5 次。但 OpenFeign 集成了Ribbon依赖和自动配置(默认也是轮询),Ribbon 也有重试的能力,此时,就可能会导致行为的混乱。(总重试次数 = OpenFeign 重试次数 x Ribbon 的重试次数,这是一个笛卡尔积。)

后来 Spring Cloud 意识到了此问题,因此做了改进(issues 467),将 OpenFeign 的重试改为 feign.Retryer#NEVER_RETRY ,即默认关闭。 Ribbon的重试机制默认配置为0,也就是默认是去除重试机制的,如果两者都开启重试,先执行ribbon重试,抛出异常之后再执行feign的重试。

所以,OpenFeign『对外』表现出的超时和重试的行为,实际上是 Ribbon 的超时和超时重试行为。我们在项目中进行的配置,也都是配置 Ribbon 的超时和超时重试

在调用方配置如下

# 全局配置
ribbon:# 请求连接的超时时间connectTimeout: 1000# 请求处理的超时时间readTimeout: 1000   #1秒# 最大重试次数MaxAutoRetries: 5# 切换实例的重试次数MaxAutoRetriesNextServer: 1#NFLoadBalancerRuleClassName: RandomRule# 对所有请求开启重试,并非只有get 请求才充实。一般不会开启这个功能。该参数和上面的3三个参数没有关系#okToRetryOnAllOperations: true
feign:hystrix:enabled: true     #默认是1s降级#client:         #config:# default:# connectTimeout: 4000  #要关掉feign超时连接时长#readTimeout: 150000
hystrix:command:default:execution:isolation:thread:timeoutInMilliseconds: 100000   #这个时间要大于ribbon重试次数的总时长,否则还没重试完就降级了

被调用方设置线程睡眠

@RestController
public class ProviderController {@Value("${server.port}")private String port;@RequestMapping(value = "/provider/{id}")public String provider(@PathVariable String id){try {Thread.sleep(5000);} catch (InterruptedException e) {return "exception:" + e.getMessage();}return "provider id = " + id + "port = " + port;}
}

测试结果:由于openfeign重试默认是关闭的,我们不用管它。被调用方,如果只启动一个被调方实例,则一共12次,因为 MaxAutoRetriesNextServer: 1 切换下一个实例再重试,下一个实例还是自己,如果被调方启动两个实例,则各6次。另外重试和熔断都开启,超时时间是1s,一共是12次,也就是12s,12s之后就会降级,而hystrix配置的timeoutInMilliseconds的15s降级,在这里是以时间短的为主。如果不对timeoutInMilliseconds进行配置,那么hystrix默认是1s,也就是1s钟之后就会降级,但是不影响ribbon的重试。

你也可以指定对某个特定服务的超时和超时重试:

则其他的请求走上面的重试,spring-provider该服务的重试单独配置

# 针对 spring-provide 的设置,注意服务名是小写
spring-provide:ribbon:connectTimeout: 1000readTimeout: 3000   MaxAutoRetries: 2MaxAutoRetriesNextServer: 2

在被调方,修改如下代码测试

@RestController
public class ProviderController {@Value("${server.port}")private String port;@RequestMapping(value = "/provider/{id}")public String provider(@PathVariable String id){try {Thread.sleep(5000);} catch (InterruptedException e) {return "exception:" + e.getMessage();}return "provider id = " + id + "port = " + port;}
}

替换底层用HTTP实现

本质上是 OpenFeign 所使用的 RestTemplate 替换底层 HTTP 实现

  • 替换成 HTTPClient

将 OpenFeign 的底层 HTTP 客户端替换成 HTTPClient 需要 2 步:

1、引入依赖:

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

2、在配置文件中启用它:

feign:httpclient:enabled: true # 激活 httpclient 的使用
  • 替换成 OkHttp

将 OpenFeign 的底层 HTTP 客户端替换成 OkHttp 需要 2 步:

1、引入依赖

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

2、配置

feign:httpclient:enabled: false  # 关闭 httpclient 的使用okhttp:enabled: true   # 激活 okhttp 的使用

日志级别(了解)

前面讲过,通过logging.level.xx=debug来设置日志级别。然而这个对Fegin客户端而言不会产生效果。因为@FeignClient注解修改的客户端在被代理时,都会创建一个新的Fegin.Logger实例。我们需要额外指定这个日志的级别才可以。然后根据 logging.level. 参数配置格式来开启 Feign 客户端的 DEBUG 日志,其中 部分为 Feign 客户端定义接口的完整路径。默认值是NONE,而NONE不会记录Feign调用过程的任何日志的,也就是说这个日志不是启动feign客户端的日志,而是feign调用远程接口时产生的日志

1)设置com.woniu包下的日志级别都为debug

logging:level:com:woniu:openfeign:ProviderOpenfeign: debug   #ProviderOpenfeign为某个feign接口

2)编写配置类,定义日志级别

内容:

@Configuration
public class FeignLogConfiguration {@BeanLogger.Level feignLoggerLevel(){return Logger.Level.FULL;}
}

这里指定的Level级别是FULL,Feign支持4种级别:

在这里插入图片描述

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

3)在FeignClient中指定配置类:

@FeignClient(value = "spring-provider",fallback = ProviderOpenfeignFallback.class, configuration = FeignLogConfiguration.class)
public interface ProviderOpenfeign {@GetMapping("/provider/{id}")String provider(@PathVariable("id") String id);
}

4)重启项目,进行测试:在通过openfeign去调用某个接口时,会有详细的信息。如果把日志级别设置为NONE,则没有。

测试:http://localhost:8280/consumerOpenfeign

ml
logging:
level:
com:
woniu:
openfeign:
ProviderOpenfeign: debug #ProviderOpenfeign为某个feign接口


2)编写配置类,定义日志级别内容:```java
@Configuration
public class FeignLogConfiguration {@BeanLogger.Level feignLoggerLevel(){return Logger.Level.FULL;}
}
```这里指定的Level级别是FULL,Feign支持4种级别:[外链图片转存中...(img-IP80Hi1k-1694490245931)]- NONE:不记录任何日志信息,这是默认值。
- BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
- HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
- FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。3)在FeignClient中指定配置类:```java
@FeignClient(value = "spring-provider",fallback = ProviderOpenfeignFallback.class, configuration = FeignLogConfiguration.class)
public interface ProviderOpenfeign {@GetMapping("/provider/{id}")String provider(@PathVariable("id") String id);
}
```4)重启项目,进行测试:在通过openfeign去调用某个接口时,会有详细的信息。如果把日志级别设置为NONE,则没有。测试:http://localhost:8280/consumerOpenfeign 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/108162.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【多线程】常见的锁策略

常见的锁策略 1. 乐观锁 vs 悲观锁2. 读写锁 vs 普通互斥锁3. 重量级锁 vs 轻量级锁4. 自旋锁&#xff08;Spin Lock&#xff09;vs 挂起等待锁5. 公平锁 vs 非公平锁6. 可重入锁 vs 不可重入锁7. Synchronized8. 相关面试题 1. 乐观锁 vs 悲观锁 悲观锁&#xff1a; 总是假设…

obsstudio下载使用

官网 Open Broadcaster Software | OBS 介绍 OBS是Open Broadcaster Software的简称&#xff0c;是一款开源&#xff0c;用于视频录制以及直播串流的软件&#xff0c;它支持Windows、Mac以及Linux操作系统。OBS使用容易、操作简单&#xff0c;对于新手小白来说非常友好。如果…

Python的pandas库来实现将Excel文件转换为JSON格式的操作

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

STM32纯中断方式发送接收数据(串行通信;keil arm5;)

除了main文件其他文件均无修改&#xff0c;正常运行--在keil arm5内

[.NET 6] IHostedService 的呼叫等等我的爱——等待Web应用准备就绪

📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:新的征程,我们面对的不是技术而是人心,人心不可测,海水不可量,唯有技术,才是深沉黑夜中的一座闪烁的灯塔 !序言 在这篇文章中,我将介绍如何等…

使用maven idea环境

目录 idea三种方式执行maven命令 工程导入 生命周期lifecycle 插件和目标 常用命令 创建模块工程后 idea三种方式执行maven命令 想在哪个工程模块上执行就点开哪一个 如果觉得双击完clean再双击install麻烦&#xff0c;可以 如果有需要还可以给命令后面加参数 ​​​ 第三种…

第27章_瑞萨MCU零基础入门系列教程之freeRTOS实验

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…

使用Java登录校验

会话技术 会话&#xff1a;用户打开浏览器&#xff0c;访问web服务器的资源&#xff0c;会话建立&#xff0c;直到有一方断开连接&#xff0c;会话结束。在一次会话可以包含多次请求和响应。 会话跟踪&#xff1a;一种维护浏览器状态的方法&#xff0c;服务器需要识别多次请求是…

iptables 目标地址转换

目录 一、实验准备 二、配置web服务器 三、配置web防火墙网卡 四、配置客户机网卡 五、测试 1、开启防火墙功能&#xff0c;设置源地址转换&#xff0c;通过改变我客户机的地址身份为web服务器同网段来实现访问 2、通过改变目标地址&#xff08;客户机&#xff09;的地址…

算法通关村18关 | 透析回溯的模板

回溯有清晰的解题模板&#xff0c; void backtracking(参数){if (终止条件){存放结果;return;}for (选择本层中的集合元素&#xff08;画成树&#xff0c;就是树节点孩子的大小) {处理节点;backtracking();回溯&#xff0c;撤销处理结果;}} 1. 从N叉树说起 在回溯之前&#x…

【C++】常用排序算法

0.前言 1.sort #include <iostream> using namespace std;// 常用排序算法 sort #include<vector> #include<algorithm>//利用仿函数 打印输出 class myPrint { public:void operator()(int val){cout << val << " ";} };//利用普通函…

OSCP系列靶场-Esay-Vegeta1保姆级

OSCP系列靶场-Esay-Vegeta1保姆级 目录 OSCP系列靶场-Esay-Vegeta1保姆级总结准备工作信息收集-端口扫描目标开放端口收集目标端口对应服务探测 信息收集-端口测试22-SSH端口的信息收集22-SSH端口版本信息与MSF利用22-SSH协议支持的登录方式22-SSH手动登录尝试(无)22-SSH弱口令…