重試,在項目需求中是非常常見的,例如遇到網絡波動等,要求某個接口或者是方法可以最多/最少調用幾次;
實作重試機制,非得用Retry這個重試架構嗎?那肯定不是,相信很多夥伴手寫一下控制流程的邏輯也可以達到重試的目的。
那麼用Retry的好處是什麼? 簡單來說,就是優雅。
Retry重試架構,支援AOP切入的方式使用,而且能使用注解;想想,重試次數、重試延遲、重試觸發條件、重試的回調方法等等我們都能很輕松結合注解以一種類似配置參數的方式去實作,優雅無疑。
那麼,我們接下來就來一起使用Springboot整合這個Retry重試架構:
首先是pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.mail</groupId>
<artifactId>elegant</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>elegant</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId >
<artifactId>aspectjweaver</artifactId >
<version>1.6.11</version >
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
然後建立一個測試重試的TestRetryService.java:
/**
* @Author : JCccc
* @CreateTime : 2019/8/16
* @Description :
**/
public interface TestRetryService {
int dignifiedTest(int code) throws Exception;
}
然後是TestRetryServiceImpl.java:
import com.mail.elegant.service.TestRetryService;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import java.time.LocalTime;
/**
* @Author : JCccc
* @CreateTime : 2019/8/16
* @Description :
**/
@Service
public class TestRetryServiceImpl implements TestRetryService {
@Override
@Retryable(value = Exception.class,maxAttempts = 3,backoff = @Backoff(delay = 2000,multiplier = 1.5))
public int dignifiedTest(int code) throws Exception{
System.out.println("dignifiedTest被調用,時間:"+LocalTime.now());
if (code==0){
throw new Exception("情況不對頭!");
}
System.out.println("dignifiedTest被調用,情況對頭了!");
return 200;
}
@Recover
public int recover(Exception e){
System.out.println("回調方法執行!!!!");
//記日志到資料庫 或者調用其餘的方法
return 400;
}
}
到這裡,已經整合完畢,最後剩下測試了,在測試前,我們先一起來看看代碼裡面的關鍵資訊的意義:
可以看到代碼裡面,這個方法上面加上了注解 ,
@Retryable(value = Exception.class,maxAttempts = 3,backoff = @Backoff(delay = 2000,multiplier = 1.5,maxDelay=360000L))
@Retryable : 注解方式标記目前方法會使用重試機制
裡面的 value: 重試的觸發機制,當遇到Exception異常的時候,觸發;
maxAttempts: 重試的次數(包括第一次調用,也就是說如果設定3次,調用一次後,如果一直失敗觸發重試,那麼還目前方法還會調用2次);
delay:重試的延遲時間,也就是距離上一次重試方法調用的間隔,機關毫秒
multiplier: delay間隔時間的倍數,也就是說,第一次重試間隔如果是2000ms,那第二次重試的時候就是2000ms 乘以這個倍數1.5,就是3000ms;
maxDelay:重試次數之間的最大時間間隔,預設為0,即忽略,如果小于delay的設定,則預設為30000L;
再來看下面的這個小方法:
@Recover
public int recover(Exception e){
System.out.println("回調方法執行!!!!");
//記日志到資料庫 或者調用其餘的方法
return 400;
}
這個方法用到了@Recover,也就是用注解方式标記當期方法為回調方法,可以看到傳參裡面寫的是 Exception e,這個是作為回調的接頭暗号(重試次數用完了,還是失敗,我們抛出這個Exception e通知觸發這個回調方法)。
PS:該回調方法與重試方法寫在同一個實作類裡面。
然後在啟動類加上開啟重試注解:
@EnableRetry
好了,基本簡單講解完畢,接下來測試看看什麼效果,
建立一個TestController.java ,寫過簡單的測試方法:
import com.mail.elegant.service.TestRetryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author : JCccc
* @CreateTime : 2019/8/16
* @Description :
**/
@RestController
public class TestController {
@Autowired
TestRetryService testRetryServiceImpl;
@GetMapping("/testRetry")
public String testRetry() throws Exception {
int code=0;
int result = testRetryServiceImpl.dignifiedTest(code);
return "result:"+result;
}
}
我們這個測試模拟的場景是,傳值code,一直是0;然後業務方法判斷如果是0,代表業務不通,失敗(網絡波動了或者是等等),然後就是觸發重試,最後如果重試幾次都不成功,然後調用回調方法(可以進行日志記錄或者調用其他業務方法等等)。
我們調用接口看看效果:
接口傳回了400,是回調方法傳回的:
看看控制台輸出情況: