知識庫 / Spring / Spring Web RSS 訂閱

設置異步重試機制(Spring)

Spring Web
HongKong
8
11:21 AM · Dec 06 ,2025

1. 概述

有時,我們需要異步執行代碼以提高應用程序的性能和響應能力。此外,我們可能希望在任何異常情況下自動重新調用代碼,因為我們預計可能會遇到偶爾的故障,例如網絡故障。

在本教程中,我們將學習如何在 Spring 應用程序中實現異步執行和自動重試。

我們將探索 Spring 對 <em >async</em><em >retry</em> 操作的支持。

2. 在 Spring Boot 中示例應用程序

讓我們假設我們需要構建一個簡單的微服務,該微服務會調用下游服務來處理一些數據。

2.1. Maven 依賴

首先,我們需要包含 spring-boot-starter-web Maven 依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2.2. 實現 Spring 服務

現在,我們將實現 EventService 類的方法,該方法調用另一個服務:

public String processEvents(List<String> events) {
    downstreamService.publishEvents(events);
    return "Completed";
}

然後,讓我們定義 DownstreamService 接口:

public interface DownstreamService {
    boolean publishEvents(List<String> events);
}

3. 使用重試實現異步執行

為了實現異步執行與重試功能,我們將使用 Spring 的實現。

我們需要配置應用程序以支持 asyncretry 功能。

3.1. 添加 Retry Maven 依賴

讓我們將 spring-retry 添加到 Maven 依賴中:

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>2.0.4</version>
</dependency>

3.2. <em @EnableAsync</em><em @EnableRetry</em> 配置

接下來,我們需要包含 <em @EnableAsync</em><em @EnableRetry</em> 註解:

@Configuration
@ComponentScan("com.baeldung.asyncwithretry")
@EnableRetry
@EnableAsync
public class AsyncConfig {
}

3.3. 使用 @Async@Retryable 註解

為了異步執行方法,我們需要使用 @Async 註解。 同樣,我們使用 @Retryable 註解 來進行重試執行。

讓我們在上述 EventService 方法中配置這些註解:

@Async
@Retryable(retryFor = RuntimeException.class, maxAttempts = 4, backoff = @Backoff(delay = 100))
public Future<String> processEvents(List<String> events) {
    LOGGER.info("Processing asynchronously with Thread {}", Thread.currentThread().getName());
    downstreamService.publishEvents(events);
    CompletableFuture<String> future = new CompletableFuture<>();
    future.complete("Completed");
    LOGGER.info("Completed async method with Thread {}", Thread.currentThread().getName());
    return future;
}

在上述代碼中,我們會在遇到 RuntimeException 異常時重試該方法,並以 Future 對象返回結果。

請注意,我們應該使用 Future 對象來包裝任何異步方法的響應

請注意,@Async 註解僅適用於公共方法,並且不能在同一類中自調用。自調用會繞過 Spring 代理調用,並在同一線程中運行。

4. 實施對 <em @Async 和 <em @Retryable 的測試

讓我們通過幾個測試用例,對 EventService 方法及其異步和重試行為進行測試。

首先,我們將實施一個測試用例,當 DownstreamService 調用沒有錯誤時:

@Test
void givenAsyncMethodHasNoRuntimeException_whenAsyncMethodIscalled_thenReturnSuccess_WithoutAnyRetry() throws Exception {
    LOGGER.info("Testing for async with retry execution with thread " + Thread.currentThread().getName()); 
    when(downstreamService.publishEvents(anyList())).thenReturn(true);
    Future<String> resultFuture = eventService.processEvents(List.of("test1"));
    while (!resultFuture.isDone() && !resultFuture.isCancelled()) {
        TimeUnit.MILLISECONDS.sleep(5);
    }
    assertTrue(resultFuture.isDone());
    assertEquals("Completed", resultFuture.get());
    verify(downstreamService, times(1)).publishEvents(anyList());
}

在上述測試中,我們正在等待 Future 完成,然後斷言結果。

然後,讓我們運行上述測試並驗證測試日誌:

18:59:24.064 [main] INFO com.baeldung.asyncwithretry.EventServiceIntegrationTest - Testing for async with retry execution with thread main
18:59:24.078 [SimpleAsyncTaskExecutor-1] INFO com.baeldung.asyncwithretry.EventService - Processing asynchronously with Thread SimpleAsyncTaskExecutor-1
18:59:24.080 [SimpleAsyncTaskExecutor-1] INFO com.baeldung.asyncwithretry.EventService - Completed async method with Thread SimpleAsyncTaskExecutor-1

從上述日誌中,我們確認服務方法在單獨的線程中運行。

接下來,我們將實現另一個測試用例,其中 DownstreamService 方法拋出 RuntimeException 異常:

@Test
void givenAsyncMethodHasRuntimeException_whenAsyncMethodIsCalled_thenReturnFailure_With_MultipleRetries() throws InterruptedException {
    LOGGER.info("Testing for async with retry execution with thread " + Thread.currentThread().getName()); 
    when(downstreamService.publishEvents(anyList())).thenThrow(RuntimeException.class);
    Future<String> resultFuture = eventService.processEvents(List.of("test1"));
    while (!resultFuture.isDone() && !resultFuture.isCancelled()) {
        TimeUnit.MILLISECONDS.sleep(5);
    }
    assertTrue(resultFuture.isDone());
    assertThrows(ExecutionException.class, resultFuture::get);
    verify(downstreamService, times(4)).publishEvents(anyList());
}

最後,讓我們使用輸出日誌驗證上述測試用例:

19:01:32.307 [main] INFO com.baeldung.asyncwithretry.EventServiceIntegrationTest - Testing for async with retry execution with thread main
19:01:32.318 [SimpleAsyncTaskExecutor-1] INFO com.baeldung.asyncwithretry.EventService - Processing asynchronously with Thread SimpleAsyncTaskExecutor-1
19:01:32.425 [SimpleAsyncTaskExecutor-1] INFO com.baeldung.asyncwithretry.EventService - Processing asynchronously with Thread SimpleAsyncTaskExecutor-1
.....

從以上日誌來看,我們確認服務方法已異步執行了四次。

5. 結論

在本文中,我們學習瞭如何使用重試機制在 Spring 中實現異步方法。

我們已經在示例應用程序中實現了這一點,並進行了幾次測試,以查看它如何處理不同的用例。我們觀察到異步代碼在單獨的線程上運行,並且可以自動重試。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.