前言
spring retry是從spring batch獨(dú)立出來的一個(gè)能功能,主要實(shí)現(xiàn)了重試和熔斷。對(duì)于重試是有場(chǎng)景限制的,不是什么場(chǎng)景都適合重試,比如參數(shù)校驗(yàn)不合法、寫操作等(要考慮寫是否冪等)都不適合重試。遠(yuǎn)程調(diào)用超時(shí)、網(wǎng)絡(luò)突然中斷可以重試。在微服務(wù)治理框架中,通常都有自己的重試與超時(shí)配置,比如dubbo可以設(shè)置retries=1,timeout=500調(diào)用失敗只重試1次,超過500ms調(diào)用仍未返回則調(diào)用失敗。在spring retry中可以指定需要重試的異常類型,并設(shè)置每次重試的間隔以及如果重試失敗是繼續(xù)重試還是熔斷(停止重試)。
Spring Retry支持集成到Spring或者Spring Boot項(xiàng)目中,而它支持AOP的切面注入寫法,所以在引入時(shí)必須引入aspectjweaver.jar包。
核心注解
核心注解3個(gè): @EnableRetry、@Retryable 和 @Recover
@EnableRetry
- 此注解用于開啟重試框架,可以修飾在SpringBoot啟動(dòng)類上面,也可以修飾在需要重試的類上
- proxyTargetClass:Boolean類型,用于指明代理方式【true:cglib代理,false:jdk動(dòng)態(tài)代理】默認(rèn)使用jdk動(dòng)態(tài)代理
@Retryable注解
被注解的方法發(fā)生異常時(shí)會(huì)重試
- value:Class[]類型,指定發(fā)生的異常進(jìn)行重試
- include:Class[]類型,和value一樣,默認(rèn)空,當(dāng)exclude也為空時(shí),所有異常都重試
- exclude:Class[]類型,指定異常不重試,默認(rèn)空,當(dāng)include也為空時(shí),所有異常都重試
- maxAttemps:int類型,重試次數(shù),默認(rèn)3
- backoff:Backoff類型,重試補(bǔ)償機(jī)制,默認(rèn)沒有
@Backoff注解
- delay:指定延遲后重試,默認(rèn)為1000L,即1s后開始重試。
- multiplier:指定延遲的倍數(shù),比如delay=5000L,multiplier=2時(shí),第一次重試為5秒后,第二次為10秒,第三次為20秒
@Recover
- 當(dāng)重試次數(shù)耗盡依然出現(xiàn)異常時(shí),執(zhí)行此異常對(duì)應(yīng)的@Recover方法。
- 異常類型需要與Recover方法參數(shù)類型保持一致。
- recover方法返回值需要與重試方法返回值保證一致。
下面是基于Spring Boot項(xiàng)目的集成步驟:
1、引入maven依賴
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、service
@Service
@Slf4j
public class RemoteService {
/**
* 添加重試注解,當(dāng)有異常時(shí)觸發(fā)重試機(jī)制.設(shè)置重試5次,默認(rèn)是3.延時(shí)2000ms再次執(zhí)行,每次延時(shí)提高1.5倍.當(dāng)返回結(jié)果不符合要求時(shí),主動(dòng)報(bào)錯(cuò)觸發(fā)重試.
* @param count
* @return
* @throws Exception
*/
@Retryable(value = {RemoteAccessException.class }, maxAttempts = 5, backoff = @Backoff(delay = 2000, multiplier = 1.5))
public String call(Integer count) throws Exception {
if(count == 10){
log.info("Remote RPC call do something... {}",LocalTime.now());
throw new RemoteAccessException("RPC調(diào)用異常");
}
return "SUCCESS";
}
/**
* 定義回調(diào),注意異常類型和方法返回值類型要與重試方法一致
* @param e
* @return
*/
@Recover
public String recover(RemoteAccessException e) {
log.info("Remote RPC Call fail",e);
return "recover SUCCESS";
}
}
3、Controller
@RestController
@RequestMapping("/retry")
@Slf4j
public class RetryController {
@Autowired
private RemoteService remoteService;
@RequestMapping("/show/{count}")
public String show(@PathVariable Integer count){
try {
return remoteService.call(count);
} catch (Exception e) {
log.error("RetryController.show Exception",e);
return "Hello SUCCESS";
}
}
}
4、springboot開啟重試
@SpringBootApplication
@EnableRetry
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
效果

采坑記錄:
1、retry重試機(jī)制無效
由于retry用到了aspect增強(qiáng),所有會(huì)有aspect的坑,就是方法內(nèi)部調(diào)用,會(huì)使aspect增強(qiáng)失效,那么retry當(dāng)然也會(huì)失效。
public class demo {
public void A() {
B();
}
@Retryable(Exception.class)
public void B() {
throw new RuntimeException("retry...");
}
}
這種情況B()不會(huì)重試。
2、recover回調(diào)報(bào)錯(cuò)
org.springframework.retry.ExhaustedRetryException: Cannot locate recovery method
報(bào)錯(cuò)顯示找不到recovery方法
解決方案就這這兩句話:
- 1、異常類型需要與Recover方法參數(shù)類型保持一致
- 2、recover方法返回值需要與重試方法返回值保證一致
異常類型和返回值要一致!