天天看點

用兩行代碼實作重試功能,spring-retry真是簡單而優雅

作者:程式那點事

背景

最近做的一個需求,需要調用第三方接口。正常情況下,接口的響應是符合要求的,隻有在網絡抖動等極少數的情況下,會存在逾時情況。因為是小機率事件,是以一次逾時之後,進行一次重試操作應該就可以了。重試很簡單,設定最多的重試次數,用一個循環來實作就好了。比如一次請求是這樣:

@Controller
public class RetryController {
    @Autowired
    private RetryRequestService retryRequestService;

    public String doSth(String param) {
        String result = retryRequestService.request(param);
        return "響應是" + result;
    }
}
           

改成重試三次,可以是這樣:

@Controller
public class RetryController {
    @Autowired
    private RetryRequestService retryRequestService;

    public String doSth(String param) {
        int count = 0;
        String result = "";
        while (count < 3) {
            try {
                result = retryRequestService.request(param);
                break;
            } catch (Exception e) {
                count++;
            }
        }
        return "響應是" + result;
    }
}
           

如果請求接口逾時(抛異常)了,那麼會繼續進入下一次循環重試。如果在逾時時間内擷取到了結果,那就結束循環,繼續往下走。

用倒是能用,但是太醜了。不好看,還狠狠地侵入了原有的代碼。是以有沒有更優雅的方式呢?

快速接入spring-retry

這麼常用的東西,肯定有輪子啊!于是spring-retry閃亮登場!

這是一個屬于Spring全家桶的項目,也是被廣泛運用的元件。在這裡我預設你是個Spring Boot的項目了哈。

使用起來非常簡單,隻需要三步。

1、引入依賴

<!--springboot項目都不用引入版本号-->
<dependency>
  <groupId>org.springframework.retry</groupId>
  <artifactId>spring-retry</artifactId>
</dependency>
<!--還是需要aop的支援的-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
</dependency>
           

2、在啟動類上加注解@EnableRetry

此舉是讓你的Spring Boot項目支援spring-retry的重試功能。像這樣:

@SpringBootApplication
@EnableRetry
@Slf4j
public class FastKindleApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(FastKindleApplication.class, args);
        String result = applicationContext.getBean(RetryController.class).doSth("");
        log.info(result);
    }
}
           

3、在需要重試的方法上加注解@Retryable

如上文所說,我們需要重試的方法是retryRequestService.request這個方法。那麼我們就在這個方法上加@Retryable注解。如下:

@Service
@Slf4j
public class RetryRequestService {
    @Autowired
    private OtherSystemSpi otherSystemSpi;

    @Retryable(value = RuntimeException.class, maxAttempts = 5, backoff = @Backoff(delay = 100))
    public String request(String param) {
        double random = Math.random();
        log.info("請求進來了,随機值為:" + random);
        if (random > 0.1) {
            throw new RuntimeException("逾時");
        }
        return otherSystemSpi.request(param);
    }
}
           

當然,我們這裡寫了個調皮的邏輯來模拟逾時。如果随機值大于0.1則抛出一個RuntimeException異常。每次請求進來時都會輸出日志。

我來解釋一下@Retryable注解中的資訊。

  • value = RuntimeException.class:是指方法抛出RuntimeException異常時,進行重試。這裡可以指定你想要攔截的異常。
  • maxAttempts:是最大重試次數。如果不寫,則是預設3次。
  • backoff = @Backoff(delay = 100):是指重試間隔。delay=100意味着下一次的重試,要等100毫秒之後才能執行。

我們來執行一下,可以看到日志輸出:

2022-03-15 23:51:19.754  INFO 3343 --- [main] c.e.fastkindle.FastKindleApplication     : Started FastKindleApplication in 0.347 seconds (JVM running for 0.536)
2022-03-15 23:51:19.762  INFO 3343 --- [main] c.e.f.service.retry.RetryRequestService  : 請求進來了,随機值為:0.11030214774098712
2022-03-15 23:51:19.867  INFO 3343 --- [main] c.e.f.service.retry.RetryRequestService  : 請求進來了,随機值為:0.09624689154608002
2022-03-15 23:51:19.867  INFO 3343 --- [main] c.e.fastkindle.FastKindleApplication     : 響應是mock
           

前兩次的随機值都大于0.1,是以進行了重試,而且注意時間,都是間隔了大概100毫秒輸出的日志。第三次的随機值小于0.1,就直接傳回資料了。

我又試了幾次,使五次請求的随機值都大于0.1,則結果是進行了五次請求,最後抛出了個異常。

2022-03-15 23:52:58.193  INFO 3449 --- [main] c.e.fastkindle.FastKindleApplication     : Started FastKindleApplication in 0.41 seconds (JVM running for 0.635)
2022-03-15 23:52:58.201  INFO 3449 --- [main] c.e.f.service.retry.RetryRequestService  : 請求進來了,随機值為:0.5265644192525288
2022-03-15 23:52:58.303  INFO 3449 --- [main] c.e.f.service.retry.RetryRequestService  : 請求進來了,随機值為:0.6343538744876432
2022-03-15 23:52:58.407  INFO 3449 --- [main] c.e.f.service.retry.RetryRequestService  : 請求進來了,随機值為:0.5482463853575078
2022-03-15 23:52:58.511  INFO 3449 --- [main] c.e.f.service.retry.RetryRequestService  : 請求進來了,随機值為:0.5624923285641071
2022-03-15 23:52:58.616  INFO 3449 --- [main] c.e.f.service.retry.RetryRequestService  : 請求進來了,随機值為:0.305945622979098
Exception in thread "main" java.lang.RuntimeException: 逾時
        at com.esparks.fastkindle.service.retry.RetryRequestService.request(RetryRequestService.java:24)
        at com.esparks.fastkindle.service.retry.RetryRequestService$FastClassBySpringCGLIB$50f0bdca.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
           

總結

好啦,咱今天就介紹一下快速地接入spring-retry來實作重試功能。更詳細的功能和實作原理,之後再詳細介紹吧(又給自己挖了個坑)

來源:https://www.cnblogs.com/codeflyer/p/16023257.html