在项目开发过程中,经常会有这样的情况:第一次执行一个操作不成功,考虑到可能是网络原因造成,就多执行几次操作,直到得到想要的结果为止,这就是重试机制。
Springboot可以通过整合Spring Retry框架实现重试。
下面讲一下整合Spring Retry框架的步骤:
1、首先要在pom.xml配置中加入spring-retry的依赖:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
2、在启动类中加入重试注解@EnableRetry。
@EnableRetry
@SpringBootApplication
public class ServerMain {
public static void main(String[] args) {
SpringApplication.run(ServerMain.class, args);
}
}
3、新建重试接口RetryService
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
/**
* @Title: RetryService.java
* @Package com.skd.modules.retry
* @Description: TODO(重试接口)
* @ImportantNotes:
*/
@Service
public class RetryService {
private final static Logger logger = LoggerFactory.getLogger(RetryService.class);
private final int totalNum = 100000;
/**
* @Retryable的参数说明: •value:抛出指定异常才会重试
* •include:和value一样,默认为空,当exclude也为空时,默认所以异常
* •exclude:指定不处理的异常
* •maxAttempts:最大重试次数,默认3次
* •backoff:重试等待策略,默认使用@Backoff,@Backoff的value默认为1000L,我们设置为2000L;multiplier(指定延迟倍数)默认为0,表示固定暂停1秒后进行重试,如果把multiplier设置为1.5,则第一次重试为2秒,第二次为3秒,第三次为4.5秒。
*/
@Retryable(value = IllegalArgumentException.class, maxAttempts = 3, backoff = @Backoff(delay = 2000L, multiplier = 1.5))
public int retry(int num) {
logger.info("减库存({})开始" + LocalTime.now(),totalNum);
// do something
if (num <= 0) {
throw new IllegalArgumentException("数量不对");
}
logger.info("减库存执行结束" + LocalTime.now());
return totalNum - num;
}
@Recover
public int recover(IllegalArgumentException e) {
logger.warn("减库存失败!!!" + LocalTime.now());
return totalNum;
}
}
4.测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ServerMain.class)
public class RetryTest {
@Autowired
private RetryService retryService;
@Test
public void test() {
int count = retryService.retry(-1);
// int count = retryService.retry(5);
System.out.println("库存为 :" + count);
}
}
最后提供一个不使用注解的方式,例如动态指定重试次数,等待时间等
// 声明式
void declarative() {
final RetryTemplate retryTemplate = new RetryTemplate();
// 重试次数
final SimpleRetryPolicy policy = new SimpleRetryPolicy(3, Collections.<Class<? extends Throwable>, Boolean>
singletonMap(Exception.class, true));
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
// 重试时间 默认1000ms。
// fixedBackOffPolicy.setBackOffPeriod(2000);
retryTemplate.setRetryPolicy(policy);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
final RetryCallback<Object, Exception> retryCallback = new RetryCallback<Object, Exception>() {
public Object doWithRetry(RetryContext context) throws Exception {
System.out.println("重试次数:"+context.getRetryCount()+", 重试时间:"+LocalTime.now());
System.err.println("do some thing");
//设置context一些属性,给RecoveryCallback传递一些属性
context.setAttribute("key1", "value1");
String result = null;
if (StringUtils.isEmpty(result))
throw new Exception("exception");
return context.getAttribute("key1");
}
};
// 如果RetryCallback执行出现指定异常, 并且超过最大重试次数依旧出现指定异常的话,就执行RecoveryCallback动作
final RecoveryCallback<Object> recoveryCallback = new RecoveryCallback<Object>() {
public Object recover(RetryContext context) throws Exception {
System.err.println("do recory operation");
System.err.println(context.getAttribute("key1"));
return null;
}
};
try {
final Object execute = retryTemplate.execute(retryCallback, recoveryCallback);
System.err.println(execute);
} catch (Exception e) {
e.printStackTrace();
}
}