feat:2.3.0

1. 修复AOP扫描的Object里面的方法,导致获取不到Retryable报错问题
This commit is contained in:
byteblogs168 2023-09-08 21:46:39 +08:00
parent 408a0f2699
commit 631a59b91c
2 changed files with 76 additions and 45 deletions

View File

@ -1,10 +1,15 @@
package com.aizuda.easy.retry.client.core.intercepter; package com.aizuda.easy.retry.client.core.intercepter;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.aizuda.easy.retry.client.core.annotation.Retryable; import com.aizuda.easy.retry.client.core.annotation.Retryable;
import com.aizuda.easy.retry.client.core.cache.GroupVersionCache; import com.aizuda.easy.retry.client.core.cache.GroupVersionCache;
import com.aizuda.easy.retry.client.core.cache.RetryerInfoCache;
import com.aizuda.easy.retry.client.core.config.EasyRetryProperties; import com.aizuda.easy.retry.client.core.config.EasyRetryProperties;
import com.aizuda.easy.retry.client.core.exception.EasyRetryClientException;
import com.aizuda.easy.retry.client.core.intercepter.RetrySiteSnapshot.EnumStage; import com.aizuda.easy.retry.client.core.intercepter.RetrySiteSnapshot.EnumStage;
import com.aizuda.easy.retry.client.core.retryer.RetryerInfo;
import com.aizuda.easy.retry.client.core.retryer.RetryerResultContext; import com.aizuda.easy.retry.client.core.retryer.RetryerResultContext;
import com.aizuda.easy.retry.client.core.strategy.RetryStrategy; import com.aizuda.easy.retry.client.core.strategy.RetryStrategy;
import com.aizuda.easy.retry.common.core.alarm.Alarm; import com.aizuda.easy.retry.common.core.alarm.Alarm;
@ -13,12 +18,14 @@ import com.aizuda.easy.retry.common.core.alarm.EasyRetryAlarmFactory;
import com.aizuda.easy.retry.common.core.enums.NotifySceneEnum; import com.aizuda.easy.retry.common.core.enums.NotifySceneEnum;
import com.aizuda.easy.retry.common.core.enums.RetryResultStatusEnum; import com.aizuda.easy.retry.common.core.enums.RetryResultStatusEnum;
import com.aizuda.easy.retry.common.core.log.LogUtils; import com.aizuda.easy.retry.common.core.log.LogUtils;
import com.aizuda.easy.retry.common.core.model.EasyRetryHeaders;
import com.aizuda.easy.retry.common.core.util.EnvironmentUtils; import com.aizuda.easy.retry.common.core.util.EnvironmentUtils;
import com.aizuda.easy.retry.server.model.dto.ConfigDTO; import com.aizuda.easy.retry.server.model.dto.ConfigDTO;
import com.google.common.base.Defaults; import com.google.common.base.Defaults;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation; import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.aop.AfterAdvice; import org.springframework.aop.AfterAdvice;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
@ -26,12 +33,14 @@ import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.env.StandardEnvironment; import org.springframework.core.env.StandardEnvironment;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
/** /**
@ -120,6 +129,8 @@ public class EasyRetryInterceptor implements MethodInterceptor, AfterAdvice, Ser
|| RetrySiteSnapshot.isRetryFlow() || RetrySiteSnapshot.isRetryFlow()
// 下游响应不重试码不开启重试 // 下游响应不重试码不开启重试
|| RetrySiteSnapshot.isRetryForStatusCode() || RetrySiteSnapshot.isRetryForStatusCode()
// 匹配异常信息
|| !validate(throwable, RetryerInfoCache.get(retryable.scene(), executorClassName))
) { ) {
if (!RetrySiteSnapshot.isMethodEntrance(methodEntrance)) { if (!RetrySiteSnapshot.isMethodEntrance(methodEntrance)) {
LogUtils.debug(log, "Non-method entry does not enable local retries. traceId:[{}] [{}]", traceId, RetrySiteSnapshot.getMethodEntrance()); LogUtils.debug(log, "Non-method entry does not enable local retries. traceId:[{}] [{}]", traceId, RetrySiteSnapshot.getMethodEntrance());
@ -131,6 +142,8 @@ public class EasyRetryInterceptor implements MethodInterceptor, AfterAdvice, Ser
LogUtils.debug(log, "Retry traffic does not enable local retries. traceId:[{}] [{}]", traceId, RetrySiteSnapshot.getRetryHeader()); LogUtils.debug(log, "Retry traffic does not enable local retries. traceId:[{}] [{}]", traceId, RetrySiteSnapshot.getRetryHeader());
} else if (RetrySiteSnapshot.isRetryForStatusCode()) { } else if (RetrySiteSnapshot.isRetryForStatusCode()) {
LogUtils.debug(log, "Existing exception retry codes do not enable local retries. traceId:[{}]", traceId); LogUtils.debug(log, "Existing exception retry codes do not enable local retries. traceId:[{}]", traceId);
} else if(!validate(throwable, RetryerInfoCache.get(retryable.scene(), executorClassName))) {
LogUtils.debug(log, "Exception mismatch. traceId:[{}]", traceId);
} else { } else {
LogUtils.debug(log, "Unknown situations do not enable local retry scenarios. traceId:[{}]", traceId); LogUtils.debug(log, "Unknown situations do not enable local retry scenarios. traceId:[{}]", traceId);
} }
@ -144,10 +157,14 @@ public class EasyRetryInterceptor implements MethodInterceptor, AfterAdvice, Ser
try { try {
// 标识重试流量
initHeaders(retryable);
RetryerResultContext context = retryStrategy.openRetry(retryable.scene(), executorClassName, point.getArguments()); RetryerResultContext context = retryStrategy.openRetry(retryable.scene(), executorClassName, point.getArguments());
LogUtils.info(log,"local retry result. traceId:[{}] message:[{}]", traceId, context);
if (RetryResultStatusEnum.SUCCESS.getStatus().equals(context.getRetryResultStatusEnum().getStatus())) { if (RetryResultStatusEnum.SUCCESS.getStatus().equals(context.getRetryResultStatusEnum().getStatus())) {
LogUtils.debug(log, "local retry successful. traceId:[{}] result:[{}]", traceId, context.getResult()); LogUtils.debug(log, "local retry successful. traceId:[{}] result:[{}]", traceId, context.getResult());
} else {
LogUtils.info(log,"local retry result. traceId:[{}] throwable:[{}]", traceId, context.getThrowable());
} }
return context; return context;
@ -164,6 +181,15 @@ public class EasyRetryInterceptor implements MethodInterceptor, AfterAdvice, Ser
return null; return null;
} }
private void initHeaders(final Retryable retryable) {
EasyRetryHeaders easyRetryHeaders = new EasyRetryHeaders();
easyRetryHeaders.setEasyRetry(Boolean.TRUE);
easyRetryHeaders.setEasyRetryId(IdUtil.getSnowflakeNextIdStr());
easyRetryHeaders.setDdl(GroupVersionCache.getDdl(retryable.scene()));
RetrySiteSnapshot.setRetryHeader(easyRetryHeaders);
}
private void sendMessage(Exception e) { private void sendMessage(Exception e) {
try { try {
@ -205,13 +231,10 @@ public class EasyRetryInterceptor implements MethodInterceptor, AfterAdvice, Ser
} }
if (retryable == null) { if (retryable == null) {
//返回当前类或父类或接口方法上标注的注解对象 // 返回当前类或父类或接口方法上标注的注解对象
retryable = AnnotatedElementUtils.findMergedAnnotation(method, Retryable.class); retryable = AnnotatedElementUtils.findMergedAnnotation(method, Retryable.class);
} }
if (retryable == null) {
//返回当前类或父类或接口上标注的注解对象
retryable = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), Retryable.class);
}
return retryable; return retryable;
} }
@ -222,4 +245,33 @@ public class EasyRetryInterceptor implements MethodInterceptor, AfterAdvice, Ser
return Integer.parseInt(order); return Integer.parseInt(order);
} }
private boolean validate(Throwable throwable, RetryerInfo retryerInfo) {
Set<Class<? extends Throwable>> exclude = retryerInfo.getExclude();
Set<Class<? extends Throwable>> include = retryerInfo.getInclude();
if (CollectionUtils.isEmpty(include) && CollectionUtils.isEmpty(exclude)) {
return true;
}
for (Class<? extends Throwable> e : include) {
if (e.isAssignableFrom(throwable.getClass())) {
return true;
}
}
if (!CollectionUtils.isEmpty(exclude)) {
for (Class<? extends Throwable> e : exclude) {
if (e.isAssignableFrom(throwable.getClass())) {
return false;
}
}
return true;
}
return false;
}
} }

View File

@ -1,13 +1,13 @@
package com.aizuda.easy.retry.client.core.intercepter; package com.aizuda.easy.retry.client.core.intercepter;
import com.aizuda.easy.retry.client.core.annotation.Retryable; import com.aizuda.easy.retry.client.core.annotation.Retryable;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.aop.Advice; import org.aopalliance.aop.Advice;
import org.springframework.aop.ClassFilter; import org.springframework.aop.ClassFilter;
import org.springframework.aop.IntroductionAdvisor; import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.MethodMatcher; import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut; import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor; import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.StaticMethodMatcherPointcut; import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.aop.support.annotation.AnnotationClassFilter; import org.springframework.aop.support.annotation.AnnotationClassFilter;
import org.springframework.aop.support.annotation.AnnotationMethodMatcher; import org.springframework.aop.support.annotation.AnnotationMethodMatcher;
@ -15,23 +15,21 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodCallback;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* @author www.byteblogs.com * @author www.byteblogs.com
* @date 2023-08-23 * @date 2023-08-23
*/ */
@Component @Configuration
@Slf4j
public class EasyRetryPointcutAdvisor extends AbstractPointcutAdvisor implements IntroductionAdvisor, BeanFactoryAware, InitializingBean { public class EasyRetryPointcutAdvisor extends AbstractPointcutAdvisor implements IntroductionAdvisor, BeanFactoryAware, InitializingBean {
private Advice advice; private Advice advice;
private Pointcut pointcut; private Pointcut pointcut;
@ -41,9 +39,7 @@ public class EasyRetryPointcutAdvisor extends AbstractPointcutAdvisor implements
@Override @Override
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
Set<Class<? extends Annotation>> retryableAnnotationTypes = new LinkedHashSet<Class<? extends Annotation>>(1); this.pointcut = buildPointcut();
retryableAnnotationTypes.add(Retryable.class);
this.pointcut = buildPointcut(retryableAnnotationTypes);
this.advice = buildAdvice(); this.advice = buildAdvice();
if (this.advice instanceof BeanFactoryAware) { if (this.advice instanceof BeanFactoryAware) {
((BeanFactoryAware) this.advice).setBeanFactory(beanFactory); ((BeanFactoryAware) this.advice).setBeanFactory(beanFactory);
@ -81,23 +77,8 @@ public class EasyRetryPointcutAdvisor extends AbstractPointcutAdvisor implements
return easyRetryInterceptor; return easyRetryInterceptor;
} }
/** protected Pointcut buildPointcut() {
* Calculate a pointcut for the given retry annotation types, if any. return new AnnotationClassOrMethodPointcut(Retryable.class);
* @param retryAnnotationTypes the retry annotation types to introspect
* @return the applicable Pointcut object, or {@code null} if none
*/
protected Pointcut buildPointcut(Set<Class<? extends Annotation>> retryAnnotationTypes) {
ComposablePointcut result = null;
for (Class<? extends Annotation> retryAnnotationType : retryAnnotationTypes) {
Pointcut filter = new AnnotationClassOrMethodPointcut(retryAnnotationType);
if (result == null) {
result = new ComposablePointcut(filter);
}
else {
result.union(filter);
}
}
return result;
} }
@Override @Override
@ -110,13 +91,13 @@ public class EasyRetryPointcutAdvisor extends AbstractPointcutAdvisor implements
private final MethodMatcher methodResolver; private final MethodMatcher methodResolver;
AnnotationClassOrMethodPointcut(Class<? extends Annotation> annotationType) { AnnotationClassOrMethodPointcut(Class<? extends Annotation> annotationType) {
this.methodResolver = new AnnotationMethodMatcher(annotationType); this.methodResolver = new AnnotationMethodMatcher(annotationType, true);
setClassFilter(new AnnotationClassOrMethodFilter(annotationType)); setClassFilter(new AnnotationClassOrMethodFilter(annotationType));
} }
@Override @Override
public boolean matches(Method method, Class<?> targetClass) { public boolean matches(Method method, Class<?> targetClass) {
return getClassFilter().matches(targetClass) || this.methodResolver.matches(method, targetClass); return this.methodResolver.matches(method, targetClass);
} }
@Override @Override
@ -133,7 +114,8 @@ public class EasyRetryPointcutAdvisor extends AbstractPointcutAdvisor implements
} }
private final class AnnotationClassOrMethodFilter extends AnnotationClassFilter {
private static final class AnnotationClassOrMethodFilter extends AnnotationClassFilter {
private final AnnotationMethodsResolver methodResolver; private final AnnotationMethodsResolver methodResolver;
@ -159,16 +141,13 @@ public class EasyRetryPointcutAdvisor extends AbstractPointcutAdvisor implements
public boolean hasAnnotatedMethods(Class<?> clazz) { public boolean hasAnnotatedMethods(Class<?> clazz) {
final AtomicBoolean found = new AtomicBoolean(false); final AtomicBoolean found = new AtomicBoolean(false);
ReflectionUtils.doWithMethods(clazz, new MethodCallback() { ReflectionUtils.doWithMethods(clazz, method -> {
@Override if (found.get()) {
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { return;
if (found.get()) { }
return; Annotation annotation = AnnotationUtils.findAnnotation(method, annotationType);
} if (annotation != null) {
Annotation annotation = AnnotationUtils.findAnnotation(method, annotationType); found.set(true);
if (annotation != null) {
found.set(true);
}
} }
}); });
return found.get(); return found.get();