我們繼續上一節針對我們的重試進行測試
驗證針對可重試的方法響應超時異常重試正確
我們可以通過 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: