java重试机制_Java重试机制修改

本文探讨了一段用于接口重试的代码,指出了其高耦合、栈溢出风险和低可复用性等问题,并提出了改进方案。通过创建一个抽象的重试模板类,实现了重试逻辑的抽取和配置参数的灵活性,支持同步和异步调用。进一步地,利用AOP切面编程,定义了一个自定义注解,实现了零侵入式的重试功能,提高了代码的可复用性和可维护性。这种方法允许在业务方法上简单添加注解即可实现重试逻辑,降低了代码的侵入性。
摘要由CSDN通过智能技术生成

最近无意间看到了一段代码,实话实说看的我有点难受,刚开始的时候还略微有点懵,只是感觉代码很长。等我捋了一遍之后,发现是一段调用远程接口,失败进行重试功能的代码。代码如下:

c06480731b6f?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

image.png

方法用到了递归,在重试次数小于零跳出。

说一下存在的问题吧:

接口重试和业务本身不发生关系,所以具有很高的耦合性

方法采用递归实现,有栈溢出的风险

重试逻辑无法进行重用

可配置性比较低

看下怎么改一下:

-抽离出重试代码,预留接口,业务代码填入,抽离工具类如下:

public abstract class MyRetryTemplate {

//重试次数

private int retryTime;

//重试时间

private int sleepTime;

//是否倍数增长

private boolean multiple = false;

/**

* 执行业务方法逻辑,由实现类实现

*

* @return

*/

public abstract T remote() throws Exception;

public T execute() throws InterruptedException {

for (int i = 1; i < retryTime + 1; i++) {

try {

return remote();

} catch (Exception e) {

System.out.println(e.getMessage());

if (multiple){

Thread.sleep(sleepTime);

}

else{

Thread.sleep(sleepTime * (i));

}

}

}

return null;

}

public T submit(ExecutorService executorService) {

Future submit = executorService.submit((Callable) () -> execute());

try {

return (T) submit.get();

} catch (InterruptedException | ExecutionException e) {

e.printStackTrace();

}

return null;

}

public MyRetryTemplate setRetryTime(int retryTime) {

this.retryTime = retryTime;

return this;

}

public MyRetryTemplate setSleepTime(int sleepTime) {

this.sleepTime = sleepTime;

return this;

}

public MyRetryTemplate setMultiple(boolean multiple) {

this.multiple = multiple;

return this;

}

}

上面的类中,值定义了重试的次数,间隔的时间,和时间的增长参数,预留了remote()抽象方法,交由具体的实现类来实现。类中使用了泛型,支持返回不同的对象。并且支持同步和异步调用。

看下修改后的代码,如下:

c06480731b6f?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

image.png

是不是感觉一下清爽了很多,这里重试相关的参数,我直接写死在了代码中,可以通过配置文件和数据库配置引入。这个方法应该还能精简,最后两句感觉还是有点多余,由于不知道是干啥的, 就暂且留在这吧。

修改后的代码,虽然解决了一些上面的问题,但是并没有完全解决,代码的侵入性,上面已经说了既然这是重试的逻辑,就不应该出现在代码中。有没有解决的办法呢,有,切面。

下面看下通过切面该怎么实现。

首先定义一个注解:

@Documented

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface Retry {

int count() default 0 ;

long sleep() default 0 ;

}

定义切面

@Aspect

@Component

public class RetryAspect {

ExecutorService executorService = Executors.newFixedThreadPool(3);

@Around(value = "@annotation(Retry)")

public Object execute(ProceedingJoinPoint point, Retry retry) throws InterruptedException {

System.out.println("----------------- Aspect ---------------------");

MyRetryTemplate myRetryTemplate = new MyRetryTemplate() {

@Override

public ParametersHolder remote() throws Throwable {

return (ParametersHolder) point.proceed();

}

}.setRetryTime(3).setSleepTime(10000).submit(executorService);

return submit;

}

}

最终实现:

c06480731b6f?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

image.png

在注解中,添加自定义的参数,便可以实现零侵入,也更容易实现方法的复用,如果有其他的业务需要实现重试,直接在业务方法上添加注解即可。

如果不想用这种方法,也可以借助第三方工具guava-retrying和 spring-retry 来实现此功能。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值