在本文中,我們將從快速介紹 Resilience4j 開始,然后深入探討其 Retry 模塊。我們將了解何時(shí)、如何使用它,以及它提供的功能。在此過程中,我們還將學(xué)習(xí)實(shí)現(xiàn)重試時(shí)的一些良好實(shí)踐。

代碼示例

本文在 GitHu 上附有工作代碼示例。

什么是 Resilience4j?

當(dāng)應(yīng)用程序通過網(wǎng)絡(luò)進(jìn)行通信時(shí),會(huì)有很多出錯(cuò)的情況。由于連接斷開、網(wǎng)絡(luò)故障、上游服務(wù)不可用等,操作可能會(huì)超時(shí)或失敗。應(yīng)用程序可能會(huì)相互過載、無響應(yīng)甚至崩潰。

Resilience4j 是一個(gè) Java 庫,可以幫助我們構(gòu)建彈性和容錯(cuò)的應(yīng)用程序。它提供了一個(gè)框架,可編寫代碼以防止和處理此類問題

Resilience4j 為 Java 8 及更高版本編寫,適用于函數(shù)接口、lambda 表達(dá)式和方法引用等結(jié)構(gòu)。

Resilience4j 模塊

讓我們快速瀏覽一下這些模塊及其用途:

模塊目的
Retry自動(dòng)重試失敗的遠(yuǎn)程操作
RateLimiter限制我們?cè)谝欢〞r(shí)間內(nèi)調(diào)用遠(yuǎn)程操作的次數(shù)
TimeLimiter調(diào)用遠(yuǎn)程操作時(shí)設(shè)置時(shí)間限制
Circuit Breaker當(dāng)遠(yuǎn)程操作持續(xù)失敗時(shí),快速失敗或執(zhí)行默認(rèn)操作
Bulkhead限制并發(fā)遠(yuǎn)程操作的數(shù)量
Cache存儲(chǔ)昂貴的遠(yuǎn)程操作的結(jié)果

使用范式

雖然每個(gè)模塊都有其抽象,但通常的使用范式如下:

  1. 創(chuàng)建一個(gè) Resilience4j 配置對(duì)象
  2. 為此類配置創(chuàng)建一個(gè) Registry 對(duì)象
  3. 從注冊(cè)表創(chuàng)建或獲取 Resilience4j 對(duì)象
  4. 將遠(yuǎn)程操作編碼為 lambda 表達(dá)式或函數(shù)式接口或通常的 Java 方法
  5. 使用提供的輔助方法之一圍繞第 4 步中的代碼創(chuàng)建裝飾器或包裝器
  6. 調(diào)用裝飾器方法來調(diào)用遠(yuǎn)程操作
    步驟 1-5 通常在應(yīng)用程序啟動(dòng)時(shí)完成一次。讓我們看看重試模塊的這些步驟:
RetryConfig config = RetryConfig.ofDefaults(); // ----> 1RetryRegistry registry = RetryRegistry.of(config); // ----> 2Retry retry = registry.retry("flightSearchService", config); // ----> 3FlightSearchService searchService = new FlightSearchService();SearchRequest request = new SearchRequest("NYC", "LAX", "07/21/2020");Supplier> flightSearchSupplier =  () -> searchService.searchFlights(request); // ----> 4Supplier> retryingFlightSearch =  Retry.decorateSupplier(retry, flightSearchSupplier); // ----> 5System.out.println(retryingFlightSearch.get()); // ----> 6

什么時(shí)候使用重試?

遠(yuǎn)程操作可以是通過網(wǎng)絡(luò)發(fā)出的任何請(qǐng)求。通常,它是以下之一:

  1. 向 REST 端點(diǎn)發(fā)送 HTTP 請(qǐng)求
  2. 調(diào)用遠(yuǎn)程過程 (RPC) 或 Web 服務(wù)
  3. 從數(shù)據(jù)存儲(chǔ)(SQL/NoSQL 數(shù)據(jù)庫、對(duì)象存儲(chǔ)等)讀取和寫入數(shù)據(jù)
  4. 向消息代理(RabbitMQ/ActiveMQ/Kafka 等)發(fā)送和接收消息

當(dāng)遠(yuǎn)程操作失敗時(shí),我們有兩種選擇——立即向我們的客戶端返回錯(cuò)誤,或者重試操作。如果重試成功,這對(duì)客戶來說是件好事——他們甚至不必知道這是一個(gè)臨時(shí)問題。

選擇哪個(gè)選項(xiàng)取決于錯(cuò)誤類型(瞬時(shí)或永久)、操作(冪等或非冪等)、客戶端(人或應(yīng)用程序)和用例。

暫時(shí)性錯(cuò)誤是暫時(shí)的,通常,如果重試,操作很可能會(huì)成功。請(qǐng)求被上游服務(wù)限制、連接斷開或由于某些服務(wù)暫時(shí)不可用而超時(shí)就是例子。

來自 REST API 的硬件故障或 404(未找到)響應(yīng)是永久性錯(cuò)誤的示例,重試無濟(jì)于事。

如果我們想應(yīng)用重試,操作必須是冪等的。假設(shè)遠(yuǎn)程服務(wù)接收并處理了我們的請(qǐng)求,但在發(fā)送響應(yīng)時(shí)出現(xiàn)問題。在這種情況下,當(dāng)我們重試時(shí),我們不希望服務(wù)將請(qǐng)求視為新請(qǐng)求或返回意外錯(cuò)誤(想想銀行轉(zhuǎn)賬)。

重試會(huì)增加 API 的響應(yīng)時(shí)間。如果客戶端是另一個(gè)應(yīng)用程序,如 cron 作業(yè)或守護(hù)進(jìn)程,這可能不是問題。但是,如果是一個(gè)人,有時(shí)最好做出響應(yīng),快速失敗并提供反饋,而不是在我們不斷重試時(shí)讓這個(gè)人等待。

對(duì)于某些關(guān)鍵用例,可靠性可能比響應(yīng)時(shí)間更重要,即使客戶是個(gè)人,我們也可能需要實(shí)現(xiàn)重試。銀行轉(zhuǎn)賬或旅行社預(yù)訂航班和旅行酒店的轉(zhuǎn)賬就是很好的例子 - 用戶期望可靠性,而不是對(duì)此類用例的即時(shí)響應(yīng)。我們可以通過立即通知用戶我們已接受他們的請(qǐng)求并在完成后通知他們來做出響應(yīng)。

使用 Resilience4j 重試模塊

RetryRegistryRetryConfigRetryresilience4j-retry 中的主要抽象。RetryRegistry 是用于創(chuàng)建和管理 Retry 對(duì)象的工廠。RetryConfig 封裝了諸如應(yīng)該嘗試重試多少次、嘗試之間等待多長時(shí)間等配置。每個(gè) Retry 對(duì)象都與一個(gè) RetryConfig 相關(guān)聯(lián)。 Retry 提供了輔助方法來為包含遠(yuǎn)程調(diào)用的函數(shù)式接口或 lambda 表達(dá)式創(chuàng)建裝飾器。

讓我們看看如何使用 retry 模塊中可用的各種功能。假設(shè)我們正在為一家航空公司建立一個(gè)網(wǎng)站,以允許其客戶搜索和預(yù)訂航班。我們的服務(wù)與 FlightSearchService 類封裝的遠(yuǎn)程服務(wù)通信。

簡單重試

在簡單重試中,如果在遠(yuǎn)程調(diào)用期間拋出 RuntimeException,則重試該操作。 我們可以配置嘗試次數(shù)、嘗試之間等待多長時(shí)間等:

RetryConfig config = RetryConfig.custom()  .maxAttempts(3)  .waitDuration(Duration.of(2, SECONDS))  .build();// Registry, Retry creation omittedFlightSearchService service = new FlightSearchService();SearchRequest request = new SearchRequest("NYC", "LAX", "07/31/2020");Supplier> flightSearchSupplier =  () -> service.searchFlights(request);Supplier> retryingFlightSearch =  Retry.decorateSupplier(retry, flightSearchSupplier);System.out.println(retryingFlightSearch.get());

我們創(chuàng)建了一個(gè) RetryConfig,指定我們最多要重試 3 次,并在兩次嘗試之間等待 2 秒。如果我們改用 RetryConfig.ofDefaults() 方法,則將使用 3 次嘗試和 500 毫秒等待持續(xù)時(shí)間的默認(rèn)值。

我們將航班搜索調(diào)用表示為 lambda 表達(dá)式 - List<Flight>Supplier。Retry.decorateSupplier() 方法使用重試功能裝飾此 Supplier。最后,我們?cè)谘b飾過的 Supplier 上調(diào)用 get() 方法來進(jìn)行遠(yuǎn)程調(diào)用。

如果我們想創(chuàng)建一個(gè)裝飾器并在代碼庫的不同位置重用它,我們將使用 decorateSupplier()。如果我們想創(chuàng)建它并立即執(zhí)行它,我們可以使用 executeSupplier() 實(shí)例方法代替:

List flights = retry.executeSupplier(  () -> service.searchFlights(request));這是顯示第一個(gè)請(qǐng)求失敗然后第二次嘗試成功的示例輸出:Searching for flights; current time = 20:51:34 975Operation failedSearching for flights; current time = 20:51:36 985Flight search successful[Flight{flightNumber=XY 765, flightDate=07/31/2020, from=NYC, to=LAX}, ...]

在已檢異常上重試

現(xiàn)在,假設(shè)我們要重試已檢查和未檢查的異常。假設(shè)我們正在調(diào)用
FlightSearchService.searchFlightsThrowingException(),它可以拋出一個(gè)已檢查的 Exception。由于 Supplier 不能拋出已檢查的異常,我們會(huì)在這一行得到編譯器錯(cuò)誤:

Supplier> flightSearchSupplier =  () -> service.searchFlightsThrowingException(request);

我們可能會(huì)嘗試在 lambda 表達(dá)式中處理 Exception 并返回 Collections.emptyList(),但這看起來不太好。更重要的是,由于我們自己捕獲 Exception,重試不再起作用:

ExceptionSupplier> flightSearchSupplier = () -> {    try {            return service.searchFlightsThrowingException(request);    } catch (Exception e) {      // dont do this, this breaks the retry!    }    return Collections.emptyList();  };

那么當(dāng)我們想要重試遠(yuǎn)程調(diào)用可能拋出的所有異常時(shí),我們應(yīng)該怎么做呢?我們可以使用
Retry.decorateCheckedSupplier()(或 executeCheckedSupplier() 實(shí)例方法)代替 Retry.decorateSupplier()

CheckedFunction0> retryingFlightSearch =  Retry.decorateCheckedSupplier(retry,    () -> service.searchFlightsThrowingException(request));try {  System.out.println(retryingFlightSearch.apply());} catch (...) {  // handle exception that can occur after retries are exhausted}

Retry.decorateCheckedSupplier() 返回一個(gè) CheckedFunction0,它表示一個(gè)沒有參數(shù)的函數(shù)。請(qǐng)注意對(duì) CheckedFunction0 對(duì)象的 apply() 調(diào)用以調(diào)用遠(yuǎn)程操作。

如果我們不想使用 Suppliers ,Retry 提供了更多的輔助裝飾器方法,如 decorateFunction()、decorateCheckedFunction()、decorateRunnable()decorateCallable() 等,以與其他語言結(jié)構(gòu)一起使用。decorate*decorateChecked* 版本之間的區(qū)別在于,decorate* 版本在 RuntimeExceptions 上重試,而 decorateChecked* 版本在 Exception 上重試。

有條件重試

上面的簡單重試示例展示了如何在調(diào)用遠(yuǎn)程服務(wù)時(shí)遇到 RuntimeException 或已檢查 Exception 時(shí)重試。在實(shí)際應(yīng)用中,我們可能不想對(duì)所有異常都重試。 例如,如果我們得到一個(gè)
AuthenticationFailedException 重試相同的請(qǐng)求將無濟(jì)于事。當(dāng)我們進(jìn)行 HTTP 調(diào)用時(shí),我們可能想要檢查 HTTP 響應(yīng)狀態(tài)代碼或在響應(yīng)中查找特定的應(yīng)用程序錯(cuò)誤代碼來決定是否應(yīng)該重試。讓我們看看如何實(shí)現(xiàn)這種有條件的重試。

Predicate-based條件重試

假設(shè)航空公司的航班服務(wù)定期初始化其數(shù)據(jù)庫中的航班數(shù)據(jù)。對(duì)于給定日期的飛行數(shù)據(jù),此內(nèi)部操作需要幾秒鐘時(shí)間。 如果我們?cè)诔跏蓟^程中調(diào)用當(dāng)天的航班搜索,該服務(wù)將返回一個(gè)特定的錯(cuò)誤代碼 FS-167。航班搜索文檔說這是一個(gè)臨時(shí)錯(cuò)誤,可以在幾秒鐘后重試該操作。

讓我們看看如何創(chuàng)建 RetryConfig

RetryConfig config = RetryConfig.custom()  .maxAttempts(3)  .waitDuration(Duration.of(3, SECONDS))  .retryOnResult(searchResponse -> searchResponse    .getErrorCode()    .equals("FS-167"))  .build();

我們使用 retryOnResult() 方法并傳遞執(zhí)行此檢查的 Predicate。這個(gè) Predicate 中的邏輯可以像我們想要的那樣復(fù)雜——它可以是對(duì)一組錯(cuò)誤代碼的檢查,也可以是一些自定義邏輯來決定是否應(yīng)該重試搜索。

Exception-based條件重試

假設(shè)我們有一個(gè)通用異常
FlightServiceBaseException,當(dāng)在與航空公司的航班服務(wù)交互期間發(fā)生任何意外時(shí)會(huì)拋出該異常。作為一般策略,我們希望在拋出此異常時(shí)重試。但是我們不想重試 SeatsUnavailableException 的一個(gè)子類 - 如果航班上沒有可用座位,重試將無濟(jì)于事。我們可以通過像這樣創(chuàng)建 RetryConfig 來做到這一點(diǎn):

RetryConfig config = RetryConfig.custom()  .maxAttempts(3)  .waitDuration(Duration.of(3, SECONDS))  .retryExceptions(FlightServiceBaseException.class)  .ignoreExceptions(SeatsUnavailableException.class)  .build();

retryExceptions() 中,我們指定了一個(gè)異常列表。ignoreExceptions() 將重試與此列表中的異常匹配或繼承的任何異常。我們把我們想忽略而不是重試的那些放入ignoreExceptions()。如果代碼在運(yùn)行時(shí)拋出一些其他異常,比如 IOException,它也不會(huì)被重試。

假設(shè)即使對(duì)于給定的異常,我們也不希望在所有情況下都重試。也許我們只想在異常具有特定錯(cuò)誤代碼或異常消息中的特定文本時(shí)重試。在這種情況下,我們可以使用 retryOnException 方法:

Predicate rateLimitPredicate = rle ->  (rle instanceof  RateLimitExceededException) &&  "RL-101".equals(((RateLimitExceededException) rle).getErrorCode());RetryConfig config = RetryConfig.custom()  .maxAttempts(3)  .waitDuration(Duration.of(1, SECONDS))  .retryOnException(rateLimitPredicate)  build();

與 predicate-based (基于謂詞)的條件重試一樣,謂詞內(nèi)的檢查可以根據(jù)需要復(fù)雜化。

退避策略

到目前為止,我們的示例有固定的重試等待時(shí)間。通常我們希望在每次嘗試后增加等待時(shí)間——這是為了讓遠(yuǎn)程服務(wù)有足夠的時(shí)間在當(dāng)前過載的情況下進(jìn)行恢復(fù)。我們可以使用 IntervalFunction 來做到這一點(diǎn)。

IntervalFunction 是一個(gè)函數(shù)式接口——它是一個(gè)以嘗試次數(shù)為參數(shù)并以毫秒為單位返回等待時(shí)間的 Function。

隨機(jī)間隔

這里我們指定嘗試之間的隨機(jī)等待時(shí)間:

RetryConfig config = RetryConfig.custom().maxAttempts(4).intervalFunction(IntervalFunction.ofRandomized(2000)).build();

IntervalFunction.ofRandomized() 有一個(gè)關(guān)聯(lián)的 randomizationFactor。我們可以將其設(shè)置為 ofRandomized() 的第二個(gè)參數(shù)。如果未設(shè)置,則采用默認(rèn)值 0.5。這個(gè) randomizationFactor 決定了隨機(jī)值的分布范圍。因此,對(duì)于上面的默認(rèn)值 0.5,生成的等待時(shí)間將介于 1000 毫秒(2000 - 2000 0.5)和 3000 毫秒(2000 + 2000 0.5)之間。

這種行為的示例輸出如下:

Searching for flights; current time = 20:27:08 729Operation failedSearching for flights; current time = 20:27:10 643Operation failedSearching for flights; current time = 20:27:13 204Operation failedSearching for flights; current time = 20:27:15 236Flight search successful[Flight{flightNumber=XY 765, flightDate=07/31/2020, from=NYC, to=LAX},...]

指數(shù)間隔

對(duì)于指數(shù)退避,我們指定兩個(gè)值 - 初始等待時(shí)間和乘數(shù)。在這種方法中,由于乘數(shù),等待時(shí)間在嘗試之間呈指數(shù)增長。例如,如果我們指定初始等待時(shí)間為 1 秒,乘數(shù)為 2,則重試將在 1 秒、2 秒、4 秒、8 秒、16 秒等之后進(jìn)行。當(dāng)客戶端是后臺(tái)作業(yè)或守護(hù)進(jìn)程時(shí),此方法是推薦的方法。

以下是我們?nèi)绾螢橹笖?shù)退避創(chuàng)建 RetryConfig

RetryConfig config = RetryConfig.custom().maxAttempts(6).intervalFunction(IntervalFunction.ofExponentialBackoff(1000, 2)).build();

這種行為的示例輸出如下:

Searching for flights; current time = 20:37:02 684Operation failedSearching for flights; current time = 20:37:03 727Operation failedSearching for flights; current time = 20:37:05 731Operation failedSearching for flights; current time = 20:37:09 731Operation failedSearching for flights; current time = 20:37:17 731

IntervalFunction 還提供了一個(gè) exponentialRandomBackoff() 方法,它結(jié)合了上述兩種方法。我們還可以提供 IntervalFunction 的自定義實(shí)現(xiàn)。

重試異步操作

直到現(xiàn)在我們看到的例子都是同步調(diào)用。讓我們看看如何重試異步操作。假設(shè)我們像這樣異步搜索航班:

CompletableFuture.supplyAsync(() -> service.searchFlights(request))  .thenAccept(System.out::println);

searchFlight() 調(diào)用發(fā)生在不同的線程上,當(dāng)它返回時(shí),返回的 List<Flight> 被傳遞給 thenAccept(),它只是打印它。

我們可以使用 Retry 對(duì)象上的 executeCompletionStage() 方法對(duì)上述異步操作進(jìn)行重試。 此方法采用兩個(gè)參數(shù) - 一個(gè) ScheduledExecutorService 將在其上安排重試,以及一個(gè) Supplier<CompletionStage> 將被裝飾。它裝飾并執(zhí)行 CompletionStage,然后返回一個(gè) CompletionStage,我們可以像以前一樣調(diào)用 thenAccept

ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();Supplier>> completionStageSupplier =  () -> CompletableFuture.supplyAsync(() -> service.searchFlights(request));retry.executeCompletionStage(scheduler, completionStageSupplier).thenAccept(System.out::println);

在實(shí)際應(yīng)用程序中,我們將使用共享線程池 (
Executors.newScheduledThreadPool()) 來調(diào)度重試,而不是此處顯示的單線程調(diào)度執(zhí)行器。

重試事件

在所有這些例子中,裝飾器都是一個(gè)黑盒子——我們不知道什么時(shí)候嘗試失敗了,框架代碼正在嘗試重試。假設(shè)對(duì)于給定的請(qǐng)求,我們想要記錄一些詳細(xì)信息,例如嘗試計(jì)數(shù)或下一次嘗試之前的等待時(shí)間。 我們可以使用在不同執(zhí)行點(diǎn)發(fā)布的重試事件來做到這一點(diǎn)。Retry 有一個(gè) EventPublisher,它具有 onRetry()、onSuccess() 等方法。

我們可以通過實(shí)現(xiàn)這些監(jiān)聽器方法來收集和記錄詳細(xì)信息:

Retry.EventPublisher publisher = retry.getEventPublisher();publisher.onRetry(event -> System.out.println(event.toString()));publisher.onSuccess(event -> System.out.println(event.toString()));

類似地,RetryRegistry 也有一個(gè) EventPublisher,它在 Retry 對(duì)象被添加或從注冊(cè)表中刪除時(shí)發(fā)布事件。

重試指標(biāo)

Retry 維護(hù)計(jì)數(shù)器以跟蹤操作的次數(shù)

  1. 第一次嘗試成功
  2. 重試后成功
  3. 沒有重試就失敗了
  4. 重試后仍失敗

每次執(zhí)行裝飾器時(shí),它都會(huì)更新這些計(jì)數(shù)器。

為什么要捕獲指標(biāo)?

捕獲并定期分析指標(biāo)可以讓我們深入了解上游服務(wù)的行為。它還可以幫助識(shí)別瓶頸和其他潛在問題

例如,如果我們發(fā)現(xiàn)某個(gè)操作通常在第一次嘗試時(shí)失敗,我們可以調(diào)查其原因。如果我們發(fā)現(xiàn)我們的請(qǐng)求在建立連接時(shí)受到限制或超時(shí),則可能表明遠(yuǎn)程服務(wù)需要額外的資源或容量。

如何捕獲指標(biāo)?

Resilience4j 使用 Micrometer 發(fā)布指標(biāo)。Micrometer 為監(jiān)控系統(tǒng)(如 Prometheus、Azure Monitor、New Relic 等)提供了儀表客戶端的外觀。因此我們可以將指標(biāo)發(fā)布到這些系統(tǒng)中的任何一個(gè)或在它們之間切換,而無需更改我們的代碼。

首先,我們像往常一樣創(chuàng)建 RetryConfigRetryRegistryRetry。然后,我們創(chuàng)建一個(gè) MeterRegistry 并將 etryRegistry 綁定到它:

MeterRegistry meterRegistry = new SimpleMeterRegistry();TaggedRetryMetrics.ofRetryRegistry(retryRegistry).bindTo(meterRegistry);

運(yùn)行幾次可重試操作后,我們顯示捕獲的指標(biāo):

Consumer meterConsumer = meter -> {  String desc = meter.getId().getDescription();  String metricName = meter.getId().getTag("kind");  Double metricValue = StreamSupport.stream(meter.measure().spliterator(), false)    .filter(m -> m.getStatistic().name().equals("COUNT"))    .findFirst()    .map(m -> m.getValue())    .orElse(0.0);  System.out.println(desc + " - " + metricName + ": " + metricValue);};meterRegistry.forEachMeter(meterConsumer);

一些示例輸出如下:

The number of successful calls without a retry attempt - successful_without_retry: 4.0The number of failed calls without a retry attempt - failed_without_retry: 0.0The number of failed calls after a retry attempt - failed_with_retry: 0.0The number of successful calls after a retry attempt - successful_with_retry: 6.0

當(dāng)然,在實(shí)際應(yīng)用中,我們會(huì)將數(shù)據(jù)導(dǎo)出到監(jiān)控系統(tǒng)并在儀表板上查看。

重試時(shí)的注意事項(xiàng)和良好實(shí)踐

服務(wù)通常提供具有內(nèi)置重試機(jī)制的客戶端庫或 SDK。對(duì)于云服務(wù)尤其如此。 例如,Azure CosmosDB 和 Azure 服務(wù)總線為客戶端庫提供內(nèi)置重試工具。 它們?cè)试S應(yīng)用程序設(shè)置重試策略來控制重試行為。

在這種情況下,最好使用內(nèi)置的重試而不是我們自己的編碼。如果我們確實(shí)需要自己編寫,我們應(yīng)該禁用內(nèi)置的默認(rèn)重試策略 - 否則,它可能導(dǎo)致嵌套重試,其中應(yīng)用程序的每次嘗試都會(huì)導(dǎo)致客戶端庫的多次嘗試。

一些云服務(wù)記錄瞬時(shí)錯(cuò)誤代碼。例如,Azure SQL 提供了它期望數(shù)據(jù)庫客戶端重試的錯(cuò)誤代碼列表。在決定為特定操作添加重試之前,最好檢查一下服務(wù)提供商是否有這樣的列表。

另一個(gè)好的做法是將我們?cè)?RetryConfig 中使用的值(例如最大嘗試次數(shù)、等待時(shí)間和可重試錯(cuò)誤代碼和異常)作為我們服務(wù)之外的配置進(jìn)行維護(hù)。如果我們發(fā)現(xiàn)新的暫時(shí)性錯(cuò)誤或者我們需要調(diào)整嘗試之間的間隔,我們可以在不構(gòu)建和重新部署服務(wù)的情況下進(jìn)行更改。

通常在重試時(shí),框架代碼中的某處可能會(huì)發(fā)生 Thread.sleep()。對(duì)于在重試之間有等待時(shí)間的同步重試就是這種情況。如果我們的代碼在 Web 應(yīng)用程序的上下文中運(yùn)行,則 Thread 很可能是 Web 服務(wù)器的請(qǐng)求處理線程。因此,如果我們進(jìn)行過多的重試,則會(huì)降低應(yīng)用程序的吞吐量。

結(jié)論

在本文中,我們了解了 Resilience4j 是什么,以及如何使用它的重試模塊使我們的應(yīng)用程序可以在應(yīng)對(duì)臨時(shí)錯(cuò)誤具備彈性。我們研究了配置重試的不同方法,以及在不同方法之間做出決定的一些示例。我們學(xué)習(xí)了一些在實(shí)施重試時(shí)要遵循的良好實(shí)踐,以及收集和分析重試指標(biāo)的重要性。

您可以使用 GitHub 上的代碼嘗試一個(gè)完整的應(yīng)用程序來演示這些想法。


本文譯自: Implementing Retry with Resilience4j - Reflectoring