首先说,它是什么?他就是一个自动重试的工具,我们可以定义:什么情况下重试、什么条件下停止、等待多久重试、重试之后依然失败怎么办、并且可以监听整个重试过程。下面是介绍如何使用它。
使用的时候只用引入guava-retrying的依赖即可,在这里我引入了一个hutool工具包,只是为了用它发送http请求,不是必须的。
<!-- guava retrying -->
<dependency>
<groupId>com.github.rholder</groupId>
<artifactId>guava-retrying</artifactId>
<version>2.0.0</version>
</dependency>
<!-- 只是使用了hutool的 HttpUtil,可以使用其他工具代替 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.2.3</version>
</dependency>
下面是一个发短信的例子,代码中自定义了等待策略和重试监听器,guava提供了很多等待策略和停止策略,我在代码注释中有说明,不再赘述。重试的条件可以根据返回结果也可以根据抛出的异常。如果重试结束的时候,依然没有成功,会抛出一个RetryException
异常,可以捕获这个异常执行兜底的方法。话不多说,直接看代码就行了。
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.github.rholder.retry.*;
import com.sixj.demo.dto.SmsBodyDTO;
import com.sixj.demo.vo.SmsSendResultVO;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
/**
* @author sixiaojie
* @date 2020-07-29-11:16
*/
@Slf4j
public class RetrySendSms {
public static SmsSendResultVO retrySendMessage(SmsBodyDTO smsBodyDTO){
Callable<SmsSendResultVO> task = ()->{
String jsonStr = JSONUtil.toJsonStr(smsBodyDTO);
log.info("[发送短信]入参 =>{}", jsonStr);
//调用第三方发短信接口,得到返回值,第一时间记录到log中
String result = HttpUtil.post("http://192.168.173.129/api/msg/SmsApi/sendSms", jsonStr,10*1000);
log.info("[发送短信]请求结果 =>{}",result);
return JSONUtil.toBean(result, SmsSendResultVO.class);
};
/*
等待策略:每次请求间隔1s
.withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECONDS))
等待策略:指数增长,以2s的指数倍增长,最大等待时间是5s
.withWaitStrategy(WaitStrategies.exponentialWait(2000, 5*1000,TimeUnit.MILLISECONDS))
等待策略:递增,默认2s,每次在原基础时间上增加5s
.withWaitStrategy(WaitStrategies.incrementingWait(2000, TimeUnit.MILLISECONDS,5*1000,TimeUnit.MILLISECONDS))
等待策略:随机区间[2s~10s]
.withWaitStrategy(WaitStrategies.randomWait(2000, TimeUnit.MILLISECONDS,10*1000,TimeUnit.MILLISECONDS))
等待策略:斐波拉契数列形式增长,从2s开始增长,最大10s
.withWaitStrategy(WaitStrategies.fibonacciWait(2000, 10*1000,TimeUnit.MILLISECONDS))
等待策略:组合,斐波拉契数列+固定1s+随机
.withWaitStrategy(WaitStrategies.join(
WaitStrategies.fibonacciWait(2000, 10*1000, TimeUnit.MILLISECONDS),
WaitStrategies.fixedWait(1000, TimeUnit.MILLISECONDS),
WaitStrategies.randomWait(2000, TimeUnit.MILLISECONDS,10*1000,TimeUnit.MILLISECONDS)))
*/
/*
停止策略:无限次重试 默认
.withStopStrategy(StopStrategies.neverStop())
停止策略:重试6次
.withStopStrategy(StopStrategies.stopAfterAttempt(6))
停止策略:延迟时间大于10s停止
.withStopStrategy(StopStrategies.stopAfterDelay(10*1000,TimeUnit.MILLISECONDS))
*/
Retryer<SmsSendResultVO> smsRetryer = RetryerBuilder.<SmsSendResultVO>newBuilder()
// 短信返回的状态失败,重试
.retryIfResult(smsSendResultVO->!smsSendResultVO.isSucceed())
// 返回的异常错误类
.retryIfExceptionOfType(Exception.class)
// 等待策略
.withWaitStrategy(new MyWaitStrategy())
// 停止策略 : 尝试请求6次
.withStopStrategy(StopStrategies.stopAfterAttempt(6))
// 监听器
.withRetryListener(new MyRetryListener())
.build();
SmsSendResultVO smsSendResultVO = null;
try {
//执行任务的重试,得到返回结果
smsSendResultVO = smsRetryer.call(task);
// ExecutionException:传入的Callable执行过程中产生了异常,但是我们在构建Retryer对象的时候并没有考虑这种情况,就会抛出这个异常
// RetryException:当所有重试结束后,依然不能成功,那么就会抛这异常
} catch (ExecutionException | RetryException e) {
log.info("===执行兜底方法===");
log.error("重试异常:{}",e.getMessage());
}
return smsSendResultVO;
}
/**
* 自定义重试监听器
*/
private static class MyRetryListener implements RetryListener{
@Override
public <V> void onRetry(Attempt<V> attempt) {
log.info("重试次数 = {}", attempt.getAttemptNumber());
if (attempt.hasException()) {
log.error("重试失败", attempt.getExceptionCause());
}
if (attempt.hasResult()) {
log.info("retry return data is:{}", attempt.getResult());
}
}
}
/**
* 自定义等待策略
* 第一次等待1s
* 第二次等待2s
* 之后的全部等待5s
*/
public static class MyWaitStrategy implements WaitStrategy{
@Override
public long computeSleepTime(Attempt failedAttempt) {
long number = failedAttempt.getAttemptNumber();
if (number == 1){
return 1000;
}
if (number == 2){
return 2*1000;
}
return 5*1000;
}
}
public static void main(String[] args) {
SmsBodyDTO smsBodyDTO = new SmsBodyDTO();
smsBodyDTO.setTemplateCode("sms_416910397024661504");
smsBodyDTO.setMobile("15501176233");
HashMap<String, String> param = new HashMap<>();
param.put("code","123456");
smsBodyDTO.setParameter(param);
SmsSendResultVO smsSendResultVO = retrySendMessage(smsBodyDTO);
System.out.println(JSONUtil.toJsonStr(smsSendResultVO));
}
}