使用Guava retryer优雅的实现接口重试机制

转载自:
使用Guava retrying优雅的实现接口重调机制
Guava retrying:基于 guava 的重试组件

实际项目中,为了考虑网络抖动,加锁并发冲突等场景,我们经常需要对异常操作进行重试。优雅的重试 其实就是将业务处理逻辑和重试逻辑分离。

下面是原文地址:



API 接口调用异常和网络异常在我们日常开发中经常会遇到,这种情况下我们需要先重试几次才能将其标识为错误并在确认错误之后发送异常提醒。

Guava retrying 可以灵活的实现这一功能。Guava retrying在支持重试次数和重试频度控制基础上,能够兼容支持多个异常或者自定义实体对象的重试源定义,让重试功能有更多的灵活性。Guava retrying也是线程安全的,入口调用逻辑采用的是Java.util.concurrent.Callable的call方法。

使用Guava retrying很简单,我们只要做以下几步:

pom文件

<guava-retry.version>2.0.0</guava-retry.version>
<dependency>
      <groupId>com.github.rholder</groupId>
      <artifactId>guava-retrying</artifactId>
      <version>${guava-retry.version}</version>
</dependency>

定义实现Callable接口的方法,以便Guava retrying能够调用

/**
    * @desc 更新可代理报销人接口
    * @author jianzhang11
    * @date 2017/3/31 15:17
    */
private static Callable<Boolean> updateReimAgentsCall = new Callable<Boolean>() {
   @Override
   public Boolean call() throws Exception {
       String url = ConfigureUtil.get(OaConstants.OA_REIM_AGENT);
       String result = HttpMethod.post(url, new ArrayList<BasicNameValuePair>());
       if(StringUtils.isEmpty(result)){
          throw new RemoteException("获取OA可报销代理人接口异常");
       }
       List<OAReimAgents> oaReimAgents = JSON.parseArray(result, OAReimAgents.class);
       if(CollectionUtils.isNotEmpty(oaReimAgents)){
           CacheUtil.put(Constants.REIM_AGENT_KEY,oaReimAgents);
           return true;
       }
       return false;
   }
};

定义Retry对象并设置相关策略

Retryer<Boolean> retryer = RetryerBuilder
                .<Boolean>newBuilder()
                // 抛出runtime异常、checked异常时都会重试,但是抛出error不会重试。
                .retryIfException()
                // 自定义 指定返回值 也需要重试:返回false也需要重试
                .retryIfResult(Predicates.equalTo(false))
                // 重试时间间隔
                .withWaitStrategy(WaitStrategies.fixedWait(10, TimeUnit.SECONDS))
                // 尝试次数
                .withStopStrategy(StopStrategies.stopAfterAttempt(3))
                .build();

    try {
        retryer.call(updateReimAgentsCall);
    } catch (ExecutionException e) {
        //e.printStackTrace();
    } catch (RetryException e) {
        logger.error("更新可代理报销人异常,需要发送提醒邮件");
    }

API说明

上面简单三步就能使用Guava retrying优雅的实现重调方法。接下来对其进行详细说明: 

  • RetryerBuilder是一个factory创建者,可以定制设置重试源且可以支持多个重试源,可以配置重试次数或重试超时时间,以及可以配置等待时间间隔,创建重试者Retryer实例。
  • RetryerBuilder的重试源支持Exception异常对象和自定义对象,通过retryIfException 和 retryIfResult设置,同时支持多个且能互相兼容。
  • retryIfException,抛出runtime异常、checked异常时都会重试,但是抛出error不会重试。
  • retryIfRuntimeException只会在抛runtime异常的时候才重试,checked异常和error都不重试。
  • retryIfExceptionOfType允许我们只在发生特定异常的时候才重试,比如 NullPointerException 和 IllegalStateException 都属于runtime异常,也包括自定义的error
    如:

    .retryIfExceptionOfType(Error.class)// 只在抛出error重试

当然我们还可以在只有出现指定的异常的时候才重试,如:

.retryIfExceptionOfType(IllegalStateException.class)
.retryIfExceptionOfType(NullPointerException.class)  

或者通过Predicate实现:

.retryIfException(Predicates.or(Predicates.instanceOf(NullPointerException.class),
                Predicates.instanceOf(IllegalStateException.class))) 

retryIfResult可以指定你的Callable方法在返回值的时候进行重试,如:

// 返回false重试
.retryIfResult(Predicates.equalTo(false))
//以_error结尾才重试
.retryIfResult(Predicates.containsPattern("_error$"))  
  • RetryListener:自定义重试监听器,可以用于异步记录错误日志,具体实例如下:

当发生重试之后,假如我们需要做一些额外的处理动作,比如发个告警邮件啥的,那么可以使用RetryListener。每次重试之后,guava-retrying会自动回调我们注册的监听。可以注册多个RetryListener,会按照注册顺序依次调用。

import com.github.rholder.retry.Attempt;
import com.github.rholder.retry.RetryListener;
import java.util.concurrent.ExecutionException;  

public class MyRetryListener<Boolean> implements RetryListener {  

    @Override
    public <Boolean> void onRetry(Attempt<Boolean> attempt) {  

        // 第几次重试,(注意:第一次重试其实是第一次调用)
        System.out.print("[retry]time=" + attempt.getAttemptNumber());  

        // 距离第一次重试的延迟
        System.out.print(",delay=" + attempt.getDelaySinceFirstAttempt());  

        // 重试结果: 是异常终止, 还是正常返回
        System.out.print(",hasException=" + attempt.hasException());
        System.out.print(",hasResult=" + attempt.hasResult());  

        // 是什么原因导致异常
        if (attempt.hasException()) {
            System.out.print(",causeBy=" + attempt.getExceptionCause().toString());
        } else {
            // 正常返回时的结果
            System.out.print(",result=" + attempt.getResult());
        }  

        // bad practice: 增加了额外的异常处理代码
        try {
            Boolean result = attempt.get();
            System.out.print(",rude get=" + result);
        } catch (ExecutionException e) {
            System.err.println("this attempt produce exception." + e.getCause().toString());
        }  

        System.out.println();
    }
} 

接下来在Retry对象中指定监听:

.withRetryListener(new MyRetryListener<>())  

效果如下:

  • StopStrategy:停止重试策略,提供三种:
  1. StopAfterDelayStrategy 设定一个最长允许的执行时间;比如设定最长执行10s,无论任务执行次数,只要重试的时候超出了最长时间,则任务终止,并返回重试异常RetryException。
  2. NeverStopStrategy 不停止,用于需要一直轮训知道返回期望结果的情况。
  3. StopAfterAttemptStrategy 设定最大重试次数,如果超出最大重试次数则停止重试,并返回重试异常。
  • WaitStrategy:等待时长策略(控制时间间隔),返回结果为下次执行时长:
  1. FixedWaitStrategy 固定等待时长策略。
  2. RandomWaitStrategy 随机等待时长策略(可以提供一个最小和最大时长,等待时长为其区间随机值)。
  3. IncrementingWaitStrategy 递增等待时长策略(提供一个初始值和步长,等待时间随重试次数增加而增加)。
  4. ExponentialWaitStrategy 指数等待时长策略。
  5. FibonacciWaitStrategy Fibonacci 等待时长策略。
  6. ExceptionWaitStrategy 异常时长等待策略。
  7. CompositeWaitStrategy 复合时长等待策略。

原文地址:https://www.cnblogs.com/boothsun/p/8330212.html

时间: 2024-08-29 14:04:26

使用Guava retryer优雅的实现接口重试机制的相关文章

guava的重试机制guava-retrying使用

1,添加maven依赖 <dependency> <groupId>com.github.rholder</groupId> <artifactId>guava-retrying</artifactId> <version>2.0.0</version> </dependency> 2,定义重试机制 Retryer<CMSResultDTO> smsRetryer = RetryerBuilder.

SpringAOP&amp;&amp;定时任务简单实现接口访问入表和定时任务接口重试

# SpringAOP&&定时任务简单实现接口访问入表和定时任务接口重试 # Spring aop Spring 定时任务 代理模式深化 1.表设计 2.Aop主要代码 @Aspect @Component public class AopUtils implements Ordered { //当前 private static final Logger logger = LoggerFactory.getLogger(AopUtils.class); public static Str

spring-retry重试机制使用

使用背景 在实际工作过程中,因网络波动.服务并发限制等原因造成接口服务调用失败,MQ发送消息失败等,可以采取重试手段,重试机制是常见的一种处理问题的手段. 重试方式有很多,如可以自己通过代码逻辑实现,但不是很优雅. 而spring-retry可以以优雅的方式实现重试: Retry重试框架,支持AOP切入的方式使用,而且能使用注解:像我们关心的重试次数.重试延迟时间.重试触发条件.重试的回调方法等等都能很轻松结合注解以一种类似配置参数的方式去实现,优雅无疑. 本文章简述springboot+ret

Volley超时重试机制详解

Volley超时重试机制 基础用法 Volley为开发者提供了可配置的超时重试机制,我们在使用时只需要为我们的Request设置自定义的RetryPolicy即可. 参考设置代码如下: int DEFAULT_TIMEOUT_MS = 10000; int DEFAULT_MAX_RETRIES = 3; StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<S

SpringCloud | FeignClient和Ribbon重试机制区别与联系

在spring cloud体系项目中,引入的重试机制保证了高可用的同时,也会带来一些其它的问题,如幂等操作或一些没必要的重试. 今天就来分别分析一下 FeignClient 和 Ribbon 重试机制的实现原理和区别,主要分为三点: 1)FeignClient重试机制分析 2)Ribbon重试机制分析 3)FeignClient和Ribbon重试机制的区别于联系 1)FeignClient 重试机制分析: FeignClient 重试机制的实现原理相对简单.首先看一下feignClient处理请

一文详解Spring Cloud Feign重试机制

前言 Feign组件默认使用Ribbon的重试机制并增加了根据状态码判断重试机制,默认情况下是不启用的.Feign使用的是Spring Retry组件,需要引入依赖才能启用. 一.POM引入Spring Retry <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> 二

RocketMQ重试机制和消息幂

一.重试机制 1.由于MQ经常处于复杂的分布式系统中,考虑网络波动,服务宕机,程序异常因素,很有可能出现消息发送或者消费失败的问题.因此,消息的重试就是所有MQ中间件必须考虑到的一个关键点.如果没有消息重试,就可能产生消息丢失的问题,可能对系统产生很大的影响.所以,秉承宁可多发消息,也不可丢失消息的原则,大部分MQ都对消息重试提供了很好的支持. 2.RocketMQ为了使用者封装了消息重试的处理流程,无需开发人员手动处理.RocketMQ支持了生产端和消费端两类重试机制. 模拟异常 Consum

接口_ _接口回调机制

在Android中到处可见接口回调机制,尤其是UI事件处理方面,本文给大家介绍android接口回调机制,涉及到android接口回调相关知识,对本文感兴趣的朋友可以参考下本篇文章 在使用接口回调的时候发现了一个经常犯的错误,就是回调函数里面的实现有可能是用多线程或者是异步任务去做的,这就会导致我们期望函数回调完毕去返回一个主函数的结果,实际发现是行不通的,因为如果回调是多线程的话你是无法和主函数同步的,也就是返回的数据是错误的,这是非常隐秘的一个错误.那有什么好的方法去实现数据的线性传递呢?先

Rocket重试机制,消息模式,刷盘方式

一.Consumer 批量消费(推模式) 可以通过 consumer.setConsumeMessageBatchMaxSize(10);//每次拉取10条 这里需要分为2种情况 Consumer端先启动 Consumer端后启动.   正常情况下:应该是Consumer需要先启动 注意:如果broker采用推模式的话,consumer先启动,会一条一条消息的消费,consumer后启动会才用批量消费 Consumer端先启动 1.Consumer.java package quickstart