SpringCloud升级之路2020。0。x版30。FeignClient实现重试
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 需要重试的场景
微服务系统中,会遇到 在线发布 ,一般的发布更新策略是:启动一个新的,启动成功之后,关闭一个旧的,直到所有的旧的都被关闭。Spring Boot 具有优雅关闭的功能,可以保证请求处理完再关闭,同时会拒绝新的请求。对于这些拒绝的请求,为了保证用户体验不受影响,是需要重试的。
云上部署的微服务,对于同一个服务,同一个请求,很可能不会所有实例都同时异常,例如: Kubernetes 集群部署的实例,可能同一个虚拟机 Node 在闲时部署了多个不同微服务实例,当压力变大时,就需要迁移和扩容。这时候由于不同的微服务压力不同,当时处于哪一个 Node 也说不定,有的可能处于压力大的,有的可能处于压力小的。对于同一个微服务,可能并不会所有实例位于的 Node 压力都大。 云上部署一般会跨可用区部署,如果有一个可用区异常,另一个可用区还可以继续提供服务。 某个业务触发了 Bug,导致实例一直在 GC,但是这种请求一般很不常见,不会发到所有实例上。
这时候,就需要我们对请求进行无感知的重试。 重试需要考虑的问题重试需要重试与 之前不同的实例 ,甚至是不处于同一个虚拟机 Node 的实例,这个主要通过 LoadBalancer 实现,可以参考之前的 LoadBalancer 部分。后面的文章,我们还会改进 LoadBalancer 重试需要考虑到底什么请求能重试,以及什么异常能重试: 假设我们有查询接口,和没有做幂等性的扣款接口,那么很直观的就能感觉出 查询接口是可以重试的,没有做幂等性的扣款接口是不能重试的 。 业务上不能重试的接口,对于特殊的异常(其实是表示请求并没有发出去的异常),我们是可以重试的。虽然是没有做幂等性的扣款接口,但是如果抛出的是原因是 Connect Timeout 的 IOException, 这样的异常代表请求还没有发出去,是可以重试的 。 重试策略 :重试几次,重试间隔。类比多处理器编程模式中的 Busy Spin 策略会造成很大的总线通量从而降低性能这个现象,如果失败立刻重试,那么在某一个实例异常导致超时的时候,会在同一时间有很多请求重试到其他实例。最好加上一定延迟。 使用 resilience4j 实现 FeignClient 重试
FeignClient 本身带重试,但是重试策略相对比较简单,同时我们还想使用断路器以及限流器还有线程隔离,resilience4j 就包含这些组件。 原理简介
Resilience4J 提供了 Retryer 重试器,官方文档地址:https://resilience4j.readme.io/docs/retry
从配置上就能理解其中的原理,但是 官方文档配置并不全面 ,如果想看所有的配置,最好还是通过源码:
RetryConfigurationProperties.java //重试间隔,默认 500ms @Nullable private Duration waitDuration; //重试间隔时间函数,和 waitDuration 只能设置一个,默认就是 waitDuration @Nullable private Class<? extends IntervalBiFunction