Spring Retry 是从 Spring batch 中独立出来的一个功能,主要实现了重试和熔断。对于那些重试后不会改变结果,毫无意义的操作,不建议使用重试,比如:对参数的合法性检查,那些重试后可能会改变结果的,最普通的比如:网络连接中断后的重试,调用远程服务超时,这类的就可以使用重试。

接下来引用一个例子来说明 。

第一步,引入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>

第二步 ,添加@Retryable和@Recover注解

import org.springframework.remoting.RemoteAccessException;
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.text.SimpleDateFormat;
import java.util.Date;

@Service
public class RemoteService {

    int index = 1;
    @Retryable(value= {RemoteAccessException.class},maxAttempts = 3,backoff = @Backoff(delay = 5000l,multiplier = 1))
    public void call() throws Exception {
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
        System.out.println("我是第 " + index + " 次调用callServiceInNet 方法...." + sf.format(new Date()));
        index++;
        throw new RemoteAccessException("RPC调用异常");
    }

    @Recover
    public void recover(RemoteAccessException e){
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
        System.err.println(" 我是 recover 方法..." + sf.format(new Date()));
        System.err.println(e.getMessage());
    }
}

注解解析:

@Retryable :被注解的方法再发生异常时,会进行重试;

        value:指定触发重试的异常,只有方法产生符合value指定的异常,才会进行重试,默认为空,表示所有;

        include: 和上述的 value 一样;

        exclude:指定出现某些异常后,不进行重试,也就是例外情况;

        maxAttempts:指定重试的次数,默认为3;

        backoff:重试补偿机制,默认没有;

@Backoff 注解:

最后一位 l ,是 long 型,不是数值 1;

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

@Recover 注解:

需要注意的是发生的异常和入参类型一致时才会回调。

 

第三步,SpringBoot 启动容器测试

package com.demo;

import com.hewb.demo.service.RemoteService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.retry.annotation.EnableRetry;

@SpringBootApplication
@EnableRetry
public class Application {

	public static void main(String[] args) throws Exception {
//		SpringApplication.run(Application.class, args);
          //"com.demo" 当前类和 RemoteService 所在的包路径,具体可以百度 ApplicationContext 
		ApplicationContext annotationContext = new AnnotationConfigApplicationContext("com.demo"); 
		RemoteService remoteService = annotationContext.getBean("remoteService", RemoteService.class);
		remoteService.call();
	}
}

注意:

        1. 在当前 Application 中需要添加 @EnableRetry 注解,启用 重试功能,这个注解是全局的。

        2.  AnnotationConfigApplicationContext("com.demo");  中的 "com.demo" 为当前类和 RemoteService 所在的包路径,具体的可以百度 ApplicationContext

 

启动成功后控制台输出的结果:

springbatch 批处理任务失败重试 spring batch retry_百度

 

上面是一个简单的 demo 例子,稍加修改就可以用在我们的实际开发中。

 

首先, RemoteService 保持不变,我们新建一个 RemoteController 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@EnableAutoConfiguration
@RequestMapping("/remote")
public class RemoteController {

    @Autowired
    private RemoteService remoteService;

    @RequestMapping("/remoteTest")
    public void remoteTest() throws Exception {
        System.out.println("===>>> 进入 remoteTest 方法");
         remoteService.call();
    }
}

更改 SpringBoot 的启动类  Application,将原先测试 RemoteService 的代码注释,用 run 方法执行。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.retry.annotation.EnableRetry;

@SpringBootApplication
@EnableRetry
public class Application {

	public static void main(String[] args) throws Exception {
		SpringApplication.run(Application.class, args);
		//"com.demo" 当前类和 RemoteService 所在的包路径,具体可以百度 ApplicationContext
//		ApplicationContext annotationContext = new AnnotationConfigApplicationContext("com.hewb");
//		RemoteService remoteService = annotationContext.getBean("remoteService", RemoteService.class);
//		remoteService.call();
	}
}

启动成功后,输入 http://localhost:8080/remote/remoteTest  访问 RemoteController 进行测试。

控制台输出结果:

springbatch 批处理任务失败重试 spring batch retry_Spring retry_02

 

这样就表示正常执行了,若在我们正式开发过程中,在 RemoteService 中用 @Retryable 注解的方法里面写我们的逻辑代码,比如调用远程服务代码,在用 @Recover 注解的方法 中填写我们异常处理代码,比如 将当前调用超时信息写入日志中,或者是其他发生异常后的处理逻辑代码。需要注意的是:用 @Recover 注解的方法 ,只有发生的异常和入参类型一致时才会回调。