五月天青色头像情侣网名,国产亚洲av片在线观看18女人,黑人巨茎大战俄罗斯美女,扒下她的小内裤打屁股

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

使用 Resilience4j 框架實(shí)現(xiàn)重試機(jī)制

2021-11-22 17:39 作者:信碼由韁  | 我要投稿


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

代碼示例

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

什么是 Resilience4j?

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

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

Resilience4j 為 Java 8 及更高版本編寫(xiě),適用于函數(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. 為此類(lèi)配置創(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)用裝飾器方法來(lái)調(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<List<Flight>> flightSearchSupplier = ?() -> searchService.searchFlights(request); // ----> 4Supplier<List<Flight>> retryingFlightSearch = ?Retry.decorateSupplier(retry, flightSearchSupplier); // ----> 5System.out.println(retryingFlightSearch.get()); // ----> 6

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

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

  1. 向 REST 端點(diǎn)發(fā)送 HTTP 請(qǐng)求

  2. 調(diào)用遠(yuǎn)程過(guò)程 (RPC) 或 Web 服務(wù)

  3. 從數(shù)據(jù)存儲(chǔ)(SQL/NoSQL 數(shù)據(jù)庫(kù)、對(duì)象存儲(chǔ)等)讀取和寫(xiě)入數(shù)據(jù)

  4. 向消息代理(RabbitMQ/ActiveMQ/Kafka 等)發(fā)送和接收消息

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

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

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

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

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

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

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

使用 Resilience4j 重試模塊

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

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

簡(jiǎn)單重試

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

RetryConfig config = RetryConfig.custom() ?.maxAttempts(3) ?.waitDuration(Duration.of(2, SECONDS)) ?.build();// Registry, Retry creation omitted FlightSearchService service = new FlightSearchService(); SearchRequest request = new SearchRequest("NYC", "LAX", "07/31/2020"); Supplier<List<Flight>> flightSearchSupplier = ?() -> service.searchFlights(request); Supplier<List<Flight>> 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飾過(guò)的?Supplier?上調(diào)用?get()?方法來(lái)進(jìn)行遠(yuǎn)程調(diào)用。

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

List<Flight> flights = retry.executeSupplier( ?() -> service.searchFlights(request));

這是顯示第一個(gè)請(qǐng)求失敗然后第二次嘗試成功的示例輸出:

Searching for flights; current time = 20:51:34 975Operation failed Searching 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<List<Flight>> flightSearchSupplier = ?() -> service.searchFlightsThrowingException(request);

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

ExceptionSupplier<List<Flight>> flightSearchSupplier = () -> { ? ?try { ? ? ? ? ? ?return service.searchFlightsThrowingException(request); ? ?} catch (Exception e) { ? ? ?// don't 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<List<Flight>> 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è)沒(méi)有參數(shù)的函數(shù)。請(qǐng)注意對(duì)?CheckedFunction0?對(duì)象的?apply()?調(diào)用以調(diào)用遠(yuǎn)程操作。

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

有條件重試

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

Predicate-based條件重試

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

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

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

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

Exception-based條件重試

假設(shè)我們有一個(gè)通用異常
FlightServiceBaseException,當(dāng)在與航空公司的航班服務(wù)交互期間發(fā)生任何意外時(shí)會(huì)拋出該異常。作為一般策略,我們希望在拋出此異常時(shí)重試。但是我們不想重試?SeatsUnavailableException?的一個(gè)子類(lèi) - 如果航班上沒(méi)有可用座位,重試將無(wú)濟(jì)于事。我們可以通過(guò)像這樣創(chuàng)建?RetryConfig?來(lái)做到這一點(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<Throwable> 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)前過(guò)載的情況下進(jìn)行恢復(fù)。我們可以使用?IntervalFunction?來(lái)做到這一點(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ù)增長(zhǎng)。例如,如果我們指定初始等待時(shí)間為 1 秒,乘數(shù)為 2,則重試將在 1 秒、2 秒、4 秒、8 秒、16 秒等之后進(jìn)行。當(dāng)客戶(hù)端是后臺(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<CompletionStage<List<Flight>>> completionStageSupplier = ?() -> CompletableFuture.supplyAsync(() -> service.searchFlights(request)); retry.executeCompletionStage(scheduler, completionStageSupplier) .thenAccept(System.out::println);

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

重試事件

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

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

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

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

重試指標(biāo)

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

  1. 第一次嘗試成功

  2. 重試后成功

  3. 沒(méi)有重試就失敗了

  4. 重試后仍失敗

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

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

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

例如,如果我們發(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 等)提供了儀表客戶(hù)端的外觀。因此我們可以將指標(biāo)發(fā)布到這些系統(tǒng)中的任何一個(gè)或在它們之間切換,而無(wú)需更改我們的代碼。

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

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

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

Consumer<Meter> 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ī)制的客戶(hù)端庫(kù)或 SDK。對(duì)于云服務(wù)尤其如此。 例如,Azure CosmosDB 和 Azure 服務(wù)總線為客戶(hù)端庫(kù)提供內(nèi)置重試工具。 它們?cè)试S應(yīng)用程序設(shè)置重試策略來(lái)控制重試行為。

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

一些云服務(wù)記錄瞬時(shí)錯(cuò)誤代碼。例如,Azure SQL 提供了它期望數(shù)據(jù)庫(kù)客戶(hù)端重試的錯(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)行過(guò)多的重試,則會(huì)降低應(yīng)用程序的吞吐量。

結(jié)論

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

您可以使用 【GitHub 上】(
https://github.com/thombergs/code-examples/tree/master/resilience4j/retry)的代碼嘗試一個(gè)完整的應(yīng)用程序來(lái)演示這些想法。

本文譯自: 【Implementing Retry with Resilience4j - Reflectoring】(
https://reflectoring.io/retry-with-resilience4j/)


使用 Resilience4j 框架實(shí)現(xiàn)重試機(jī)制的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
商丘市| 铁力市| 汾西县| 包头市| 邹城市| 巴里| 宝山区| 舒兰市| 东至县| 黔西县| 惠水县| 中山市| 兰考县| 瑞昌市| 射洪县| 临沂市| 泰顺县| 宜宾市| 滨州市| 鞍山市| 永昌县| 抚州市| 广东省| 东阳市| 芜湖县| 建瓯市| 松阳县| 旺苍县| 卓尼县| 华安县| 郧西县| 太谷县| 凤冈县| 曲麻莱县| 横峰县| 卢湾区| 静宁县| 沭阳县| 灵石县| 西丰县| 伊川县|