使用Resilience4J增強Spring WebClient容錯性 – Arnold
這次我們將深入探討如何將 Resilience4J CircuitBreaker 與 Spring WebClient 整合。
我將向您展示兩種將 Resilience4J 與 WebClient 整合的方法。首先使用註釋,然後以程式設計方式。兩者都將相當容易。
案例:
@RestController @RequiredArgsConstructor public class ApiController { private final ExternalApi externalApi; @GetMapping("/foo") public Mono<String> foo() { return externalApi.callExternalApiFoo(); } } |
該應用程式正在使用 Web Reactive:
呼叫外部客戶端:
@Component @RequiredArgsConstructor public class ExternalApi { private final WebClient webClient; public Mono<String> callExternalApiFoo() { return webClient.get().uri("/external-foo").retrieve().bodyToMono(String.class); } } |
WebClient正在呼叫/external-foo路徑上的API,並將響應體解析為一個字串。返回的型別將再次是Mono<String>,因為我們是在反應式世界中。
WebClient的配置:
@Configuration public class ExternalApiConfig { @Bean public WebClient webClient() { return WebClient.create("http://localhost:9090"); } } |
這就是目前的全部內容。WebClient將呼叫http://localhost:9090/external-foo API。
我沒有建立一個全新的Spring應用來實現/external-foo API,而是使用WireMock來建立一個模擬伺服器,並編寫一個測試案例來驗證我們想要的場景。
讓我們先在build.gradle中新增WireMock的依賴項。
testImplementation "com.github.tomakehurst:wiremock-jre8:2.31.0"
測試程式碼:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class ApiControllerTest { @RegisterExtension static WireMockExtension EXTERNAL_SERVICE = WireMockExtension.newInstance() .options(WireMockConfiguration.wireMockConfig().port(9090)) .build(); @Autowired private TestRestTemplate restTemplate; @Test public void testFoo() throws Exception { EXTERNAL_SERVICE.stubFor(get("/external-foo").willReturn(serverError())); for (int i = 0; i < 5; i++) { ResponseEntity<String> response = restTemplate.getForEntity("/foo", String.class); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); } for (int i = 0; i < 5; i++) { ResponseEntity<String> response = restTemplate.getForEntity("/foo", String.class); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); } } } |
該測試將在9090埠啟動一個WireMock伺服器。它還將啟動一個Spring Boot網路伺服器,然後WireMock伺服器將在/external-foo API端點上以HTTP 500s響應。
然後,我們的Spring應用程式對/foo API的前5次呼叫應該以HTTP 500失敗,因為WebClient在應用程式中會有未捕獲的異常,預設情況下,這些異常也會被翻譯成HTTP 500。
在這5次API呼叫之後,接下來的5次呼叫應該以HTTP 503 - Service Unavailable失敗,表明Resilience4J CircuitBreaker已經開啟。
如果你執行這個測試案例,它顯然會失敗,因為我們還沒有設定任何CircuitBreaker。
基於註解的Resilience4J CircuitBreaker
對於註解驅動的CircuitBreakers,我們需要一些依賴性。
implementation 'org.springframework.boot:spring-boot-starter-aop' implementation 'io.github.resilience4j:resilience4j-spring-boot2:1.7.1' implementation "io.github.resilience4j:resilience4j-reactor:1.7.1" |
最後一個依賴 io.github.resilience4j:resilience4j-reactor:1.7.1 顯然只有在你執行一個反應式Spring應用時才需要。
現在,讓我們使用Resilience4J提供的註解。
@Component @RequiredArgsConstructor public class ExternalApi { private final WebClient webClient; @CircuitBreaker(name = "externalServiceFoo") public Mono<String> callExternalApiFoo() { return webClient.get().uri("/external-foo").retrieve().bodyToMono(String.class); } } |
這就是了。方法上的CircuitBreaker註解。由於我們有resilience4j-reactor的依賴,它將識別Mono的返回型別,並自動將斷路寫入執行流程。很棒吧?
註解中的externalServiceFoo是我們的CircuitBreaker的名字,我們將在一秒鐘內進行配置。
讓我們回到我們的配置類中,新增一個CircuitBreakerCustomizer。
@Configuration public class ExternalApiConfig { @Bean public WebClient webClient() { return WebClient.create("http://localhost:9090"); } @Bean public CircuitBreakerConfigCustomizer externalServiceFooCircuitBreakerConfig() { return CircuitBreakerConfigCustomizer .of("externalServiceFoo", builder -> builder.slidingWindowSize(10) .slidingWindowType(COUNT_BASED) .waitDurationInOpenState(Duration.ofSeconds(5)) .minimumNumberOfCalls(5) .failureRateThreshold(50.0f)); } } |
這將配置CircuitBreaker有一個COUNT_BASED的滑動視窗,大小為10。它不會對前5次呼叫(minimumNumberOfCalls)的CircuitBreaker進行評估,並將在50%的失敗率時觸發;在開放狀態下等待5秒。
由於測試期望在CircuitBreaker開啟後有一個HTTP 503 - Service Unavailable的響應,我們必須先實現這個異常處理。
我們要建立一個新的異常處理程式。
@ControllerAdvice public class ApiExceptionHandler { @ExceptionHandler({CallNotPermittedException.class}) @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE) public void handle() { } } |
這將完成工作。
現在,如果你啟動這個測試案例,它仍然會失敗。為什麼呢?
這是因為在CircuitBreakerCustomizer中,我們只能覆蓋現有的配置。在我們的案例中,我們要覆蓋externalServiceFoo的配置,而在Resilience4J的上下文中,這個配置顯然還不存在。
如何讓Resilience4J知道CircuitBreaker的配置?
很簡單,只要到application.properties中新增這一行。
resilience4j.circuitbreaker.instances.externalServiceFoo.slidingWindowType=COUNT_BASED
這將在Resilience4J的CircuitBreaker登錄檔中用預設設定建立配置物件,然後我們提供的值將覆蓋預設值。
你現在可以重新啟動測試,它應該是綠色的。
程式化的Resilience4J CircuitBreaker構成
如果你不喜歡神奇的註釋和麵向方面的程式設計概念,你也可以以程式設計方式使用Resilience4J CircuitBreaker。
首先,我們必須建立CircuitBreaker配置。它被儲存在Resilience4J提供的CircuitBreakerRegistry類中。
@Configuration public class ExternalApiConfig { @Bean public WebClient webClient() { return WebClient.create("http://localhost:9090"); } @Bean public CircuitBreakerRegistry circuitBreakerRegistry() { CircuitBreakerConfig externalServiceFooConfig = CircuitBreakerConfig.custom() .slidingWindowSize(10) .slidingWindowType(COUNT_BASED) .waitDurationInOpenState(Duration.ofSeconds(5)) .minimumNumberOfCalls(5) .failureRateThreshold(50.0f) .build(); return CircuitBreakerRegistry.of( Map.of("externalServiceFoo", externalServiceFooConfig) ); } } |
同樣的CircuitBreaker設定,只是用另一種方式來配置。我們可以從application.properties中刪除這一行,我們不再需要它了。
如何在WebClient中使用它?很簡單。
@Component @RequiredArgsConstructor public class ExternalApi { private final CircuitBreakerRegistry circuitBreakerRegistry; private final WebClient webClient; public Mono<String> callExternalApiFoo() { CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("externalServiceFoo", "externalServiceFoo"); return webClient.get() .uri("/external-foo") .retrieve() .bodyToMono(String.class) .transformDeferred(CircuitBreakerOperator.of(circuitBreaker)); } } |
我們自動連線CircuitBreakerRegistry以獲得對預先配置的CircuitBreaker的訪問。在該方法中,我們檢索CircuitBreaker例項,然後使用resilience4j-reactor模組中的CircuitBreakerOperator,將API呼叫與CircuitBreaker例項組成。
當然,我們不需要在測試中改變任何東西,因為我們只是修改了內部實現如何實現斷路。
如果你執行這個測試用例,它應該通過。
總結
使用 Resilience4J 的 WebClient 很簡單:
如您所見,將 Resilience4J 與 Spring WebClient 整合以實現彈性目的非常容易。使用斷路器只是道路上的第一步;Resilience4J 還有很多其他功能,您可以像使用 CircuitBreaker 一樣使用它們。
你可以在 GitHub 上找到基於註解的配置。
相關文章
- Apache Kafka和Spring Boot的容錯和可靠訊息傳遞 – Arnold GalovicsApacheKafkaSpring Boot
- 圖解resilience4j容錯機制圖解
- 增強版本的自開發SAP WebClient UI Repository Information SystemWebclientUIORM
- Spring AOP 增強介紹Spring
- Spring WebClient vs. RestTemplateSpringWebclientREST
- Spring Cloud Hystrix 容錯保護SpringCloud
- SpringCloud升級之路2020.0.x版-39. 改造 resilience4j 粘合 WebClientSpringGCCloudWebclient
- openGauss MySQL相容性增強MySql
- IDEA 2024.1:Spring支援增強、GitHub Action支援增強、更新HTTP Client等IdeaSpringGithubHTTPclient
- VirtualBox安裝增強功能報錯
- SpringBoot(14)——使用WebClientSpring BootWebclient
- Spring Cloud Hystrix 服務容錯保護SpringCloud
- Spring Cloud Hystrix:服務容錯保護SpringCloud
- 使用 @Audited 增強Spring Boot 應用程式的資料審計能力Spring Boot
- Spring Boot整合Hystrix實現服務容錯Spring Boot
- 使用Kafka分割槽擴充套件Spring Batch大資料排程批處理 – ArnoldKafka套件SpringBAT大資料
- LLDB+Python指令碼:增強LLDB除錯LLDBPython指令碼除錯
- list增強for迴圈刪除元素報錯
- 增強邊緣計算的安全性
- 消細轉強容往由子性土pla
- Arnold置亂
- NextDNS 與 Firefox 合作幫助增強使用者隱私和安全性DNSFirefox
- 別再用老舊架構了!單元化構建超強彈性和容錯系統!架構
- 什麼是伺服器的容錯性呢伺服器
- Java 中使用 Failsafe 實現容錯JavaAI
- ebay增強可用性的4個原則
- 使用PWA增強你的github pagesGithub
- resilience4j
- WebClient: Spring的新的HTTP反應式客戶端 - spring.ioWebclientSpringHTTP客戶端
- Spring Boot響應式客戶端WebClient入門Spring Boot客戶端Webclient
- 使用Resilience4J實現斷路器模式模式
- ORACLE GoldenGate 使用技巧-容錯處理等OracleGo
- Nydus 加速映象一致性校驗增強
- 在 ASP.NET Core Web API中使用 Polly 構建彈性容錯的微服務ASP.NETWebAPI微服務
- Spring Boot2+Resilience4j實現容錯之BulkheadSpring Boot
- Spring Boot中增強對MongoDB的配置(連線池等)Spring BootMongoDB
- 使用note++開發SAP WebClient UIWebclientUI
- 如何在SAP WebClient UI裡使用jChartFXWebclientUI