diff --git a/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/intercepter/RetrySiteSnapshot.java b/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/intercepter/RetrySiteSnapshot.java index c1e95e33..0d664c90 100644 --- a/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/intercepter/RetrySiteSnapshot.java +++ b/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/intercepter/RetrySiteSnapshot.java @@ -1,5 +1,6 @@ package com.aizuda.easy.retry.client.core.intercepter; +import cn.hutool.core.util.StrUtil; import com.aizuda.easy.retry.common.core.constant.SystemConstants; import com.aizuda.easy.retry.common.core.model.EasyRetryHeaders; import lombok.Getter; @@ -65,6 +66,10 @@ public class RetrySiteSnapshot { } public static boolean isMethodEntrance(String methodEntrance) { + if (StrUtil.isBlank(getMethodEntrance())) { + return false; + } + return getMethodEntrance().equals(methodEntrance); } @@ -109,7 +114,8 @@ public class RetrySiteSnapshot { } public static boolean isRetryForStatusCode() { - return Objects.nonNull(getRetryStatusCode()) && getRetryStatusCode().equals(SystemConstants.EASY_RETRY_STATUS_CODE); + return Objects.nonNull(getRetryStatusCode()) && getRetryStatusCode() + .equals(SystemConstants.EASY_RETRY_STATUS_CODE); } public static Long getEntryMethodTime() { @@ -124,11 +130,11 @@ public class RetrySiteSnapshot { ENTRY_METHOD_TIME.remove(); } - public static void removeRetryHeader(){ + public static void removeRetryHeader() { RETRY_HEADER.remove(); } - public static void removeRetryStatusCode(){ + public static void removeRetryStatusCode() { RETRY_STATUS_CODE.remove(); } @@ -166,6 +172,11 @@ public class RetrySiteSnapshot { * 远程重试阶段 */ REMOTE(2), + + /** + * 手动提交数据 + */ + MANUAL_REPORT(3), ; private final int stage; diff --git a/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/retryer/EasyRetryTemplate.java b/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/retryer/EasyRetryTemplate.java index 1a3d8205..b9dc2dab 100644 --- a/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/retryer/EasyRetryTemplate.java +++ b/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/retryer/EasyRetryTemplate.java @@ -1,8 +1,13 @@ package com.aizuda.easy.retry.client.core.retryer; +import cn.hutool.core.util.StrUtil; import com.aizuda.easy.retry.client.core.RetryOperations; +import com.aizuda.easy.retry.client.core.intercepter.RetrySiteSnapshot; import com.aizuda.easy.retry.client.core.strategy.ExecutorMethod; import com.aizuda.easy.retry.client.core.strategy.RetryStrategy; +import com.aizuda.easy.retry.common.core.model.EasyRetryHeaders; + +import java.util.Objects; /** * 手动生成重试任务模板类 @@ -20,7 +25,20 @@ public class EasyRetryTemplate implements RetryOperations { @Override public void executeRetry() { - retryStrategy.openRetry(scene, executorMethodClass.getName(), params); + + Integer stage = RetrySiteSnapshot.getStage(); + try { + retryStrategy.openRetry(scene, executorMethodClass.getName(), params); + } finally { + // stage == null 则非嵌套重试, 需求清除线程记录的数据信息 + // stage != null 则由上层调度的进行清除线程记录的数据信息 + if (Objects.isNull(stage)) { + RetrySiteSnapshot.removeAll(); + } else { + // 还原原始的重试阶段 + RetrySiteSnapshot.setStage(stage); + } + } } protected void setExecutorMethodClass( diff --git a/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/retryer/RetryTaskTemplateBuilder.java b/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/retryer/RetryTaskTemplateBuilder.java index c8167a32..4f3a95aa 100644 --- a/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/retryer/RetryTaskTemplateBuilder.java +++ b/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/retryer/RetryTaskTemplateBuilder.java @@ -2,6 +2,7 @@ package com.aizuda.easy.retry.client.core.retryer; import com.aizuda.easy.retry.client.core.strategy.ExecutorMethod; import com.aizuda.easy.retry.client.core.strategy.LocalRetryStrategies; +import com.aizuda.easy.retry.client.core.strategy.ManualRetryStrategies; import com.aizuda.easy.retry.client.core.strategy.RetryStrategy; import com.aizuda.easy.retry.common.core.context.SpringContext; @@ -41,7 +42,7 @@ public class RetryTaskTemplateBuilder { easyRetryTemplate.setParams(params); easyRetryTemplate.setExecutorMethodClass(executorMethodClass); easyRetryTemplate.setScene(scene); - RetryStrategy retryStrategy = SpringContext.getBeanByType(LocalRetryStrategies.class); + RetryStrategy retryStrategy = SpringContext.getBeanByType(ManualRetryStrategies.class); easyRetryTemplate.setRetryStrategy(retryStrategy); return easyRetryTemplate; } diff --git a/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/strategy/AbstractRetryStrategies.java b/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/strategy/AbstractRetryStrategies.java index 67b5fb16..e79eaf26 100644 --- a/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/strategy/AbstractRetryStrategies.java +++ b/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/strategy/AbstractRetryStrategies.java @@ -27,7 +27,7 @@ import java.util.function.Consumer; public abstract class AbstractRetryStrategies implements RetryStrategy { @Autowired - private List EasyRetryListeners; + private List easyRetryListeners; @Override public RetryerResultContext openRetry(String sceneName, String executorClassName, Object[] params) { @@ -52,7 +52,7 @@ public abstract class AbstractRetryStrategies implements RetryStrategy { retryerResultContext.setRetryerInfo(retryerInfo); try { - for (EasyRetryListener EasyRetryListener : EasyRetryListeners) { + for (EasyRetryListener EasyRetryListener : easyRetryListeners) { EasyRetryListener.beforeRetry(sceneName, executorClassName, params); } @@ -79,7 +79,7 @@ public abstract class AbstractRetryStrategies implements RetryStrategy { Object result = retryerResultContext.getResult(); RetryerInfo retryerInfo = retryerResultContext.getRetryerInfo(); - for (EasyRetryListener EasyRetryListener : EasyRetryListeners) { + for (EasyRetryListener EasyRetryListener : easyRetryListeners) { EasyRetryListener.successOnRetry(result, retryerInfo.getScene(), retryerInfo.getExecutorClassName()); } @@ -99,7 +99,7 @@ public abstract class AbstractRetryStrategies implements RetryStrategy { RetryerInfo retryerInfo = context.getRetryerInfo(); try { - for (EasyRetryListener EasyRetryListener : EasyRetryListeners) { + for (EasyRetryListener EasyRetryListener : easyRetryListeners) { EasyRetryListener .failureOnRetry(retryerInfo.getScene(), retryerInfo.getExecutorClassName(), throwable); } diff --git a/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/strategy/LocalRetryStrategies.java b/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/strategy/LocalRetryStrategies.java index 785a17b8..57a8c178 100644 --- a/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/strategy/LocalRetryStrategies.java +++ b/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/strategy/LocalRetryStrategies.java @@ -5,21 +5,21 @@ import com.aizuda.easy.retry.client.core.RetryExecutorParameter; import com.aizuda.easy.retry.client.core.exception.EasyRetryClientException; import com.aizuda.easy.retry.client.core.intercepter.RetrySiteSnapshot; import com.aizuda.easy.retry.client.core.report.ReportHandler; -import com.github.rholder.retry.*; -import com.google.common.base.Predicate; import com.aizuda.easy.retry.client.core.retryer.RetryType; import com.aizuda.easy.retry.client.core.retryer.RetryerInfo; import com.aizuda.easy.retry.client.core.retryer.RetryerResultContext; import com.aizuda.easy.retry.common.core.enums.RetryResultStatusEnum; import com.aizuda.easy.retry.common.core.log.LogUtils; +import com.github.rholder.retry.Attempt; +import com.github.rholder.retry.RetryListener; +import com.github.rholder.retry.StopStrategies; +import com.github.rholder.retry.StopStrategy; +import com.github.rholder.retry.WaitStrategies; +import com.github.rholder.retry.WaitStrategy; +import com.google.common.base.Predicate; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.DefaultTransactionDefinition; -import org.springframework.transaction.support.TransactionSynchronizationManager; import java.util.Collections; import java.util.List; @@ -96,7 +96,7 @@ public class LocalRetryStrategies extends AbstractRetryStrategies { if (RetryType.LOCAL_REMOTE.name().equals(retryerInfo.getRetryType().name())){ // 上报 log.debug("上报 scene:[{}]", retryerInfo.getScene()); - reportHandler.asyncReport(retryerInfo.getScene(), retryerInfo.getExecutorClassName(), params); + doReport(retryerInfo, params); } }; } @@ -115,21 +115,7 @@ public class LocalRetryStrategies extends AbstractRetryStrategies { // 仅仅是远程重试则直接上报 log.debug("上报 scene:[{}]", retryerInfo.getScene()); return () -> { - - if (retryerInfo.isAsync()) { - if (retryerInfo.isForceReport()) { - reportHandler.asyncReportWithForce(retryerInfo.getScene(), retryerInfo.getExecutorClassName(), params); - } else { - reportHandler.asyncReport(retryerInfo.getScene(), retryerInfo.getExecutorClassName(), params); - } - } else { - if (retryerInfo.isForceReport()) { - reportHandler.syncReport(retryerInfo.getScene(), retryerInfo.getExecutorClassName(), params, retryerInfo.getTimeout(), retryerInfo.getUnit()); - } else { - reportHandler.syncReport(retryerInfo.getScene(), retryerInfo.getExecutorClassName(), params, retryerInfo.getTimeout(), retryerInfo.getUnit()); - } - } - + doReport(retryerInfo, params); RetrySiteSnapshot.setStage(RetrySiteSnapshot.EnumStage.REMOTE.getStage()); return null; }; @@ -139,6 +125,31 @@ public class LocalRetryStrategies extends AbstractRetryStrategies { } + /** + * 上报数据 + * + * @param retryerInfo 定义重试场景的信息 + * @param params 执行参数 + */ + private void doReport(final RetryerInfo retryerInfo, final Object[] params) { + + if (retryerInfo.isAsync()) { + if (retryerInfo.isForceReport()) { + reportHandler.asyncReportWithForce(retryerInfo.getScene(), retryerInfo.getExecutorClassName(), params); + } else { + reportHandler.asyncReport(retryerInfo.getScene(), retryerInfo.getExecutorClassName(), params); + } + } else { + if (retryerInfo.isForceReport()) { + reportHandler.syncReport(retryerInfo.getScene(), retryerInfo.getExecutorClassName(), + params, retryerInfo.getTimeout(), retryerInfo.getUnit()); + } else { + reportHandler.syncReport(retryerInfo.getScene(), retryerInfo.getExecutorClassName(), + params, retryerInfo.getTimeout(), retryerInfo.getUnit()); + } + } + } + @Override public RetryExecutorParameter getRetryExecutorParameter(RetryerInfo retryerInfo) { diff --git a/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/strategy/ManualRetryStrategies.java b/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/strategy/ManualRetryStrategies.java new file mode 100644 index 00000000..9ebf9f51 --- /dev/null +++ b/easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core/strategy/ManualRetryStrategies.java @@ -0,0 +1,166 @@ +package com.aizuda.easy.retry.client.core.strategy; + +import com.aizuda.easy.retry.client.core.RetryExecutor; +import com.aizuda.easy.retry.client.core.RetryExecutorParameter; +import com.aizuda.easy.retry.client.core.intercepter.RetrySiteSnapshot; +import com.aizuda.easy.retry.client.core.report.ReportHandler; +import com.aizuda.easy.retry.client.core.retryer.RetryType; +import com.aizuda.easy.retry.client.core.retryer.RetryerInfo; +import com.aizuda.easy.retry.client.core.retryer.RetryerResultContext; +import com.aizuda.easy.retry.common.core.enums.RetryResultStatusEnum; +import com.aizuda.easy.retry.common.core.log.LogUtils; +import com.github.rholder.retry.Attempt; +import com.github.rholder.retry.RetryListener; +import com.github.rholder.retry.StopStrategies; +import com.github.rholder.retry.StopStrategy; +import com.github.rholder.retry.WaitStrategies; +import com.github.rholder.retry.WaitStrategy; +import com.google.common.base.Predicate; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * @author: www.byteblogs.com + * @date : 2023-05-15 18:19 + */ +@Component +@Slf4j +public class ManualRetryStrategies extends AbstractRetryStrategies { + + @Autowired + private ReportHandler reportHandler; + + @Override + protected void setStage() { + RetrySiteSnapshot.setStage(RetrySiteSnapshot.EnumStage.MANUAL_REPORT.getStage()); + } + + @Override + protected Consumer doRetrySuccessConsumer(final RetryerResultContext context) { + return o -> { + LogUtils.debug(log, "ManualRetryStrategies doRetrySuccessConsumer "); + }; + } + + @Override + protected void error(final RetryerResultContext context) { + context.setRetryResultStatusEnum(RetryResultStatusEnum.FAILURE); + } + + @Override + protected boolean preValidator(final RetryerInfo retryerInfo, final RetryerResultContext resultContext) { + + if (retryerInfo.isForceReport()) { + return true; + } + + if (RetrySiteSnapshot.isRunning()) { + resultContext.setRetryResultStatusEnum(RetryResultStatusEnum.FAILURE); + resultContext.setMessage("执行重试检验不通过 原因: 存在正在运行的重试任务"); + return false; + } + + return true; + } + + @Override + protected void unexpectedError(final Exception e, final RetryerResultContext retryerResultContext) { + retryerResultContext.setRetryResultStatusEnum(RetryResultStatusEnum.FAILURE); + } + + @Override + protected void success(final RetryerResultContext retryerResultContext) { + retryerResultContext.setRetryResultStatusEnum(RetryResultStatusEnum.SUCCESS); + } + + @Override + protected Consumer doGetRetryErrorConsumer(final RetryerInfo retryerInfo, final Object[] params) { + return throwable -> { + LogUtils.debug(log, "ManualRetryStrategies doGetRetryErrorConsumer "); + }; + } + + @Override + protected Callable doGetCallable(final RetryExecutor retryExecutor, Object[] params) { + RetryerInfo retryerInfo = retryExecutor.getRetryerInfo(); + return () -> doReport(retryerInfo, params); + + } + + /** + * 上报数据 + * + * @param retryerInfo 定义重试场景的信息 + * @param params 执行参数 + */ + private boolean doReport(final RetryerInfo retryerInfo, final Object[] params) { + + if (retryerInfo.isAsync()) { + if (retryerInfo.isForceReport()) { + return reportHandler + .asyncReportWithForce(retryerInfo.getScene(), retryerInfo.getExecutorClassName(), params); + } else { + return reportHandler.asyncReport(retryerInfo.getScene(), retryerInfo.getExecutorClassName(), params); + } + } else { + if (retryerInfo.isForceReport()) { + return reportHandler.syncReport(retryerInfo.getScene(), retryerInfo.getExecutorClassName(), + params, retryerInfo.getTimeout(), retryerInfo.getUnit()); + } else { + return reportHandler.syncReport(retryerInfo.getScene(), retryerInfo.getExecutorClassName(), + params, retryerInfo.getTimeout(), retryerInfo.getUnit()); + } + } + } + + @Override + protected RetryExecutorParameter getRetryExecutorParameter(RetryerInfo retryerInfo) { + return new RetryExecutorParameter() { + + @Override + public Predicate exceptionPredicate() { + return throwable -> ManualRetryStrategies.super.validate(throwable.getClass(), retryerInfo); + } + + @Override + public WaitStrategy backOff() { + return WaitStrategies.fixedWait(500, TimeUnit.MILLISECONDS); + } + + @Override + public StopStrategy stop() { + return StopStrategies.stopAfterAttempt(5); + } + + @Override + public List getRetryListeners() { + return Collections.singletonList(new RetryListener() { + @Override + public void onRetry(Attempt attempt) { + if (attempt.hasResult()) { + LogUtils.error(log, "easy-retry 手动创建重试数据成功,第[{}]次调度", attempt.getAttemptNumber()); + } + + if (attempt.hasException()) { + LogUtils.error(log, "easy-retry 手动创建重试数据失败,第[{}]次调度 ", attempt.getAttemptNumber(), + attempt.getExceptionCause()); + } + + } + }); + } + }; + } + + @Override + public boolean supports(final int stage, final RetryType retryType) { + return RetrySiteSnapshot.EnumStage.MANUAL_REPORT.getStage() == stage; + } +} diff --git a/example/src/main/java/com/example/demo/CustomSyncCreateTask.java b/example/src/main/java/com/example/demo/CustomSyncCreateTask.java index 09c6b108..4ebdf8f2 100644 --- a/example/src/main/java/com/example/demo/CustomSyncCreateTask.java +++ b/example/src/main/java/com/example/demo/CustomSyncCreateTask.java @@ -14,7 +14,7 @@ import java.util.concurrent.TimeUnit; * @date : 2022-03-07 14:07 * @since 1.3.0 */ -@ExecutorMethodRegister(scene = CustomSyncCreateTask.SCENE, async = false, timeout = 10000, unit = TimeUnit.MILLISECONDS) +@ExecutorMethodRegister(scene = CustomSyncCreateTask.SCENE, async = false, timeout = 10000, unit = TimeUnit.MILLISECONDS, forceReport = false) @Slf4j public class CustomSyncCreateTask implements ExecutorMethod { diff --git a/example/src/main/java/com/example/demo/NestMethodService.java b/example/src/main/java/com/example/demo/NestMethodService.java index b2b75bd5..7e84d0d0 100644 --- a/example/src/main/java/com/example/demo/NestMethodService.java +++ b/example/src/main/java/com/example/demo/NestMethodService.java @@ -1,10 +1,16 @@ package com.example.demo; import com.aizuda.easy.retry.client.core.annotation.Retryable; +import com.aizuda.easy.retry.client.core.exception.EasyRetryClientException; +import com.aizuda.easy.retry.client.core.retryer.EasyRetryTemplate; +import com.aizuda.easy.retry.client.core.retryer.RetryTaskTemplateBuilder; +import com.example.model.Zoo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; +import java.util.Random; import java.util.UUID; /** @@ -22,4 +28,25 @@ public class NestMethodService { public void testNestMethod() { testExistsTransactionalRetryService.testSimpleInsert(UUID.randomUUID().toString()); } + + @Retryable(scene = "testNestMethodForCustomSyncCreateTask" , isThrowException = false) + @Transactional + public void testNestMethodForCustomSyncCreateTask() { + + Random random = new Random(); + int i = random.nextInt(5); + if (i <= 2) { + throw new EasyRetryClientException("测试注解重试和手动重试"); + } + + Zoo zoo = new Zoo(); + zoo.setNow(LocalDateTime.now()); + EasyRetryTemplate retryTemplate = RetryTaskTemplateBuilder.newBuilder() + .withExecutorMethod(CustomSyncCreateTask.class) + .withParam(zoo) + .withScene(CustomSyncCreateTask.SCENE) + .build(); + + retryTemplate.executeRetry(); + } } diff --git a/example/src/test/java/com/example/EasyRetryTemplateTest.java b/example/src/test/java/com/example/EasyRetryTemplateTest.java index 4222a78c..ae43bcc1 100644 --- a/example/src/test/java/com/example/EasyRetryTemplateTest.java +++ b/example/src/test/java/com/example/EasyRetryTemplateTest.java @@ -4,10 +4,12 @@ import com.aizuda.easy.retry.client.core.retryer.EasyRetryTemplate; import com.aizuda.easy.retry.client.core.retryer.RetryTaskTemplateBuilder; import com.example.demo.CustomAsyncCreateTask; import com.example.demo.CustomSyncCreateTask; +import com.example.demo.NestMethodService; import com.example.model.Cat; import com.example.model.Zoo; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.time.LocalDateTime; @@ -20,6 +22,9 @@ import java.time.LocalDateTime; @Slf4j public class EasyRetryTemplateTest { + @Autowired + private NestMethodService nestMethodService; + @Test public void generateAsyncTaskTest() throws InterruptedException { @@ -52,4 +57,12 @@ public class EasyRetryTemplateTest { retryTemplate.executeRetry(); } + + @Test + public void testNestMethodForCustomSyncCreateTask() throws InterruptedException { + nestMethodService.testNestMethodForCustomSyncCreateTask(); + + Thread.sleep(90000); + } + }