主页 > 创业  > 

OpenFeign的简单介绍和功能实操

OpenFeign的简单介绍和功能实操
前言

本文主要做一下OpenFeign的简单介绍和功能实操,实操主要是OpenFeign的超时和重试,在阅读本文章前,请完成《Nacos 注册中心介绍与实操》内的Nacos多模块生产消费者项目

什么是OpenFeign

OpenFeign全名Spring Cloud OpenFeign,是SpringCloud开发团队基于Feign开发的框架,声明式Web服务客户端

Feign是一种声明式、模板化的HTTP客户端,可用于调用HTTP API实现微服务之间的远程服务调用。它的特点是使用少量的配置定义服务客户端接口,可以实现简单和可重用的RPC调用。Feign实现了声明式调用,允许程序员只需定义接口并作出一些小的注释,就可以实现一个完整的客户端,从而开发简单,潜在的问题可以被检测出来,程序的可维护性和可读性也更好。Feign支持动态服务发现,可以在接口地址变更或服务重新发布后实现自动切换,避免了因配置变更导致的杂乱代码,更具有拓展性。Feign解决了服务之间依赖过于厚实的一种解决方案,相比REST或RPC,Feign可以减少大量不必要的代码。它基于可插拔修改的过滤链,默认支持多种HTTP请求和响应。
OpenFeign功能升级

OpenFeign在Feign的基础上提供了增强和扩展功能: 1、更好的集成SpringCloud其他组件:可以和服务发现、负载均衡组件一起使用 2、支持@FeignClient注解:OpenFeign引入该注解作为Feign客户端标识,可以方便地定义和使用远程服务调用 3、错误处理改进:OpenFeign对异常进行了增强,提供了更好的错误信息和异常处理机制,让开发者更为准备的判断错误所在。

如:OpenFeign提供的错误解码器(DefaultErrorDecoder)和回退策略(当服务端返回错误响应或请求失败时,OpenFeign会调用回退策略中的逻辑,提供一个默认的处理结果)。

4、更丰富的配置项:可以对Feign客户端进行配置,如超时时间、重传次数等


OpenFeign客户端

客户端与服务端的代码创建请前往Nacos 注册中心介绍与实操这篇文章上参考完成。

① 要使用OpenFeign需要使用@EnableFeignClients进行开启

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

② 编写接口服务,并使用@FeginClient(name=“xxxx”)来指定调用的服务站点

@Service @FeignClient(name = "nacos-provider") public interface UserService { @RequestMapping("/user/getInfo") public String getInfo(@RequestParam("name") String name); }

③ 调用接口

@RestController public class OrderController { private final UserService userService; @Autowired public OrderController(UserService userService) { this.userService = userService; } @RequestMapping("/order") public String getInfo(){ if(userService == null){ return null; } return userService.getInfo(" Spring Cloud"); } }

④ 最终结果


OpenFeign中的超时重试机制

众所周知,在这个机器交互过程中,网络是最为复杂和难以控制的,而我们的微服务,讲究的就是一个高并发高可用,那么解决交互上存在的网络问题是十分必要的。

那么在OpenFeign中采用的方式就是超时重传,和TCP协议中的超时重传机制一样,链接或者消息在一定时间没有回应就会重新发送一次。

其实这很容易理解,就像我们在平时打电话,你沟通时,过一会对面都没有回应,你也会“喂,喂,听得见吗?”的消息确认。


开启超时重传机制

OpenFeign在默认情况下是不会开启这个机制的,需要我们人为去开启,那么开启需要通过下面两个步骤

配置超时重传覆盖Retryer对象

1、配置

spring: cloud: openfeign: client: config: default: # 全局配置 connect-timeout: 1000 # ms 链接超时时间 read-timeout: 1000 # ms 读取超时时间

2、覆盖

在消费者目录下的config包下构建并注入IOC容器

@Configuration public class RetryerConfig { @Bean public Retryer retryer(){ return new Retryer.Default( 1000, // 重试间隔时间 1000, // 最大间隔时间 3 // 最大重试次数 ); } }

为了演示超时重传的触发,我们在生产者代码上使用Thread.sleep(2000)来让线程睡眠并且在进打印调用的时刻,这个睡眠时间超过配置中的read-time=1000

@RestController @RequestMapping("/user") public class UserController { @RequestMapping("/getInfo") public String getInfo(@RequestParam("name") String name){ System.out.println("provider.getInfo方法执行时间:"+ LocalDateTime.now()); try { Thread.sleep(2000); } catch (InterruptedException e) { throw new RuntimeException(e); } return "producer" + name; } }

生产者控制台信息:

客户端结果:


下面我们队这个结果进行一个简单的分析: ① 在consumer.getInfo()对provider.getInfo(“Spring Cloud”)进行调用的时候,由于provider.getInfo(“Spring Cloud”)中进行了睡眠,并且(sleep=2000ms) > (read-time=1000ms),所以引发了超时现象


② 超时后,由于我们设置了超时重试次数,那么OpenFeign将会按照这个Retryer中的规则进行重试,再次调用了provider.getInfo(“Spring Cloud”)方法,再次打印了方法执行时间


③ 在重试了一定次数后,我们的consumer.getInfo()都收不到回应,那么就会导致客户端获取信息失败,然后就报了500错误,任务是服务端出错了。


以上就是超时重传的简单实操,不过需要做一点简单的补充,可能有同学已经发现了,provider控制台打印的时间上有点和想象中的不一样 这里我们可以看到重试是两秒左右才进行的,而我们connect-time = 1000ms ||read-time=1000ms,都不为一秒啊,为什么是两秒才调用,而不是一秒呢????

如果有以上问题的同学,应该是忘记了Retryer中的一个参数,重试间隔时间=1000ms,这就以为则,如果我们出现了超时问题,具体重传的时间还需要加上重试间隔时间,才是真正调用服务的时间:(connect-time > 1000 || read-time > 1000)+ (period = 1000) == executetime


自定义超时重试机制

无敌的Spring Cloud肯定也思考到了灵活性,所以也提供了自定义超时重试机制的方式,分为下面两个步骤:

自定义超时重试机制(实现Retryer接口,重写continueOrPropagate);设置配置文件 三种自定义超时重试类 固定时间间隔:也就是说,每次重试开始时间间隔一样,例如:上面的实操例子增长性间隔:例如:第一次超时重试间隔1秒,第二次2秒,第三次3秒,具体如何增长看业务实现的增长函数随机时间间隔:重试间隔在一定范围内随机 自定义超时类实操

说明 :这个实操基于第一种自定义超时类来实现,不需要注入IOC

1、自定义超时重试机制

public class MyRetryConfig implements Retryer { private final int maxAttempts; // 最大尝试次数 private final long intervalTime;// 重试间隔时间 private int attempt; // 当前尝试次数 public MyRetryConfig() { this.maxAttempts = 3; this.intervalTime = 1000; this.attempt = 0; } public MyRetryConfig(int maxAttempts, long intervalTime) { this.maxAttempts = 3; this.intervalTime = 1000; this.attempt = 0; } @Override public void continueOrPropagate(RetryableException e) { // 假如当前尝试次数超过了最大尝试次数就抛出异常 if(++this.attempt > this.maxAttempts){ throw e; } long curInterval = this.intervalTime; // 打印日志 System.out.println(LocalDateTime.now() + "执行了一次重试"); try { Thread.sleep(curInterval); } catch (InterruptedException ex) { throw new RuntimeException(ex); } } /** * 克隆(创建独立的实例) * @return */ @Override public Retryer clone() { // 克隆的间隔时间和重试次数当然要和被克隆对象一直,所以有参构造更为正确 return new MyRetryConfig(maxAttempts, intervalTime); } }

2、将自定义超时重传类声明到yml配置文件中

关键词:retryer: com.example.consumer.config.MyRetryConfig

spring: application: # 服务注册站点 name: nacos-consumer #命名不能使用‘_’,早期SpringCloud不支持 cloud: nacos: # Nacos认证信息 discovery: username: nacos password: nacos # Nacos 服务发现与注册配置,其中子属性server-addr指定Nacos服务器主机和端口 server-addr: localhost:8848 namespace: public # 注册到nacos的指定namespace,默认public register-enabled: false openfeign: client: config: default: # 全局配置 connect-timeout: 1000 # ms 链接超时时间 read-timeout: 1000 # ms 读取超时时间 retryer: com.example.consumer.config.MyRetryConfig server: port: 8080

测试结果:

超时重试底层原理 超时原理

超时原理其实很简单,OpenFeign超时底层实现是通过配置HTTP客户端来实现,通过你对配置文件的connect-timeout和read-timeout来底层进行设置

OpenFeign底层的HTTP客户端可以使用Apache HttpClient或者OK HttpClient来实现,默认是ApacheHttpClient

重试原理

通过观察OpenFeign 的源码实现就可以了解重试功能的底层实现,它的源码在 SynchronousMethodHandler 的 invoke 方法下,如下所示

**public Object invoke(Object[] argv) throws Throwable { RequestTemplate template = this.buildTemplateFromArgs.create(argv); Request.Options options = this.findOptions(argv); Retryer retryer = this.retryer.clone(); // 一直重试 while(true) { try { // 如果得到结果就return退出循环 return this.executeAndDecode(template, options); } catch (RetryableException var9) { RetryableException e = var9; try { // 如果遇到异常则调用Retryer的continueOrPropagate retryer.continueOrPropagate(e); } catch (RetryableException var8) { Throwable cause = var8.getCause(); if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) { throw cause; } throw var8; } if (this.logLevel != Level.NONE) { this.logger.logRetry(this.metadata.configKey(), this.logLevel); } } } }

Retryer的continueOrPropagate方法底层实现:

public void continueOrPropagate(RetryableException e) { if (this.attempt++ >= this.maxAttempts) { throw e; } else { long interval; if (e.retryAfter() != null) { interval = e.retryAfter().getTime() - this.currentTimeMillis(); if (interval > this.maxPeriod) { interval = this.maxPeriod; } if (interval < 0L) { return; } } else { interval = this.nextMaxInterval(); } try { Thread.sleep(interval); } catch (InterruptedException var5) { Thread.currentThread().interrupt(); throw e; } this.sleptForMillis += interval; } }
补充说明

OpenFeign实现原理 1.注解 2.动态代理实现—>功能扩展 3.RestTemplate发送HTTP请求 4.HTTP框架来实现Web请求->Apache HttpClient或者OK HttpClient


END 希望对你有帮助
标签:

OpenFeign的简单介绍和功能实操由讯客互联创业栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“OpenFeign的简单介绍和功能实操