Spring框架中一个有用的小组件之Spring Retry组件详解

网友投稿 305 2022-10-10


Spring框架中一个有用的小组件之Spring Retry组件详解

1、概述

Spring Retry 是Spring框架中的一个组件,

它提供了自动重新调用失败操作的能力。这在错误可能是暂时发生的(如瞬时网络故障)的情况下很有帮助。

在本文中,我们将看到使用Spring Retry的各种方式:注解、RetryTemplate以及回调。

2、Maven依赖

让我们首先将spring-retry依赖项添加到我们的pom.xml文件中:

org.springframework.retry

spring-retry

1.2.5.RELEASE

我们还需要将Spring AOP添加到我们的项目中:

org.springframework

spring-aspects

5.2.8.RELEASE

可以查看Maven Central来获取最新版本的spring-retry

和spring-aspects 依赖项。

3、开启Spring Retry

要在应用程序中启用Spring Retry,我们需要将@EnableRetry注释添加到我们的@Configuration类:

@Configuration

@EnableRetry

public class AppConfig { ... }

4、使用Spring Retry

4.1、@Retryable而不用恢复

我们可以使用@Retryable注解为方法添加重试功能:

@Service

public interface MyService {

@Retryable(value = RuntimeException.class)

void retryService(String sql);

}

在这里,当抛出RuntimeException时尝试重试。

根据@Retryable的默认行为,重试最多可能发生3次,重试之间有1秒的延迟。

4.2、@Retryable和@Recover

现在让我们使用@Recover注解添加一个恢复方法:

@Service

public interface MyService {

@Retryable(value = SQLException.class)

void retryServiceWithRecovery(String sql) throws SQLException;

@Recover

void recover(SQLException e, String sql);

}

这里,当抛出SQLException时重试会尝试运行。 当@Retryable方法因指定异常而失败时,@Recover注解定义了一个单独的恢复方法。

因此,如果retryServiceWithRecovery方法在三次尝试之后还是抛出了SQLException,那么recover()方法将被调用。

恢复处理程序的第一个参数应该是Throwable类型(可选)和相同的返回类型。其余的参数按相同顺序从失败方法的参数列表中填充。

4.3、自定义@Retryable的行为

为了自定义重试的行为,我们可以使用参数maxAttempts和backoff:

@Service

public interface MyService {

@Retryable( value = SQLException.class,

maxAttempts = 2, backoff = @Backoff(delay = 100))

void retryServiceWithCustomization(String sql) throws SQLException;

}

这样最多将有两次尝试和100毫秒的延迟。

4.4、使用Spring Properties

我们还可以在@Retryable注解中使用properties。

为了演示这一点,我们将看到如何将delay和maxAttempts的值外部化到一个properties文件中。

首先,让我们在名为retryConfig.properties的文件中定义属性:

retry.maxAttempts=2

retry.maxDelay=100

然后我们指示@Configuration类加载这个文件:

@PropertySource("classpath:retryConfig.properties")

public class AppConfig { ... }

// ...

最后,我们可以在@Retryable的定义中注入retry.maxAttempts和retry.maxDelay的值:

@Service

public interface MyService {

@Retryable( value = SQLException.class, maxAttemptsExpression = "${retry.maxAttempts}",

backoff = @Backoff(delayExpression = "${retry.maxDelay}"))

void retryServiceWithExternalizedConfiguration(String sql) throws SQLException;

}

请注意,我们现在使用的是maxAttemptsExpression和delayExpression而不是maxAttempts和delay。

5、RetryTemplate

5.1、RetryOperations

Spring Retry提供了RetryOperations接口,它提供了一组execute()方法:

public interface RetryOperations {

T execute(RetryCallback retryCallback) throws Exception;

...

}

execute()方法的参数RetryCallback,是一个接口,可以插入需要在失败时重试的业务逻辑:

public interface RetryCallback {

T doWithRetry(RetryContext context) throws Throwable;

}

5.2、RetryTemplate配置

RetryTemplate是RetryOperations的一个实现。

让我们在@Configuration类中配置一个RetryTemplate的bean:

@Configuration

public class AppConfig {

//...

@Bean

public RetryTemplate retryTemplate() {

RetryTemplate retryTemplate = new RetryTemplate();

FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();

fixedBackOffPolicy.setBackOffPeriod(2000l);

retryTemplate.setBackOffPolicy(fixedBackOffPolicy);

SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();

retryPolicy.setMaxAttempts(2);

retryTemplate.setRetryPolicy(retryPolicy);

return retryTemplate;

}

}

这个RetryPolicy确定了何时应该重试操作。

其中SimpleRetryPolicy定义了重试的固定次数,另一方面,BackOffPolicy用于控制重试尝试之间的回退。

最后,FixedBackOffPolicy会使重试在继续之前暂停一段固定的时间。

5.3、使用RetryTemplate

要使用重试处理来运行代码,我们可以调用retryTemplate.execute()方法:

retryTemplate.execute(new RetryCallback() {

@Override

public Void doWithRetry(RetryContext arg0) {

myService.templateRetryService();

...

}

});

我们可以使用lambda表达式代替匿名类:

retryTemplate.execute(arg0 -> {

myService.templateRetryService();

return null;

});

6、监听器

监听器在重试时提供另外的回调。我们可以用这些来关注跨不同重试的各个横切点。

6.1、添加回调

回调在RetryListener接口中提供:

public class DefaultListenerShttp://upport extends RetryListenerSupport {

@Override

public void close(RetryContext context,

RetryCallback callback, Throwable throwable) {

logger.info("onClose");

...

super.close(context, callback, throwable);

}

@Override

public void onError(RetryContext context,

RetryCallback callback, Throwable throwable) {

logger.info("onError");

...

super.onError(context, callback, throwable);

}

@Override

public boolean open(RetryContext context,

RetryCallback callback) {

logger.info("onOpen");

...

return super.open(context, callback);

}

}

open和close的回调在整个重试之前和之后执行,而onError应用于单个RetryCallback调用。

6.2、注册监听器

接下来,我们将我们的监听器(DefaultListenerSupport)注册到我们的RetryTemplate bean:

@Configuration

public class AppConfig {

...

@Bean

public RetryTemplate retryTemplate() {

RetryTemplate retryTemplate = new RetryTemplate();

...

retryTemplate.registerListener(new DefaultListenerSupport());

return retryTemplate;

}

}

7、测试结果

为了完成我们的示例,让我们验证一下结果:

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(

classes = AppConfig.class,

loader = AnnotationConfigContextLoader.class)

public class SpringRetryIntegrationTest {

@Autowired

private MyService myService;

@Autowired

private RetryTemplate retryTemplate;

@Test(expected = RuntimeException.class)

public void givenTemplateRetryService_whenCallWithException_thenRetry() {

retryTemplate.execute(arg0 -> {

myService.templateRetryService();

return null;

});

}

}

从测试日志中可以看出,我们已经正确配置了RetryTemplate和RetryListener:

2020-01-09 20:04:10 [main] INFO  c.p.s.DefaultListenerSupport - onOpen

2020-01-09 20:04:10 [main] INFO  c.pinmost.springretry.MyServiceImpl - throw RuntimeException in method templateRetryService()

2020-01-09 20:04:10 [main] INFO  c.p.s.DefaultListenerSupport - onError

2020-01-09 20:04:12 [main] INFO  c.pinmost.springretry.MyServiceImpl - throw RuntimeException in method templateRetryService()

2020-01-09 20:04:12 [main] INFO  c.p.s.DefaultListenerSupport - onError

2020-01-09 20:04:12 [main] INFO  c.p.s.DefaultListenerSupport - onClose

8、结论

在本文中,我们看到了如何使用注解、RetryTemplate和回调监听器来使用Spring Retry。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Linux下Oenssl命令及搭建私有CA
下一篇:如何构建安全监控平台(穷人也能玩安全)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~