feat: 1.3.0

1. 优化手动重试模块
This commit is contained in:
byteblogs168 2023-05-15 18:49:54 +08:00
parent 0e6d43681a
commit ff42df5229
9 changed files with 280 additions and 33 deletions
easy-retry-client-core/src/main/java/com/aizuda/easy/retry/client/core
example/src

View File

@ -1,5 +1,6 @@
package com.aizuda.easy.retry.client.core.intercepter; 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.constant.SystemConstants;
import com.aizuda.easy.retry.common.core.model.EasyRetryHeaders; import com.aizuda.easy.retry.common.core.model.EasyRetryHeaders;
import lombok.Getter; import lombok.Getter;
@ -65,6 +66,10 @@ public class RetrySiteSnapshot {
} }
public static boolean isMethodEntrance(String methodEntrance) { public static boolean isMethodEntrance(String methodEntrance) {
if (StrUtil.isBlank(getMethodEntrance())) {
return false;
}
return getMethodEntrance().equals(methodEntrance); return getMethodEntrance().equals(methodEntrance);
} }
@ -109,7 +114,8 @@ public class RetrySiteSnapshot {
} }
public static boolean isRetryForStatusCode() { 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() { public static Long getEntryMethodTime() {
@ -124,11 +130,11 @@ public class RetrySiteSnapshot {
ENTRY_METHOD_TIME.remove(); ENTRY_METHOD_TIME.remove();
} }
public static void removeRetryHeader(){ public static void removeRetryHeader() {
RETRY_HEADER.remove(); RETRY_HEADER.remove();
} }
public static void removeRetryStatusCode(){ public static void removeRetryStatusCode() {
RETRY_STATUS_CODE.remove(); RETRY_STATUS_CODE.remove();
} }
@ -166,6 +172,11 @@ public class RetrySiteSnapshot {
* 远程重试阶段 * 远程重试阶段
*/ */
REMOTE(2), REMOTE(2),
/**
* 手动提交数据
*/
MANUAL_REPORT(3),
; ;
private final int stage; private final int stage;

View File

@ -1,8 +1,13 @@
package com.aizuda.easy.retry.client.core.retryer; 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.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.ExecutorMethod;
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.model.EasyRetryHeaders;
import java.util.Objects;
/** /**
* 手动生成重试任务模板类 * 手动生成重试任务模板类
@ -20,7 +25,20 @@ public class EasyRetryTemplate implements RetryOperations {
@Override @Override
public void executeRetry() { 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( protected void setExecutorMethodClass(

View File

@ -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.ExecutorMethod;
import com.aizuda.easy.retry.client.core.strategy.LocalRetryStrategies; 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.client.core.strategy.RetryStrategy;
import com.aizuda.easy.retry.common.core.context.SpringContext; import com.aizuda.easy.retry.common.core.context.SpringContext;
@ -41,7 +42,7 @@ public class RetryTaskTemplateBuilder {
easyRetryTemplate.setParams(params); easyRetryTemplate.setParams(params);
easyRetryTemplate.setExecutorMethodClass(executorMethodClass); easyRetryTemplate.setExecutorMethodClass(executorMethodClass);
easyRetryTemplate.setScene(scene); easyRetryTemplate.setScene(scene);
RetryStrategy retryStrategy = SpringContext.getBeanByType(LocalRetryStrategies.class); RetryStrategy retryStrategy = SpringContext.getBeanByType(ManualRetryStrategies.class);
easyRetryTemplate.setRetryStrategy(retryStrategy); easyRetryTemplate.setRetryStrategy(retryStrategy);
return easyRetryTemplate; return easyRetryTemplate;
} }

View File

@ -27,7 +27,7 @@ import java.util.function.Consumer;
public abstract class AbstractRetryStrategies implements RetryStrategy { public abstract class AbstractRetryStrategies implements RetryStrategy {
@Autowired @Autowired
private List<EasyRetryListener> EasyRetryListeners; private List<EasyRetryListener> easyRetryListeners;
@Override @Override
public RetryerResultContext openRetry(String sceneName, String executorClassName, Object[] params) { public RetryerResultContext openRetry(String sceneName, String executorClassName, Object[] params) {
@ -52,7 +52,7 @@ public abstract class AbstractRetryStrategies implements RetryStrategy {
retryerResultContext.setRetryerInfo(retryerInfo); retryerResultContext.setRetryerInfo(retryerInfo);
try { try {
for (EasyRetryListener EasyRetryListener : EasyRetryListeners) { for (EasyRetryListener EasyRetryListener : easyRetryListeners) {
EasyRetryListener.beforeRetry(sceneName, executorClassName, params); EasyRetryListener.beforeRetry(sceneName, executorClassName, params);
} }
@ -79,7 +79,7 @@ public abstract class AbstractRetryStrategies implements RetryStrategy {
Object result = retryerResultContext.getResult(); Object result = retryerResultContext.getResult();
RetryerInfo retryerInfo = retryerResultContext.getRetryerInfo(); RetryerInfo retryerInfo = retryerResultContext.getRetryerInfo();
for (EasyRetryListener EasyRetryListener : EasyRetryListeners) { for (EasyRetryListener EasyRetryListener : easyRetryListeners) {
EasyRetryListener.successOnRetry(result, retryerInfo.getScene(), retryerInfo.getExecutorClassName()); EasyRetryListener.successOnRetry(result, retryerInfo.getScene(), retryerInfo.getExecutorClassName());
} }
@ -99,7 +99,7 @@ public abstract class AbstractRetryStrategies implements RetryStrategy {
RetryerInfo retryerInfo = context.getRetryerInfo(); RetryerInfo retryerInfo = context.getRetryerInfo();
try { try {
for (EasyRetryListener EasyRetryListener : EasyRetryListeners) { for (EasyRetryListener EasyRetryListener : easyRetryListeners) {
EasyRetryListener EasyRetryListener
.failureOnRetry(retryerInfo.getScene(), retryerInfo.getExecutorClassName(), throwable); .failureOnRetry(retryerInfo.getScene(), retryerInfo.getExecutorClassName(), throwable);
} }

View File

@ -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.exception.EasyRetryClientException;
import com.aizuda.easy.retry.client.core.intercepter.RetrySiteSnapshot; 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.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.RetryType;
import com.aizuda.easy.retry.client.core.retryer.RetryerInfo; 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.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.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 lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; 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.Collections;
import java.util.List; import java.util.List;
@ -96,7 +96,7 @@ public class LocalRetryStrategies extends AbstractRetryStrategies {
if (RetryType.LOCAL_REMOTE.name().equals(retryerInfo.getRetryType().name())){ if (RetryType.LOCAL_REMOTE.name().equals(retryerInfo.getRetryType().name())){
// 上报 // 上报
log.debug("上报 scene:[{}]", retryerInfo.getScene()); 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()); log.debug("上报 scene:[{}]", retryerInfo.getScene());
return () -> { return () -> {
doReport(retryerInfo, 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());
}
}
RetrySiteSnapshot.setStage(RetrySiteSnapshot.EnumStage.REMOTE.getStage()); RetrySiteSnapshot.setStage(RetrySiteSnapshot.EnumStage.REMOTE.getStage());
return null; 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 @Override
public RetryExecutorParameter<WaitStrategy, StopStrategy> getRetryExecutorParameter(RetryerInfo retryerInfo) { public RetryExecutorParameter<WaitStrategy, StopStrategy> getRetryExecutorParameter(RetryerInfo retryerInfo) {

View File

@ -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<Object> 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<Throwable> doGetRetryErrorConsumer(final RetryerInfo retryerInfo, final Object[] params) {
return throwable -> {
LogUtils.debug(log, "ManualRetryStrategies doGetRetryErrorConsumer ");
};
}
@Override
protected Callable doGetCallable(final RetryExecutor<WaitStrategy, StopStrategy> 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<WaitStrategy, StopStrategy> getRetryExecutorParameter(RetryerInfo retryerInfo) {
return new RetryExecutorParameter<WaitStrategy, StopStrategy>() {
@Override
public Predicate<Throwable> 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<RetryListener> getRetryListeners() {
return Collections.singletonList(new RetryListener() {
@Override
public <V> void onRetry(Attempt<V> 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;
}
}

View File

@ -14,7 +14,7 @@ import java.util.concurrent.TimeUnit;
* @date : 2022-03-07 14:07 * @date : 2022-03-07 14:07
* @since 1.3.0 * @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 @Slf4j
public class CustomSyncCreateTask implements ExecutorMethod { public class CustomSyncCreateTask implements ExecutorMethod {

View File

@ -1,10 +1,16 @@
package com.example.demo; package com.example.demo;
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.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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.Random;
import java.util.UUID; import java.util.UUID;
/** /**
@ -22,4 +28,25 @@ public class NestMethodService {
public void testNestMethod() { public void testNestMethod() {
testExistsTransactionalRetryService.testSimpleInsert(UUID.randomUUID().toString()); 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();
}
} }

View File

@ -4,10 +4,12 @@ import com.aizuda.easy.retry.client.core.retryer.EasyRetryTemplate;
import com.aizuda.easy.retry.client.core.retryer.RetryTaskTemplateBuilder; import com.aizuda.easy.retry.client.core.retryer.RetryTaskTemplateBuilder;
import com.example.demo.CustomAsyncCreateTask; import com.example.demo.CustomAsyncCreateTask;
import com.example.demo.CustomSyncCreateTask; import com.example.demo.CustomSyncCreateTask;
import com.example.demo.NestMethodService;
import com.example.model.Cat; import com.example.model.Cat;
import com.example.model.Zoo; import com.example.model.Zoo;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@ -20,6 +22,9 @@ import java.time.LocalDateTime;
@Slf4j @Slf4j
public class EasyRetryTemplateTest { public class EasyRetryTemplateTest {
@Autowired
private NestMethodService nestMethodService;
@Test @Test
public void generateAsyncTaskTest() throws InterruptedException { public void generateAsyncTaskTest() throws InterruptedException {
@ -52,4 +57,12 @@ public class EasyRetryTemplateTest {
retryTemplate.executeRetry(); retryTemplate.executeRetry();
} }
@Test
public void testNestMethodForCustomSyncCreateTask() throws InterruptedException {
nestMethodService.testNestMethodForCustomSyncCreateTask();
Thread.sleep(90000);
}
} }