Spring Boot 2.x 实践记:Retry(annotion)
Posted mickjoust
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot 2.x 实践记:Retry(annotion)相关的知识,希望对你有一定的参考价值。
目录
- 1、为什么要重试(retry)?
- 2、spring-retry
- 3、实战
- 4、测试
- 5、小结
1. 为什么需要重试(retry)?
在我们的Web项目中,通常会遇到一些场景,导致业务获取结果异常,比如:
- 网络中断
- 网络超时
- 服务器宕机
- 数据库崩溃
- 程序死锁
- 秒杀大促
在这些情况下,故障是一定会发生的,而我们的系统能否继续为用户提供服务是关键中的关键。
重试机制,是解决这类问题的一个大的策略和原则。
重试的原理很简单:当一次请求发生异常时,间隔一定时间后,重新继续请求,直到达到设置的重试次数用完后,才认为请求出现异常。
随着微服务、云原生应用的增多,服务之间交互的情况也越来越多,重试就变得越来越重要。
熟悉Spring Cloud源码的同学应该知道,spring-cloud-commons依赖了一个spring-retry模块。看下官方 定义:
- 该项目为Spring应用程序提供了声明式重试支持。它用于Spring Batch,Spring Integration等项目。显式用法也支持命令重试。
简单说,就是用Spring的风格实现的一种重试最佳实践。
所以,今天,我们就借助spring-retry模块,来实战一下重试方案。
2. spring-retry
spring-retry模块中,有三个关键注解:
- @EnableRetry:在spring boot项目中启用spring-retry功能
- @Retryable:表示需要重试的方法,有相关参数支持
- @Recover:表示重试失败后指定运行的方法
3. 实战
- github实战代码地址:https://github.com/mickjoust1018/springboot2-in-action/tree/master/springboot2-in-action-retry
3.1. 场景概述
- 创建一个Spring Boot 2项目,提供Rest API服务;
- 服务包含两个可测试开关:一个是否开启异常重试,一个开启重试后是否直接触发异常;
- 重试次数设置为3次,间隔1000ms,服务调用成功异常后打印字符串。
3.2. 环境配置
- JDK:open-jdk-8
- Maven:3.x
- spring-boot:2.x
- spring-retry:1.x
- spring-aspects: 5.x(spring-retry 的 AOP支持)
- spring-context:5.x
- Postman:测试工具
3.3. 创建Spring boot项目
第一步,我们需要创建一个Spring Boot 2项目,一般使用自动生成的方法。
点击网站:https://start.spring.io/
选择好版本后,下载包含框架项目的zip文件,使用IDE打开即可。
3.4. Maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
3.5. @EnableRetry
为了使用spring-retry功能,我们需要在启动类中加入放置@EnableRetry。
先创建启动类 AppStart.java
,增加 @SpringBootApplication
,然后添加 @EnableRetry
即可。
package com.mickjoust.demo.springboot2_in_action.retry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.retry.annotation.EnableRetry;
/**
* @author mickjoust
**/
@EnableRetry
@SpringBootApplication
public class AppStart
public static void main(String[] args)
SpringApplication.run(AppStart.class,args);
3.6. 创建Restful API
创建一个Rest控制器,该控制器用于调用后端服务类,在该类中我们将模拟异常,并且spring-retry模块将自动重试。
package com.mickjoust.demo.springboot2_in_action.retry.annotion;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author mickjoust
**/
@RestController
public class TestRestController
@Resource
private BackendService backendService;
@GetMapping("/retry")
@ExceptionHandler( Exception.class )
public String doRetryTest(@RequestParam(required = false) boolean openretry,
@RequestParam(required = false) boolean openfallback)
System.out.println("===============================");
return backendService.service(openretry, openfallback);
在API中,我们添加了两个可选的请求参数作为模拟异常的开关。
- openretry:模拟异常的开关,以便spring-retry可以重试。
- openfallback:模拟每次请求必然遇见异常。
3.7. 定义异常
spring-retry在重试时需要捕获异常进行处理,这里我们先定义一个简单的运行时异常,模拟服务不可用的异常。
package com.mickjoust.demo.springboot2_in_action.retry;
/**
* @author mickjoust
**/
public class BackendNotAvailableException extends RuntimeException
public BackendNotAvailableException(String message)
super(message);
public BackendNotAvailableException(String message, Throwable cause)
super(message, cause);
3.8. 实现单服务接口
现在,我们需要创建一个用于调用的后台服务接口和实现。
案例里,并没有调用任何真实的外部服务调用,只是通过添加一些随机逻辑来模拟成功或失败。
package com.mickjoust.demo.springboot2_in_action.retry.annotion;
import com.mickjoust.demo.springboot2_in_action.retry.BackendNotAvailableException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
/**
* @author mickjoust
**/
public interface BackendService
@Retryable(value = BackendNotAvailableException.class ,
maxAttempts = 3,
backoff = @Backoff(delay = 1000))
String service(boolean openretry, boolean openfallback);
@Recover
String serviceFallback(BackendNotAvailableException e);
package com.mickjoust.demo.springboot2_in_action.retry.annotion;
import com.mickjoust.demo.springboot2_in_action.retry.BackendNotAvailableException;
import org.springframework.stereotype.Service;
import java.util.Random;
/**
* @author mickjoust
**/
@Service("backendService")
public class BackendServiceImpl implements BackendService
private static int count = 0;
@Override
public String service(boolean openretry, boolean openfallback)
if (openretry)
System.out.println("模拟开关已打开,开始模拟异常......");
if (openfallback)
count++;
System.out.println("模拟直接异常开关已打开,直接进行重试......"+count+"次");
throw new BackendNotAvailableException(
"抛出异常,spring-retry进行重试!");
System.out.println("......模拟随机异常(是否整除2)......");
int random = new Random().nextInt(4);
System.out.println("随机数为 : " + random);
if (random % 2 == 0)
count++;
System.out.println("出现随机异常......进行重试......"+count+"次");
throw new BackendNotAvailableException("抛出异常,spring-retry进行重试!");
System.out.println("调用正常!");
count=0;
return "Hello Service!";
@Override
public String serviceFallback(BackendNotAvailableException e)
System.out.println("所有重试已完成, 调用serviceFallback方法!!!");
count=0;
return "所有重试已完成, 调用serviceFallback方法!!!";
说明:
- @Retryable:这个注释是说,如果我们的接口方法抛出RemoteServiceNotAvailableException 异常,则在返回响应之前最多重试3次,同时,每次重试都会间隔1秒的延迟。
- @Recover:如果达到最大重试次数后,依然出现异常,则使用此方法返回响应。
前面说过,在调用后台服务的实际方法中,我们是通过添加自定义开关参数来控制Exception的。
代码逻辑很简单,只要满足条件就返回期望的异常并触发充实,否则返回成功响应。
另外,我们还基于“随机数”添加了一些随机逻辑,以模拟故障的随机性。
4. 测试
通过使用postman,在REST请求中传递参数来模拟重试请求。
4.1. 场景1:直接异常重试
输入请求如下:
http://localhost:8080/retry?openretry=true&openfallback=true
期望结果:
- 基于该参数,我们期望后端服务调用中直接出现异常,尝试完3次后,返回备用方法的返回。
在日志打印中,能看到尝试了3次重试都触发异常后,返回备用方法打印。
4.2. 场景2:随机异常重试
输入请求如下:
http://localhost:8080/retry?openretry=true&openfallback=false
期望结果:
- 可能直接调用成功,可能在重试范围内成功,可能是用完重试次数返回备用方法的结果。
我们通过获取随机数,来模拟异常,同样能够出发重试或者直接成功。
5. 小结
到此,我们已经知道了如何使用 spring-retry
模块,轻松实现基于异常Exception的重试的办法。
所以,如果下次遇到需要重试请求的场景,可以尝试使用这种方法。
欢迎留言评论。
参考资源
以上是关于Spring Boot 2.x 实践记:Retry(annotion)的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot 2.x 实践记:Retry(annotion)