Spring错误异常重试框架guava-retrying

官网:https://github.com/rholder/guava-retryinghtml

Maven:https://mvnrepository.com/artifact/com.github.rholder/guava-retryingjava

下面示例是基于Spring Boot的,可是均可以用于Spring项目。目前最新版是2.0.0。git

集成步骤:github

POM引入:web

        <!-- https://mvnrepository.com/artifact/com.github.rholder/guava-retrying -->
        <dependency>
            <groupId>com.github.rholder</groupId>
            <artifactId>guava-retrying</artifactId>
            <version>2.0.0</version>
        </dependency>

直接一个类里面进行操做,基于匿名内部类实现。spring

package com.jsoft.springboottest.springboottest1.controller;

import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.github.rholder.retry.RetryException;
import com.github.rholder.retry.Retryer;
import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
import com.github.rholder.retry.WaitStrategies;
import com.google.common.base.Predicates;

@RestController
public class TestController {
    
    private static final Logger logger = LoggerFactory.getLogger(TestController.class);
    
    @RequestMapping("/show")
    public String show(){        
        Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder() .retryIfResult(Predicates.<Boolean>isNull())// 设置自定义段元重试源 .retryIfExceptionOfType(Exception.class)// 设置异常重试源 .retryIfRuntimeException()// 设置异常重试源 .withStopStrategy(StopStrategies.stopAfterAttempt(5))// 设置重试5次,一样能够设置重试超时时间 .withWaitStrategy(WaitStrategies.fixedWait(5L, TimeUnit.SECONDS))// 设置每次重试间隔,5秒 .build();  try {
            retryer.call(new Callable<Boolean>() {
                int i = 0;

                @Override
                public Boolean call() throws Exception {
                    i++;
                    logger.info("第{}次执行!", i);
                    // do something
                    if (i<6) {// 模拟错2次
                        logger.info("模拟执行失败!");
                        throw new IOException("异常");
                    }
                    logger.info("模拟执行成功!");
                    return true;                   
                }
            });
        } catch (RetryException e) {
            logger.info("超太重试次数", e);
        } catch (ExecutionException e) {
            logger.info("重试框架异常", e);
        }
        
        return "Hello World";        
    }


}

示例工程:https://github.com/easonjim/5_java_example/tree/master/springboottest/springboottest5segmentfault

详细介绍:springboot

使用场景网络

在平常开发中,咱们常常会遇到须要调用外部服务和接口的场景。外部服务对于调用者来讲通常都是不可靠的,尤为是在网络环境比较差的状况下,网络抖动很容易致使请求超时等异常状况,这时候就须要使用失败重试策略从新调用 API 接口来获取。重试策略在服务治理方面也有很普遍的使用,经过定时检测,来查看服务是否存活(Active)。app

Guava Retrying是一个灵活方便的重试组件,包含了多种的重试策略,并且扩展起来很是容易。

用做者的话来讲:

This is a small extension to Google’s Guava library to allow for the creation of configurable retrying strategies for an arbitrary function call, such as something that talks to a remote service with flaky uptime.

使用Guava-retrying你能够自定义来执行重试,同时也能够监控每次重试的结果和行为,最重要的基于 Guava 风格的重试方式真的很方便。

代码示例

如下会简单列出guava-retrying的使用方式:

若是抛出IOException则重试,若是返回结果为null或者等于2则重试,固定等待时长为300 ms,最多尝试3次; 

Callable<Integer> task = new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        return 2;
    }
};

Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder()
        .retryIfResult(Predicates.<Integer>isNull())
        .retryIfResult(Predicates.equalTo(2))
        .retryIfExceptionOfType(IOException.class)
        .withStopStrategy(StopStrategies.stopAfterAttempt(3))
        .withWaitStrategy(WaitStrategies.fixedWait(300, TimeUnit.MILLISECONDS))
        .build();
try {
    retryer.call(task);
} catch (ExecutionException e) {
    e.printStackTrace();
} catch (RetryException e) {
    e.printStackTrace();
}

出现异常则执行重试,每次任务执行最长执行时间限定为 3 s,重试间隔时间初始为 3 s,最多重试 1 分钟,随着重试次数的增长每次递增 1 s,每次重试失败,打印日志;

@Override
public Integer call() throws Exception {
        return 2;
    }
};

Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder()
        .retryIfException()
        .withStopStrategy(StopStrategies.stopAfterDelay(30,TimeUnit.SECONDS))
        .withWaitStrategy(WaitStrategies.incrementingWait(3, TimeUnit.SECONDS,1,TimeUnit.SECONDS))
        .withAttemptTimeLimiter(AttemptTimeLimiters.<Integer>fixedTimeLimit(3,TimeUnit.SECONDS))
        .withRetryListener(new RetryListener() {
            @Override
            public <V> void onRetry(Attempt<V> attempt) {
                if (attempt.hasException()){
                    attempt.getExceptionCause().printStackTrace();
                }
            }
        })
        .build();
try {
    retryer.call(task);
} catch (ExecutionException e) {
    e.printStackTrace();
} catch (RetryException e) {
    e.printStackTrace();
} 

核心执行逻辑分析:

long startTime = System.nanoTime();
for (int attemptNumber = 1; ; attemptNumber++) {
    Attempt<V> attempt;
    try {
        // 执行成功
        V result = attemptTimeLimiter.call(callable);
        attempt = new ResultAttempt<V>(result, attemptNumber, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime));
    } catch (Throwable t) {
        // 执行失败
        attempt = new ExceptionAttempt<V>(t, attemptNumber, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime));
    }
    // 监听器处理
    for (RetryListener listener : listeners) {
        listener.onRetry(attempt);
    }
    // 是否符合终止策略
    if (!rejectionPredicate.apply(attempt)) {
        return attempt.get();
    }
    // 是否符合中止策略
    if (stopStrategy.shouldStop(attempt)) {
        throw new RetryException(attemptNumber, attempt);
    } else {
        // 计算下次重试间隔时间
        long sleepTime = waitStrategy.computeSleepTime(attempt);
        try {
            blockStrategy.block(sleepTime);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RetryException(attemptNumber, attempt);
        }
    }
}

 依赖引入

<dependency>
      <groupId>com.github.rholder</groupId>
      <artifactId>guava-retrying</artifactId>
      <version>2.0.0</version>
</dependency>

主要接口及策略介绍:

Attempt:一次执行任务;

AttemptTimeLimiter:单次任务执行时间限制(若是单次任务执行超时,则终止执行当前任务);

BlockStrategies:任务阻塞策略(通俗的讲就是当前任务执行完,下次任务还没开始这段时间作什么……),默认策略为:BlockStrategies.THREAD_SLEEP_STRATEGY 也就是调用 Thread.sleep(sleepTime);

RetryException:重试异常;

RetryListener:自定义重试监听器,能够用于异步记录错误日志;

StopStrategy:中止重试策略,提供三种:

  • StopAfterDelayStrategy :设定一个最长容许的执行时间;好比设定最长执行10s,不管任务执行次数,只要重试的时候超出了最长时间,则任务终止,并返回重试异常RetryException
  • NeverStopStrategy :不中止,用于须要一直轮训知道返回指望结果的状况;
  • StopAfterAttemptStrategy :设定最大重试次数,若是超出最大重试次数则中止重试,并返回重试异常;

WaitStrategy:等待时长策略(控制时间间隔),返回结果为下次执行时长:

  • FixedWaitStrategy:固定等待时长策略;
  • RandomWaitStrategy:随机等待时长策略(能够提供一个最小和最大时长,等待时长为其区间随机值)
  • IncrementingWaitStrategy:递增等待时长策略(提供一个初始值和步长,等待时间随重试次数增长而增长)
  • ExponentialWaitStrategy:指数等待时长策略;
  • FibonacciWaitStrategy :Fibonacci 等待时长策略;
  • ExceptionWaitStrategy :异常时长等待策略;
  • CompositeWaitStrategy :复合时长等待策略;

 

参考:

http://blog.csdn.net/aitangyong/article/details/53894997

http://www.javashuo.com/article/p-ggbrwvbr-da.html

http://blog.csdn.net/aitangyong/article/details/53886293

http://baijiahao.baidu.com/s?id=1575327487081031&wfr=spider&for=pc

http://www.cnblogs.com/jianzh5/p/6651799.html

http://lintrip.com/2016/05/27/guava-retry/(以上部份内容转自此篇文章)

相关文章
相关标签/搜索