feat(sj_1.1.0): 工作流全局上下问题
This commit is contained in:
parent
8d6213d165
commit
a7029da547
@ -1,10 +1,15 @@
|
|||||||
package com.aizuda.snailjob.template.datasource.persistence.po;
|
package com.aizuda.snailjob.template.datasource.persistence.po;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工作流批次
|
* 工作流批次
|
||||||
*
|
*
|
||||||
@ -66,4 +71,15 @@ public class WorkflowTaskBatch extends CreateUpdateDt {
|
|||||||
*/
|
*/
|
||||||
private Integer deleted;
|
private Integer deleted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 版本号
|
||||||
|
*/
|
||||||
|
@TableField(value = "version", update = "%s+1")
|
||||||
|
private Integer version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局上下文
|
||||||
|
*/
|
||||||
|
private String wfContext;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -42,4 +42,6 @@ public class JobExecutorResultDTO {
|
|||||||
|
|
||||||
private Integer isLeaf;
|
private Integer isLeaf;
|
||||||
|
|
||||||
|
private String wfContext;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,14 +14,13 @@ import com.aizuda.snailjob.server.job.task.support.JobTaskConverter;
|
|||||||
import com.aizuda.snailjob.server.job.task.support.JobTaskStopHandler;
|
import com.aizuda.snailjob.server.job.task.support.JobTaskStopHandler;
|
||||||
import com.aizuda.snailjob.server.job.task.support.handler.DistributedLockHandler;
|
import com.aizuda.snailjob.server.job.task.support.handler.DistributedLockHandler;
|
||||||
import com.aizuda.snailjob.server.job.task.support.handler.JobTaskBatchHandler;
|
import com.aizuda.snailjob.server.job.task.support.handler.JobTaskBatchHandler;
|
||||||
|
import com.aizuda.snailjob.server.job.task.support.handler.WorkflowBatchHandler;
|
||||||
import com.aizuda.snailjob.server.job.task.support.stop.JobTaskStopFactory;
|
import com.aizuda.snailjob.server.job.task.support.stop.JobTaskStopFactory;
|
||||||
import com.aizuda.snailjob.server.job.task.support.stop.TaskStopJobContext;
|
import com.aizuda.snailjob.server.job.task.support.stop.TaskStopJobContext;
|
||||||
import com.aizuda.snailjob.template.datasource.persistence.mapper.JobTaskMapper;
|
import com.aizuda.snailjob.template.datasource.persistence.mapper.JobTaskMapper;
|
||||||
import com.aizuda.snailjob.template.datasource.persistence.po.JobTask;
|
import com.aizuda.snailjob.template.datasource.persistence.po.JobTask;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@ -42,6 +41,7 @@ public class JobExecutorResultActor extends AbstractActor {
|
|||||||
private static final String KEY = "job_complete_{0}_{1}";
|
private static final String KEY = "job_complete_{0}_{1}";
|
||||||
private final JobTaskMapper jobTaskMapper;
|
private final JobTaskMapper jobTaskMapper;
|
||||||
private final JobTaskBatchHandler jobTaskBatchHandler;
|
private final JobTaskBatchHandler jobTaskBatchHandler;
|
||||||
|
private final WorkflowBatchHandler workflowBatchHandler;
|
||||||
private final DistributedLockHandler distributedLockHandler;
|
private final DistributedLockHandler distributedLockHandler;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -63,6 +63,9 @@ public class JobExecutorResultActor extends AbstractActor {
|
|||||||
new LambdaUpdateWrapper<JobTask>().eq(JobTask::getId, result.getTaskId())),
|
new LambdaUpdateWrapper<JobTask>().eq(JobTask::getId, result.getTaskId())),
|
||||||
() -> new SnailJobServerException("更新任务实例失败"));
|
() -> new SnailJobServerException("更新任务实例失败"));
|
||||||
|
|
||||||
|
// 更新工作流的全局上下文 如果并发更新失败则需要自旋重试更新
|
||||||
|
// workflowBatchHandler.mergeWorkflowContextAndRetry(result.getWorkflowTaskBatchId(), result.getWfContext());
|
||||||
|
|
||||||
// 除MAP和MAP_REDUCE 任务之外,其他任务都是叶子节点
|
// 除MAP和MAP_REDUCE 任务之外,其他任务都是叶子节点
|
||||||
if (Objects.nonNull(result.getIsLeaf()) && StatusEnum.NO.getStatus().equals(result.getIsLeaf())) {
|
if (Objects.nonNull(result.getIsLeaf()) && StatusEnum.NO.getStatus().equals(result.getIsLeaf())) {
|
||||||
return;
|
return;
|
||||||
|
@ -89,6 +89,9 @@ public abstract class AbstractWorkflowExecutor implements WorkflowExecutor, Init
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 合并job task的结果到全局上下文中
|
||||||
|
workflowBatchHandler.mergeAllWorkflowContext(context.getWorkflowTaskBatchId(), context.getTaskBatchId());
|
||||||
|
|
||||||
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
|
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||||
@Override
|
@Override
|
||||||
protected void doInTransactionWithoutResult(final TransactionStatus status) {
|
protected void doInTransactionWithoutResult(final TransactionStatus status) {
|
||||||
|
@ -17,6 +17,8 @@ import com.aizuda.snailjob.server.common.enums.LogicalConditionEnum;
|
|||||||
import com.aizuda.snailjob.server.common.exception.SnailJobServerException;
|
import com.aizuda.snailjob.server.common.exception.SnailJobServerException;
|
||||||
import com.aizuda.snailjob.server.job.task.support.alarm.event.WorkflowTaskFailAlarmEvent;
|
import com.aizuda.snailjob.server.job.task.support.alarm.event.WorkflowTaskFailAlarmEvent;
|
||||||
import com.aizuda.snailjob.server.job.task.support.expression.ExpressionInvocationHandler;
|
import com.aizuda.snailjob.server.job.task.support.expression.ExpressionInvocationHandler;
|
||||||
|
import com.aizuda.snailjob.server.job.task.support.handler.WorkflowBatchHandler;
|
||||||
|
import com.aizuda.snailjob.template.datasource.persistence.mapper.JobTaskBatchMapper;
|
||||||
import com.aizuda.snailjob.template.datasource.persistence.mapper.JobTaskMapper;
|
import com.aizuda.snailjob.template.datasource.persistence.mapper.JobTaskMapper;
|
||||||
import com.aizuda.snailjob.template.datasource.persistence.po.JobTask;
|
import com.aizuda.snailjob.template.datasource.persistence.po.JobTask;
|
||||||
import com.aizuda.snailjob.template.datasource.persistence.po.JobTaskBatch;
|
import com.aizuda.snailjob.template.datasource.persistence.po.JobTaskBatch;
|
||||||
@ -41,6 +43,7 @@ import java.util.Optional;
|
|||||||
public class DecisionWorkflowExecutor extends AbstractWorkflowExecutor {
|
public class DecisionWorkflowExecutor extends AbstractWorkflowExecutor {
|
||||||
private final JobTaskMapper jobTaskMapper;
|
private final JobTaskMapper jobTaskMapper;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WorkflowNodeTypeEnum getWorkflowNodeType() {
|
public WorkflowNodeTypeEnum getWorkflowNodeType() {
|
||||||
return WorkflowNodeTypeEnum.DECISION;
|
return WorkflowNodeTypeEnum.DECISION;
|
||||||
@ -77,6 +80,8 @@ public class DecisionWorkflowExecutor extends AbstractWorkflowExecutor {
|
|||||||
List<JobTask> jobTasks = jobTaskMapper.selectList(new LambdaQueryWrapper<JobTask>()
|
List<JobTask> jobTasks = jobTaskMapper.selectList(new LambdaQueryWrapper<JobTask>()
|
||||||
.select(JobTask::getResultMessage)
|
.select(JobTask::getResultMessage)
|
||||||
.eq(JobTask::getTaskBatchId, context.getTaskBatchId()));
|
.eq(JobTask::getTaskBatchId, context.getTaskBatchId()));
|
||||||
|
|
||||||
|
|
||||||
List<String> taskResult = Lists.newArrayList();
|
List<String> taskResult = Lists.newArrayList();
|
||||||
Boolean tempResult = null;
|
Boolean tempResult = null;
|
||||||
if (CollUtil.isEmpty(jobTasks)) {
|
if (CollUtil.isEmpty(jobTasks)) {
|
||||||
|
@ -3,11 +3,13 @@ package com.aizuda.snailjob.server.job.task.support.handler;
|
|||||||
import akka.actor.ActorRef;
|
import akka.actor.ActorRef;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.aizuda.snailjob.common.core.constant.SystemConstants;
|
import com.aizuda.snailjob.common.core.constant.SystemConstants;
|
||||||
import com.aizuda.snailjob.common.core.context.SpringContext;
|
import com.aizuda.snailjob.common.core.context.SpringContext;
|
||||||
import com.aizuda.snailjob.common.core.enums.JobOperationReasonEnum;
|
import com.aizuda.snailjob.common.core.enums.JobOperationReasonEnum;
|
||||||
import com.aizuda.snailjob.common.core.enums.JobTaskBatchStatusEnum;
|
import com.aizuda.snailjob.common.core.enums.JobTaskBatchStatusEnum;
|
||||||
import com.aizuda.snailjob.common.core.enums.JobTaskStatusEnum;
|
import com.aizuda.snailjob.common.core.enums.JobTaskStatusEnum;
|
||||||
|
import com.aizuda.snailjob.common.core.util.JsonUtil;
|
||||||
import com.aizuda.snailjob.common.core.util.StreamUtils;
|
import com.aizuda.snailjob.common.core.util.StreamUtils;
|
||||||
import com.aizuda.snailjob.common.log.SnailJobLog;
|
import com.aizuda.snailjob.common.log.SnailJobLog;
|
||||||
import com.aizuda.snailjob.server.common.akka.ActorGenerator;
|
import com.aizuda.snailjob.server.common.akka.ActorGenerator;
|
||||||
@ -24,12 +26,16 @@ import com.aizuda.snailjob.server.job.task.support.stop.JobTaskStopFactory;
|
|||||||
import com.aizuda.snailjob.server.job.task.support.stop.TaskStopJobContext;
|
import com.aizuda.snailjob.server.job.task.support.stop.TaskStopJobContext;
|
||||||
import com.aizuda.snailjob.template.datasource.persistence.mapper.JobMapper;
|
import com.aizuda.snailjob.template.datasource.persistence.mapper.JobMapper;
|
||||||
import com.aizuda.snailjob.template.datasource.persistence.mapper.JobTaskBatchMapper;
|
import com.aizuda.snailjob.template.datasource.persistence.mapper.JobTaskBatchMapper;
|
||||||
|
import com.aizuda.snailjob.template.datasource.persistence.mapper.JobTaskMapper;
|
||||||
import com.aizuda.snailjob.template.datasource.persistence.mapper.WorkflowTaskBatchMapper;
|
import com.aizuda.snailjob.template.datasource.persistence.mapper.WorkflowTaskBatchMapper;
|
||||||
import com.aizuda.snailjob.template.datasource.persistence.po.Job;
|
import com.aizuda.snailjob.template.datasource.persistence.po.Job;
|
||||||
|
import com.aizuda.snailjob.template.datasource.persistence.po.JobTask;
|
||||||
import com.aizuda.snailjob.template.datasource.persistence.po.JobTaskBatch;
|
import com.aizuda.snailjob.template.datasource.persistence.po.JobTaskBatch;
|
||||||
import com.aizuda.snailjob.template.datasource.persistence.po.WorkflowTaskBatch;
|
import com.aizuda.snailjob.template.datasource.persistence.po.WorkflowTaskBatch;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.github.rholder.retry.*;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.graph.MutableGraph;
|
import com.google.common.graph.MutableGraph;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@ -38,6 +44,9 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.aizuda.snailjob.common.core.enums.JobOperationReasonEnum.WORKFLOW_SUCCESSOR_SKIP_EXECUTION;
|
import static com.aizuda.snailjob.common.core.enums.JobOperationReasonEnum.WORKFLOW_SUCCESSOR_SKIP_EXECUTION;
|
||||||
import static com.aizuda.snailjob.common.core.enums.JobTaskBatchStatusEnum.NOT_COMPLETE;
|
import static com.aizuda.snailjob.common.core.enums.JobTaskBatchStatusEnum.NOT_COMPLETE;
|
||||||
@ -54,6 +63,7 @@ public class WorkflowBatchHandler {
|
|||||||
private final WorkflowTaskBatchMapper workflowTaskBatchMapper;
|
private final WorkflowTaskBatchMapper workflowTaskBatchMapper;
|
||||||
private final JobMapper jobMapper;
|
private final JobMapper jobMapper;
|
||||||
private final JobTaskBatchMapper jobTaskBatchMapper;
|
private final JobTaskBatchMapper jobTaskBatchMapper;
|
||||||
|
private final JobTaskMapper jobTaskMapper;
|
||||||
|
|
||||||
private static boolean checkLeafCompleted(MutableGraph<Long> graph, Map<Long,
|
private static boolean checkLeafCompleted(MutableGraph<Long> graph, Map<Long,
|
||||||
List<JobTaskBatch>> currentWorkflowNodeMap, Set<Long> parentIds) {
|
List<JobTaskBatch>> currentWorkflowNodeMap, Set<Long> parentIds) {
|
||||||
@ -305,4 +315,106 @@ public class WorkflowBatchHandler {
|
|||||||
SnailJobLog.LOCAL.error("任务调度执行失败", e);
|
SnailJobLog.LOCAL.error("任务调度执行失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void mergeWorkflowContextAndRetry(Long workflowTaskBatchId, String waitMergeContext) {
|
||||||
|
if (StrUtil.isBlank(waitMergeContext) || Objects.isNull(workflowTaskBatchId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
|
||||||
|
.retryIfResult(result -> result.equals(Boolean.FALSE))
|
||||||
|
.retryIfException(ex -> true)
|
||||||
|
.withWaitStrategy(WaitStrategies.fixedWait(500, TimeUnit.MILLISECONDS))
|
||||||
|
// 重试3秒
|
||||||
|
.withStopStrategy(StopStrategies.stopAfterAttempt(6))
|
||||||
|
.withRetryListener(new RetryListener() {
|
||||||
|
@Override
|
||||||
|
public <V> void onRetry(final Attempt<V> attempt) {
|
||||||
|
Object result = null;
|
||||||
|
if (attempt.hasResult()) {
|
||||||
|
try {
|
||||||
|
result = attempt.get();
|
||||||
|
} catch (ExecutionException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SnailJobLog.LOCAL.info("第【{}】次尝试更新上下文. result:[{}] treadName:[{}]",
|
||||||
|
attempt.getAttemptNumber(), result, Thread.currentThread().getName());
|
||||||
|
}
|
||||||
|
}).build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
retryer.call(() -> mergeWorkflowContext(workflowTaskBatchId, JsonUtil.parseHashMap(waitMergeContext, Object.class)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
SnailJobLog.LOCAL.warn("update workflow global context error. workflowTaskBatchId:[{}] waitMergeContext:[{}]",
|
||||||
|
workflowTaskBatchId, waitMergeContext, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean mergeAllWorkflowContext(Long workflowTaskBatchId, Long taskBatchId) {
|
||||||
|
List<JobTask> jobTasks = jobTaskMapper.selectList(new LambdaQueryWrapper<JobTask>()
|
||||||
|
.select(JobTask::getResultMessage, JobTask::getId)
|
||||||
|
.eq(JobTask::getTaskBatchId, taskBatchId));
|
||||||
|
if (CollUtil.isEmpty(jobTasks)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Map<String, Object>> maps = jobTasks.stream().map(r -> {
|
||||||
|
try {
|
||||||
|
return JsonUtil.parseHashMap(r.getResultMessage(), Object.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
SnailJobLog.LOCAL.warn("taskId:[{}] result value 不是一个json对象. result:[{}]", r.getId(), r.getResultMessage());
|
||||||
|
}
|
||||||
|
return new HashMap<String, Object>();
|
||||||
|
}).collect(Collectors.toSet());
|
||||||
|
|
||||||
|
Map<String, Object> mergeMap = Maps.newHashMap();
|
||||||
|
for (Map<String, Object> map : maps) {
|
||||||
|
mergeMaps(mergeMap, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergeWorkflowContext(workflowTaskBatchId, mergeMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并客户端上报的上下问题信息
|
||||||
|
*
|
||||||
|
* @param workflowTaskBatchId 工作流批次
|
||||||
|
* @param waitMergeContext 待合并的上下文
|
||||||
|
*/
|
||||||
|
public boolean mergeWorkflowContext(Long workflowTaskBatchId, Map<String, Object> waitMergeContext) {
|
||||||
|
if (CollUtil.isEmpty(waitMergeContext) || Objects.isNull(workflowTaskBatchId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WorkflowTaskBatch workflowTaskBatch = workflowTaskBatchMapper.selectOne(
|
||||||
|
new LambdaQueryWrapper<WorkflowTaskBatch>()
|
||||||
|
.select(WorkflowTaskBatch::getWfContext, WorkflowTaskBatch::getVersion)
|
||||||
|
.eq(WorkflowTaskBatch::getId, workflowTaskBatchId)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Objects.isNull(workflowTaskBatch)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String wfContext = workflowTaskBatch.getWfContext();
|
||||||
|
if (StrUtil.isNotBlank(wfContext)) {
|
||||||
|
mergeMaps(waitMergeContext, JsonUtil.parseHashMap(wfContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
int version = workflowTaskBatch.getVersion();
|
||||||
|
workflowTaskBatch.setWfContext(JsonUtil.toJsonString(waitMergeContext));
|
||||||
|
workflowTaskBatch.setVersion(null);
|
||||||
|
return 1 == workflowTaskBatchMapper.update(workflowTaskBatch, new LambdaQueryWrapper<WorkflowTaskBatch>()
|
||||||
|
.eq(WorkflowTaskBatch::getId, workflowTaskBatchId)
|
||||||
|
.eq(WorkflowTaskBatch::getVersion, version)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void mergeMaps(Map<String, Object> mainMap, Map<String, Object> waitMergeMap) {
|
||||||
|
for (Map.Entry<String, Object> entry : waitMergeMap.entrySet()) {
|
||||||
|
mainMap.merge(entry.getKey(), entry.getValue(), (v1, v2) -> v2);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user