feat: 1.3.0

1. 优化手动重试模块
This commit is contained in:
byteblogs168 2023-05-15 17:09:59 +08:00
parent c2aa5ba79a
commit 0e6d43681a
13 changed files with 183 additions and 148 deletions

View File

@ -9,29 +9,8 @@ package com.aizuda.easy.retry.client.core;
public interface RetryOperations {
/**
* 生成重试任务并上报服务端且存在嵌套重试情况下不上报服务端
*
* 执行重试
*/
void generateAsyncTask();
/**
* 生成重试任务并上报服务端
*
* @param forceReport true: 表示存在嵌套重试情况下强制上报服务端 false: 表示存在嵌套重试情况下不上报服务端
*/
void generateAsyncTask(boolean forceReport);
/**
* 生成重试任务并上报服务端且存在嵌套重试情况下不上报服务端
*
*/
Boolean generateSyncTask();
/**
* 生成重试任务并上报服务端
*
* @param forceReport true: 表示存在嵌套重试情况下强制上报服务端 false: 表示存在嵌套重试情况下不上报服务端
*/
Boolean generateSyncTask(boolean forceReport);
void executeRetry();
}

View File

@ -12,6 +12,7 @@ import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;
/**
* 在使用手动生成重试任务时通过ExecutorMethodRegister配置注册重试场景
@ -64,4 +65,32 @@ public @interface ExecutorMethodRegister {
*/
String bizNo() default "";
/**
* 异步上报数据到服务端
*
* @return boolean
*/
boolean async() default true;
/**
* 是否强制上报数据到服务端
*
* @return boolean
*/
boolean forceReport() default false;
/**
* 同步(async:false)上报数据需要配置超时时间
*
* @return 超时时间
*/
long timeout() default 60 * 1000;
/**
* 超时时间单位
*
* @return TimeUnit
*/
TimeUnit unit() default TimeUnit.MILLISECONDS;
}

View File

@ -10,6 +10,7 @@ import com.aizuda.easy.retry.client.core.callback.SimpleRetryCompleteCallback;
import com.aizuda.easy.retry.client.core.retryer.RetryType;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
* 重试接入入口
@ -101,5 +102,26 @@ public @interface Retryable {
*/
boolean isThrowException() default true;
/**
* 异步上报数据到服务端
*
* @return boolean
*/
boolean async() default true;
/**
* 同步(async:false)上报数据需要配置超时时间
*
* @return 超时时间
*/
long timeout() default 60 * 1000;
/**
* 超时时间单位
*
* @return TimeUnit
*/
TimeUnit unit() default TimeUnit.MILLISECONDS;
}

View File

@ -1,7 +1,9 @@
package com.aizuda.easy.retry.client.core.executor;
import cn.hutool.core.lang.Assert;
import com.aizuda.easy.retry.client.core.RetryExecutorParameter;
import com.aizuda.easy.retry.client.core.cache.RetryerInfoCache;
import com.aizuda.easy.retry.client.core.exception.EasyRetryClientException;
import com.github.rholder.retry.*;
import com.aizuda.easy.retry.common.core.log.LogUtils;
import lombok.extern.slf4j.Slf4j;
@ -10,14 +12,18 @@ import java.util.concurrent.Callable;
import java.util.function.Consumer;
/**
* Guava 重试执行器
*
* @author: www.byteblogs.com
* @date : 2022-03-03 18:07
* @since 1.3.0
*/
@Slf4j
public class GuavaRetryExecutor extends AbstractRetryExecutor<WaitStrategy, StopStrategy> {
public GuavaRetryExecutor(String sceneName, String executorClassName) {
retryerInfo = RetryerInfoCache.get(sceneName, executorClassName);
Assert.notNull(retryerInfo, () -> new EasyRetryClientException("retryerInfo is null sceneName:[{}] executorClassName:[{}]", sceneName, executorClassName));
}
public GuavaRetryExecutor() {

View File

@ -23,6 +23,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
/**
* 扫描手动注入重试方法
@ -61,6 +62,10 @@ public class ExecutorMethodScanner implements Scanner, ApplicationContextAware {
Class<? extends IdempotentIdGenerate> idempotentIdGenerate = retryable.idempotentId();
Method executorMethodName = executorNotProxy.getMethod("doExecute", Object.class);
Class<? extends RetryCompleteCallback> retryCompleteCallback = retryable.retryCompleteCallback();
boolean async = retryable.async();
long timeout = retryable.timeout();
TimeUnit unit = retryable.unit();
boolean forceReport = retryable.forceReport();
return new RetryerInfo(retryable.scene(),
executorClassName,
@ -69,13 +74,17 @@ public class ExecutorMethodScanner implements Scanner, ApplicationContextAware {
executor,
executorMethodName,
RetryType.ONLY_REMOTE,
0,
0,
1,
1,
idempotentIdGenerate,
StringUtils.EMPTY,
(Class<? extends ExecutorMethod>) executor.getClass(),
true,
retryCompleteCallback
Boolean.TRUE,
retryCompleteCallback,
async,
forceReport,
timeout,
unit
);
}catch (Exception e) {
LogUtils.error(log, "{}重试信息加载报错:{}", executor.getClass().getName(), e);

View File

@ -18,7 +18,12 @@ import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author: www.byteblogs.com
@ -45,7 +50,8 @@ public class RetryableScanner implements Scanner, ApplicationContextAware {
Map<Method, Retryable> annotatedMethods = null;
try {
annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(),
(MethodIntrospector.MetadataLookup<Retryable>) method -> AnnotatedElementUtils.findMergedAnnotation(method, Retryable.class));
(MethodIntrospector.MetadataLookup<Retryable>) method -> AnnotatedElementUtils
.findMergedAnnotation(method, Retryable.class));
} catch (Throwable ex) {
LogUtils.error(log, "{}重试信息加载报错:{}", beanDefinitionName, ex);
}
@ -80,26 +86,33 @@ public class RetryableScanner implements Scanner, ApplicationContextAware {
Class<? extends ExecutorMethod> retryMethod = retryable.retryMethod();
boolean throwException = retryable.isThrowException();
Class<? extends RetryCompleteCallback> retryCompleteCallback = retryable.retryCompleteCallback();
boolean async = retryable.async();
long timeout = retryable.timeout();
TimeUnit unit = retryable.unit();
return new RetryerInfo(retryable.scene(),
executorClassName,
new HashSet<>(Arrays.asList(include)),
new HashSet<>(Arrays.asList(exclude)),
executor,
executorMethodName,
retryType,
localTimes,
localInterval,
idempotentIdGenerate,
bizNo,
retryMethod,
throwException,
retryCompleteCallback
executorClassName,
new HashSet<>(Arrays.asList(include)),
new HashSet<>(Arrays.asList(exclude)),
executor,
executorMethodName,
retryType,
localTimes,
localInterval,
idempotentIdGenerate,
bizNo,
retryMethod,
throwException,
retryCompleteCallback,
async,
Boolean.FALSE, // 基于注解的上报不允许强制上报
timeout,
unit
);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
this.applicationContext = applicationContext;
}
}

View File

@ -1,10 +1,8 @@
package com.aizuda.easy.retry.client.core.retryer;
import com.aizuda.easy.retry.client.core.RetryOperations;
import com.aizuda.easy.retry.client.core.report.ReportHandler;
import com.aizuda.easy.retry.client.core.strategy.ExecutorMethod;
import java.util.concurrent.TimeUnit;
import com.aizuda.easy.retry.client.core.strategy.RetryStrategy;
/**
* 手动生成重试任务模板类
@ -18,36 +16,11 @@ public class EasyRetryTemplate implements RetryOperations {
private Class<? extends ExecutorMethod> executorMethodClass;
private String scene;
private Object[] params;
private ReportHandler reportHandler;
private long timeout = 60 * 1000;
private TimeUnit unit = TimeUnit.MILLISECONDS;
private RetryStrategy retryStrategy;
@Override
public void generateAsyncTask() {
generateAsyncTask(Boolean.FALSE);
}
@Override
public void generateAsyncTask(boolean forceReport) {
if (forceReport) {
reportHandler.asyncReportWithForce(scene, executorMethodClass.getName(), params);
} else {
reportHandler.asyncReport(scene, executorMethodClass.getName(), params);
}
}
@Override
public Boolean generateSyncTask() {
return generateSyncTask(Boolean.FALSE);
}
@Override
public Boolean generateSyncTask(final boolean forceReport) {
if (forceReport) {
return reportHandler.syncReportWithForce(scene, executorMethodClass.getName(), params, timeout, unit);
} else {
return reportHandler.syncReport(scene, executorMethodClass.getName(), params, timeout, unit);
}
public void executeRetry() {
retryStrategy.openRetry(scene, executorMethodClass.getName(), params);
}
protected void setExecutorMethodClass(
@ -63,15 +36,7 @@ public class EasyRetryTemplate implements RetryOperations {
this.params = params;
}
protected void setReportHandler(final ReportHandler reportHandler) {
this.reportHandler = reportHandler;
}
protected void setTimeout(final long timeout) {
this.timeout = timeout;
}
protected void setUnit(final TimeUnit unit) {
this.unit = unit;
protected void setRetryStrategy(final RetryStrategy retryStrategy) {
this.retryStrategy = retryStrategy;
}
}

View File

@ -1,11 +1,10 @@
package com.aizuda.easy.retry.client.core.retryer;
import com.aizuda.easy.retry.client.core.report.ReportHandler;
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.RetryStrategy;
import com.aizuda.easy.retry.common.core.context.SpringContext;
import java.util.concurrent.TimeUnit;
/**
* 构建重试模板对象
*
@ -17,8 +16,6 @@ public class RetryTaskTemplateBuilder {
private Class<? extends ExecutorMethod> executorMethodClass;
private String scene;
private Object[] params;
private long timeout = 60*1000;
private TimeUnit unit = TimeUnit.MILLISECONDS;
public static RetryTaskTemplateBuilder newBuilder() {
return new RetryTaskTemplateBuilder();
@ -39,25 +36,13 @@ public class RetryTaskTemplateBuilder {
return this;
}
public RetryTaskTemplateBuilder timeout(long timeout) {
this.timeout = timeout;
return this;
}
public RetryTaskTemplateBuilder unit(TimeUnit unit) {
this.unit = unit;
return this;
}
public EasyRetryTemplate build() {
EasyRetryTemplate easyRetryTemplate = new EasyRetryTemplate();
easyRetryTemplate.setParams(params);
easyRetryTemplate.setExecutorMethodClass(executorMethodClass);
easyRetryTemplate.setScene(scene);
easyRetryTemplate.setTimeout(timeout);
easyRetryTemplate.setUnit(unit);
ReportHandler reportHandler = SpringContext.CONTEXT.getBean(ReportHandler.class);
easyRetryTemplate.setReportHandler(reportHandler);
RetryStrategy retryStrategy = SpringContext.getBeanByType(LocalRetryStrategies.class);
easyRetryTemplate.setRetryStrategy(retryStrategy);
return easyRetryTemplate;
}
}

View File

@ -8,10 +8,14 @@ import lombok.Data;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* 定义重试场景的信息
*
* @author: www.byteblogs.com
* @date : 2022-03-03 15:06
* @since 1.0.0
*/
@Data
@AllArgsConstructor
@ -31,4 +35,10 @@ public class RetryerInfo {
private final Class<? extends ExecutorMethod> executorMethod;
private final boolean isThrowException;
private final Class<? extends RetryCompleteCallback> retryCompleteCallback;
private final boolean async;
private final boolean forceReport;
private final long timeout;
private final TimeUnit unit;
}

View File

@ -37,8 +37,6 @@ public class LocalRetryStrategies extends AbstractRetryStrategies {
@Autowired
private ReportHandler reportHandler;
@Autowired(required = false)
private PlatformTransactionManager platformTransactionManager;
@Override
public boolean supports(int stage, RetryType retryType) {
@ -112,30 +110,26 @@ public class LocalRetryStrategies extends AbstractRetryStrategies {
// 如果是仅仅本地重试或本地_远程模式则先支持重试
case ONLY_LOCAL:
case LOCAL_REMOTE:
return () -> {
if (TransactionSynchronizationManager.isActualTransactionActive()) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
TransactionStatus transaction = platformTransactionManager.getTransaction(def);
Object execute;
try {
execute = retryExecutor.execute(params);
platformTransactionManager.commit(transaction);
} catch (Exception e) {
platformTransactionManager.rollback(transaction);
throw e;
}
return execute;
} else {
return retryExecutor.execute(params);
}
};
return () -> retryExecutor.execute(params);
case ONLY_REMOTE:
// 仅仅是远程重试则直接上报
log.debug("上报 scene:[{}]", retryerInfo.getScene());
return () -> {
reportHandler.asyncReport(retryerInfo.getScene(), retryerInfo.getExecutorClassName(), 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());
return null;
};

View File

@ -3,26 +3,24 @@ package com.example.demo;
import com.aizuda.easy.retry.client.core.annotation.ExecutorMethodRegister;
import com.aizuda.easy.retry.client.core.strategy.ExecutorMethod;
import com.aizuda.easy.retry.common.core.util.JsonUtil;
import com.example.model.Cat;
import com.example.model.Zoo;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
/**
* 测试手动重试并同步上报任务
*
* @author: www.byteblogs.com
* @date : 2022-03-07 14:07
* @since 1.3.0
*/
@ExecutorMethodRegister(scene = CustomCreateTask.SCENE)
@ExecutorMethodRegister(scene = CustomAsyncCreateTask.SCENE, async = true)
@Slf4j
public class CustomCreateTask implements ExecutorMethod {
public class CustomAsyncCreateTask implements ExecutorMethod {
public static final String SCENE = "customCreateTask";
public static final String SCENE = "customAsyncCreateTask";
@Override
public Object doExecute(Object obj) {
log.info("测试自定义重试方法 MyExecutorMethod params:[{}]", JsonUtil.toJsonString(obj));
log.info(SCENE + " params:[{}]", JsonUtil.toJsonString(obj));
return "测试成功";
}

View File

@ -0,0 +1,29 @@
package com.example.demo;
import com.aizuda.easy.retry.client.core.annotation.ExecutorMethodRegister;
import com.aizuda.easy.retry.client.core.strategy.ExecutorMethod;
import com.aizuda.easy.retry.common.core.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
/**
* 测试手动重试并同步上报任务
*
* @author: www.byteblogs.com
* @date : 2022-03-07 14:07
* @since 1.3.0
*/
@ExecutorMethodRegister(scene = CustomSyncCreateTask.SCENE, async = false, timeout = 10000, unit = TimeUnit.MILLISECONDS)
@Slf4j
public class CustomSyncCreateTask implements ExecutorMethod {
public static final String SCENE = "customSyncCreateTask";
@Override
public Object doExecute(Object obj) {
log.info(SCENE + " params:[{}]", JsonUtil.toJsonString(obj));
return "测试成功";
}
}

View File

@ -1,9 +1,9 @@
package com.example;
import cn.hutool.core.lang.Assert;
import com.aizuda.easy.retry.client.core.retryer.EasyRetryTemplate;
import com.aizuda.easy.retry.client.core.retryer.RetryTaskTemplateBuilder;
import com.example.demo.CustomCreateTask;
import com.example.demo.CustomAsyncCreateTask;
import com.example.demo.CustomSyncCreateTask;
import com.example.model.Cat;
import com.example.model.Zoo;
import lombok.extern.slf4j.Slf4j;
@ -11,10 +11,9 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDateTime;
import java.util.UUID;
/**
* @author: shuguang.zhang
* @author: www.byteblogs.com
* @date : 2023-05-10 13:47
*/
@SpringBootTest
@ -29,31 +28,28 @@ public class EasyRetryTemplateTest {
Zoo zoo = new Zoo();
zoo.setNow(LocalDateTime.now());
EasyRetryTemplate retryTemplate = RetryTaskTemplateBuilder.newBuilder()
.withExecutorMethod(CustomCreateTask.class)
.withExecutorMethod(CustomAsyncCreateTask.class)
.withParam(zoo)
.withScene(CustomCreateTask.SCENE)
.withScene(CustomAsyncCreateTask.SCENE)
.build();
retryTemplate.generateAsyncTask(true);
retryTemplate.executeRetry();
Thread.sleep(90000);
}
@Test
public void generateSyncTask() throws InterruptedException {
public void generateSyncTask() {
Cat cat = new Cat();
cat.setName("zsd");
Zoo zoo = new Zoo();
zoo.setNow(LocalDateTime.now());
EasyRetryTemplate retryTemplate = RetryTaskTemplateBuilder.newBuilder()
.withExecutorMethod(CustomCreateTask.class)
.withExecutorMethod(CustomSyncCreateTask.class)
.withParam(zoo)
.withScene(CustomCreateTask.SCENE)
.withScene(CustomSyncCreateTask.SCENE)
.build();
Boolean aBoolean = retryTemplate.generateSyncTask(true);
Assert.isTrue(aBoolean);
retryTemplate.executeRetry();
}
}