SpringCloud升級之路2020.0.x版-34.驗證重試配置正確性(3)

SpringCloud升級之路2020.0.x版-34.驗證重試配置正確性(3)

本系列程式碼地址:https://github。com/JoJoTec/spring-cloud-parent

我們繼續上一節針對我們的重試進行測試

驗證針對可重試的方法響應超時異常重試正確

我們可以透過 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”,})@Log4j2public 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 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();}

編寫測試,還是透過獲取呼叫負載均衡獲取例項的次數確定請求呼叫了多少次。

@Testpublic 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();}

編寫測試,還是透過獲取呼叫負載均衡獲取例項的次數確定請求呼叫了多少次。

@Testpublic 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); }}