一个注解搞定失败重试,优雅又方便,超级简单

一个注解搞定失败重试,优雅又方便,超级简单

 

你好呀,我是小羊。

在我们平时工作中,有时候会有一些需要重试的场景,大家一般怎么处理这种需求呢?硬编码还是递归?今天介绍一种非常简单的处理方式,一起来看看吧。

1.递归实现重试需求

业务需求中比如调用一个外部接口,有可能调用成功失败,毕竟实际生产环境会有很多不确定因素,比如网络延时了,或者数据库查询慢了,或者下游接口熔断了等等。如果调用失败了,需要我们这边重试3次,如果重试成功了,就继续后面的逻辑。

一个注解搞定失败重试,优雅又方便,超级简单

常见失败重试场景


实现这段逻辑很简单,比如下面使用递归的方式来做这个功能。

@Service
public class TestService {

    @Autowired
    private Test1Service test1Service;

    public void postData(int retryNum){
        boolean result = test1Service.add();
        if (!result && retryNum<3) {
            this.postData(retryNum+1);
        }
    }
}

上面的代码实现起来也很简单,先调用一下test1Service.add() 方法,判断一下结果和重试次数,如果失败了并且重试次数小于3,就继续调用 test1Service.add() 方法。并且把重试次数加一。功能基本上没有什么问题,后面需求变更,如果需要在重试的时候加上一些等待时间。你也想到了方案,加一个Thread.sleep() 即可。

@Service
public class TestService {

    @Autowired
    private Test1Service test1Service;

    public void postData(int retryNum) throws InterruptedException {
        boolean result = test1Service.add();
        if (!result && retryNum<3) {
            Thread.sleep(500);
            this.postData(retryNum+1);
        }
    }
}

如果后面还需要加需求,每次重试的时间都不一样。实现也没有问题,加一个随机数就行

@Service
public class TestService {

    @Autowired
    private Test1Service test1Service;

    public void postData(int retryNum) throws InterruptedException {
        boolean result = test1Service.add();
        if (!result && retryNum<3) {
            Random random = new Random();
            Thread.sleep(random.nextInt(100));
            this.postData(retryNum+1);
        }
    }
}

递归虽然可以实现重试功能,但是代码有点啰嗦,有很多重试的逻辑,也不太方便阅读。

2.@Retryable注解简介

@Retryable是Spring Framework中的一个注解,用于实现方法的重试机制。当一个方法使用@Retryable注解标记时,Spring会在方法执行时捕获指定的异常,并在发生异常时尝试重新执行该方法,直到满足指定的重试条件为止。

@Retryable注解提供了一些属性,可以用来指定重试的条件,包括重试次数、重试的延迟时间、重试的异常类型等。通过这些属性,可以灵活地配置方法的重试行为。

@Retryable注解通常与@EnableRetry注解一起使用,@EnableRetry注解用于启用Spring的重试机制。

3.@Retryable使用方法

添加maven 依赖

        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>

开启重试,启动类增加@EnableRetry 注解

@SpringBootApplication
@EnableRetry
public class SpringbootRetryableApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootRetryableApplication.class, args);
    }

}

在需要重试的方法增加 @Retryable 即可实现重试

    @Retryable()
    public void testFailRetry(String test) throws IOException {
        log.info("测试4"+test);
        throw new IllegalArgumentException("test4");
    }

然后调用这个方法,抛出异常就可以让方法重试了。

一个注解搞定失败重试,优雅又方便,超级简单

默认是所有异常都会重试的,我们可以指定或者忽略一些异常重试。

@Retryable(include= IllegalArgumentException.class)

或者忽略一些异常

@Retryable(exclude= NullPointerException.class)

@Retryable 注解有以下常用参数:

value:指定需要进行重试的异常类型。默认情况下,会对所有异常进行重试。你可以通过设置 value 参数来指定需要重试的异常类型,例如 @Retryable(value = {CustomException.class})。

maxAttempts:指定最大重试次数。默认值为 3。你可以通过设置 maxAttempts 参数来指定重试的最大次数,例如 @Retryable(maxAttempts = 5)。

backoff:指定重试的退避策略。默认情况下,退避策略是固定间隔。你可以通过设置 backoff 参数来指定不同的退避策略。例如,@Retryable(backoff = @Backoff(delay = 1000, multiplier = 2.0)) 表示使用指数退避策略,初始延迟为 1000 毫秒,乘数为 2.0。

include 和 exclude:用于指定需要包含或排除的异常类型。include 参数用于指定需要重试的异常类型,exclude 参数用于指定不需要重试的异常类型。例如,@Retryable(include = {CustomException.class}, exclude = {IllegalArgumentException.class}) 表示只对 CustomException 异常进行重试,而不对 IllegalArgumentException 异常进行重试。

比如下面这个配置,空指针异常进行重试,并且重试5次,以100ms 为基数,随机间隔重试。

@Retryable(include= NullPointerException.class, maxAttempts = 5,backoff = @Backoff(delay = 100, multiplier = 2.0,random = true))

对于重试之后还是失败的情况,可以使用  @Recover 注解捕获失败,做一些兜底的操作

@Slf4j
public class ParentService {

    @Recover
    public void save(Exception e,String test) throws Exception {
        log.info("回调"+test+this.getClass());
        if (this.getClass().equals(TestService.class)){
            log.info("TestService");
        } else if(this.getClass().equals(Test2Service.class)){
            log.info("Test2Service");
        }else if(this.getClass().equals(Test4Service.class)){
            log.info("Test4Service");
        }

        throw e;
    }
}

4.@Retryable 注意事项

需要注意一些情况下 @Retryable 会失效:

  1. 重试方法不是公共方法:@Retryable注解只能应用于公共方法(public method),如果你尝试在私有方法或者受保护的方法上使用@Retryable注解,重试机制将不会生效。

  2. 重试方法是静态方法:@Retryable注解也不支持静态方法,因为它是基于代理对象来实现的,而静态方法无法被代理。

  3. 重试方法与调用它的非重试方法在同一个类中。

  4. 代码中捕获了异常,没有抛出。

  5. @Retryable 是通过aop 实现的,如果进入类的第一个方法没有加@Retryable 注解,后面的方法加了注解,也是会失效的。

public void testFailRetry(String test) throws IOException {
        testFailRetry1(test);
    }

    @Retryable()
    public void testFailRetry1(String test) throws IOException {
        log.info("测试5"+test);
        throw new IllegalArgumentException("test5");
    }

5.总结

@Retryable注解提供了方法级别的重试机制,它的使用有以下几个优点:

  1. 提高系统可靠性:当方法执行失败时,使用重试机制可以在一定程度上提高系统的可靠性。有些临时性的故障可能只是偶发的,通过重试可以让系统在故障恢复后自动继续执行,而无需手动干预。

  2. 减少对外部服务的影响:当应用程序依赖于外部服务时,外部服务的故障可能会导致应用程序的部分功能失效。通过重试机制,可以在外部服务恢复正常后自动重试受影响的方法,减少对外部服务故障的影响。

  3. 降低错误处理复杂度:有些错误可能是临时性的,例如网络超时、数据库连接异常等。通过重试机制,可以避免编写复杂的错误处理逻辑,而是让系统自动尝试恢复。

  4. 避免过度重试:通过@Retryable注解的属性配置,可以灵活地控制重试的次数、延迟时间等,避免过度重试导致系统负载过重或者对外部服务造成过大压力。

6.代码

https://gitee.com/yangzheng1/springboot-retryable

7.往期精彩推荐


  国内免费使用chatGPT的方法

数据库实时同步方案

推荐一款开源的大屏报表项目

欢迎点赞转发给我鼓励~

一个注解搞定失败重试,优雅又方便,超级简单一个注解搞定失败重试,优雅又方便,超级简单关注我获取更多IT分享 一个注解搞定失败重试,优雅又方便,超级简单一个注解搞定失败重试,优雅又方便,超级简单

原文始发于微信公众号(小羊架构):一个注解搞定失败重试,优雅又方便,超级简单

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/260082.html

(0)
Java朝阳的头像Java朝阳

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!