环境:Spring5.3.23
1. 前言
在软件开发中,业务重试是一个常见的问题。当某些业务操作失败时,我们需要重新尝试,以确保操作的正确性和可靠性。Spring AOP(面向切面编程)是一种强大的编程技术,可以帮助我们在业务重试方面实现更高效和可靠的处理。
本文将介绍如何通过Spring AOP实现业务重试,包括重试机制的基本原理、Spring AOP的概念和实现方式,以及一个具体的业务重试示例。通过本文的学习,读者将能够了解如何利用Spring AOP解决业务重试问题,提高软件的可靠性和稳定性。
2. 为什么要重试
提高系统的可靠性:系统运行过程中,网络延迟、服务故障、超时等原因可能导致部分请求失败。通过重试机制,可以增加系统的可靠性,减少因临时故障导致的问题。
保障数据的一致性:在涉及数据操作的系统,如金融系统、电商系统等,如果一次操作失败,可能会导致数据的不一致。通过重试机制,可以增加数据的一致性,减少数据错误的可能性。
用户体验的优化:对于用户来说,一次失败的操作可能会影响他们的使用体验。通过重试机制,可以在一定程度上优化用户体验,让用户有更多的容错空间。
防止偶然错误:有时候,一些偶然的错误,如输入错误、操作失误等,可以通过重试机制进行修正。
3. 重试实现
通过结合注解和AOP(面向切面编程),实现一个高效且灵活的重试机制。首先,我们使用注解来标记需要重试的方法。
(RetentionPolicy.RUNTIME)(ElementType.METHOD)public static Retry {// 定义了重试的次数int nums() default 3 ;}
接下来,我们使用AOP来拦截标记了@Retry注解的方法。在AOP中,我们可以通过切面(aspect)和切入点(pointcut)来定义拦截的规则。
public class RetryAspect {("@annotation(retry)")public Object retry(ProceedingJoinPoint pjp, Retry retry) throws Throwable {// 获取重试的次数int maxRetries = retry.nums() ;int numAttempts = 0 ;RuntimeException rt ;do {numAttempts++ ;try {return pjp.proceed() ;} catch(RuntimeException ex) {System.err.printf("第次【%d】重试..%n", numAttempts) ;rt = ex ;// 休眠1s进行重试TimeUnit.SECONDS.sleep(1) ;}} while(numAttempts <= maxRetries) ;// 重试耗尽后还是失败,则抛出异常throw rt ;}}
在上面的切面中,我们实现一个重试机制。当切入点匹配的方法被调用时,切面会拦截该调用,并尝试重新执行该方法。如果重试次数超过了指定的最大重试次数,或者最后一次重试仍然失败,切面将抛出一个异常,以终止调用链。
测试
public class UserService {@Retrypublic void save(int a) {if (a == 3) {throw new RuntimeException("参数错误") ;}System.out.println("保存成功") ;}}
执行结果
发生异常,进行重试发生异常,进行重试发生异常,进行重试发生异常,进行重试Exception in thread "main" java.lang.RuntimeException: 参数错误
首次调用后发生异常后,进行了3次重试。
以上就是一个简单的重试实现的原理。
4. 事务一起工作
当你的业务方法是一个事务的方法,如果按照上面的方法执行,那么每次都重试都是在同一个事务中执行,这时候可能就会有问题。如果业务执行是先查询业务数据的状态,根据不同的状态做接下来的操作。那么这时候如果你的事务隔离级别是可重复读,那么不管你重试多少次都会是第一次查询的结果。如果在这种情况下,你则需要将重试的优先级设置的比事务的要高,我们只需要在RetryAspect添加@Order即可
(-1)public class RetryAspect {}
这样就能确保每次重试都在一个新的事务中执行。
你也可以通过Spring提供retry实现业务的重试
<dependency><groupId>org.springframework.retry</groupId><artifactId>spring-retry</artifactId><version>${spring-retry.version}</version></dependency>
这儿库非常的强大。
完毕!!!



