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

乾貨滿滿張雜湊發表於2021-11-15

本系列程式碼地址: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",
})
@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);
    }
}

微信搜尋“我的程式設計喵”關注公眾號,每日一刷,輕鬆提升技術,斬獲各種offer

相關文章