需求:夜間定時任務比較多,偶爾遇到請求超時的情況,也可能是網絡波動,總之需要做請求失敗重試。
1,引入spring-retry註解
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.2.RELEASE</version>
</dependency>
2,啓動類開啓重試註解掃描
@SpringBootApplication
@EnableRetry
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
3,業務類添加@Retryable
package com.example.demo.service;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.remoting.RemoteAccessException;
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.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Random;
@Service
public class Testservice {
@Retryable(value = {RemoteAccessException.class} ,backoff = @Backoff(delay = 3000l, multiplier = 0))
public void main(){
System.out.println("-----------發起請求");
// String url = "http://127.0.0.1:8100/api/materials-product/updateRedis";
// String res = requestByGetMethod(url);
// JSONObject jo = JSONObject.parseObject(res);
// int a = Integer.parseInt(jo.get("data").toString());
//生成0~10之間的隨機數
Random r = new Random();
int a = r.nextInt(5);
if (a > 2) {
System.out.println("請求超時");
throw new RemoteAccessException("請求超時或中斷");
} else {
System.out.println("---------請求成功");
}
}
@Recover()
public void backroll(RemoteAccessException e){
System.out.println("----->重試失敗執行回調" + e.toString());
}
//真實遠程連接
public static String requestByGetMethod(String url) {
CloseableHttpClient httpClient = HttpClients.createDefault();
StringBuilder entityStringBuilder = null;
try {
HttpPost get = new HttpPost(url);
CloseableHttpResponse httpResponse = null;
httpResponse = httpClient.execute(get);
try {
HttpEntity entity = httpResponse.getEntity();
entityStringBuilder = new StringBuilder();
if (null != entity) {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent(), "UTF-8"), 8 * 1024);
String line = null;
while ((line = bufferedReader.readLine()) != null) {
entityStringBuilder.append(line);
}
}
} finally {
httpResponse.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (httpClient != null) {
httpClient.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return entityStringBuilder.toString();
}
}
4,運行測試
因爲請求超時和網絡中斷情況不方便模擬,以上代碼先通過隨機數本地模擬請求失敗的情況。 需要寫一個簡單的控制器不斷髮起請求。
@RestController
public class testController {
@Autowired
private Testservice testservice;
@PostMapping("test")
public String updateRedis() {
testservice.main();
return "已發送";
}
}
4,運行測試
---------請求成功
-----------發起請求
---------請求成功
-----------發起請求
---------請求成功
-----------發起請求
請求超時
-----------發起請求
請求超時
-----------發起請求
請求超時
----->重試失敗執行回調org.springframework.remoting.RemoteAccessException: 請求超時或中斷
5,註解詳解
1.@Retryable參數的說明
- value:拋出指定異常纔會重試
- include:和value一樣,默認爲空,當exclude也爲空時,默認所以異常
- exclude:指定不處理的異常
- maxAttempts:最大重試次數,默認3次
- backoff:重試等待策略,默認使用@Backoff,@Backoff的value默認爲1000L,我們設置爲2000L;multiplier(指定延遲倍數)默認爲0,表示固定暫停1秒後進行重試,如果把multiplier設置爲1.5,則第一次重試爲2秒,第二次爲3秒,第三次爲4.5秒。
2.@Recover註解
當重試耗盡時,RetryOperations可以將控制傳遞給另一個回調,用於@Retryable重試失敗後處理方法,此方法裏的異常一定要是@Retryable方法裏拋出的異常,否則不會調用這個方法。
3.注意
使用了@Retryable的方法在本類中使用沒有效果,只有在其他類中使用@Autowired注入或者@Bean才能生效。
要觸發@Recover方法,@Retryable方法不能存在返回值,只能是void
使用@Retryable的方法裏不能使用try catch包裹,要在方法上拋出異常,不然不會觸發