解鎖 Spring Boot:四種重試機制方案全攻略
前言
在分布式系統(tǒng)和微服務(wù)架構(gòu)盛行的當(dāng)下,服務(wù)調(diào)用失敗的情況時有發(fā)生,網(wǎng)絡(luò)波動、服務(wù)過載或臨時故障等因素都可能導(dǎo)致調(diào)用失敗。為提升系統(tǒng)可用性,重試機制成為處理臨時性故障的有效手段。
本文將探討Spring Boot中4種重試機制的實現(xiàn)方案,助力開發(fā)者根據(jù)項目需求精準(zhǔn)選型。
Guava Retrying
基本原理
Guava Retrying 是 Google Guava 庫提供的重試機制,通過構(gòu)建器模式實現(xiàn)靈活配置,可自定義重試條件、停止策略、等待策略等,為開發(fā)者提供了強大的控制能力。
使用方法
主要采用編程方式,通過RetryerBuilder構(gòu)建Retryer實例,設(shè)置重試條件、等待策略、停止策略等。還可添加重試監(jiān)聽器,在重試過程中記錄日志或執(zhí)行其他操作。也可將Retryer定義為可復(fù)用的bean,提高代碼復(fù)用性。
詳細案例見《如何優(yōu)雅實現(xiàn)接口服務(wù)的重試機制》,推薦這款
Spring Retry
基本原理
Spring Retry作為Spring官方出品的重試框架,借助AOP實現(xiàn)方法調(diào)用重試。核心組件涵蓋定義重試操作接口的RetryOperations,其默認(rèn)實現(xiàn)RetryTemplate,以及定義重試條件的RetryPolicy、控制重試間隔的BackOffPolicy和最終失敗恢復(fù)策略的RecoveryCallback。當(dāng)方法調(diào)用失敗,它會依據(jù)配置策略自動重試,直至成功或達到最大重試次數(shù)。
使用方法
- 注解方式:使用@Retryable注解標(biāo)記需重試的方法,指定觸發(fā)重試的異常類型、最大重試次數(shù)和退避策略。@Recover注解用于定義重試失敗后的恢復(fù)方法,代碼簡潔直觀,適用于簡單場景。
- 編程方式:通過注入RetryTemplate實例,在方法內(nèi)調(diào)用execute方法,傳入重試業(yè)務(wù)邏輯和失敗恢復(fù)邏輯。還可自定義RetryTemplate的重試策略和退避策略,滿足復(fù)雜業(yè)務(wù)需求。
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
@Override
@Retryable(maxAttempts = 3, backoff = @Backoff(delay = 2000, multiplier = 2))
public void addOrder() {
System.out.println("重試...");
int i = 3 / 0;
// addOrder
}
@Recover
public void recover(RuntimeException e) {
log.error("達到最大重試次數(shù)", e);
}
}
public void normalSpringRetry() {
// 表示哪些異常需要重試,key表示異常的字節(jié)碼,value為true表示需要重試
Map<Class<? extends Throwable>, Boolean> exceptionMap = new HashMap<>();
exceptionMap.put(RetryException.class, true);
// 構(gòu)建重試模板實例
RetryTemplate retryTemplate = new RetryTemplate();
// 設(shè)置重試回退操作策略,主要設(shè)置重試間隔時間
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
long fixedPeriodTime = 1000L;
backOffPolicy.setBackOffPeriod(fixedPeriodTime);
// 設(shè)置重試策略,主要設(shè)置重試次數(shù)
int maxRetryTimes = 3;
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(maxRetryTimes, exceptionMap);
retryTemplate.setRetryPolicy(retryPolicy);
retryTemplate.setBackOffPolicy(backOffPolicy);
Boolean execute = retryTemplate.execute(
//RetryCallback
retryContext -> {
String hello = helloService.hello();
log.info("調(diào)用的結(jié)果:{}", hello);
returntrue;
},
// RecoverCallBack
retryContext -> {
//RecoveryCallback
log.info("已達到最大重試次數(shù)");
returnfalse;
}
);
}
Resilience4j Retry
基本原理
Resilience4j Retry受Netflix Hystrix啟發(fā),是輕量級容錯庫。它采用函數(shù)式編程風(fēng)格和裝飾器模式實現(xiàn)重試功能,具備基于函數(shù)式接口、無外部依賴、可與其他容錯機制無縫集成以及提供豐富監(jiān)控指標(biāo)等特性。
使用方法
- 注解方式:使用@Retry注解標(biāo)記方法,指定重試實例名稱和降級方法,簡潔方便,適用于簡單業(yè)務(wù)場景。
- 編程方式:從RetryRegistry獲取已配置的重試實例,使用Retry.decorateCheckedSupplier方法裝飾業(yè)務(wù)方法,在try-catch塊中執(zhí)行重試邏輯并處理失敗情況,靈活性更高。
resilience4j.retry:
instances:
backendService:
maxAttempts: 3
waitDuration: 1s
enableExponentialBackoff: true
exponentialBackoffMultiplier: 2
retryExceptions:
- java.io.IOException
- java.util.concurrent.TimeoutException
@Service
public class OrderServiceImpl implements OrderService {
@Override
@Retry(name = "addOrder", fallbackMethod = "recover")
public void addOrder() {
System.out.println("重試...");
int i = 3 / 0;
// addOrder
}
public void recover((String param, Exception ex) {
log.error("達到最大重試次數(shù)", e);
}
}
@Service
public class HelloService {
private final RetryRegistry retryRegistry;
public String executeWithRetry(String param) {
// 獲取已配置的重試實例
Retry retry = retryRegistry.retry("helloService");
// 創(chuàng)建一個可重試的函數(shù)
CheckedFunction0<String> retryableFunction = Retry.decorateCheckedSupplier(
retry, () -> callHelloService(param));
try {
// 執(zhí)行重試函數(shù)
return retryableFunction.apply();
} catch (Throwable throwable) {
return"降級響應(yīng): " + param;
}
}
private String callHelloService(String param) throws IOException {
if (Math.random() > 0.7) {
throw new IOException("服務(wù)連接失敗");
}
return"后端服務(wù)響應(yīng): " + param;
}
}
Failsafe
基本原理
Failsafe專注于高性能和低延遲場景,支持同步和異步重試,具有靈活的重試策略和極少的依賴。其設(shè)計目標(biāo)是提供簡潔高效的重試機制,讓開發(fā)者能輕松應(yīng)對各種重試需求。
使用方法
主要通過編程方式,利用流式API進行配置。可配置重試策略,包括處理的異常類型、最大重試次數(shù)、延遲時間、最大持續(xù)時間、退避策略等,還可添加重試和失敗監(jiān)聽器。同時支持異步重試和帶降級的重試,滿足不同業(yè)務(wù)場景需求。也可將重試策略和降級策略定義為可復(fù)用的bean,提升代碼復(fù)用性。
@Slf4j
@Service
public class FailsafeService {
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 -> log.info("第{}次重試,上次異常: {}",
event.getAttemptCount(),
event.getLastException().getMessage()))
.onFailure(event -> log.error("重試失敗,嘗試次數(shù): {}, 總耗時: {}ms",
event.getAttemptCount(),
event.getElapsedTime().toMillis()))
.build()
)
.get(() -> {
log.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(() -> {
log.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;
});
}
}
總結(jié)
在實際開發(fā)中,開發(fā)者可根據(jù)業(yè)務(wù)場景的特點,靈活運用這些重試機制,提升系統(tǒng)的容錯能力和用戶體驗。