SpringCloud升级之路2020.0.x版-34.验证重试配置正确性(3)
验证针对可重试的方法响应超时异常重试正确
我们可以通过 httpbin.org 的 /delay/响应时间秒
来实现请求响应超时。例如 /delay/3
就会延迟三秒后返回。这个接口也是可以接受任何类型的 HTTP 请求方法。
我们先来指定关于 Feign 超时的配置 Options:
//SpringExtension也包含了 Mockito 相关的 Extension,所以 @Mock 等注解也生效了 @ExtendWith(SpringExtension.class) @SpringBootTest(properties = { //关闭 eureka client "eureka.client.enabled=false", //默认请求重试次数为 3 "resilience4j.retry.configs.default.maxAttempts=3", //指定默认响应超时为 2s "feign.client.config.default.readTimeout=2000", }) @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); DiscoveryClient spy = Mockito.spy(DiscoveryClient.class); //微服务 testService1 有一个实例即 service1Instance1 Mockito.when(spy.getInstances("testService1")) .thenReturn(List.of(service1Instance1)); return spy; } } }复制代码
我们分别定义会超时和不会超时的接口:
@FeignClient(name = "testService1", contextId = "testService1Client") public interface TestService1Client { @GetMapping("/delay/1") String testGetDelayOneSecond(); @GetMapping("/delay/3") String testGetDelayThreeSeconds(); }复制代码
编写测试,还是通过获取调用负载均衡获取实例的次数确定请求调用了多少次。
@Test public void testTimeOutAndRetry() throws InterruptedException { 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(); //不超时,则不会有重试,也不会有异常导致 fallback String s = testService1Client.testGetDelayOneSecond(); //没有重试,只会请求一次 Assertions.assertEquals(1, atomicInteger.get() - start); //防止断路器影响 circuitBreakerRegistry.getAllCircuitBreakers().asJava().forEach(CircuitBreaker::reset); start = atomicInteger.get(); //超时,并且方法可以重试,所以会请求 3 次 try { s = testService1Client.testGetDelayThreeSeconds(); } catch(Exception e) {} Assertions.assertEquals(3, atomicInteger.get() - start); } }复制代码
验证针对不可重试的方法响应超时异常不能重试
对于 GET 方法,我们默认是可以重试的。但是一般扣款这种涉及修改请求的接口,我们会使用其他方法例如 POST。这一类方法一般请求超时我们不会直接重试的。我们还是通过 httporg.bin 的延迟接口进行测试:
@FeignClient(name = "testService1", contextId = "testService1Client") public interface TestService1Client { @PostMapping("/delay/3") String testPostDelayThreeSeconds(); }复制代码
编写测试,还是通过获取调用负载均衡获取实例的次数确定请求调用了多少次。
@Test public void testTimeOutAndRetry() throws InterruptedException { 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(); //不超时,则不会有重试,也不会有异常导致 fallback String s = testService1Client.testPostDelayThreeSeconds(); //没有重试,只会请求一次 Assertions.assertEquals(1, atomicInteger.get() - start); } }
作者:干货满满张哈希
链接:https://juejin.cn/post/7030821970149113893