Retry重试机制是什么意思

2023-06-13

这篇文章主要介绍“Retry重试机制是什么意思”,在日常操作中,相信很多人在Retry重试机制是什么意思问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Retry重试机制是什么意思”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

1.前言

在项目开发中,有的时候,总是避免不了调用第三方服务接口,有的时候可能因为网络等情况的因素,我们需要重试几次才能调用成功,所以就需要一重试机制来保证接口的正常访问。

2.guava-retrying

这是谷歌guava提供的工具包,我们可以在项目中引入相应的包,很轻松实现重试访问。guava-retrying中大量运用的策略模式,可以自定义各种执行重试策略。下面是简单实用步骤。

①. pom引入
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>19.0</version>
</dependency>
<dependency>
    <groupId>com.github.rholder</groupId>
    <artifactId>guava-retrying</artifactId>
    <version>2.0.0</version>
</dependency>
②. 基础业务代码

我这里实现一个对接口的简单访问,以此来模拟访问第三方接口。

@GetMapping("/retrytest")
public Map<String, Object> retrytest(){
    Map<String, Object> resultMap = Maps.newHashMap();
    resultMap.put("id", 1001L);
    resultMap.put("msg", "测试");
    Map<String, Object> tokenMap = Maps.newHashMap();
    tokenMap.put("token", UUID.randomUUID().toString());
    resultMap.put("data", tokenMap);
    return resultMap;
}
③.Retryer Bean
  1. Retryer中定义重试的各种策略,在执行call方法的时候,会将这些重试策略一一使用。

  2. RetryListener是重试监听器,可以监听每次重试的过程。

  3. BlockStrategy是自定义阻塞策略。

@Bean
public Retryer<String> retry(){
    RetryListener retryListener = new RetryListener() {
        @Override
        public <V> void onRetry(Attempt<V> attempt) {
            try {
                if(attempt.hasException()){
                    log.error("---"+ Arrays.toString(attempt.getExceptionCause().getStackTrace()));
                }else {
                    log.info("---"+ attempt.get().toString());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };
    BlockStrategy blockStrategy = new BlockStrategy() {
        @Override
        public void block(long sleepTime) throws InterruptedException {
            LocalDateTime startTime = LocalDateTime.now();
            long start = System.currentTimeMillis();
            long end = start;
            log.info("[SpinBlockStrategy]...begin wait.");
            while (end - start <= sleepTime) {
                end = System.currentTimeMillis();
            }
            //使用Java8新增的Duration计算时间间隔
            Duration duration = Duration.between(startTime, LocalDateTime.now());
            log.info("[SpinBlockStrategy]...end wait.duration={}", duration.toMillis());
        }
    };
    Retryer<String> retryer = RetryerBuilder.<String>newBuilder()
            //retryIf 重试条件
            .retryIfException()
            .retryIfRuntimeException()
            .retryIfExceptionOfType(Exception.class)
            .retryIfException(Predicates.equalTo(new Exception()))
            //等待策略:每次请求间隔1s
            //  .withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECONDS))
            //停止策略 : 尝试请求3次
            .withStopStrategy(StopStrategies.stopAfterAttempt(3))
            //时间限制 : 某次请求不得超过2s , 类似: TimeLimiter timeLimiter = new SimpleTimeLimiter();
            .withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(2, TimeUnit.SECONDS))
            .withRetryListener(retryListener)
            .withBlockStrategy(blockStrategy)
            .build();
    return retryer;
}
④.Callable实现

编写retryer调用call方法的具体实现逻辑。

@Slf4j
public class RetryCallable implements Callable<String> {

    private RestTemplate restTemplate;

    public RetryCallable(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    int times = 1;
    @Override
    public String call() {
        log.info("call times={}", times++);
        log.info("请求时间: {}", LocalDateTime.now());
        //对远程地址的访问
        return Objects.requireNonNull(
                restTemplate.getForObject("http://localhost:8080/retrytest", Object.class)).toString();
    }
}
⑤. 重试器 call调用
@GetMapping("/hello")
public Object hello(@RequestParam(required = false) String token){
    log.info("hello "+ token);
    Map result = null;
    String msg = "";
    try {
        //定义请求实现 利用重试器调用请求
        String callResult = retryer.call(new RetryCallable(restTemplate));
        result = new Gson().fromJson(callResult, Map.class);
    } catch (Exception e) {
        e.printStackTrace();
        msg = e.getMessage();
        log.warn("请求失败:{}",e.getMessage());
    }
    HashMap<String, Object> resultData = Maps.newHashMap();
    resultData.put("data",result);
    resultData.put("token",token);
    resultData.put("msg",msg);
    return resultData;
}

调用接口测试

2019-10-12 13:46:23.863  INFO 68012 --- [nio-8080-exec-1] com.wj.retry.controller.HelloController  : hello f5b78e95-87f7-435e-b9be-04bcb88ad056
2019-10-12 13:46:23.865  INFO 68012 --- [pool-1-thread-1] com.wj.retry.controller.RetryCallable    : call times=1
2019-10-12 13:46:23.875  INFO 68012 --- [pool-1-thread-1] com.wj.retry.controller.RetryCallable    : 请求时间: 2019-10-12T13:46:23.874
2019-10-12 13:46:24.068  INFO 68012 --- [nio-8080-exec-1] com.wj.retry.RetryApplication            : ---{msg=测试, data={token=eacb4a99-9ef9-4581-b8e5-28fdabac1c52}, id=1001}

若失败会调用多次(按照重试器中定义的策略)并,抛出异常

3.spring-retry

上面的实现会写相对较多的代码,若使用spring-retry则相对简单多了,可以基于注解实现

①. pom配置
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>
②. 注解配置重试策略

@Retryable的参数说明:

  1. value:抛出指定异常才会重试

  2. include:和value一样,默认为空,当exclude也为空时,默认所以异常

  3. exclude:指定不处理的异常

  4. maxAttempts:最大重试次数,默认3次

  5. backoff:重试等待策略,默认使用@Backoff,@Backoff的value默认为1000L,我们设置为2000L;multiplier(指定延迟倍数)默认为0,表示固定暂停1秒后进行重试,如果把multiplier设置为1.5,则第一次重试为2秒,第二次为3秒,第三次为4.5秒。

 @Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000L, multiplier = 1.5))
public Object retry(@RequestParam(required = false) String token) {
    String msg = "";
    log.info("springretry");
    log.info("请求时间: {}", LocalDateTime.now());
    String resultStr = Objects.requireNonNull(
            restTemplate.getForObject("http://localhost:8080/retrytest", Object.class)).toString();
    Map result = new Gson().fromJson(resultStr, Map.class);
    HashMap<String, Object> resultData = Maps.newHashMap();
    resultData.put("data",result);
    resultData.put("token",token);
    resultData.put("msg",msg);
    return resultData;
}

最后在启动类上加上@EnableRetry注解开启重试机制。ok,就这样两个步骤就完成了

4.举例重试机制的运用

除了上面调用第三方服务接口可能会用到重试机制,在微服务项目中,服务之间的通信,重试机制可以说是随处可见。

在springcloud中Ribbon,feign,Hystrix等组件都可以自己配置重试机制,来达到提高能正常通信的成功率。

此外,在各种消息中间件中也都有重试机制的体现,例如kafka,消息发送失败可以重新发送,消息消费失败了,也可以配置重试机制,可以最大程度达到消息的不丢失。

5.目标服务要考虑的问题

可以考虑这么一个事情,若目标逻辑执行时间过长,超出了重试的等待时间,客户端就要发起重试,那么服务端就会出现重复调用执行的问题,所以,有重试机制就要考虑幂等性的问题。

到此,关于“Retry重试机制是什么意思”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注本站网站,小编会继续努力为大家带来更多实用的文章!

《Retry重试机制是什么意思.doc》

下载本文的Word格式文档,以方便收藏与打印。