spring-retry重试机制使用

使用背景

在实际工作过程中,因网络波动、服务并发限制等原因造成接口服务调用失败,MQ发送消息失败等,可以采取重试手段,重试机制是常见的一种处理问题的手段。

重试方式有很多,如可以自己通过代码逻辑实现,但不是很优雅。

而spring-retry可以以优雅的方式实现重试:

Retry重试框架,支持AOP切入的方式使用,而且能使用注解;像我们关心的重试次数、重试延迟时间、重试触发条件、重试的回调方法等等都能很轻松结合注解以一种类似配置参数的方式去实现,优雅无疑。

 本文章简述springboot+retry的使用。

1、添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.jia</groupId>
    <artifactId>bill</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>bill</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 重试依赖-->
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId >
            <artifactId>aspectjweaver</artifactId >
        </dependency>
       
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>


    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2、在启动类上加@EnableRetry注解,表示启动重试机制

 1 package com.jia.bill;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.retry.annotation.EnableRetry;
 6 
 7 @EnableRetry
 8 @SpringBootApplication
 9 public class BillApplication {
10 
11     public static void main(String[] args) {
12         SpringApplication.run(BillApplication.class, args);
13     }
14 
15 }

3、定义一个简单的需要重试的接口服务

package com.jia.bill.service;

public interface TestRetryService {
    int retryServiceTest(int code) throws Exception;
}
package com.jia.bill.service.serviceImpl;

import com.jia.bill.service.TestRetryService;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

import java.time.LocalTime;

@Service
public class TestRetryServiceImpl implements TestRetryService {

@Override
@Retryable(value = Exception.class,maxAttempts = 3,backoff = @Backoff(delay = 2000,multiplier = 1.5))
public int retryServiceTest(int code) throws Exception{
System.out.println("retryServiceTest被调用,时间:"+ LocalTime.now());
        if (code==0){
throw new Exception("请求参数为0,出现异常");
// throw new IllegalArgumentException("非法数据");
}
System.out.println("
retryServiceTest被调用,情况对头了!");
        return 200;
}

@Recover
public int recover(Exception e){
System.out.println("重试次数结束后还有异常,回调方法开始执行");
//可调用其余的方法
return 400;
}

}


当调用TestRetryServiceImpl 中的retryServiceTest时,如果抛出了异常Exception,就会被重试,重试3次。

但是如果三次都失败了,并且写了recover方法时,就会执行recover方法了。如果没有此方法会直接抛出异常。

其中注解解释:

@Retryable : 注解方式标记当前方法会使用重试机制

   里面的 value: 重试的触发机制,当遇到Exception异常的时候,触发;
           maxAttempts: 重试的次数(包括第一次调用,也就是说如果设置3次,调用一次后,如果一直失败触发重试,那么还当前方法还会调用2次);
           delay:重试的延迟时间,也就是距离上一次重试方法调用的间隔,单位毫秒
          multiplier: delay间隔时间的倍数,也就是说,第一次重试间隔如果是2000ms,那第二次重试的时候就是2000ms 乘以这个倍数1.5,就是3000ms;

          maxDelay:重试次数之间的最大时间间隔,默认为0,即忽略,如果小于delay的设置,则默认为30000L;
@Recover,也就是用注解方式标记当期方法为回调方法,可以看到传参里面写的是 Exception e,这个是作为回调的接头暗号(重试次数用完了,还是失败,我们抛出这个Exception e通知触发这个回调方法)。

4、controller简单编写

package com.jia.bill.controller;

import com.jia.bill.service.TestRetryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestRetryController {
    @Autowired
    TestRetryService testRetryServiceImpl;

    @GetMapping("/testRetry")
    public String testRetry() throws Exception {
        int code = 0;
        int result = testRetryServiceImpl.retryServiceTest(code);
        return "result:" + result;
    }

}

5、运行结果

retryServiceTest,时间:19:12:24.590
retryServiceTest,时间:19:12:26.591
retryServiceTest,时间:19:12:29.592
重试次数结束后还有异常,回调方法开始执行

 

posted @ 2020-01-20 19:34  happy老家  阅读(1546)  评论(0编辑  收藏  举报