feat&fix: 1.1.0
1. 变更重试流量请求头信息 2. 修复管理界面日期显示问题 3. 停止策略改为责任链模式
This commit is contained in:
parent
9d2c4e3e83
commit
3275a24632
@ -2,7 +2,7 @@ package com.aizuda.easy.retry.client.core.intercepter;
|
||||
|
||||
import com.aizuda.easy.retry.common.core.constant.SystemConstants;
|
||||
import com.aizuda.easy.retry.common.core.log.LogUtils;
|
||||
import com.aizuda.easy.retry.common.core.model.XRetryHeaders;
|
||||
import com.aizuda.easy.retry.common.core.model.EasyRetryHeaders;
|
||||
import com.aizuda.easy.retry.common.core.util.JsonUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
@ -28,13 +28,13 @@ public class HeaderAspect {
|
||||
|
||||
public void before(){
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
String xRetry = attributes.getRequest().getHeader(SystemConstants.X_RETRY_HEAD_KEY);
|
||||
String xRetry = attributes.getRequest().getHeader(SystemConstants.EASY_RETRY_HEAD_KEY);
|
||||
if (Objects.nonNull(xRetry)) {
|
||||
// 标记进入方法的时间
|
||||
RetrySiteSnapshot.setEntryMethodTime(System.currentTimeMillis());
|
||||
|
||||
LogUtils.info(log, "easy-retry 拦截器 xRetry:[{}]", xRetry);
|
||||
RetrySiteSnapshot.setRetryHeader(JsonUtil.parseObject(xRetry, XRetryHeaders.class));
|
||||
RetrySiteSnapshot.setRetryHeader(JsonUtil.parseObject(xRetry, EasyRetryHeaders.class));
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ public class HeaderAspect {
|
||||
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
HttpServletResponse response = attributes.getResponse();
|
||||
response.addHeader(SystemConstants.X_RETRY_STATUS_CODE_KEY, RetrySiteSnapshot.getRetryStatusCode());
|
||||
response.addHeader(SystemConstants.EASY_RETRY_STATUS_CODE_KEY, RetrySiteSnapshot.getRetryStatusCode());
|
||||
|
||||
// 服务端重试的在com.x.retry.client.core.client.RetryEndPoint 中进行清除threadLocal
|
||||
if (Objects.nonNull(RetrySiteSnapshot.getStage()) && RetrySiteSnapshot.EnumStage.REMOTE.getStage() == RetrySiteSnapshot.getStage()) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package com.aizuda.easy.retry.client.core.intercepter;
|
||||
|
||||
import com.aizuda.easy.retry.common.core.constant.SystemConstants;
|
||||
import com.aizuda.easy.retry.common.core.model.XRetryHeaders;
|
||||
import com.aizuda.easy.retry.common.core.model.EasyRetryHeaders;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Objects;
|
||||
@ -32,7 +32,7 @@ public class RetrySiteSnapshot {
|
||||
/**
|
||||
* 重试请求头
|
||||
*/
|
||||
private static final ThreadLocal<XRetryHeaders> RETRY_HEADER = new ThreadLocal<>();
|
||||
private static final ThreadLocal<EasyRetryHeaders> RETRY_HEADER = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
@ -76,11 +76,11 @@ public class RetrySiteSnapshot {
|
||||
return EnumStatus.RUNNING.status == getStatus();
|
||||
}
|
||||
|
||||
public static XRetryHeaders getRetryHeader() {
|
||||
public static EasyRetryHeaders getRetryHeader() {
|
||||
return RETRY_HEADER.get();
|
||||
}
|
||||
|
||||
public static void setRetryHeader(XRetryHeaders headers) {
|
||||
public static void setRetryHeader(EasyRetryHeaders headers) {
|
||||
RETRY_HEADER.set(headers);
|
||||
}
|
||||
|
||||
@ -88,9 +88,9 @@ public class RetrySiteSnapshot {
|
||||
* 是否是重试流量
|
||||
*/
|
||||
public static boolean isRetryFlow() {
|
||||
XRetryHeaders retryHeader = getRetryHeader();
|
||||
EasyRetryHeaders retryHeader = getRetryHeader();
|
||||
if (Objects.nonNull(retryHeader)) {
|
||||
return retryHeader.isXRetry();
|
||||
return retryHeader.isEasyRetry();
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -105,7 +105,7 @@ public class RetrySiteSnapshot {
|
||||
}
|
||||
|
||||
public static boolean isRetryForStatusCode() {
|
||||
return Objects.nonNull(getRetryStatusCode()) && getRetryStatusCode().equals(SystemConstants.X_RETRY_STATUS_CODE);
|
||||
return Objects.nonNull(getRetryStatusCode()) && getRetryStatusCode().equals(SystemConstants.EASY_RETRY_STATUS_CODE);
|
||||
}
|
||||
|
||||
public static Long getEntryMethodTime() {
|
||||
|
@ -4,7 +4,7 @@ import com.aizuda.easy.retry.client.core.exception.EasyRetryClientException;
|
||||
import com.aizuda.easy.retry.client.core.intercepter.RetrySiteSnapshot;
|
||||
import com.aizuda.easy.retry.common.core.constant.SystemConstants;
|
||||
import com.aizuda.easy.retry.common.core.log.LogUtils;
|
||||
import com.aizuda.easy.retry.common.core.model.XRetryHeaders;
|
||||
import com.aizuda.easy.retry.common.core.model.EasyRetryHeaders;
|
||||
import com.aizuda.easy.retry.common.core.util.JsonUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@ -30,7 +30,7 @@ public class RequestHeaderPlugins {
|
||||
public static Map<String, String> requestHeader() {
|
||||
|
||||
Map<String, String> header = new HashMap<>();
|
||||
XRetryHeaders retryHeader = RetrySiteSnapshot.getRetryHeader();
|
||||
EasyRetryHeaders retryHeader = RetrySiteSnapshot.getRetryHeader();
|
||||
|
||||
// 传递请求头
|
||||
if (Objects.nonNull(retryHeader)) {
|
||||
@ -44,7 +44,7 @@ public class RequestHeaderPlugins {
|
||||
throw new EasyRetryClientException("调用链超时, 不在继续调用后面请求");
|
||||
}
|
||||
|
||||
header.put(SystemConstants.X_RETRY_HEAD_KEY, JsonUtil.toJsonString(retryHeader));
|
||||
header.put(SystemConstants.EASY_RETRY_HEAD_KEY, JsonUtil.toJsonString(retryHeader));
|
||||
}
|
||||
|
||||
return header;
|
||||
|
@ -23,8 +23,8 @@ public class ResponseHeaderPlugins {
|
||||
public static void responseHeader(Map<String, List<String>> header) {
|
||||
|
||||
// 获取不重试标志
|
||||
if (header.containsKey(SystemConstants.X_RETRY_STATUS_CODE_KEY)) {
|
||||
List<String> statusCode = header.get(SystemConstants.X_RETRY_STATUS_CODE_KEY);
|
||||
if (header.containsKey(SystemConstants.EASY_RETRY_STATUS_CODE_KEY)) {
|
||||
List<String> statusCode = header.get(SystemConstants.EASY_RETRY_STATUS_CODE_KEY);
|
||||
RetrySiteSnapshot.setRetryStatusCode(statusCode.get(0));
|
||||
}
|
||||
}
|
||||
|
@ -9,17 +9,17 @@ public class SystemConstants {
|
||||
/**
|
||||
* 请求头 key
|
||||
*/
|
||||
public static final String X_RETRY_HEAD_KEY = "easy-retry";
|
||||
public static final String EASY_RETRY_HEAD_KEY = "easy-retry";
|
||||
|
||||
/**
|
||||
* 异常重试码 key
|
||||
*/
|
||||
public static final String X_RETRY_STATUS_CODE_KEY = "easy-retry-status";
|
||||
public static final String EASY_RETRY_STATUS_CODE_KEY = "easy-retry-status";
|
||||
|
||||
/**
|
||||
* 异常重试码
|
||||
*/
|
||||
public static final String X_RETRY_STATUS_CODE = "519";
|
||||
public static final String EASY_RETRY_STATUS_CODE = "519";
|
||||
|
||||
|
||||
|
||||
|
@ -9,17 +9,17 @@ import lombok.Data;
|
||||
* @date : 2022-04-16 22:20
|
||||
*/
|
||||
@Data
|
||||
public class XRetryHeaders {
|
||||
public class EasyRetryHeaders {
|
||||
|
||||
/**
|
||||
* 是否是重试流量
|
||||
*/
|
||||
private boolean xRetry;
|
||||
private boolean easyRetry;
|
||||
|
||||
/**
|
||||
* 重试下发的ID
|
||||
*/
|
||||
private String xRetryId;
|
||||
private String easyRetryId;
|
||||
|
||||
/**
|
||||
* 调用链超时时间 单位毫秒(ms)
|
@ -14,10 +14,12 @@ import java.sql.Timestamp;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* @author: byteblogs
|
||||
@ -107,6 +109,8 @@ public class JsonUtil {
|
||||
* 内部类,处理Json
|
||||
*/
|
||||
public static class JsonMapper {
|
||||
private static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
|
||||
private static String YYYY_MM_DD = "yyyy-MM-dd";
|
||||
|
||||
private static ObjectMapper objectMapper = jacksonObjectMapper();
|
||||
|
||||
@ -114,6 +118,8 @@ public class JsonUtil {
|
||||
|
||||
// 初始化全局Jackson 序列化工具
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS);
|
||||
DateTimeFormatter localDateFormatter = DateTimeFormatter.ofPattern(YYYY_MM_DD);
|
||||
|
||||
// 忽略未知属性
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
@ -129,27 +135,27 @@ public class JsonUtil {
|
||||
javaTimeModule.addSerializer(LocalDate.class, new JsonSerializer<LocalDate>() {
|
||||
@Override
|
||||
public void serialize(LocalDate localDate, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
|
||||
jsonGenerator.writeNumber(String.valueOf(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli()));
|
||||
jsonGenerator.writeString(localDate.format(localDateFormatter));
|
||||
}
|
||||
});
|
||||
javaTimeModule.addSerializer(LocalDateTime.class, new JsonSerializer<LocalDateTime>() {
|
||||
@Override
|
||||
public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
|
||||
jsonGenerator.writeNumber(String.valueOf(localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()));
|
||||
jsonGenerator.writeString(localDateTime.format(dateTimeFormatter));
|
||||
}
|
||||
});
|
||||
javaTimeModule.addDeserializer(LocalDate.class, new JsonDeserializer<LocalDate>() {
|
||||
@Override
|
||||
public LocalDate deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
|
||||
String value = jsonParser.getValueAsString();
|
||||
return StringUtils.isBlank(value) ? null : new Timestamp(Long.valueOf(value.trim())).toLocalDateTime().toLocalDate();
|
||||
return StringUtils.isBlank(value) ? null : LocalDateTime.parse(value, DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS)).toLocalDate();
|
||||
}
|
||||
});
|
||||
javaTimeModule.addDeserializer(LocalDateTime.class, new JsonDeserializer<LocalDateTime>() {
|
||||
@Override
|
||||
public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
|
||||
String value = jsonParser.getValueAsString();
|
||||
return StringUtils.isBlank(value) ? null : new Timestamp(Long.valueOf(value.trim())).toLocalDateTime();
|
||||
return StringUtils.isBlank(value) ? null : LocalDateTime.parse(value, DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -26,6 +26,7 @@ public class RetryTaskLogServiceImpl implements RetryTaskLogService {
|
||||
private RetryTaskLogMapper retryTaskLogMapper;
|
||||
|
||||
private RetryTaskLogResponseVOConverter retryTaskLogResponseVOConverter = new RetryTaskLogResponseVOConverter();
|
||||
|
||||
@Override
|
||||
public PageResult<List<RetryTaskLogResponseVO>> getRetryTaskLogPage(RetryTaskLogQueryVO queryVO) {
|
||||
|
||||
@ -46,7 +47,7 @@ public class RetryTaskLogServiceImpl implements RetryTaskLogService {
|
||||
}
|
||||
|
||||
retryTaskLogLambdaQueryWrapper.select(RetryTaskLog::getGroupName, RetryTaskLog::getId, RetryTaskLog::getSceneName,
|
||||
RetryTaskLog::getBizId, RetryTaskLog::getBizNo, RetryTaskLog::getErrorMessage, RetryTaskLog::getRetryStatus);
|
||||
RetryTaskLog::getBizId, RetryTaskLog::getBizNo, RetryTaskLog::getErrorMessage, RetryTaskLog::getRetryStatus, RetryTaskLog::getCreateDt);
|
||||
PageDTO<RetryTaskLog> retryTaskLogPageDTO = retryTaskLogMapper.selectPage(pageDTO, retryTaskLogLambdaQueryWrapper.orderByDesc(RetryTaskLog::getCreateDt));
|
||||
|
||||
return new PageResult<>(
|
||||
|
@ -22,6 +22,20 @@ public interface RetryContext<V> {
|
||||
*/
|
||||
void setCallResult(V v);
|
||||
|
||||
/**
|
||||
* 调用客户端发生异常信息
|
||||
*
|
||||
* @param e 异常
|
||||
*/
|
||||
void setException(Exception e);
|
||||
|
||||
/**
|
||||
* 是否发生异常
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean hasException();
|
||||
|
||||
/**
|
||||
* 等待策略
|
||||
*
|
||||
|
@ -15,4 +15,21 @@ public interface StopStrategy {
|
||||
* @return
|
||||
*/
|
||||
boolean shouldStop(RetryContext retryContext);
|
||||
|
||||
/**
|
||||
* 是否触发此停止策略
|
||||
*
|
||||
* @param retryContext {@link RetryContext} 重试上下文
|
||||
* @return
|
||||
*/
|
||||
boolean supports(RetryContext retryContext);
|
||||
|
||||
/**
|
||||
* 按照正序排列重试过滤器
|
||||
* 若相同则按照加入的顺序
|
||||
*
|
||||
* @return 排序的值
|
||||
*/
|
||||
int order();
|
||||
|
||||
}
|
||||
|
@ -5,7 +5,9 @@ import com.aizuda.easy.retry.server.persistence.mybatis.po.ServerNode;
|
||||
import com.aizuda.easy.retry.server.support.RetryContext;
|
||||
import com.aizuda.easy.retry.server.support.WaitStrategy;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@ -15,6 +17,7 @@ import java.util.Set;
|
||||
* @date : 2021-11-29 18:32
|
||||
*/
|
||||
@Data
|
||||
@Getter
|
||||
public class MaxAttemptsPersistenceRetryContext<V> implements RetryContext<V> {
|
||||
|
||||
/**
|
||||
@ -22,6 +25,11 @@ public class MaxAttemptsPersistenceRetryContext<V> implements RetryContext<V> {
|
||||
*/
|
||||
private V callResult;
|
||||
|
||||
/**
|
||||
* 异常信息
|
||||
*/
|
||||
private Exception exception;
|
||||
|
||||
/**
|
||||
* 等待策略
|
||||
*/
|
||||
@ -46,4 +54,14 @@ public class MaxAttemptsPersistenceRetryContext<V> implements RetryContext<V> {
|
||||
public void setCallResult(V v) {
|
||||
this.callResult = v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setException(Exception e) {
|
||||
this.exception = e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasException() {
|
||||
return Objects.nonNull(exception);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import com.aizuda.easy.retry.client.model.RetryCallbackDTO;
|
||||
import com.aizuda.easy.retry.common.core.constant.SystemConstants;
|
||||
import com.aizuda.easy.retry.common.core.log.LogUtils;
|
||||
import com.aizuda.easy.retry.common.core.model.Result;
|
||||
import com.aizuda.easy.retry.common.core.model.XRetryHeaders;
|
||||
import com.aizuda.easy.retry.common.core.model.EasyRetryHeaders;
|
||||
import com.aizuda.easy.retry.common.core.util.JsonUtil;
|
||||
import com.aizuda.easy.retry.server.persistence.mybatis.po.RetryTask;
|
||||
import com.aizuda.easy.retry.server.persistence.mybatis.po.ServerNode;
|
||||
@ -62,10 +62,10 @@ public class CallbackRetryResultActor extends AbstractActor {
|
||||
|
||||
// 设置header
|
||||
HttpHeaders requestHeaders = new HttpHeaders();
|
||||
XRetryHeaders xRetryHeaders = new XRetryHeaders();
|
||||
xRetryHeaders.setXRetry(Boolean.TRUE);
|
||||
xRetryHeaders.setXRetryId(IdUtil.simpleUUID());
|
||||
requestHeaders.add(SystemConstants.X_RETRY_HEAD_KEY, JsonUtil.toJsonString(xRetryHeaders));
|
||||
EasyRetryHeaders easyRetryHeaders = new EasyRetryHeaders();
|
||||
easyRetryHeaders.setEasyRetry(Boolean.TRUE);
|
||||
easyRetryHeaders.setEasyRetryId(IdUtil.simpleUUID());
|
||||
requestHeaders.add(SystemConstants.EASY_RETRY_HEAD_KEY, JsonUtil.toJsonString(easyRetryHeaders));
|
||||
|
||||
HttpEntity<RetryCallbackDTO> requestEntity = new HttpEntity<>(retryCallbackDTO, requestHeaders);
|
||||
|
||||
|
@ -8,7 +8,7 @@ import com.aizuda.easy.retry.client.model.DispatchRetryResultDTO;
|
||||
import com.aizuda.easy.retry.common.core.constant.SystemConstants;
|
||||
import com.aizuda.easy.retry.common.core.log.LogUtils;
|
||||
import com.aizuda.easy.retry.common.core.model.Result;
|
||||
import com.aizuda.easy.retry.common.core.model.XRetryHeaders;
|
||||
import com.aizuda.easy.retry.common.core.model.EasyRetryHeaders;
|
||||
import com.aizuda.easy.retry.common.core.util.JsonUtil;
|
||||
import com.aizuda.easy.retry.server.exception.EasyRetryServerException;
|
||||
import com.aizuda.easy.retry.server.persistence.mybatis.mapper.RetryTaskLogMapper;
|
||||
@ -72,7 +72,10 @@ public class ExecUnitActor extends AbstractActor {
|
||||
try {
|
||||
|
||||
if (Objects.nonNull(serverNode)) {
|
||||
Object call = retryExecutor.call((Callable<Result<DispatchRetryResultDTO>>) () -> callClient(retryTask, retryTaskLog, serverNode));
|
||||
retryExecutor.call((Callable<Result<DispatchRetryResultDTO>>) () -> callClient(retryTask, retryTaskLog, serverNode));
|
||||
if (context.hasException()) {
|
||||
retryTaskLog.setErrorMessage(context.getException().getMessage());
|
||||
}
|
||||
} else {
|
||||
retryTaskLog.setErrorMessage("暂无可用的客户端POD");
|
||||
}
|
||||
@ -113,10 +116,10 @@ public class ExecUnitActor extends AbstractActor {
|
||||
|
||||
// 设置header
|
||||
HttpHeaders requestHeaders = new HttpHeaders();
|
||||
XRetryHeaders xRetryHeaders = new XRetryHeaders();
|
||||
xRetryHeaders.setXRetry(Boolean.TRUE);
|
||||
xRetryHeaders.setXRetryId(IdUtil.simpleUUID());
|
||||
requestHeaders.add(SystemConstants.X_RETRY_HEAD_KEY, JsonUtil.toJsonString(xRetryHeaders));
|
||||
EasyRetryHeaders easyRetryHeaders = new EasyRetryHeaders();
|
||||
easyRetryHeaders.setEasyRetry(Boolean.TRUE);
|
||||
easyRetryHeaders.setEasyRetryId(IdUtil.simpleUUID());
|
||||
requestHeaders.add(SystemConstants.EASY_RETRY_HEAD_KEY, JsonUtil.toJsonString(easyRetryHeaders));
|
||||
|
||||
HttpEntity<DispatchRetryDTO> requestEntity = new HttpEntity<>(dispatchRetryDTO, requestHeaders);
|
||||
|
||||
|
@ -107,6 +107,7 @@ public class ScanGroupActor extends AbstractActor {
|
||||
retryContext.setServerNode(clientNodeAllocateHandler.getServerNode(retryTask.getGroupName()));
|
||||
|
||||
RetryExecutor<Result<DispatchRetryResultDTO>> executor = RetryBuilder.<Result<DispatchRetryResultDTO>>newBuilder()
|
||||
.withStopStrategy(StopStrategies.stopException())
|
||||
.withStopStrategy(StopStrategies.stopResultStatus())
|
||||
.withWaitStrategy(getWaitWaitStrategy(groupName, retryTask.getSceneName()))
|
||||
.withFilterStrategy(FilterStrategies.delayLevelFilter())
|
||||
|
@ -17,7 +17,7 @@ import java.util.*;
|
||||
*/
|
||||
public class RetryBuilder<V> {
|
||||
|
||||
private StopStrategy stopStrategy;
|
||||
private List<StopStrategy> stopStrategies;
|
||||
private WaitStrategy waitStrategy;
|
||||
private List<FilterStrategy> filterStrategies;
|
||||
private RetryContext<V> retryContext;
|
||||
@ -41,7 +41,11 @@ public class RetryBuilder<V> {
|
||||
}
|
||||
|
||||
public RetryBuilder<V> withStopStrategy(StopStrategy stopStrategy) {
|
||||
this.stopStrategy = stopStrategy;
|
||||
if (CollectionUtils.isEmpty(stopStrategies)) {
|
||||
stopStrategies = new ArrayList<>();
|
||||
}
|
||||
|
||||
stopStrategies.add(stopStrategy);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -56,14 +60,16 @@ public class RetryBuilder<V> {
|
||||
throw new EasyRetryServerException("waitStrategy 不能为null");
|
||||
}
|
||||
|
||||
if (Objects.isNull(stopStrategy)) {
|
||||
throw new EasyRetryServerException("stopStrategy 不能为null");
|
||||
}
|
||||
|
||||
if (Objects.isNull(retryContext)) {
|
||||
throw new EasyRetryServerException("retryContext 不能为null");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(stopStrategies)) {
|
||||
stopStrategies = Collections.EMPTY_LIST;
|
||||
} else {
|
||||
stopStrategies.sort(Comparator.comparingInt(StopStrategy::order));
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(filterStrategies)) {
|
||||
filterStrategies = Collections.EMPTY_LIST;
|
||||
} else {
|
||||
@ -72,7 +78,7 @@ public class RetryBuilder<V> {
|
||||
|
||||
retryContext.setWaitStrategy(waitStrategy);
|
||||
|
||||
return new RetryExecutor<V>(stopStrategy, waitStrategy, filterStrategies, retryContext);
|
||||
return new RetryExecutor<V>(stopStrategies, waitStrategy, filterStrategies, retryContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import com.aizuda.easy.retry.server.support.WaitStrategy;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
@ -20,16 +21,16 @@ import java.util.concurrent.Callable;
|
||||
@Slf4j
|
||||
public class RetryExecutor<V> {
|
||||
|
||||
private final StopStrategy stopStrategy;
|
||||
private final List<StopStrategy> stopStrategies;
|
||||
private final WaitStrategy waitStrategy;
|
||||
private final List<FilterStrategy> filterStrategies;
|
||||
private final RetryContext<V> retryContext;
|
||||
|
||||
public RetryExecutor(StopStrategy stopStrategy,
|
||||
public RetryExecutor(List<StopStrategy> stopStrategies,
|
||||
WaitStrategy waitStrategy,
|
||||
List<FilterStrategy> filterStrategies,
|
||||
RetryContext<V> retryContext) {
|
||||
this.stopStrategy = stopStrategy;
|
||||
this.stopStrategies = stopStrategies;
|
||||
this.waitStrategy = waitStrategy;
|
||||
this.filterStrategies = filterStrategies;
|
||||
this.retryContext = retryContext;
|
||||
@ -59,17 +60,29 @@ public class RetryExecutor<V> {
|
||||
V call = null;
|
||||
try {
|
||||
call = callable.call();
|
||||
retryContext.setCallResult(call);
|
||||
} catch (Exception e) {
|
||||
log.error("客户端执行失败: [{}]", retryContext.getRetryTask());
|
||||
retryContext.setException(e);
|
||||
}
|
||||
|
||||
retryContext.setCallResult(call);
|
||||
|
||||
// 计算下次触发时间
|
||||
retryContext.getRetryTask().setNextTriggerAt(waitStrategy.computeRetryTime(retryContext));
|
||||
|
||||
boolean isStop = Boolean.TRUE;
|
||||
|
||||
// 触发停止策略判断
|
||||
for (StopStrategy stopStrategy : stopStrategies) {
|
||||
if (stopStrategy.supports(retryContext)) {
|
||||
// 必须责任链中的所有停止策略都判断为停止,此时才判定为重试完成
|
||||
if (!stopStrategy.shouldStop(retryContext)) {
|
||||
isStop = Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActorRef actorRef;
|
||||
if (stopStrategy.shouldStop(retryContext)) {
|
||||
if (isStop) {
|
||||
// 状态变为完成 FinishActor
|
||||
actorRef = ActorGenerator.finishActor();
|
||||
} else {
|
||||
@ -78,6 +91,7 @@ public class RetryExecutor<V> {
|
||||
}
|
||||
|
||||
actorRef.tell(retryContext.getRetryTask(), actorRef);
|
||||
|
||||
return call;
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,16 @@ public class StopStrategies {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据重试结果终止
|
||||
* 调用客户端发生异常触发停止策略
|
||||
*
|
||||
* @return {@link ExceptionStopStrategy} 调用客户端发生异常触发停止策略
|
||||
*/
|
||||
public static StopStrategy stopException() {
|
||||
return new ExceptionStopStrategy();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据客户端返回结果判断是否终止重试
|
||||
*
|
||||
* @return {@link ResultStatusStopStrategy} 重试结果停止策略
|
||||
*/
|
||||
@ -31,7 +40,28 @@ public class StopStrategies {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据重试结果集判断是否应该停止
|
||||
* 调用客户端发生异常触发停止策略
|
||||
*/
|
||||
private static final class ExceptionStopStrategy implements StopStrategy {
|
||||
|
||||
@Override
|
||||
public boolean shouldStop(RetryContext retryContext) {
|
||||
return !retryContext.hasException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(RetryContext retryContext) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int order() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据客户端返回结果集判断是否应该停止
|
||||
*
|
||||
* 1、{@link Result#getStatus()} 不为1 则继续重试
|
||||
* 2、根据{@link Result#getData()}中的statusCode判断是否停止
|
||||
@ -59,6 +89,16 @@ public class StopStrategies {
|
||||
Integer status = RetryResultStatusEnum.getRetryResultStatusEnum(statusCode).getStatus();
|
||||
return RetryResultStatusEnum.SUCCESS.getStatus().equals(status) || RetryResultStatusEnum.STOP.getStatus().equals(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(RetryContext retryContext) {
|
||||
return retryContext instanceof MaxAttemptsPersistenceRetryContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int order() {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
<!DOCTYPE html><html lang="zh-cmn-Hans"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/logo.png"><title>Easy-Retry</title><style>.first-loading-wrp{display:flex;justify-content:center;align-items:center;flex-direction:column;min-height:420px;height:100%}.first-loading-wrp>h1{font-size:128px}.first-loading-wrp .loading-wrp{padding:98px;display:flex;justify-content:center;align-items:center}.dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style><link href="/css/chunk-39be5683.cd4961ff.css" rel="prefetch"><link href="/css/chunk-4a2f6b49.a04e8eb5.css" rel="prefetch"><link href="/css/chunk-77aaac2a.99cbbcce.css" rel="prefetch"><link href="/css/user.6ccd4506.css" rel="prefetch"><link href="/js/chunk-08798c7e.984d2f3a.js" rel="prefetch"><link href="/js/chunk-1bf36f2c.ab7c2647.js" rel="prefetch"><link href="/js/chunk-244a93b4.d6cff8a4.js" rel="prefetch"><link href="/js/chunk-2515aa86.c58c1b7a.js" rel="prefetch"><link href="/js/chunk-2672acfa.bc252574.js" rel="prefetch"><link href="/js/chunk-2d21a08f.667d7e9a.js" rel="prefetch"><link href="/js/chunk-35f6c8ac.bf2fbc64.js" rel="prefetch"><link href="/js/chunk-39be5683.17f16b23.js" rel="prefetch"><link href="/js/chunk-4a2f6b49.745ee7e8.js" rel="prefetch"><link href="/js/chunk-71608044.5af14532.js" rel="prefetch"><link href="/js/chunk-77aaac2a.a8c64402.js" rel="prefetch"><link href="/js/chunk-bf3addda.365f48f3.js" rel="prefetch"><link href="/js/fail.40283b49.js" rel="prefetch"><link href="/js/lang-zh-CN-account-settings.f8f25eaf.js" rel="prefetch"><link href="/js/lang-zh-CN-account.f7a734c3.js" rel="prefetch"><link href="/js/lang-zh-CN-dashboard-analysis.d7cabd65.js" rel="prefetch"><link href="/js/lang-zh-CN-dashboard.ffd6ecbd.js" rel="prefetch"><link href="/js/lang-zh-CN-form-basicForm.ff3088ac.js" rel="prefetch"><link href="/js/lang-zh-CN-form.39cd9999.js" rel="prefetch"><link href="/js/lang-zh-CN-global.bf0df5c8.js" rel="prefetch"><link href="/js/lang-zh-CN-menu.25425a62.js" rel="prefetch"><link href="/js/lang-zh-CN-result-fail.232762aa.js" rel="prefetch"><link href="/js/lang-zh-CN-result-success.3519c60c.js" rel="prefetch"><link href="/js/lang-zh-CN-result.b3df3bc6.js" rel="prefetch"><link href="/js/lang-zh-CN-setting.8c2ce690.js" rel="prefetch"><link href="/js/lang-zh-CN-user.81513cba.js" rel="prefetch"><link href="/js/lang-zh-CN.6ae67127.js" rel="prefetch"><link href="/js/user.a4cdfea5.js" rel="prefetch"><link href="/css/app.c8c81dfd.css" rel="preload" as="style"><link href="/css/chunk-vendors.f716a607.css" rel="preload" as="style"><link href="/js/app.58d24c5a.js" rel="preload" as="script"><link href="/js/chunk-vendors.b8c3b6d4.js" rel="preload" as="script"><link href="/css/chunk-vendors.f716a607.css" rel="stylesheet"><link href="/css/app.c8c81dfd.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"><div class="first-loading-wrp"><h2>Easy-Retry</h2><div class="loading-wrp"><span class="dot dot-spin"><i></i><i></i><i></i><i></i></span></div><div style="display: flex; justify-content: center; align-items: center;">分布式重试服务平台</div></div></div><script src="//cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js"></script><script src="//cdn.jsdelivr.net/npm/vue-router@3.5.1/dist/vue-router.min.js"></script><script src="//cdn.jsdelivr.net/npm/vuex@3.1.1/dist/vuex.min.js"></script><script src="//cdn.jsdelivr.net/npm/axios@0.21.1/dist/axios.min.js"></script><script src="/js/chunk-vendors.b8c3b6d4.js"></script><script src="/js/app.58d24c5a.js"></script></body></html>
|
||||
<!DOCTYPE html><html lang="zh-cmn-Hans"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/logo.png"><title>Easy-Retry</title><style>.first-loading-wrp{display:flex;justify-content:center;align-items:center;flex-direction:column;min-height:420px;height:100%}.first-loading-wrp>h1{font-size:128px}.first-loading-wrp .loading-wrp{padding:98px;display:flex;justify-content:center;align-items:center}.dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style><link href="/css/chunk-39be5683.cd4961ff.css" rel="prefetch"><link href="/css/chunk-4a2f6b49.a04e8eb5.css" rel="prefetch"><link href="/css/chunk-77aaac2a.99cbbcce.css" rel="prefetch"><link href="/css/user.6ccd4506.css" rel="prefetch"><link href="/js/chunk-08798c7e.bfbd5a35.js" rel="prefetch"><link href="/js/chunk-1bf36f2c.b33b2536.js" rel="prefetch"><link href="/js/chunk-244a93b4.89d86add.js" rel="prefetch"><link href="/js/chunk-2515aa86.c6f32c4c.js" rel="prefetch"><link href="/js/chunk-2672acfa.a6a69a7f.js" rel="prefetch"><link href="/js/chunk-2d21a08f.10f9edd8.js" rel="prefetch"><link href="/js/chunk-35f6c8ac.4166a8c0.js" rel="prefetch"><link href="/js/chunk-39be5683.8052daba.js" rel="prefetch"><link href="/js/chunk-4a2f6b49.8726a153.js" rel="prefetch"><link href="/js/chunk-71608044.e21176f4.js" rel="prefetch"><link href="/js/chunk-77aaac2a.a8f6aa61.js" rel="prefetch"><link href="/js/chunk-bf3addda.fece787c.js" rel="prefetch"><link href="/js/fail.f78b643f.js" rel="prefetch"><link href="/js/lang-zh-CN-account-settings.f8f25eaf.js" rel="prefetch"><link href="/js/lang-zh-CN-account.f7a734c3.js" rel="prefetch"><link href="/js/lang-zh-CN-dashboard-analysis.d7cabd65.js" rel="prefetch"><link href="/js/lang-zh-CN-dashboard.ffd6ecbd.js" rel="prefetch"><link href="/js/lang-zh-CN-form-basicForm.ff3088ac.js" rel="prefetch"><link href="/js/lang-zh-CN-form.39cd9999.js" rel="prefetch"><link href="/js/lang-zh-CN-global.bf0df5c8.js" rel="prefetch"><link href="/js/lang-zh-CN-menu.25425a62.js" rel="prefetch"><link href="/js/lang-zh-CN-result-fail.232762aa.js" rel="prefetch"><link href="/js/lang-zh-CN-result-success.3519c60c.js" rel="prefetch"><link href="/js/lang-zh-CN-result.b3df3bc6.js" rel="prefetch"><link href="/js/lang-zh-CN-setting.8c2ce690.js" rel="prefetch"><link href="/js/lang-zh-CN-user.81513cba.js" rel="prefetch"><link href="/js/lang-zh-CN.6ae67127.js" rel="prefetch"><link href="/js/user.d6fd464c.js" rel="prefetch"><link href="/css/app.c8c81dfd.css" rel="preload" as="style"><link href="/css/chunk-vendors.f716a607.css" rel="preload" as="style"><link href="/js/app.a2b44034.js" rel="preload" as="script"><link href="/js/chunk-vendors.b8c3b6d4.js" rel="preload" as="script"><link href="/css/chunk-vendors.f716a607.css" rel="stylesheet"><link href="/css/app.c8c81dfd.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"><div class="first-loading-wrp"><h2>Easy-Retry</h2><div class="loading-wrp"><span class="dot dot-spin"><i></i><i></i><i></i><i></i></span></div><div style="display: flex; justify-content: center; align-items: center;">分布式重试服务平台</div></div></div><script src="//cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js"></script><script src="//cdn.jsdelivr.net/npm/vue-router@3.5.1/dist/vue-router.min.js"></script><script src="//cdn.jsdelivr.net/npm/vuex@3.1.1/dist/vuex.min.js"></script><script src="//cdn.jsdelivr.net/npm/axios@0.21.1/dist/axios.min.js"></script><script src="/js/chunk-vendors.b8c3b6d4.js"></script><script src="/js/app.a2b44034.js"></script></body></html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2308
example/log/sys.log
Normal file
2308
example/log/sys.log
Normal file
File diff suppressed because it is too large
Load Diff
@ -25,11 +25,11 @@ public class SchoolController {
|
||||
@GetMapping("/id")
|
||||
public Result getSchool(HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
String header = request.getHeader(SystemConstants.X_RETRY_HEAD_KEY);
|
||||
String header = request.getHeader(SystemConstants.EASY_RETRY_HEAD_KEY);
|
||||
System.out.println(header);
|
||||
|
||||
if (RetrySiteSnapshot.isRetryFlow()) {
|
||||
response.addHeader(SystemConstants.X_RETRY_STATUS_CODE_KEY, SystemConstants.X_RETRY_STATUS_CODE);
|
||||
response.addHeader(SystemConstants.EASY_RETRY_STATUS_CODE_KEY, SystemConstants.EASY_RETRY_STATUS_CODE);
|
||||
}
|
||||
|
||||
if (true) {
|
||||
|
@ -35,7 +35,6 @@ export default {
|
||||
},
|
||||
viewCharts (viewRecords, type = 'day') {
|
||||
var ds = new DataSet()
|
||||
console.log(viewRecords)
|
||||
if (viewRecords === undefined || viewRecords === null) {
|
||||
return
|
||||
}
|
||||
|
@ -110,7 +110,6 @@ import AInput from 'ant-design-vue/es/input/Input'
|
||||
import Edit from '@/views/list/table/Edit'
|
||||
import { getAllGroupNameList, getRetryTaskPage, getSceneList, updateRetryTaskStatus, batchDelete } from '@/api/manage'
|
||||
import { STable } from '@/components'
|
||||
import moment from 'moment'
|
||||
import SaveRetryTask from './form/SaveRetryTask'
|
||||
import BatchUpdateRetryTaskInfo from './form/BatchUpdateRetryTaskInfo'
|
||||
|
||||
@ -167,8 +166,7 @@ export default {
|
||||
{
|
||||
title: '下次触发时间',
|
||||
dataIndex: 'nextTriggerAt',
|
||||
needTotal: false,
|
||||
customRender: (text) => moment(text).format('YYYY-MM-DD HH:mm:ss')
|
||||
needTotal: false
|
||||
},
|
||||
{
|
||||
title: '重试次数',
|
||||
@ -183,8 +181,7 @@ export default {
|
||||
{
|
||||
title: '更新时间',
|
||||
dataIndex: 'updateDt',
|
||||
sorter: true,
|
||||
customRender: (text) => moment(text).format('YYYY-MM-DD HH:mm:ss')
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
|
Loading…
Reference in New Issue
Block a user