SpringCloud升级之路2020.0.x版-34.验证重试配置正确性(2)

我们继续上一节针对我们的重试进行测试
验证针对限流器异常的重试正确
通过系列前面的源码分析,我们知道 spring-cloud-openfeign 的 FeignClient 其实是懒加载的。所以我们实现的断路器也是懒加载的,需要先调用,之后才会初始化线程隔离。所以这里如果我们要模拟线程隔离满的异常,需要先手动读取载入线程隔离,之后才能获取对应实例的线程隔离,将线程池填充满。
我们先定义一个 FeignClient:
@FeignClient(name = "testService1", contextId = "testService1Client")
public interface TestService1Client {
@GetMapping("/anything")
HttpBinAnythingResponse anything();
}
使用前面同样的方式,给这个微服务添加实例:
//SpringExtension也包含了 Mockito 相关的 Extension,所以 @Mock 等注解也生效了
@ExtendWith(SpringExtension.class)
@SpringBootTest(properties = {
//关闭 eureka client
"eureka.client.enabled=false",
//默认请求重试次数为 3
"resilience4j.retry.configs.default.maxAttempts=3",
//增加断路器配置
"resilience4j.circuitbreaker.configs.default.failureRateThreshold=50",
"resilience4j.circuitbreaker.configs.default.slidingWindowType=COUNT_BASED",
"resilience4j.circuitbreaker.configs.default.slidingWindowSize=5",
"resilience4j.circuitbreaker.configs.default.minimumNumberOfCalls=2",
})
@Log4j2
public class OpenFeignClientTest {
@SpringBootApplication
@Configuration
public static class App {
@Bean
public DiscoveryClient discoveryClient() {
//模拟两个服务实例
ServiceInstance service1Instance1 = Mockito.spy(ServiceInstance.class);
ServiceInstance service1Instance3 = Mockito.spy(ServiceInstance.class);
Map<String, String> zone1 = Map.ofEntries(
Map.entry("zone", "zone1")
);
when(service1Instance1.getMetadata()).thenReturn(zone1);
when(service1Instance1.getInstanceId()).thenReturn("service1Instance1");
when(service1Instance1.getHost()).thenReturn("httpbin.org");
when(service1Instance1.getPort()).thenReturn(80);
when(service1Instance3.getMetadata()).thenReturn(zone1);
when(service1Instance3.getInstanceId()).thenReturn("service1Instance3");
//这其实就是 httpbin.org ,为了和第一个实例进行区分加上 www
when(service1Instance3.getHost()).thenReturn("www.httpbin.org");
DiscoveryClient spy = Mockito.spy(DiscoveryClient.class);
//微服务 testService3 有两个实例即 service1Instance1 和 service1Instance4
Mockito.when(spy.getInstances("testService1"))
.thenReturn(List.of(service1Instance1, service1Instance3));
return spy;
}
}
}
然后,编写测试代码:
@Test
public void testRetryOnBulkheadException() {
//防止断路器影响
circuitBreakerRegistry.getAllCircuitBreakers().asJava().forEach(CircuitBreaker::reset);
this.testService1Client.anything();
ThreadPoolBulkhead threadPoolBulkhead;
try {
threadPoolBulkhead = threadPoolBulkheadRegistry
.bulkhead("testService1Client:httpbin.org:80", "testService1Client");
} catch (ConfigurationNotFoundException e) {
//找不到就用默认配置
threadPoolBulkhead = threadPoolBulkheadRegistry
.bulkhead("testService1Client:httpbin.org:80");
}
//线程队列我们配置的是 1,线程池大小是 10,这样会将线程池填充满
for (int i = 0; i < 10 + 1; i++) {
threadPoolBulkhead.submit(() -> {
try {
//这样任务永远不会结束了
Thread.currentThread().join();
}
catch (InterruptedException e) {
e.printStackTrace();
}
});
}
//调用多次,调用成功即对断路器异常重试了
for (int i = 0; i < 10; i++) {
this.testService1Client.anything();
}
}
运行测试,日志中可以看出,针对线程池满的异常进行重试了:
2021-11-13 03:35:16.371 INFO [,,] 3824 --- [ main] c.g.j.s.c.w.f.DefaultErrorDecoder : TestService1Client#anything() response: 584-Bulkhead 'testService1Client:httpbin.org:80' is full and does not permit further calls, should retry: true
验证针对非 2xx 响应码可重试的方法重试正确
我们通过使用 http.bin 的 /status/{statusCode} 接口,这个接口会根据路径参数 statusCode 返回对应状态码的响应:
@FeignClient(name = "testService1", contextId = "testService1Client")
public interface TestService1Client {
@GetMapping("/status/500")
String testGetRetryStatus500();
}
我们如何感知被重试三次呢?每次调用,就会从负载均衡器获取一个服务实例。在负载均衡器代码中,我们使用了根据当前 sleuth 的上下文的 traceId 的缓存,每次调用,traceId 对应的 position 值就会加 1。我们可以通过观察这个值的变化获取到究竟本次请求调用了几次负载均衡器,也就是做了几次调用。
编写测试:
@Test
public void testNon2xxRetry() {
Span span = tracer.nextSpan();
try (Tracer.SpanInScope cleared = tracer.withSpanInScope(span)) {
//防止断路器影响
circuitBreakerRegistry.getAllCircuitBreakers().asJava().forEach(CircuitBreaker::reset);
long l = span.context().traceId();
RoundRobinWithRequestSeparatedPositionLoadBalancer loadBalancerClientFactoryInstance
= (RoundRobinWithRequestSeparatedPositionLoadBalancer) loadBalancerClientFactory.getInstance("testService1");
AtomicInteger atomicInteger = loadBalancerClientFactoryInstance.getPositionCache().get(l);
int start = atomicInteger.get();
try {
//get 方法会重试
testService1Client.testGetRetryStatus500();
} catch (Exception e) {
}
//因为每次调用都会失败,所以会重试配置的 3 次
Assertions.assertEquals(3, atomicInteger.get() - start);
}
}
验证针对非 2xx 响应码不可重试的方法没有重试
我们通过使用 http.bin 的 /status/{statusCode} 接口,这个接口会根据路径参数 statusCode 返回对应状态码的响应,并且支持各种 HTTP 请求方式:
@FeignClient(name = "testService1", contextId = "testService1Client")
public interface TestService1Client {
@PostMapping("/status/500")
String testPostRetryStatus500();
}
默认情况下,我们只会对 GET 方法重试,对于其他 HTTP 请求方法,是不会重试的:
@Test
public void testNon2xxRetry() {
Span span = tracer.nextSpan();
try (Tracer.SpanInScope cleared = tracer.withSpanInScope(span)) {
//防止断路器影响
circuitBreakerRegistry.getAllCircuitBreakers().asJava().forEach(CircuitBreaker::reset);
long l = span.context().traceId();
RoundRobinWithRequestSeparatedPositionLoadBalancer loadBalancerClientFactoryInstance
= (RoundRobinWithRequestSeparatedPositionLoadBalancer) loadBalancerClientFactory.getInstance("testService1");
AtomicInteger atomicInteger = loadBalancerClientFactoryInstance.getPositionCache().get(l);
int start = atomicInteger.get();
try {
//post 方法不会重试
testService1Client.testPostRetryStatus500();
} catch (Exception e) {
}
//不会重试,因此只会被调用 1 次
Assertions.assertEquals(1, atomicInteger.get() - start);
}
}
微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer:

SpringCloud升级之路2020.0.x版-34.验证重试配置正确性(2)的更多相关文章
- SpringCloud升级之路2020.0.x版-34.验证重试配置正确性(1)
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 在前面一节,我们利用 resilience4j 粘合了 OpenFeign 实现了断路器. ...
- SpringCloud升级之路2020.0.x版-34.验证重试配置正确性(3)
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 我们继续上一节针对我们的重试进行测试 验证针对可重试的方法响应超时异常重试正确 我们可以通 ...
- SpringCloud升级之路2020.0.x版-35. 验证线程隔离正确性
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 上一节我们通过单元测试验证了重试的正确性,这一节我们来验证我们线程隔离的正确性,主要包括: ...
- SpringCloud升级之路2020.0.x版-33. 实现重试、断路器以及线程隔离源码
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 在前面两节,我们梳理了实现 Feign 断路器以及线程隔离的思路,并说明了如何优化目前的负 ...
- SpringCloud升级之路2020.0.x版-36. 验证断路器正确性
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 上一节我们通过单元测试验证了线程隔离的正确性,这一节我们来验证我们断路器的正确性,主要包括 ...
- SpringCloud升级之路2020.0.x版-13.UnderTow 核心配置
本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford Undertow ...
- SpringCloud升级之路2020.0.x版-14.UnderTow AccessLog 配置介绍
本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford server: u ...
- SpringCloud升级之路2020.0.x版-1.背景
本系列为之前系列的整理重启版,随着项目的发展以及项目中的使用,之前系列里面很多东西发生了变化,并且还有一些东西之前系列并没有提到,所以重启这个系列重新整理下,欢迎各位留言交流,谢谢!~ Spring ...
- SpringCloud升级之路2020.0.x版-41. SpringCloudGateway 基本流程讲解(1)
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 接下来,将进入我们升级之路的又一大模块,即网关模块.网关模块我们废弃了已经进入维护状态的 ...
随机推荐
- 鸿蒙内核源码分析(汇编传参篇) | 如何传递复杂的参数 | 百篇博客分析OpenHarmony源码 | v23.02
百篇博客系列篇.本篇为: v23.xx 鸿蒙内核源码分析(汇编传参篇) | 如何传递复杂的参数 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU在哪 ...
- Hive On Spark保姆级攻略
声明: 此博客参考了官网的配置方式,并结合笔者在实践网上部分帖子时的踩坑经历整理而成 这里贴上官方配置说明: [官方]: https://cwiki.apache.org//confluence/di ...
- WPF进阶技巧和实战08-依赖属性与绑定01
依赖项属性 定义依赖项属性 注意:只能为依赖对象(继承自DependencyObject的类)添加依赖项属性.WPF中的元素基本上都继承自DependencyObject类. 静态字段 名称约定(属性 ...
- Linux学习笔记整理-1
内核检测常用的7个命令: fdisk命令:用于检查磁盘使用情况,以及可以对磁盘进行分区. #fdisk -l 列出系统内所有能找到的设备的分区 #fdisk /dev/sda 列出sda磁盘的分区情况 ...
- python-matplotlib学习(1)
1 import matplotlib.pyplot as plt 2 import numpy as np 3 4 x=np.linspace(-1,1,50) 5 y=2*x+1 6 plt.pl ...
- 如何借助 JuiceFS 为 AI 模型训练提速 7 倍
背景 海量且优质的数据集是一个好的 AI 模型的基石之一,如何存储.管理这些数据集,以及在模型训练时提升 I/O 效率一直都是 AI 平台工程师和算法科学家特别关注的事情.不论是单机训练还是分布式训练 ...
- ArcPy数据列表遍历
ArcPy数据列表遍历 批处理脚本的首要任务之一是为可用数据编写目录,以便在处理过程中可以遍历数据. ArcPy 具有多个专为创建此类列表而构建的函数. 函数 说明 ListFields(datase ...
- Dapr-简介及环境搭建
一.Dapr是什么? Dapr 是一个可移植的.事件驱动的运行时,它使任何开发人员能够轻松构建出弹性的.无状态和有状态的应用程序,并可运行在云平台或边缘计算中,它同时也支持多种编程语言和开发框架. 在 ...
- Python 3.10 正式发布,新增模式匹配,同事用了直呼真香!
关注微信公众号:K哥爬虫,QQ交流群:808574309,持续分享爬虫进阶.JS/安卓逆向等技术干货! 前几天,也就是 10 月 4 日,Python 发布了 3.10.0 版本,什么?3.9 之后居 ...
- PAT (Basic Level) Practice (中文)1022 D进制的A+B (20分)
1022 D进制的A+B (20分) 输入两个非负 10 进制整数 A 和 B ( ≤ 230 −1),输出 A+B 的 D (1<D≤10)进制数. 输入格式: 输入在一行中依次给出 3 ...