SpringBoot實現(xiàn)重試機(jī)制的四種方案
一、Spring Retry
1. 基本原理
Spring Retry是Spring官方提供的重試框架,作為Spring生態(tài)系統(tǒng)的一部分,它通過AOP(面向切面編程)實現(xiàn)了對方法調(diào)用的重試能力。當(dāng)方法調(diào)用失敗時,Spring Retry會根據(jù)配置的策略自動重新執(zhí)行該方法,直到成功或達(dá)到最大重試次數(shù)。
Spring Retry的核心組件包括:
- RetryOperations:定義重試操作的接口
- RetryTemplate:RetryOperations的默認(rèn)實現(xiàn)
- RetryPolicy:定義何時進(jìn)行重試(如最大次數(shù)、重試的異常類型等)
- BackOffPolicy:定義重試間隔策略(如固定間隔、指數(shù)退避等)
- RecoveryCallback:定義最終失敗后的恢復(fù)策略
2. 集成配置
在SpringBoot項目中集成Spring Retry:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
然后在啟動類上啟用重試功能:
@SpringBootApplication
@EnableRetry
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3. 使用方法
Spring Retry提供了注解方式和編程方式兩種使用方法。
注解方式
@Service
public class RemoteServiceClient {
private static final Logger logger = LoggerFactory.getLogger(RemoteServiceClient.class);
@Retryable(value = {ServiceException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 1000, multiplier = 2))
public String callRemoteService(String param) {
logger.info("調(diào)用遠(yuǎn)程服務(wù),參數(shù): {}", param);
// 模擬調(diào)用失敗
if (Math.random() > 0.7) {
logger.error("服務(wù)調(diào)用失敗");
throw new ServiceException("遠(yuǎn)程服務(wù)暫時不可用");
}
return "調(diào)用成功: " + param;
}
@Recover
public String recover(ServiceException e, String param) {
logger.warn("重試失敗,執(zhí)行恢復(fù)方法, 參數(shù): {}", param);
return "降級響應(yīng): " + param;
}
}
在上面的例子中:
@Retryable注解定義了需要重試的方法,包括觸發(fā)重試的異常類型、最大重試次數(shù)和退避策略backoff屬性設(shè)置初始延遲1秒,且每次延遲時間翻倍(指數(shù)退避)@Recover注解定義了重試失敗后的恢復(fù)方法
編程方式
@Service
public class RemoteServiceClient {
private final RetryTemplate retryTemplate;
@Autowired
public RemoteServiceClient(RetryTemplate retryTemplate) {
this.retryTemplate = retryTemplate;
}
public String callWithRetry(String param) {
return retryTemplate.execute(context -> {
// 重試的業(yè)務(wù)邏輯
if (Math.random() > 0.7) {
throw new ServiceException("服務(wù)暫時不可用");
}
return "調(diào)用成功: " + param;
}, context -> {
// 重試失敗后的恢復(fù)邏輯
return "降級響應(yīng): " + param;
});
}
@Bean
public RetryTemplate retryTemplate() {
RetryTemplate template = new RetryTemplate();
// 設(shè)置重試策略
SimpleRetryPolicy policy = new SimpleRetryPolicy();
policy.setMaxAttempts(3);
// 設(shè)置退避策略
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(1000);
backOffPolicy.setMultiplier(2.0);
template.setRetryPolicy(policy);
template.setBackOffPolicy(backOffPolicy);
return template;
}
}
4. 優(yōu)缺點
優(yōu)點
- 與Spring生態(tài)系統(tǒng)完美集成
- 提供了豐富的重試策略和配置選項
- 支持注解和編程兩種方式,使用靈活
- 可以精確控制重試的異常類型
- 支持聲明式事務(wù)回滾和提交
缺點
- 依賴Spring框架
- 代碼侵入性相對較強(qiáng)
- 在復(fù)雜場景下配置略顯復(fù)雜
- 與其他容錯機(jī)制集成需要額外工作
5. 適用場景
- Spring生態(tài)系統(tǒng)的項目
- 需要精細(xì)控制重試條件和策略的場景
- 與Spring事務(wù)結(jié)合的業(yè)務(wù)場景
- 方法級別的重試需求
二、Resilience4j Retry
1. 基本原理
Resilience4j是受Netflix Hystrix啟發(fā)而創(chuàng)建的輕量級容錯庫,其中Resilience4j Retry模塊提供了強(qiáng)大的重試功能。與Spring Retry不同,Resilience4j采用函數(shù)式編程風(fēng)格,使用裝飾器模式實現(xiàn)重試功能。
Resilience4j Retry的特點:
- 基于函數(shù)式接口
- 無外部依賴,輕量級設(shè)計
- 可與其他容錯機(jī)制(如斷路器、限流器)無縫集成
- 提供豐富的監(jiān)控指標(biāo)
2. 集成配置
在SpringBoot項目中集成Resilience4j Retry:
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
配置application.yml:
resilience4j.retry:
instances:
backendService:
maxAttempts: 3
waitDuration: 1s
enableExponentialBackoff: true
exponentialBackoffMultiplier: 2
retryExceptions:
- java.io.IOException
- java.util.concurrent.TimeoutException
3. 使用方法
Resilience4j同樣支持注解方式和編程方式。
注解方式
@Service
public class BackendService {
private static final Logger logger = LoggerFactory.getLogger(BackendService.class);
@Retry(name = "backendService", fallbackMethod = "fallbackCall")
public String callBackendService(String param) {
logger.info("調(diào)用后端服務(wù),參數(shù): {}", param);
if (Math.random() > 0.7) {
logger.error("服務(wù)調(diào)用失敗");
throw new IOException("服務(wù)連接失敗");
}
return "后端服務(wù)響應(yīng): " + param;
}
public String fallbackCall(String param, Exception ex) {
logger.warn("所有重試失敗,執(zhí)行降級方法,參數(shù): {}", param);
return "降級響應(yīng): " + param;
}
}
編程方式
@Service
public class BackendService {
private final RetryRegistry retryRegistry;
private final Logger logger = LoggerFactory.getLogger(BackendService.class);
@Autowired
public BackendService(RetryRegistry retryRegistry) {
this.retryRegistry = retryRegistry;
}
public String executeWithRetry(String param) {
// 獲取已配置的重試實例
Retry retry = retryRegistry.retry("backendService");
// 創(chuàng)建一個可重試的函數(shù)
CheckedFunction0<String> retryableFunction = Retry.decorateCheckedSupplier(
retry, () -> callBackendService(param));
try {
// 執(zhí)行重試函數(shù)
return retryableFunction.apply();
} catch (Throwable throwable) {
logger.error("重試失敗: {}", throwable.getMessage());
return "降級響應(yīng): " + param;
}
}
private String callBackendService(String param) throws IOException {
logger.info("調(diào)用后端服務(wù),參數(shù): {}", param);
if (Math.random() > 0.7) {
throw new IOException("服務(wù)連接失敗");
}
return "后端服務(wù)響應(yīng): " + param;
}
}
4. 優(yōu)缺點
優(yōu)點
- 輕量級設(shè)計,無外部依賴
- 函數(shù)式編程風(fēng)格,代碼簡潔
- 提供豐富的監(jiān)控和統(tǒng)計指標(biāo)
- 可與斷路器、限流器等容錯機(jī)制無縫集成
- 支持多種高級重試策略
缺點
- 學(xué)習(xí)曲線相對陡峭,尤其是函數(shù)式概念
- 對于不熟悉函數(shù)式編程的開發(fā)者可能不夠直觀
- 某些高級功能需要額外配置
5. 適用場景
- 需要與其他容錯機(jī)制結(jié)合的復(fù)雜場景
- 微服務(wù)架構(gòu)中的服務(wù)間調(diào)用
- 需要詳細(xì)監(jiān)控指標(biāo)的系統(tǒng)
三、Guava Retrying
1. 基本原理
Guava Retrying是Google Guava庫提供的重試機(jī)制,它提供了一個簡單靈活的API來實現(xiàn)重試功能。
Guava Retrying通過構(gòu)建器模式提供了靈活的重試配置,可以自定義重試條件、停止策略、等待策略等。
2. 集成配置
在SpringBoot項目中集成Guava Retrying:
<dependency>
<groupId>com.github.rholder</groupId>
<artifactId>guava-retrying</artifactId>
<version>2.0.0</version>
</dependency>
3. 使用方法
Guava Retrying主要采用編程方式使用:
@Service
public class ExternalServiceClient {
private static final Logger logger = LoggerFactory.getLogger(ExternalServiceClient.class);
public String callExternalService(String param) {
Retryer<String> retryer = RetryerBuilder.<String>newBuilder()
.retryIfException() // 發(fā)生任何異常時重試
.retryIfResult(result -> result == null) // 結(jié)果為null時重試
.withWaitStrategy(WaitStrategies.exponentialWait(1000, 10000, TimeUnit.MILLISECONDS)) // 指數(shù)退避
.withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 最多重試3次
.withRetryListener(new RetryListener() {
@Override
public <V> void onRetry(Attempt<V> attempt) {
logger.info("第{}次重試", attempt.getAttemptNumber());
if (attempt.hasException()) {
logger.error("異常: {}", attempt.getExceptionCause().getMessage());
}
}
})
.build();
try {
return retryer.call(() -> {
logger.info("調(diào)用外部服務(wù),參數(shù): {}", param);
// 模擬服務(wù)調(diào)用
if (Math.random() > 0.7) {
throw new RuntimeException("服務(wù)暫時不可用");
}
return "外部服務(wù)響應(yīng): " + param;
});
} catch (RetryException | ExecutionException e) {
logger.error("重試失敗: {}", e.getMessage());
return "降級響應(yīng): " + param;
}
}
}
在SpringBoot中創(chuàng)建可復(fù)用的Retryer bean:
@Configuration
public class RetryConfig {
@Bean
public <T> Retryer<T> defaultRetryer() {
return RetryerBuilder.<T>newBuilder()
.retryIfException()
.retryIfResult(Predicates.isNull())
.withWaitStrategy(WaitStrategies.exponentialWait(100, 1000, TimeUnit.MILLISECONDS))
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
.build();
}
}
@Service
public class ServiceWithRetry {
private final Retryer<String> retryer;
@Autowired
public ServiceWithRetry(Retryer<String> retryer) {
this.retryer = retryer;
}
public String executeWithRetry(String input) throws ExecutionException, RetryException {
return retryer.call(() -> {
// 業(yè)務(wù)邏輯
return processInput(input);
});
}
}
4. 高級特性
Guava Retrying提供了豐富的定制選項:
Retryer<String> complexRetryer = RetryerBuilder.<String>newBuilder()
// 定制重試條件
.retryIfExceptionOfType(IOException.class)
.retryIfException(e -> e instanceof TimeoutException)
.retryIfResult(result -> result != null && result.contains("error"))
// 定制等待策略
.withWaitStrategy(WaitStrategies.join(
WaitStrategies.fixedWait(1000, TimeUnit.MILLISECONDS),
WaitStrategies.randomWait(1000, TimeUnit.MILLISECONDS, 2000, TimeUnit.MILLISECONDS)
))
// 定制停止策略
.withStopStrategy(StopStrategies.stopAfterDelay(30, TimeUnit.SECONDS))
// 定制阻塞策略
.withBlockStrategy(BlockStrategies.threadSleepStrategy())
.build();
5. 優(yōu)缺點
優(yōu)點
- API簡單直觀,容易上手
- 高度可定制的重試條件、等待策略和停止策略
- 不依賴Spring框架,可在任何Java項目中使用
缺點
- 沒有注解支持,只能通過編程方式使用
- 缺乏與Spring生態(tài)系統(tǒng)的深度集成
- 沒有內(nèi)置的監(jiān)控和統(tǒng)計功能
- 已停止更新
6. 適用場景
- 簡單的重試需求
- 非Spring項目或?qū)pring依賴較少的項目
- 需要高度自定義重試邏輯的場景
四、Failsafe
1. 基本原理
failsafe是一個相對較新的Java重試庫,專注于高性能和低延遲場景。它的設(shè)計目標(biāo)是提供一個簡單、高效的重試機(jī)制,同時保持API的簡潔性和使用的便捷性。failsafe支持同步和異步重試,具有靈活的重試策略和最小的依賴。
2. 集成配置
在SpringBoot項目中集成failsafe
<dependency>
<groupId>dev.failsafe</groupId>
<artifactId>failsafe</artifactId>
<version>3.3.2</version>
</dependency>
failsafe通常通過dev.failsafe:failsafe庫來使用,這是一個現(xiàn)代化的重試和容錯庫。
3. 使用方法
failsafe主要采用編程方式使用,具有流式API設(shè)計
@Service
public class FailsafeService {
private static final Logger logger = LoggerFactory.getLogger(FailsafeService.class);
public String executeWithRetry(String param) {
return Failsafe.with(
// 配置重試策略
RetryPolicy.<String>builder()
.handle(IOException.class, TimeoutException.class)
.withMaxRetries(3)
.withDelay(Duration.ofSeconds(1))
.withMaxDuration(Duration.ofSeconds(10))
.withBackoff(Duration.ofMillis(100), Duration.ofSeconds(2))
.onRetry(event -> logger.info("第{}次重試,上次異常: {}",
event.getAttemptCount(),
event.getLastException().getMessage()))
.onFailure(event -> logger.error("重試失敗,嘗試次數(shù): {}, 總耗時: {}ms",
event.getAttemptCount(),
event.getElapsedTime().toMillis()))
.build()
)
.get(() -> {
logger.info("執(zhí)行操作,參數(shù): {}", param);
// 模擬操作
if (Math.random() > 0.7) {
throw new IOException("操作暫時失敗");
}
return "操作成功: " + param;
});
}
// 異步重試示例
public CompletableFuture<String> executeWithRetryAsync(String param) {
return Failsafe.with(
RetryPolicy.<String>builder()
.handle(IOException.class)
.withMaxRetries(3)
.withBackoff(Duration.ofMillis(100), Duration.ofSeconds(1))
.build()
)
.getAsync(() -> {
logger.info("異步執(zhí)行操作,參數(shù): {}", param);
// 模擬異步操作
if (Math.random() > 0.7) {
throw new IOException("異步操作暫時失敗");
}
return "異步操作成功: " + param;
});
}
// 帶降級的重試示例
public String executeWithFallback(String param) {
return Failsafe.with(
RetryPolicy.<String>builder()
.handle(IOException.class)
.withMaxRetries(3)
.build(),
// 降級策略
Fallback.of(e -> "降級響應(yīng): " + param)
)
.get(() -> {
// 業(yè)務(wù)邏輯
if (Math.random() > 0.7) {
throw new IOException("操作失敗");
}
return "操作成功: " + param;
});
}
}
在SpringBoot中創(chuàng)建可復(fù)用的RetryPolicy bean
@Configuration
public class FailsafeConfig {
@Bean
public RetryPolicy<Object> defaultRetryPolicy() {
return RetryPolicy.builder()
.handle(Exception.class)
.withMaxRetries(3)
.withBackoff(Duration.ofMillis(100), Duration.ofSeconds(1), 2.0)
.build();
}
@Bean
public Fallback<Object> defaultFallback() {
return Fallback.of(e -> {
if (e instanceof ServiceException) {
return "服務(wù)異常降級";
}
return "通用降級響應(yīng)";
});
}
}
@Service
public class ServiceWithFailsafeRetry {
private final RetryPolicy<Object> defaultRetryPolicy;
private final Fallback<Object> defaultFallback;
@Autowired
public ServiceWithFailsafeRetry(RetryPolicy<Object> defaultRetryPolicy,
Fallback<Object> defaultFallback) {
this.defaultRetryPolicy = defaultRetryPolicy;
this.defaultFallback = defaultFallback;
}
public String executeWithRetry(String input) {
return Failsafe.with(defaultRetryPolicy, defaultFallback)
.get(() -> {
// 業(yè)務(wù)邏輯
return processInput(input);
});
}
}
4. 優(yōu)缺點
優(yōu)點
- 極高的性能,適合高頻調(diào)用場景
- 支持同步和異步重試
- 輕量級,依賴少
- 與CompletableFuture良好集成
- 內(nèi)置豐富的監(jiān)聽器機(jī)制
缺點
- 沒有注解支持,只能通過編程方式使用
- 與Spring框架集成度不高
- 近幾年更新也不活躍
5. 適用場景
- 高性能、低延遲要求的應(yīng)用
- 需要異步重試能力的場景
- 需要細(xì)粒度控制重試行為的場景
五、四種重試機(jī)制的對比
| 特性 | Spring Retry | Resilience4j Retry | Guava Retrying | Failsafe |
|---|---|---|---|---|
| 編程模型 | AOP + 命令式 | 函數(shù)式 | 命令式 | 流式 |
| 注解支持 | 支持 | 支持 | 不支持 | 不支持 |
| 依賴 | Spring | 無外部依賴 | Guava | 最小依賴 |
| 性能開銷 | 中等 | 低 | 中等 | 極低 |
| 異步支持 | 有限 | 良好 | 有限 | 優(yōu)秀 |
| 監(jiān)控集成 | 有限 | 豐富 | 無 | 基本 |
| 配置方式 | 注解/編程 | 配置文件/注解/編程 | 編程 | 編程 |
| 與其他容錯機(jī)制集成 | 有限 | 原生支持 | 無 | 良好 |
| 學(xué)習(xí)曲線 | 中等 | 較陡 | 平緩 | 平緩 |
| 可定制性 | 高 | 高 | 高 | 高 |
| 適用場景 | Spring項目 | 微服務(wù)/云原生應(yīng)用 | 簡單場景/非Spring項目 | 高性能場景 |
六、最佳實踐與注意事項
1. 通用最佳實踐
- 確保冪等性:重試機(jī)制最適合用于冪等操作,即多次執(zhí)行產(chǎn)生相同結(jié)果的操作。對于非冪等操作,需要特別小心。
- 設(shè)置合理的超時和重試次數(shù):避免無限重試或重試時間過長,通常3-5次足夠處理大多數(shù)臨時故障。
- 使用指數(shù)退避策略:隨著重試次數(shù)增加,逐漸增加重試間隔,避免對目標(biāo)服務(wù)造成過大壓力。
- 區(qū)分臨時故障和永久故障:只對可能自行恢復(fù)的臨時故障進(jìn)行重試,對于永久性錯誤不應(yīng)重試。
- 添加監(jiān)控和日志:記錄重試次數(shù)、成功率等指標(biāo),便于問題排查和性能優(yōu)化。
2. 避免常見陷阱
- 重試風(fēng)暴:當(dāng)多個客戶端同時對一個故障服務(wù)進(jìn)行重試時,可能導(dǎo)致服務(wù)負(fù)載激增。
- 資源泄漏:重試過程中要確保資源(如數(shù)據(jù)庫連接、HTTP連接)正確釋放。
- 過度重試:過度重試可能導(dǎo)致性能下降,應(yīng)設(shè)置合理的最大重試次數(shù)和總超時時間。
- 重試成本:某些操作重試成本高昂(如涉及第三方付費API),需謹(jǐn)慎設(shè)計重試策略。
七、總結(jié)
選擇合適的重試機(jī)制應(yīng)基于項目的技術(shù)棧、復(fù)雜度和需求。無論選擇哪種機(jī)制,都應(yīng)遵循重試的最佳實踐,避免常見陷阱,確保系統(tǒng)的穩(wěn)定性和可靠性。
以上就是SpringBoot實現(xiàn)重試機(jī)制的四種方案的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot重試機(jī)制的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java?for循環(huán)內(nèi)執(zhí)行多線程問題
這篇文章主要介紹了java?for循環(huán)內(nèi)執(zhí)行多線程問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03
SpringBoot使用Redis的zset統(tǒng)計在線用戶信息
這篇文章主要介紹了SpringBoot使用Redis的zset統(tǒng)計在線用戶信息,幫助大家更好的理解和學(xué)習(xí)使用SpringBoot框架,感興趣的朋友可以了解下2021-04-04
Java設(shè)計模式之單態(tài)模式(Singleton模式)介紹
這篇文章主要介紹了Java設(shè)計模式之單態(tài)模式(Singleton模式)介紹,本文講解了如何使用單例模式、使用單例模式注意事項等內(nèi)容,需要的朋友可以參考下2015-03-03
解析Java的Spring框架的BeanPostProcessor發(fā)布處理器
這篇文章主要介紹了Java的Spring框架的BeanPostProcessor發(fā)布處理器,Spring是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2015-12-12
Java?數(shù)據(jù)結(jié)構(gòu)深入理解ArrayList與順序表
ArrayList?類是一個可以動態(tài)修改的數(shù)組,與普通數(shù)組的區(qū)別就是它是沒有固定大小的限制,我們可以添加或刪除元素。ArrayList?繼承了?AbstractList?,并實現(xiàn)了?List?接口,順序表是將元素順序地存放在一塊連續(xù)的存儲區(qū)里,元素間的順序關(guān)系由它們的存儲順序自然表示2022-04-04

