这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战
本系列代码地址:github.com/JoJoTec/spr…
实现 WeClient 的 NamedContextFactory
我们要实现的是不同微服务自动配置装载不同的 WebClient Bean,这样就可以通过 NamedContextFactory 实现。我们先来编写下实现这个 NamedContextFactory 整个的加载流程的代码,其结构图如下所示:
1 | ini复制代码# AutoConfiguration |
在 spring.factories 定义了自动装载的自动配置类 WebClientAutoConfiguration
1 | less复制代码@Import(WebClientConfiguration.class) |
WebClientAutoConfiguration 这个自动配置类 Import 了 WebClientConfiguration
1 | less复制代码@Configuration(proxyBeanMethods = false) |
WebClientConfiguration 中创建了 WebClientNamedContextFactory 这个 NamedContextFactory 的 Bean。在这个 NamedContextFactory 中,定义了默认配置 WebClientDefaultConfiguration。在这个默认配置中,主要是给每个微服务都定义了一个 WebClient
定义 WebClient 的配置类
我们编写下上一节定义的配置,包括:
- 微服务名称
- 微服务地址,服务地址,不填写则为 http://微服务名称
- 连接超时,使用 Duration,这样我们可以用更直观的配置了,例如 5ms,6s,7m 等等
- 响应超时,使用 Duration,这样我们可以用更直观的配置了,例如 5ms,6s,7m 等等
- 可以重试的路径,默认只对 GET 方法重试,通过这个配置增加针对某些非 GET 方法的路径的重试;同时,这些路径可以使用 * 等路径匹配符,即 Spring 中的 AntPathMatcher 进行路径匹配多个路径。例如
/query/order/**
WebClientConfigurationProperties
1 | typescript复制代码@Data |
粘合 WebClient 与 resilience4j
接下来粘合 WebClient 与 resilience4j 实现断路器以及重试逻辑,WebClient 基于 project-reactor 实现,resilience4j 官方提供了与 project-reactor 的粘合库:
1 | xml复制代码<!--粘合 project-reactor 与 resilience4j,这个在异步场景经常会用到--> |
参考官方文档,我们可以像下面这样给普通的 WebClient 增加相关组件:
增加重试器:
1 | ini复制代码//由于还是在前面弄好的 spring-cloud 环境下,所以还是可以这样获取配置对应的 retry |
这个 RetryOperator 其实就是使用了 project-reactor 中的 retryWhen 方法实现了 resilience4j 的 retry 机制:
1 | swift复制代码@Override |
可以看出,其实主要填充了:
doOnNext(context::handleResult)
: 在有响应之后调用,将响应结果传入 retry 的 Context,判断是否需要重试以及重试间隔是多久,并且抛出异常RetryDueToResultException
retryWhen(reactor.util.retry.Retry.withThrowable(errors -> errors.flatMap(context::handleErrors)))
:捕捉异常RetryDueToResultException
,根据其中的间隔时间,返回 reactor 的重试间隔:Mono.delay(Duration.ofMillis(waitDurationMillis))
doOnComplete(context::onComplete)
:请求完成,没有异常之后,调用 retry 的 complete 进行清理
增加断路器:
1 | ini复制代码//由于还是在前面弄好的 spring-cloud 环境下,所以还是可以这样获取配置对应的 circuitBreaker |
类似的,CircuitBreakerOperator 其实也是粘合断路器与 reactor 的 publisher 中的一些 stage 方法,将结果的成功或者失败记录入断路器,这里需要注意,可能有的链路能走到 onNext,可能有的链路能走到 onComplete,也有可能都走到,所以这两个方法都要记录成功,并且保证只记录一次:
1 | java复制代码class CircuitBreakerSubscriber<T> extends AbstractSubscriber<T> { |
我们会使用这个库进行粘合,但是不会直接使用上面的代码,因为考虑到:
- 需要在重试以及断路中加一些日志,便于日后的优化
- 需要定义重试的 Exception,并且与断路器相结合,将非 2xx 的响应码也封装成特定的异常
- 需要在断路器相关的 Operator 中增加类似于 FeignClient 中的负载均衡的数据更新,使得负载均衡更加智能
在下面一节我们会详细说明我们是如何实现的有断路器以及重试逻辑和负载均衡数据更新的 WebClient。
微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer:
本文转载自: 掘金