Guava-Retrying自动重试工具

首先说,它是什么?他就是一个自动重试的工具,我们可以定义:什么情况下重试、什么条件下停止、等待多久重试、重试之后依然失败怎么办、并且可以监听整个重试过程。下面是介绍如何使用它。

使用的时候只用引入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));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值