由于网络环境不太稳定,一些接口方法需要加入重试。正好看到Google's Guava library 项目https://github.com/rholder/guava-retrying,里面功能比较完善。但是官方没有提供注解方式使用,频繁使用会有点麻烦。所以查阅相关资料后编写了支持注解使用的方法
定义注解类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* des: guava retrying 注解使用
* doc: https://github.com/rholder/guava-retrying
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Retry {
//指定异常时重试 由于pjp.proceed();需要try catch 异常,会有问题暂时没找到解决方法,先注释
// Class[] exceptionClass() default {};
//出现Exception时重试
boolean retryIfException() default false;
//程序出现RuntimeException异常时重试
boolean retryIfRuntimeException() default false;
//重试次数
int attemptNumber() default 3;
//重试间隔 ms
long waitStrategySleepTime() default 1000;
//持续时间; 期间
long duration() default 0;
//返回值为指定字符串时重试
String returnResult() default "willRertyString";
//返回值为false时重试(默认关闭) 不支持同时设置指定返回字符串重试
boolean closeReturnFalseRetry() default true;
}
AOP类
package net.capitalonline.retry.retryaspect;
import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
import com.github.rholder.retry.WaitStrategies;
import com.google.common.base.Predicates;
import net.capitalonline.retry.Retry;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
/**
* des: guava retrying 注解使用
* doc: https://github.com/rholder/guava-retrying
*/
@Aspect
@Component
public class RetryAspect {
@Pointcut("@annotation(net.capitalonline.retry.Retry)")
public void MethodCallConstraintPointcut(){
}
@Around(value = "@annotation(net.capitalonline.retry.Retry)")
public Object monitorAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("enter retry aspect");
Method method;
if (pjp.getSignature() instanceof MethodSignature) { //判断注解是否method 上
MethodSignature signature = (MethodSignature) pjp.getSignature();
method = signature.getMethod();
} else {
return null;
}
Retry annotation = method.getDeclaredAnnotation(Retry.class);
//重试时间,重试次数
if (annotation.duration() <= 0 && annotation.attemptNumber() <= 1) {
return pjp.proceed();
}
//不能设置开启returnFalse重试 和指定返回值重试
if(!annotation.closeReturnFalseRetry() && !annotation.returnResult().equals("willRertyString")){
return pjp.proceed();
}
RetryerBuilder builder = RetryerBuilder.newBuilder();
//重试次数
if (annotation.attemptNumber() > 0) {
builder.withStopStrategy(StopStrategies.stopAfterAttempt(annotation.attemptNumber()));
}
//退出策略
if (annotation.duration() > 0) {
builder.withStopStrategy(StopStrategies.stopAfterDelay(annotation.duration(), TimeUnit.MILLISECONDS));
}
//重试间间隔等待策略
if (annotation.waitStrategySleepTime() > 0) {
builder.withWaitStrategy(WaitStrategies.fixedWait(annotation.waitStrategySleepTime(), TimeUnit.MILLISECONDS));
}
// //停止重试的策略
// if (annotation.exceptionClass().length > 0) {
// for (Class retryThrowable : annotation.exceptionClass()) {
// if (retryThrowable != null && Throwable.class.isAssignableFrom(retryThrowable)) {
// builder.retryIfExceptionOfType(retryThrowable);
// }
// }
// }
//RuntimeException时重试
if (annotation.retryIfRuntimeException()){
builder.retryIfRuntimeException();
}
if (annotation.retryIfException()){
builder.retryIfException();
}
if (!annotation.returnResult().equals("willRertyString")){
builder.retryIfResult(Predicates.equalTo(annotation.returnResult()));
}
if (!annotation.closeReturnFalseRetry()){
builder.retryIfResult(Predicates.equalTo(annotation.closeReturnFalseRetry()));
}
return builder.build().call(() -> {
try {
System.out.println("aspect exec method "+ method.getName());
return pjp.proceed();
} catch (Throwable throwable) {
if (throwable instanceof Exception) {
throw (Exception) throwable;
} else {
throw new Exception(throwable);
}
}
});
// Callable<Object> callable=new Callable<Object>() {
// @Override
// public Object call() throws Exception {
// try {
// return pjp.proceed();
// }catch (Throwable e){
// e.printStackTrace();
// System.out.println("测试");
// return null;
// }
// }
// };
// try {
// return builder.build().call(callable);
// }catch (Exception e){
// System.out.println("重试");
// return "error";
// }
}
}
application.yaml 配置中加入
aop:
proxy-target-class: true
使用实例
import org.springframework.stereotype.Component;
import java.lang.reflect.UndeclaredThrowableException;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class TestA {
int x=0;
@Retry(retryIfException = true, waitStrategySleepTime = 1200)
public void test() throws RemoteException{
try {
SimpleDateFormat sdf = new SimpleDateFormat();// 格式化时间
sdf.applyPattern("yyyy-MM-dd HH:mm:ss a");// a为am/pm的标记
Date date = new Date();// 获取当前时间
System.out.println("现在时间:" + sdf.format(date)); // 输出已经格式化的现在时间(24小时制)
int a=1/x;
System.out.println("1231");
} catch (Exception e) {
System.out.println("nonono");
throw new RemoteException("12321");
}
}
}
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
@WebAppConfiguration
@ContextConfiguration(classes = Application.class)
public class Tmpmytest {
@Autowired
TestA testA;
@Test
public void tmptest() {
try {
testA.test();
} catch (Exception e) {
System.out.println("ada");
}
}
}