本文是精讲响应式 WebClient 第 6 篇,前篇的 blog 拜访地址如下:
- 精讲响应式 webclient 第 1 篇 - 响应式非阻塞 IO 与根底用法
- 精讲响应式 WebClient 第 2 篇 -GET 申请阻塞与非阻塞调用办法详解
- 精讲响应式 WebClient 第 3 篇 -POST、DELETE、PUT 办法应用
- 精讲响应式 WebClient 第 4 篇 - 文件上传与下载
- 精讲响应式 WebClient 第 5 篇 - 申请超时设置与异样解决
在上一篇咱们为大家介绍了 WebClient 的异样解决办法,咱们能够对指定的异样进行解决,也能够分类解决 400-499、500-599 状态码的 HTTP 异样。
咱们本节为大家介绍的实际上是另外一种异样解决机制:申请失败之后主动重试。当 WebClient 发动申请,没有失去失常的响应后果,它就会每隔一段时间再次发送申请,能够发送 n 次,这个 n 是咱们自定义的。n 次申请都失败了,最初再将异样抛出,能够通过咱们上一节交给大家的办法进行异样解决。也就是针对连贯超时异样、读写超时异样等,或者是 HTTP 响应后果为非正常状态码(不是 200 状态码段),都在主动重试机制的领域内。
如果您感觉我的文章对您有帮忙的话,请帮忙点赞或分享,您的反对是我不竭的创作能源!
一、申请异样重试
上面的代码是申请 ”http://jsonplaceholder.typicode.com” 网站的服务,该网站是一个收费提供 HTTP 申请测试的服务端网站,咱们能够用它测试 WebClient。须要留神的是:失常的 GET 办法申请地址是 ”/posts/1″,我特意的把它写错成为 ”/postss/1″,这样能够触发 404 资源无奈找到的异样。
public class ReTryTest {
@Test
public void testRetry() {WebClient webClient = WebClient.builder()
.baseUrl("http://jsonplaceholder.typicode.com")
.build();
Mono<String> mono = webClient
.get() //GET 申请
.uri("/postss/1") // 申请门路, 留神为了制作异样,这里是错的
.retrieve() // 获取申请后果
.bodyToMono(String.class) // 用 Mono 接管单个非汇合对象数据
.doOnError(Exception.class, err -> { // 解决异样
System.out.println(LocalDateTime.now() + "--- 产生谬误:" +err.getMessage());
})
.retry(3);
System.out.println("=====" + mono.block());
}
}
- doOnError 异样解决是咱们在上一节文章中为大家介绍的异样处理函数,咱们在这里打印日志,察看重试次数
- retry(3)就是重点了,示意申请失败之后重试 3 次申请。也能够应用 retry()无参办法,不设置次数,能够有限重试。这样显然不好,咱们个别不必。
上面是 doOnError 中打印的控制台输入内容,一共打印了 4 次。(一次失败 + 三次重试失败)
二、重试工夫距离设置
下面的申请重试办法,申请失败之后立刻重试,在很短的工夫内就实现了 3 次重试。如果这是在生产环境下,可能你的服务端因为资源缓和造成申请响应超时等异样,这种重试机制无疑会让本就不堪重负的服务端雪上加霜。咱们上面交给大家一种为重试设置工夫距离的办法:
.retryBackoff(3, Duration.ofSeconds(5));
- 第一个参数依然示意重试 3 次
- 第二个参数示意按 指数增长 的工夫距离重试,第一次重试距离 5 秒,第二次距离 10 秒(5 x2),第三次距离 20 秒(5x2x2)
源码如下:
三、retryWhen 办法
下面的 retryBackoff 办法尽管曾经肯定水平上缓解了申请重试导致的服务端的压力,然而它还是不分场景的一直重试。
- 在理论的开发中,能够申请重试的场景应该是:网络异样、申请超时异样、服务端忽然面临高并发导致的长期解决能力有余导致的超时等这种因为内部起因导致的异样场景。
- 对于那些因为程序员编写的 bug、资源拜访权限有余、资源找不到、HTTP 版本不受反对等造成的异样,重试一万次也不会胜利,反而可能因为你一直的重试造成服务器解体。
所以说 Webclient 曾经在源码中,将 retryBackoff()标记为废除,倡议应用 retryWhen()代替它。retryWhen()能够指定针对某些异样进行重试,其余异样不做重试。
为了应用 retryWhen(), 须要引入上面的包
<dependency>
<groupId>io.projectreactor.addons</groupId>
<artifactId>reactor-extra</artifactId>
</dependency>
3.1. 人为制作超时异样 - 用于测试
为了可能制作申请超时的异样场景,咱们给连贯超时设置为 5 毫秒,即:让所有申请肯定会超时。(没有任何申请能在 5 毫秒内实现网络连接)
// 认为设置申请超时工夫为 5 毫秒,也就是申请肯定会超时,肯定会抛出 ConnectTimeoutException
TcpClient tcpClient = TcpClient
.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5); // 5 毫秒
WebClient webClient = WebClient.builder()
.baseUrl("http://jsonplaceholder.typicode.com")
.clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient)))
.build();
3.2. 测试 retryWhen
用 Retry 对象定义申请重试的条件,也就是 retryWhen 的 when
Retry<?> retry = Retry.onlyIf(x -> x.exception() instanceof ConnectTimeoutException)
.retryMax(3) // 重试 3 次
.backoff(Backoff.exponential(Duration.ofSeconds(5),Duration.ofSeconds(60),2,true));
Mono<String> mono = webClient
.get() //GET 申请
.uri("/posts/1") // 申请门路, 这里的申请门路是正确的
.retrieve()
.bodyToMono(String.class)
.retryWhen(retry); // 满足 Retry 条件进行重试
System.out.println("=====" + mono.block());
Retry.onlyIf(x -> x.exception() instanceof ConnectTimeoutException)
示意只有针对 ConnectTimeoutException 连贯超市异样才进行申请重试,这里应用了 java8 的 Predicate 语法- Backoff.exponential 示意按指数增长的工夫距离进行重试,能够本人指定指数重试因子,即指数的计数。这里咱们依然应用 2 作为指数重试因子,第一次重试距离 5 秒,第二次距离 10 秒(5 x2),第三次距离 20 秒(5x2x2)
- 为避免间隔时间指数级有限缩短,Backoff.exponential 最长的重试距离不能超过 60 秒,第二个参数。
- retryWhen(retry) 满足 retry 条件进行重试
3.3.retryWhen 的其余办法
- onlyIf()示意捕捉到指定的某个异样,进行申请重试
- allBut()示意除了某个异样之外,其余的异样被捕捉则进行申请重试
- any() 示意针对所有异样,进行申请重试
- anyOf()示意指定某些异样类型,进行申请重试
backOff 示意重试的工夫距离
- exponential()指数级增长的工夫距离
- fix()示意固定的工夫距离
欢送关注我的博客,外面有很多精品合集
- 本文转载注明出处(必须带连贯,不能只转文字):字母哥博客。
感觉对您有帮忙的话,帮我点赞、分享!您的反对是我不竭的创作能源!。另外,笔者最近一段时间输入了如下的精品内容,期待您的关注。
- 《手摸手教你学 Spring Boot2.0》
- 《Spring Security-JWT-OAuth2 一本通》
- 《实战前后端拆散 RBAC 权限管理系统》
- 《实战 SpringCloud 微服务从青铜到王者》
- 《VUE 深入浅出系列》