前言
作為後端程式設計師,我們的日常工作就是呼叫一些第三方服務,將資料存入資料庫,返回資訊給前端。但你不能保證所有的事情一直都很順利。像有些第三方API,偶爾會出現超時。此時,我們要重試幾次,這取決於你的重試策略。
下面舉一個我在日常開發中多次看到的例子:
public interface OutSource {
List<Integer> getResult() throws TimeOutException;
}
@Service
public class OutSourceImpl implements OutSource {
static Random random = new Random();
@Override
public List<Integer> getResult() {
//mock failure
if (random.nextInt(2) == 1)
throw new TimeOutException();
return List.of(1, 2, 3);
}
}
@Slf4j
@Service
public class ManuallyRetryService {
@Autowired
private OutSource outSource;
public List<Integer> getOutSourceResult(String data, int retryTimes) {
log.info("trigger time:{}", retryTimes);
if (retryTimes > 3) {
return List.of();
}
try {
List<Integer> lst = outSource.getResult();
if (!CollectionUtils.isEmpty(lst)) {
return lst;
}
log.error("getOutSourceResult error, data:{}", data);
} catch (TimeOutException e) {
log.error("getOutSourceResult timeout", e);
}
// 遞迴呼叫
return getOutSourceResult(data, retryTimes + 1);
}
}
@Slf4j
@RestController
public class RetryTestController {
@Autowired
private ManuallyRetryService manuallyRetryService;
@GetMapping("manually")
public String manuallyRetry() {
List<Integer> result = manuallyRetryService.getOutSourceResult("haha", 0);
if (!CollectionUtils.isEmpty(result)) {
return "ok";
}
return "fail";
}
}
看看上面這段程式碼,我認為它可以正常工作,當retryTimes
達到4時,無論如何我們都會得到最終結果。但是你覺得寫的好嗎?優雅嗎?下面我來介紹Spring中的一個元件:spring-retry
,我們不妨來試一試。
Spring-Retry介紹使用
spring-retry
是Spring中的提供的一個重試框架,提供了註解的方式,在不入侵原有業務邏輯程式碼的方式下,優雅的實現重處理功能。
安裝依賴
- 如果你的是gradle應用,引入下面的依賴
implementation 'org.springframework.boot:spring-boot-starter-aop''org.springframework.boot:spring-boot-starter-aop'
implementation 'org.springframework.retry:spring-retry'
- 如果你的專案使用的是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>
啟用重試功能
新增@EnableRetry
註解在入口的類上從而啟用功能。
@SpringBootApplication
//看過來
@EnableRetry
public class TestSpringApplication {
public static void main(String[] args) {
SpringApplication.run(TestSpringApplication.class, args);
}
}
應用
我們以前面的為例,看看怎麼使用,如下面的程式碼:
public interface OutSource {
List<Integer> getResult() throws TimeOutException;
}
@Service
public class OutSourceImpl implements OutSource {
static Random random = new Random();
@Override
public List<Integer> getResult() {
//mock failure will throw an exception every time
throw new TimeOutException();
}
}
@Slf4j
@Service
public class RetryableService {
@Autowired
private OutSource outSource;
// 看這裡
@Retryable(value = {TimeOutException.class}, maxAttempts = 3)
public List<Integer> getOutSourceResult(String data) {
log.info("trigger timestamp:{}", System.currentTimeMillis() / 1000);
List<Integer> lst = outSource.getResult();
if (!CollectionUtils.isEmpty(lst)) {
return lst;
}
log.error("getOutSourceResult error, data:{}", data);
return null;
}
}
@Slf4j
@RestController
public class RetryTestController {
@Autowired
private RetryableService retryableService;
@GetMapping("retryable")
public String manuallyRetry2() {
try {
List<Integer> result = retryableService.getOutSourceResult("aaaa");
if (!CollectionUtils.isEmpty(result)) {
return "ok";
}
} catch (Exception e) {
log.error("retryable final exception", e);
}
return "fail";
}
}
- 關鍵在於
Service
層中的實現類中新增了@Retryable
註解,實現了重試, 指定value是TimeOutException
異常會進行重試,最大重試maxAttempts
3次。
驗證
這一次,當我們訪問http://localhost:8080/retryable時,我們將看到瀏覽器上的結果失敗。然後在你的終端上看到:
INFO 66776 --- [nio-9997-exec-1] c.m.testspring.service.RetryableService : trigger timestamp:1668236840
INFO 66776 --- [nio-9997-exec-1] c.m.testspring.service.RetryableService : trigger timestamp:1668236841
INFO 66776 --- [nio-9997-exec-1] c.m.testspring.service.RetryableService : trigger timestamp:1668236842
ERROR 66776 --- [nio-9997-exec-1] c.m.t.controller.RetryTestController : retryable final exception
總結
本文分享了spring-retry
重試框架最基礎的使用,可以無侵入業務程式碼進行重試。關於spring-retry
更多的使用建議可以自己去官網https://github.com/spring-projects/spring-retry 探索。
如果本文對你有幫助的話,請留下一個贊吧
歡迎關注個人公眾號——JAVA旭陽
更多學習資料請移步:程式設計師成神之路