当我们在使用Spring框架开发分布式系统时,出现网络或数据库等调用失败是比较常见的。而这些失败可能是暂时性的,例如网络短暂阻塞,或者是由于并发访问导致的故障,这些问题都可以通过重试来解决。Spring Retry正是为了解决这类重试问题而生的。
Spring Retry 是一个用于基于 Spring 的应用中重试操作的框架。它提供了一致的模板和注释支持,以便在不同情况下进行简单或复杂的重试,包括恢复数据库连接,使用其他服务,访问源数据等。下面是使用Spring Retry的完整攻略,包含使用步骤和示例。
步骤1:添加Spring Retry 依赖
在Maven项目中使用Spring Retry无需额外添加配置,只需要在pom.xml文件中引入Spring Retry的依赖即可:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.5.RELEASE</version>
</dependency>
如果是Gradle项目,则需在build.gradle中添加以下依赖:
compile("org.springframework.retry:spring-retry:1.2.5.RELEASE")
步骤2:声明重试模板RetryTemplate
在Spring Retry中,重试操作使用一个RetryTemplate对象来进行。RetryTemplate对象包含了一些基本的重试策略,例如固定间隔重试、指数衰减重试、以及连接异常等异常自动重试等。在使用RetryTemplate之前,需要先声明一个Bean:
@Configuration
public class RetryConfig {
private final long DEFAULT_BACKOFF_INITIAL_INTERVAL = 1000; // 重试间隔时间
private final int DEFAULT_MAX_ATTEMPTS = 3; // 最大重试次数
@Bean
public RetryTemplate retryTemplate() {
// 创建重试模板
RetryTemplate retryTemplate = new RetryTemplate();
// 创建重试策略, 此处使用了指数退避策略
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(DEFAULT_BACKOFF_INITIAL_INTERVAL);
retryTemplate.setBackOffPolicy(backOffPolicy);
// 创建重试异常
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(DEFAULT_MAX_ATTEMPTS);
retryTemplate.setRetryPolicy(retryPolicy);
// 增加一个拦截器,用于区分重试时候的业务逻辑和异常逻辑
Map<Class<? extends Throwable>, Boolean> exceptionMap = new HashMap<>();
exceptionMap.put(Exception.class, false);
exceptionMap.put(BusinessException.class, true); // 标识业务异常不重试
SimpleRetryPolicy policy = new SimpleRetryPolicy(DEFAULT_MAX_ATTEMPTS, exceptionMap, true);
retryTemplate.setRetryPolicy(policy);
return retryTemplate;
}
}
在上述代码示例中,通过@Bean注解声明了一个名为retryTemplate的Bean,并设置了重试模板RetryTemplate的基本属性,包括重试的次数和重试策略。值得注意的是,以上代码初始化RetryTemplate使用了指数退避策略进行重试,为了更好的理解指数退避策略,此处给出一段代码:
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(DEFAULT_BACKOFF_INITIAL_INTERVAL);
retryTemplate.setBackOffPolicy(backOffPolicy);
在代码中,EXPONENTIAL是退避策略的一种,退避策略是指在某个节点上重试前需要等待多长时间。在这个例子中,建议的等待时间为DEFAULT_BACKOFF_INITIAL_INTERVAL,随着每次重试,等待时间会增加,但等待时间不会超过最大等待时间。这样一来,在失败几次后,它将不再重试,而且不会花费太多时间。因为指数退避/指数反馈通常用于控制冲突指数的增长率,这是一种比较可行的策略,尤其是考虑运行时间的时候。
步骤3:在需要重试的业务方法上添加@Retryable注解
在上述步骤中,我们成功的声明了一个名为retryTemplate的Bean,并设置了它的重试模板。现在在需要重试的业务方法上添加@Retryable注解即可:
@Service
public class MyService {
// 重试模板
@Autowired
private RetryTemplate retryTemplate;
// 在方法上添加@Retryable注解
@Retryable(include = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000L, multiplier = 2))
public void doSomething() {
// TODO: 业务逻辑
}
}
在上述代码中,我们添加了@Retryable注解并设置了最大的重试次数为3,也就是说如果方法执行失败了,那么Spring会自动帮我们尝试重试三次,直到成功,其中include指定需要重试的异常,backoff表示重试间隔,delay为重试间隔时间,multiplier为重试间隔时间afterBackoff *= multiplier。这里指定了重试间隔时间为2秒,并且在每次重试时,睡眠时间都会自动乘以2进行指数递增,因此第一次重试会睡眠2秒,第二次重试会睡眠4秒,以此类推。
示例1:实现指数递归重试
以下示例是基于RedisTemplate和Zookeeper的重试操作,代码示例中的业务逻辑是尝试向Zookeeper注册一个Redis集群节点。在这个示例中,我们按照给定次数(3次)进行重试,重试间隔初始间隔为500毫秒,每一次重试增长两次,在达到最大重试次数之前,如果整个操作超时,也将重试。
@Service
@Slf4j
public class RegistryService {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private CuratorFramework curatorFramework;
@Autowired
private RetryTemplate retryTemplate;
private void registryNode() throws Exception {
// 业务逻辑
// 重试逻辑,重试3次,并加入异常白名单
final Bootstrap bootstrap = this;
try {
RetryCallback<String, RuntimeException> callback = new RetryCallback<String, RuntimeException>() {
public String doWithRetry(RetryContext context) throws RuntimeException {
log.debug("准备注册Redis节点到Zookeeper...");
try {
// 尝试节点注册
curatorFramework.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/config/node/ip");
return "success";
} catch (Exception e) {
log.error("注册Redis节点到Zookeeper失败,继续尝试注册", e);
throw e;
}
}
};
String result = retryTemplate.execute(callback);
log.debug("注册Redis节点到Zookeeper{}", result);
} catch (Exception e) {
log.error("向Zookeeper注册Redis节点失败", e);
}
}
}
在上述代码中,我们通过RetryCallback接口实现了重试模板。RetryCallback接口有一个方法doWithRetry,会在方法执行失败之后被调用,重试模板会在指定的异常之间进行抛出和重试,并在重试前检查重试策略。此外,我们在重试逻辑中指定了一个加入异常白名单的方法,当发生异常时候便不再重试。
示例2: 实现简单重试
以下示例是基于在数据库调用失败的情况下进行重试尝试的案例。
@Service
public class OrderServiceImpl {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 声明重试模板
@Autowired
private RetryTemplate retryTemplate;
@Override
public void doOrder() {
// 业务逻辑
// 重试逻辑
final OrderServiceImpl orderService = this;
try {
RetryCallback<Object, Exception> retryCallback = new RetryCallback<Object, Exception>() {
@Override
public Object doWithRetry(RetryContext context) throws Exception {
// 重试次数:3
log.debug("do something...,剩余尝试次数:" + context.getRetryCount());
//数据库调用异常,进行重试
throw new IOException("调用错误");
}
};
//设置重试模板属性
retryTemplate.execute(retryCallback);
} catch (Exception e) {
log.error(e.getMessage());
}
//结果输出
log.debug("执行完毕!");
}
}
我们在上述代码中,使用@Retryable注解实现了简单重试,不管是什么问题,只要被包含在重试策略(Exception.class)之内,都会自动进行重试。我们也可以通过其他的注解属性限制重试的机会,如指定最大重试次数,或对不同的异常类型进行不同的重试策略。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring重试支持Spring Retry的方法 - Python技术站