diff --git a/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/intercepter/EasyRetryInterceptor.java b/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/intercepter/EasyRetryInterceptor.java index 18111ce8..8d242908 100644 --- a/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/intercepter/EasyRetryInterceptor.java +++ b/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/intercepter/EasyRetryInterceptor.java @@ -1,10 +1,15 @@ 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 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.RetryerInfoCache; 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.retryer.RetryerInfo; import com.aizuda.easy.retry.client.core.retryer.RetryerResultContext; import com.aizuda.easy.retry.client.core.strategy.RetryStrategy; 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.RetryResultStatusEnum; 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.server.model.dto.ConfigDTO; import com.google.common.base.Defaults; import lombok.extern.slf4j.Slf4j; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; +import org.aspectj.lang.ProceedingJoinPoint; import org.springframework.aop.AfterAdvice; import org.springframework.beans.factory.annotation.Autowired; 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.env.StandardEnvironment; import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; import java.io.Serializable; import java.lang.reflect.Method; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Objects; +import java.util.Set; import java.util.UUID; /** @@ -120,6 +129,8 @@ public class EasyRetryInterceptor implements MethodInterceptor, AfterAdvice, Ser || RetrySiteSnapshot.isRetryFlow() // 下游响应不重试码,不开启重试 || RetrySiteSnapshot.isRetryForStatusCode() + // 匹配异常信息 + || !validate(throwable, RetryerInfoCache.get(retryable.scene(), executorClassName)) ) { if (!RetrySiteSnapshot.isMethodEntrance(methodEntrance)) { 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()); } else if (RetrySiteSnapshot.isRetryForStatusCode()) { 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 { 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 { + // 标识重试流量 + initHeaders(retryable); + 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())) { 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; @@ -164,6 +181,15 @@ public class EasyRetryInterceptor implements MethodInterceptor, AfterAdvice, Ser 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) { try { @@ -205,13 +231,10 @@ public class EasyRetryInterceptor implements MethodInterceptor, AfterAdvice, Ser } if (retryable == null) { - //返回当前类或父类或接口方法上标注的注解对象 + // 返回当前类或父类或接口方法上标注的注解对象 retryable = AnnotatedElementUtils.findMergedAnnotation(method, Retryable.class); } - if (retryable == null) { - //返回当前类或父类或接口上标注的注解对象 - retryable = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), Retryable.class); - } + return retryable; } @@ -222,4 +245,33 @@ public class EasyRetryInterceptor implements MethodInterceptor, AfterAdvice, Ser return Integer.parseInt(order); } + + + private boolean validate(Throwable throwable, RetryerInfo retryerInfo) { + + Set> exclude = retryerInfo.getExclude(); + Set> include = retryerInfo.getInclude(); + + if (CollectionUtils.isEmpty(include) && CollectionUtils.isEmpty(exclude)) { + return true; + } + + for (Class e : include) { + if (e.isAssignableFrom(throwable.getClass())) { + return true; + } + } + + if (!CollectionUtils.isEmpty(exclude)) { + for (Class e : exclude) { + if (e.isAssignableFrom(throwable.getClass())) { + return false; + } + } + + return true; + } + + return false; + } } diff --git a/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/intercepter/EasyRetryPointcutAdvisor.java b/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/intercepter/EasyRetryPointcutAdvisor.java index 84edd771..2cbbb704 100644 --- a/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/intercepter/EasyRetryPointcutAdvisor.java +++ b/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/intercepter/EasyRetryPointcutAdvisor.java @@ -1,13 +1,13 @@ package com.aizuda.easy.retry.client.core.intercepter; import com.aizuda.easy.retry.client.core.annotation.Retryable; +import lombok.extern.slf4j.Slf4j; import org.aopalliance.aop.Advice; import org.springframework.aop.ClassFilter; import org.springframework.aop.IntroductionAdvisor; import org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut; import org.springframework.aop.support.AbstractPointcutAdvisor; -import org.springframework.aop.support.ComposablePointcut; import org.springframework.aop.support.StaticMethodMatcherPointcut; import org.springframework.aop.support.annotation.AnnotationClassFilter; 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.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; -import org.springframework.util.ReflectionUtils.MethodCallback; import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import java.util.LinkedHashSet; -import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; /** * @author www.byteblogs.com * @date 2023-08-23 */ -@Component +@Configuration +@Slf4j public class EasyRetryPointcutAdvisor extends AbstractPointcutAdvisor implements IntroductionAdvisor, BeanFactoryAware, InitializingBean { private Advice advice; private Pointcut pointcut; @@ -41,9 +39,7 @@ public class EasyRetryPointcutAdvisor extends AbstractPointcutAdvisor implements @Override public void afterPropertiesSet() throws Exception { - Set> retryableAnnotationTypes = new LinkedHashSet>(1); - retryableAnnotationTypes.add(Retryable.class); - this.pointcut = buildPointcut(retryableAnnotationTypes); + this.pointcut = buildPointcut(); this.advice = buildAdvice(); if (this.advice instanceof BeanFactoryAware) { ((BeanFactoryAware) this.advice).setBeanFactory(beanFactory); @@ -81,23 +77,8 @@ public class EasyRetryPointcutAdvisor extends AbstractPointcutAdvisor implements return easyRetryInterceptor; } - /** - * Calculate a pointcut for the given retry annotation types, if any. - * @param retryAnnotationTypes the retry annotation types to introspect - * @return the applicable Pointcut object, or {@code null} if none - */ - protected Pointcut buildPointcut(Set> retryAnnotationTypes) { - ComposablePointcut result = null; - for (Class retryAnnotationType : retryAnnotationTypes) { - Pointcut filter = new AnnotationClassOrMethodPointcut(retryAnnotationType); - if (result == null) { - result = new ComposablePointcut(filter); - } - else { - result.union(filter); - } - } - return result; + protected Pointcut buildPointcut() { + return new AnnotationClassOrMethodPointcut(Retryable.class); } @Override @@ -110,13 +91,13 @@ public class EasyRetryPointcutAdvisor extends AbstractPointcutAdvisor implements private final MethodMatcher methodResolver; AnnotationClassOrMethodPointcut(Class annotationType) { - this.methodResolver = new AnnotationMethodMatcher(annotationType); + this.methodResolver = new AnnotationMethodMatcher(annotationType, true); setClassFilter(new AnnotationClassOrMethodFilter(annotationType)); } @Override public boolean matches(Method method, Class targetClass) { - return getClassFilter().matches(targetClass) || this.methodResolver.matches(method, targetClass); + return this.methodResolver.matches(method, targetClass); } @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; @@ -159,16 +141,13 @@ public class EasyRetryPointcutAdvisor extends AbstractPointcutAdvisor implements public boolean hasAnnotatedMethods(Class clazz) { final AtomicBoolean found = new AtomicBoolean(false); - ReflectionUtils.doWithMethods(clazz, new MethodCallback() { - @Override - public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { - if (found.get()) { - return; - } - Annotation annotation = AnnotationUtils.findAnnotation(method, annotationType); - if (annotation != null) { - found.set(true); - } + ReflectionUtils.doWithMethods(clazz, method -> { + if (found.get()) { + return; + } + Annotation annotation = AnnotationUtils.findAnnotation(method, annotationType); + if (annotation != null) { + found.set(true); } }); return found.get();