需求:夜间定时任务比较多,偶尔遇到请求超时的情况,也可能是网络波动,总之需要做请求失败重试。
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包裹,要在方法上抛出异常,不然不会触发