针对Spring的Spring Retry 我发现了这样一个大家都不知道的技巧!

外部服务对于调用者来说一般都是不可靠的,尤其是在网络环境比较差的情况下,网络抖动很容易导致请求超时等异常情况,这时候就需要使用失败重试策略重新调用 API 接口来获取。重试策略在服务治理方面也有很广泛的使用,通过定时检测,来查看服务是否存活。

Spring异常重试框架Spring Retry

Spring Retry支持集成到Spring或者Spring Boot项目中,而它支持AOP的切面注入写法,所以在引入时必须引入aspectjweaver.jar包。

1.引入maven依赖

 <dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.1.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.6</version>
</dependency>

2.添加@Retryable和@Recover注解

@Retryable注解,被注解的方法发生异常时会重试

  • value:指定发生的异常进行重试
  • include:和value一样,默认空,当exclude也为空时,所有异常都重试
  • exclude:指定异常不重试,默认空,当include也为空时,所有异常都重试
  • maxAttemps:重试次数,默认3
  • backoff:重试补偿机制,默认没有

@Backoff注解

  • delay:指定延迟后重试
    multiplier:指定延迟的倍数,比如delay=5000l,multiplier=2时,第一次重试为5秒后,第二次为10秒,第三次为20秒

@Recover注解:
当重试到达指定次数时,被注解的方法将被回调,可以在该方法中进行日志处理。需要注意的是发生的异常和入参类型一致时才会回调。

@Service
public class RemoteService {
   

@Retryable(value = {
   Exception.class}, maxAttempts = 5, backoff = @Backoff(delay = 5000L, multiplier = 1))
public void call() {
   
    System.out.println(LocalDateTime.now() + ": do something...");
    throw new RuntimeException(LocalDateTime.now() + ": 运行调用异常");
}

@Recover
public void recover(Exception e) {
   
    System.out.println(e.getMessage());
}

3.启用重试功能

启动类上面添加@EnableRetry注解,启用重试功能,或者在使用retry的service上面添加也可以,或者Configuration配置类上面。
建议所有的Enable配置加在启动类上,可以清晰的统一管理使用的功能。

@SpringBootApplication
@EnableRetry
public class App {
   
    public static void main(String[] args) throws Exception {
   
        ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
        System.out.println("Start app success.");
        RemoteService bean = context.getBean(RemoteService.class);
        bean.call();
    }
}

4.启动服务,运行测试

通过在启动类Context调用服务看到如下打印:
2019-03-09T15:22:12.781: do something...
2019-03-09T15:22:17.808: do something...
2019-03-09T15:22:22.835: do something...
2019-03-09T15:22:27.861: do something...
2019-03-09T15:22:32.887: do something...
2019-03-09T15:22:32.887: 运行调用异常

基于guava的重试组件Guava-Retryer
直接看组件作者对此组件的介绍:
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.(这是对Google的guava库的一个小扩展,允许为任意函数调用创建可配置的重试策略,例如与运行时间不稳定的远程服务对话的策略。)
第一步引入maven坐标:

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

1.其主要接口及策略介绍

  • 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 :复合时长等待策略;

2.根据结果判断是否重试

使用场景:如果返回值决定是否要重试。
重试接口:

private static Callable<String> callableWithResult() {
    return new Callable<String>() {
        int counter = 0;

        public String call() throws Exception {
            counter++;
            System.out.println(LocalDateTime.now() + ": do something... " + counter);
            if (counter < 5) {
                return "james";
            }
            return "kobe";
        }
    };
}

测试:

public static void main(String[] args) {
   
    Retryer<String> retry = RetryerBuilder.<String>newBuilder()
            .retryIfResult(result -> !result.contains("kobe")).build();
    retry.call(callableWithResult());
}

输出:

2019-03-09T15:40:23.706: do something... 1
2019-03-09T15:40:23.710: do something... 2
2019-03-09T15:40:23.711: do something... 3
2019-03-09T15:40:23.711: do something... 4
2019-03-09T15:40:23.711: do something... 5

3.根据异常判断是否重试

使用场景:根据抛出异常类型判断是否执行重试。
重试接口:

private static Callable<String> callableWithResult(
  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 33
    评论
当然可以!下面是一个简单的Spring Retry的示例代码: 首先,您需要在您的项目中添加Spring Retry的依赖。如果您使用Maven,可以在pom.xml文件中添加以下依赖项: ```xml <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> <version>1.3.1</version> </dependency> ``` 接下来,您可以创建一个包含重试逻辑的方法: ```java import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Retryable; import org.springframework.stereotype.Component; @Component public class MyService { @Retryable(maxAttempts = 3, backoff = @Backoff(delay = 1000)) public void performOperation() throws Exception { // 模拟一个可能会失败的操作 if (Math.random() < 0.5) { throw new Exception("操作失败"); } else { System.out.println("操作成功"); } } } ``` 在上述示例中,我们使用了`@Retryable`注解来标记`performOperation`方法,表示该方法可以进行重试。`maxAttempts`属性指定最大重试次数,`backoff`属性指定重试间隔(这里设置为1秒)。 最后,您可以在其他类中调用`MyService`的方法进行重试: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MyApp { @Autowired private MyService myService; public static void main(String[] args) { SpringApplication.run(MyApp.class, args); } public void run() throws Exception { myService.performOperation(); } } ``` 在上述示例中,我们通过`@Autowired`注解将`MyService`注入到`MyApp`类中,并在`run`方法中调用`performOperation`方法进行重试。 这只是一个简单的示例,您可以根据自己的需求和业务逻辑进行更复杂的重试配置和操作。希望对您有所帮助!
评论 33
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java架构没有996

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值