摘要:本系列代碼地址上一節我們通過單元測試驗證了線程隔離的正確性,這一節我們來驗證我們斷路器的正確性,主要包括驗證配置正確加載即我們在配置例如中的加入的的配置被正確加載應用了。
上一節我們通過單元測試驗證了線程隔離的正確性,這一節我們來驗證我們斷路器的正確性,主要包括:
application.yml
)中的加入的 Resilience4j 的配置被正確加載應用了。與之前驗證重試類似,我們可以定義不同的 FeignClient,之后檢查 resilience4j 加載的斷路器配置來驗證線程隔離配置的正確加載。
并且,與重試配置不同的是,通過系列前面的源碼分析,我們知道 spring-cloud-openfeign 的 FeignClient 其實是懶加載的。所以我們實現的斷路器也是懶加載的,需要先調用,之后才會初始化斷路器。所以這里我們需要先進行調用之后,再驗證斷路器配置。
首先定義兩個 FeignClient,微服務分別是 testService1 和 testService2,contextId 分別是 testService1Client 和 testService2Client
@FeignClient(name = "testService1", contextId = "testService1Client")public interface TestService1Client { @GetMapping("/anything") HttpBinAnythingResponse anything();}@FeignClient(name = "testService2", contextId = "testService2Client") public interface TestService2Client { @GetMapping("/anything") HttpBinAnythingResponse anything();}
然后,我們增加 Spring 配置,并且給兩個微服務都添加一個實例,使用 SpringExtension 編寫單元測試類:
//SpringExtension也包含了 Mockito 相關的 Extension,所以 @Mock 等注解也生效了@ExtendWith(SpringExtension.class)@SpringBootTest(properties = { //默認請求重試次數為 3 "resilience4j.retry.configs.default.maxAttempts=3", // testService2Client 里面的所有方法請求重試次數為 2 "resilience4j.retry.configs.testService2Client.maxAttempts=2", //默認斷路器配置 "resilience4j.circuitbreaker.configs.default.slidingWindowSize=5", "resilience4j.circuitbreaker.configs.default.minimumNumberOfCalls=2", //testService2Client 的 斷路器配置 "resilience4j.circuitbreaker.configs.testService2Client.failureRateThreshold=30", "resilience4j.circuitbreaker.configs.testService2Client.minimumNumberOfCalls=10", })@Log4j2public class OpenFeignClientTest { @SpringBootApplication @Configuration public static class App { @Bean public DiscoveryClient discoveryClient() { //模擬兩個服務實例 ServiceInstance service1Instance1 = Mockito.spy(ServiceInstance.class); ServiceInstance service2Instance2 = 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("www.httpbin.org"); when(service1Instance1.getPort()).thenReturn(80); when(service2Instance2.getInstanceId()).thenReturn("service1Instance2"); when(service2Instance2.getHost()).thenReturn("httpbin.org"); when(service2Instance2.getPort()).thenReturn(80); DiscoveryClient spy = Mockito.spy(DiscoveryClient.class); Mockito.when(spy.getInstances("testService1")) .thenReturn(List.of(service1Instance1)); Mockito.when(spy.getInstances("testService2")) .thenReturn(List.of(service2Instance2)); return spy; } }}
編寫測試代碼,驗證配置正確:
@Test public void testConfigureCircuitBreaker() { //防止斷路器影響 circuitBreakerRegistry.getAllCircuitBreakers().asJava().forEach(CircuitBreaker::reset); //調用下這兩個 FeignClient 確保對應的 NamedContext 被初始化 testService1Client.anything(); testService2Client.anything(); //驗證斷路器的實際配置,符合我們的填入的配置 List circuitBreakers = circuitBreakerRegistry.getAllCircuitBreakers().asJava(); Set collect = circuitBreakers.stream().map(CircuitBreaker::getName) .filter(name -> { try { return name.contains(TestService1Client.class.getMethod("anything").toGenericString()) || name.contains(TestService2Client.class.getMethod("anything").toGenericString()); } catch (NoSuchMethodException e) { return false; } }).collect(Collectors.toSet()); Assertions.assertEquals(collect.size(), 2); circuitBreakers.forEach(circuitBreaker -> { if (circuitBreaker.getName().contains(TestService1Client.class.getName())) { Assertions.assertEquals((int) circuitBreaker.getCircuitBreakerConfig().getFailureRateThreshold(), (int) DEFAULT_FAILURE_RATE_THRESHOLD); Assertions.assertEquals(circuitBreaker.getCircuitBreakerConfig().getMinimumNumberOfCalls(), DEFAULT_MINIMUM_NUMBER_OF_CALLS); } else if (circuitBreaker.getName().contains(TestService2Client.class.getName())) { Assertions.assertEquals((int) circuitBreaker.getCircuitBreakerConfig().getFailureRateThreshold(), (int) TEST_SERVICE_2_FAILURE_RATE_THRESHOLD); Assertions.assertEquals(circuitBreaker.getCircuitBreakerConfig().getMinimumNumberOfCalls(), TEST_SERVICE_2_MINIMUM_NUMBER_OF_CALLS); } }); }
我們給 TestService1Client 添加一個方法:
@GetMapping("/status/500")String testCircuitBreakerStatus500();
這個方法一定會調用失敗,從而導致斷路器打開。經過 2 次失敗以上后(因為配置最少觸發斷路器打開的請求個數為 2),驗證斷路器狀態:
@Testpublic void testCircuitBreakerOpenBasedOnServiceAndMethod() { //防止斷路器影響 circuitBreakerRegistry.getAllCircuitBreakers().asJava().forEach(CircuitBreaker::reset); AtomicBoolean passed = new AtomicBoolean(false); for (int i = 0; i < 10; i++) { //多次調用會導致斷路器打開 try { System.out.println(testService1Client.testCircuitBreakerStatus500()); } catch(Exception e) {} List circuitBreakers = circuitBreakerRegistry.getAllCircuitBreakers().asJava(); circuitBreakers.stream().filter(circuitBreaker -> { return circuitBreaker.getName().contains("testCircuitBreakerStatus500") && circuitBreaker.getName().contains("TestService1Client"); }).findFirst().ifPresent(circuitBreaker -> { //驗證對應微服務和方法的斷路器被打開 if (circuitBreaker.getState().equals(CircuitBreaker.State.OPEN)) { passed.set(true); //斷路器打開后,調用其他方法,不會拋出斷路器打開異常 testService1Client.testAnything(); } }); } Assertions.assertTrue(passed.get());}
這樣,我們就成功驗證了,驗證斷路器是基于服務和方法打開的。
微信搜索“我的編程喵”關注公眾號,每日一刷,輕松提升技術,斬獲各種offer:
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/123768.html
摘要:在上面打開一個微服務某個實例的一個路徑的斷路器之后,我們調用其他的路徑,無論多少次,都成功并且調用負載均衡器獲取服務實例的次數等于調用次數,代表沒有重試,也就是沒有斷路器異常。 本系列代碼地址:??https://github.com/JoJoTec/spring-cloud-parent??我們來測試下前面封裝好的 We...
摘要:本系列代碼地址我們繼續上一節,繼續使用測試我們自己封裝的測試針對重試測試針對重試針對響應超時,我們需要驗證重試僅針對可以重試的方法包括方法以及配置的可重試方法,針對不可重試的方法沒有重試。本系列代碼地址:https://github.com/JoJoTec/spring-cloud-parent我們繼續上一節,繼續使用 spock 測試我們自己封裝的 WebClient測試針對 readTi...
摘要:對于異步的請求,使用的是異步客戶端即。要實現的配置設計以及使用舉例要實現的配置設計以及使用舉例首先,我們要實現的,其包含三個重試重試的要在負載均衡之前,因為重試的時候,我們會從負載均衡器獲取另一個實例進行重試,而不是在同一個實例上重試多次。 本系列代碼地址:https://github.com/JoJoTec/spring-cloud-parent 為何需要封裝異步 HT...
摘要:將請求封裝成將請求封裝成的接口定義是但是最外層傳進來的參數是和,需要將他們封裝成,這個工作就是在中做的。其實主要任務就是將各種參數封裝成除了和本次請求相關的和,還有會話管理器,編碼解碼器配置,國際化配置還有用于擴展。本系列代碼地址:https://github.com/JoJoTec/spring-cloud-parent接下來,將進入我們升級之路的又一大模塊,即網關模塊。網關模塊我們廢棄了...
摘要:在這里,會將上下文中載入的拼接成,然后調用其方法的,它是的處理請求業務的起點。添加相關依賴之后,會有這個。路由權重相關配置功能相關實現類,這個我們這里不關心。本系列代碼地址:https://github.com/JoJoTec/spring-cloud-parent我們繼續分析上一節提到的 WebHandler,經過將請求封裝成 ServerWebExchange 的 HttpWebHand...
閱讀 3490·2021-11-18 10:07
閱讀 1589·2021-11-04 16:08
閱讀 1511·2021-11-02 14:43
閱讀 1088·2021-10-09 09:59
閱讀 844·2021-09-08 10:43
閱讀 1079·2021-09-07 09:59
閱讀 962·2019-12-27 11:56
閱讀 1011·2019-08-30 15:56