[ server ] [ scheduler ]- script scheduler - 1

This commit is contained in:
zeaslity
2023-01-17 12:05:04 +08:00
parent 4812756408
commit 8ef3b271b1
26 changed files with 709 additions and 109 deletions

View File

@@ -10,7 +10,6 @@ import io.wdd.common.beans.rabbitmq.OctopusMessageType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.IOException; import java.io.IOException;
import static io.wdd.agent.config.utils.NacosConfigurationCollector.ALL_FUNCTION_MAP; import static io.wdd.agent.config.utils.NacosConfigurationCollector.ALL_FUNCTION_MAP;
@@ -31,19 +30,24 @@ public class OMHandlerExecutor extends AbstractOctopusMessageHandler {
@Override @Override
public boolean handle(OctopusMessage octopusMessage) { public boolean handle(OctopusMessage octopusMessage) {
if (!octopusMessage.getType().equals(OctopusMessageType.EXECUTOR)) { if (!octopusMessage
.getType()
.equals(OctopusMessageType.EXECUTOR)) {
return next.handle(octopusMessage); return next.handle(octopusMessage);
} }
try { try {
ExecutionMessage executionMessage = objectMapper.readValue((String) octopusMessage.getContent(), new TypeReference<ExecutionMessage>() { // 需要首先解析成 ExecutionMessage
}); ExecutionMessage executionMessage = objectMapper.readValue(
(String) octopusMessage.getContent(),
// System.out.println("executionMessage = " + executionMessage); new TypeReference<ExecutionMessage>() {
}
);
String executionType = executionMessage.getType(); String executionType = executionMessage.getType();
if (ALL_FUNCTION_MAP.containsKey(executionType)) { if (ALL_FUNCTION_MAP.containsKey(executionType)) {
// execute the exist function // execute the exist function
functionExecutor.execute(executionMessage); functionExecutor.execute(executionMessage);

View File

@@ -5,7 +5,7 @@ import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.NacosException;
import io.wdd.agent.executor.config.FunctionReader; import io.wdd.common.utils.FunctionReader;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
@@ -13,7 +13,6 @@ import org.springframework.stereotype.Component;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*; import java.util.*;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -53,9 +52,6 @@ public class NacosConfigurationCollector {
@Value("${octopus.status.name}") @Value("${octopus.status.name}")
public String appStatusDataId; public String appStatusDataId;
@Resource
FunctionReader functionReader;
@PostConstruct @PostConstruct
private void CollectAllFunctionFromNacos() { private void CollectAllFunctionFromNacos() {
@@ -158,18 +154,19 @@ public class NacosConfigurationCollector {
yaml.loadAll(allApplicationNeedToMonitorStatus).iterator().forEachRemaining(realFunction -> { yaml.loadAll(allApplicationNeedToMonitorStatus).iterator().forEachRemaining(realFunction -> {
// 其实没有意义
if (!(realFunction instanceof LinkedHashMap)) { if (!(realFunction instanceof LinkedHashMap)) {
System.out.println("realFunction = " + realFunction); System.out.println("realFunction = " + realFunction);
} }
Map<String, String> stringMap = (Map<String, String>) realFunction; Map<String, String> stringMap = (Map<String, String>) realFunction;
// 拿到实际的功能名称 如 AgentReboot AgentUpdate
Optional<String> functionName = stringMap.keySet().stream().findFirst(); Optional<String> functionName = stringMap.keySet().stream().findFirst();
List<List<String>> commandList = FunctionReader.ReadStringToCommandList(stringMap.get(functionName.get()));
List<List<String>> commandList = functionReader.ReadStringToCommandList(stringMap.get(functionName.get()));
/*log.info("Function {} , content is {}", functionName.get(), commandList);*/ /*log.info("Function {} , content is {}", functionName.get(), commandList);*/
ALL_FUNCTION_MAP.put(functionName.get(), commandList); ALL_FUNCTION_MAP.put(functionName.get(), commandList);
}); });

View File

@@ -15,8 +15,9 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.concurrent.ExecutorService;
import java.util.concurrent.*; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@Configuration @Configuration
@@ -39,10 +40,10 @@ public class CommandExecutor {
* @param executionMessage get from EXECUTOR_HANDLERju * @param executionMessage get from EXECUTOR_HANDLERju
*/ */
public void execute(ExecutionMessage executionMessage) { public void execute(ExecutionMessage executionMessage) {
this.execute(
executionMessage.getResultKey(),
this.execute(executionMessage.getResultKey(), executionMessage.getCommandList()); executionMessage.getCommandList()
);
} }
@@ -51,14 +52,20 @@ public class CommandExecutor {
ProcessBuilder processBuilder = new ProcessBuilder(command); ProcessBuilder processBuilder = new ProcessBuilder(command);
return this.processExecute(streamKey, processBuilder); return this.processExecute(
streamKey,
processBuilder
);
} }
public int execute(String streamKey, String... command) { public int execute(String streamKey, String... command) {
ProcessBuilder processBuilder = new ProcessBuilder(command); ProcessBuilder processBuilder = new ProcessBuilder(command);
return this.processExecute(streamKey, processBuilder); return this.processExecute(
streamKey,
processBuilder
);
} }
@@ -82,14 +89,20 @@ public class CommandExecutor {
)); ));
// cache log lines // cache log lines
logToArrayListCache.cacheLog(streamKey, process); logToArrayListCache.cacheLog(
streamKey,
process
);
// start to send the result log // start to send the result log
streamSender.startToWaitLog(streamKey); streamSender.startToWaitLog(streamKey);
// todo this will stuck the process and rabbitmq message will reentry the queue // todo this will stuck the process and rabbitmq message will reentry the queue
// get the command result must also be a timeout smaller than the process // get the command result must also be a timeout smaller than the process
boolean waitFor = process.waitFor(50, TimeUnit.SECONDS); boolean waitFor = process.waitFor(
50,
TimeUnit.SECONDS
);
// end send logs // end send logs
streamSender.endWaitLog(streamKey); streamSender.endWaitLog(streamKey);
@@ -99,27 +112,45 @@ public class CommandExecutor {
processResult = process.exitValue(); processResult = process.exitValue();
} }
log.debug("current shell command {} result is {}", processBuilder.command(), processResult); log.debug(
"current shell command {} result is {}",
processBuilder.command(),
processResult
);
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {
log.error("Shell command error ! {} + {}", e.getCause(), e.getMessage()); log.error(
"Shell command error ! {} + {}",
e.getCause(),
e.getMessage()
);
} }
return processResult; return processResult;
} }
private Runnable StopStuckCommandProcess(Process process, int processMaxWaitSeconds) { private Runnable StopStuckCommandProcess(Process process, int processMaxWaitSeconds) {
return () -> { return () -> {
try { try {
log.debug("daemon thread start to wait for {} s for the result", processMaxWaitSeconds); log.debug(
"daemon thread start to wait for {} s for the result",
processMaxWaitSeconds
);
TimeUnit.SECONDS.sleep(processMaxWaitSeconds); TimeUnit.SECONDS.sleep(processMaxWaitSeconds);
if (process.isAlive()) { if (process.isAlive()) {
log.warn("Command [ {} ] stuck for {} s, destroy the command process !", process.info().commandLine().get(), processMaxWaitSeconds); log.warn(
"Command [ {} ] stuck for {} s, destroy the command process !",
process
.info()
.commandLine()
.get(),
processMaxWaitSeconds
);
// shutdown the process // shutdown the process
process.destroyForcibly(); process.destroyForcibly();
@@ -158,7 +189,9 @@ public class CommandExecutor {
TimeUnit.SECONDS.sleep(1); TimeUnit.SECONDS.sleep(1);
// clear the log Cache Thread scope // clear the log Cache Thread scope
logToArrayListCache.getExecutionCmdCachedLogArrayList(streamKey).clear(); logToArrayListCache
.getExecutionCmdCachedLogArrayList(streamKey)
.clear();
// clear the stream sender // clear the stream sender
streamSender.clearLocalCache(streamKey); streamSender.clearLocalCache(streamKey);

View File

@@ -1,4 +1,4 @@
package io.wdd.agent.executor.config; package io.wdd.common.utils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -17,7 +17,7 @@ import java.util.stream.Collectors;
public class FunctionReader { public class FunctionReader {
public List<List<String>> ReadFileToCommandList(String functionFilePath) { public static List<List<String>> ReadFileToCommandList(String functionFilePath) {
// https://www.digitalocean.com/community/tutorials/java-read-file-line-by-line // https://www.digitalocean.com/community/tutorials/java-read-file-line-by-line
@@ -26,7 +26,10 @@ public class FunctionReader {
try { try {
BufferedReader bufferedReader = new BufferedReader(new FileReader(functionFilePath)); BufferedReader bufferedReader = new BufferedReader(new FileReader(functionFilePath));
result = doReadContent(result, bufferedReader); result = doReadContent(
result,
bufferedReader
);
} catch (IOException e) { } catch (IOException e) {
@@ -38,15 +41,19 @@ public class FunctionReader {
} }
public List<List<String>> ReadStringToCommandList(String functionContent) { public static List<List<String>> ReadStringToCommandList(String functionContent) {
List<List<String>> result = null; List<List<String>> result = null;
try { try {
// 构造一个 buffered Reader
BufferedReader bufferedReader = new BufferedReader(new StringReader(functionContent)); BufferedReader bufferedReader = new BufferedReader(new StringReader(functionContent));
result = doReadContent(result, bufferedReader);
// 执行read操作
result = doReadContent(
result,
bufferedReader
);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@@ -56,7 +63,8 @@ public class FunctionReader {
} }
private List<List<String>> doReadContent(List<List<String>> result, BufferedReader bufferedReader) throws IOException { private static List<List<String>> doReadContent(List<List<String>> result, BufferedReader bufferedReader) throws IOException {
String line = bufferedReader.readLine(); String line = bufferedReader.readLine();
if (line != null) { if (line != null) {
@@ -65,16 +73,19 @@ public class FunctionReader {
while (line != null) { while (line != null) {
if (!StringUtils.isEmpty(line)) { if (!StringUtils.isEmpty(line)) {
result.add(this.SplitLineToCommandList(line)); result.add(SplitLineToCommandList(line));
} }
line = bufferedReader.readLine(); line = bufferedReader.readLine();
} }
return result; return result;
} }
public List<String> SplitLineToCommandList(String commandLine) { public static List<String> SplitLineToCommandList(String commandLine) {
return Arrays.stream(commandLine.split(" ")).collect(Collectors.toList()); return Arrays
.stream(commandLine.split(" "))
.collect(Collectors.toList());
} }

View File

@@ -5,6 +5,7 @@ import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam; import io.swagger.annotations.ApiParam;
import io.wdd.common.beans.response.R; import io.wdd.common.beans.response.R;
import io.wdd.rpc.scheduler.beans.ScriptSchedulerVO;
import io.wdd.rpc.scheduler.service.QuartzSchedulerService; import io.wdd.rpc.scheduler.service.QuartzSchedulerService;
import org.quartz.Trigger; import org.quartz.Trigger;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -22,38 +23,63 @@ public class SchedulerController {
QuartzSchedulerService octopusQuartzService; QuartzSchedulerService octopusQuartzService;
@ApiOperation(value = "查询所有job") /**
@GetMapping(value = "/queryAllJob") * --------------------------------------------------------------
public R<List<Map<String, Object>>> queryAllQuartzJob() { * 页面定时任务部分
* 应该只有脚本功能才可以定时,目前一阶段的功能
* */
return R.ok(octopusQuartzService.queryAllJob()); @ApiOperation(value = "新增一个定时脚本任务")
} @PostMapping(value = "/script/create")
public R<String> createScriptScheduler(
@ApiParam(name = "scheduleScript") @RequestBody() ScriptSchedulerVO scriptSchedulerVO
@ApiOperation(value = "查询所有运行job")
@PostMapping(value = "/queryRunJob")
public R<List<Map<String, Object>>> queryRunQuartzJob() {
return R.ok(octopusQuartzService.queryRunJob());
}
@ApiOperation(value = "删除一个job")
@PostMapping(value = "/deleteJob/")
public R<String> deleteJob(
@ApiParam(name = "jobName") @RequestParam("jobName") String jobName
) { ) {
boolean deleteJob = octopusQuartzService.deleteJob( octopusQuartzService.createScriptScheduledMission(scriptSchedulerVO);
jobName,
jobName return R.ok("ok");
}
/**
* --------------------------------------------------------------
* 普通的定时任务查询功能
* */
@ApiOperation(value = "查询所有mission")
@GetMapping(value = "/queryAllMission")
public R<List<Map<String, Object>>> queryAllQuartzMission() {
return R.ok(octopusQuartzService.queryAllMission());
}
@ApiOperation(value = "查询所有运行mission")
@PostMapping(value = "/queryRunMission")
public R<List<Map<String, Object>>> queryRunQuartzMission() {
return R.ok(octopusQuartzService.queryRunMission());
}
@ApiOperation(value = "删除一个mission")
@PostMapping(value = "/deleteMission/")
public R<String> deleteMission(
@ApiParam(name = "missionName") @RequestParam("missionName") String missionName
) {
boolean deleteMission = octopusQuartzService.deleteMission(
missionName,
missionName
); );
String result = String.format( String result = String.format(
"删除任务[ %s ]结果为 [ %s ]", "删除任务[ %s ]结果为 [ %s ]",
jobName, missionName,
deleteJob deleteMission
); );
if (deleteJob) { if (deleteMission) {
return R.ok(result); return R.ok(result);
} else { } else {
return R.failed(result); return R.failed(result);
@@ -61,17 +87,17 @@ public class SchedulerController {
} }
@ApiOperation(value = "修改job的cron时间") @ApiOperation(value = "修改mission的cron时间")
@PostMapping(value = "/updateJob/{jobName}") @PostMapping(value = "/updateMission/{missionName}")
public void deleteJob( public void deleteMission(
@ApiParam(name = "jobName") @RequestParam("jobName") String jobName, @ApiParam(name = "missionName") @RequestParam("missionName") String missionName,
@ApiParam(name = "jobCronTime") @RequestParam("jobCronTime") String jobCronTime @ApiParam(name = "missionCronTime") @RequestParam("missionCronTime") String missionCronTime
) { ) {
octopusQuartzService.updateJob( octopusQuartzService.updateMission(
jobName, missionName,
jobName, missionName,
jobCronTime missionCronTime
); );
} }

View File

@@ -10,8 +10,8 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.Map; import java.util.Map;
import static io.wdd.rpc.status.MonitorAllAgentStatus.ALL_AGENT_HEALTHY_STATUS_MAP; import static io.wdd.rpc.scheduler.service.status.MonitorAllAgentStatus.ALL_AGENT_HEALTHY_STATUS_MAP;
import static io.wdd.rpc.status.MonitorAllAgentStatus.HEALTHY_STATUS_AGENT_LIST_MAP; import static io.wdd.rpc.scheduler.service.status.MonitorAllAgentStatus.HEALTHY_STATUS_AGENT_LIST_MAP;
@RestController @RestController
@Api("Agent运行状态Controller") @Api("Agent运行状态Controller")

View File

@@ -1,8 +1,7 @@
package io.wdd.rpc.execute.result; package io.wdd.rpc.execute.result;
import io.wdd.rpc.execute.config.CommandReaderConfig; import io.wdd.rpc.scheduler.service.status.AgentStatusStreamReader;
import io.wdd.rpc.status.AgentStatusStreamReader;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;

View File

@@ -19,7 +19,7 @@ import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static io.wdd.rpc.execute.service.ExecutionResultDaemonHandler.WAIT_EXECUTION_RESULT_LIST; import static io.wdd.rpc.execute.service.ExecutionResultDaemonHandler.WAIT_EXECUTION_RESULT_LIST;
import static io.wdd.rpc.status.MonitorAllAgentStatus.ALL_AGENT_TOPIC_NAME_SET; import static io.wdd.rpc.scheduler.service.status.MonitorAllAgentStatus.ALL_AGENT_TOPIC_NAME_SET;
@Service @Service
@Slf4j @Slf4j

View File

@@ -0,0 +1,22 @@
package io.wdd.rpc.scheduler.beans;
import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.util.List;
@ApiModel("定时脚本任务-中间转换状态-实体类")
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder(toBuilder = true)
public class ScriptSchedulerDTO extends ScriptSchedulerVO{
List<List<String>> commandList;
List<String> targetMachineList;
}

View File

@@ -0,0 +1,114 @@
package io.wdd.rpc.scheduler.beans;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import javax.annotation.Nullable;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder(toBuilder = true)
@ApiModel("定时脚本任务的VO实体类")
public class ScriptSchedulerVO {
public static final String SCHEDULE_MISSION_GROUP_NAME = "SCRIPT_SCHEDULER";
/**
*
*/
@TableId(value = "scheduler_uuid")
@Nullable
private String schedulerUuid;
/**
*
*/
@TableField(value = "name")
@NotNull
private String name;
/**
*
*/
@TableField(value = "cron_express")
@NotNull
private String cronExpress;
/**
*
*/
@TableField(value = "description")
@Nullable
private String description;
/**
* 脚本任务的内容
*/
@TableField(value = "script_content")
@NotNull
private String scriptContent;
/**
* 执行目标机器agent_topic_name列表使用, 分隔
*/
@TableField(value = "target_machine")
@Nullable
private String targetMachine;
/**
* 与 execution_log表的主键对应,方便查询执行日志
*/
@TableField(value = "last_execution_id")
@Nullable
private Long lastExecutionId;
/**
* 与 execution_log表的 result_key 对应,方便查询执行日志
*/
@TableField(value = "last_execution_result_key")
@Nullable
private String lastExecutionResultKey;
/**
* 任务上次执行状态
*/
@TableField(value = "last_execution_status")
@Nullable
private Integer lastExecutionStatus;
/**
* 定时脚本任务创建时间
*/
@TableField(value = "create_time")
@NotNull
private LocalDateTime createTime;
/**
* 上次更新时间
*/
@TableField(value = "update_time")
@Nullable
private LocalDateTime updateTime;
/**
* 任务下次计划执行时间
*/
@TableField(value = "next_schedule_time")
@Nullable
private LocalDateTime nextScheduleTime;
/**
* 任务上次计划执行时间
*/
@TableField(value = "last_schedule_time")
@Nullable
private LocalDateTime lastScheduleTime;
}

View File

@@ -1,6 +1,6 @@
package io.wdd.rpc.scheduler.job; package io.wdd.rpc.scheduler.job;
import io.wdd.rpc.status.AgentRuntimeMetricStatus; import io.wdd.rpc.scheduler.service.status.AgentRuntimeMetricStatus;
import org.quartz.JobDataMap; import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext; import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException; import org.quartz.JobExecutionException;
@@ -8,8 +8,8 @@ import org.springframework.scheduling.quartz.QuartzJobBean;
import javax.annotation.Resource; import javax.annotation.Resource;
import static io.wdd.rpc.status.AgentRuntimeMetricStatus.METRIC_REPORT_TIMES_COUNT; import static io.wdd.rpc.scheduler.service.status.AgentRuntimeMetricStatus.METRIC_REPORT_TIMES_COUNT;
import static io.wdd.rpc.status.AgentRuntimeMetricStatus.METRIC_REPORT_TIME_PINCH; import static io.wdd.rpc.scheduler.service.status.AgentRuntimeMetricStatus.METRIC_REPORT_TIME_PINCH;
public class AgentRunMetricStatusJob extends QuartzJobBean { public class AgentRunMetricStatusJob extends QuartzJobBean {

View File

@@ -0,0 +1,34 @@
package io.wdd.rpc.scheduler.job;
import io.wdd.rpc.scheduler.beans.ScriptSchedulerDTO;
import io.wdd.rpc.scheduler.service.script.AgentApplyScheduledScript;
import io.wdd.server.beans.po.ScriptSchedulerPO;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
import javax.annotation.Resource;
public class AgentScriptSchedulerJob extends QuartzJobBean {
@Resource
AgentApplyScheduledScript agentApplyScheduledScript;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 解析 Scheduler 模块传递过来的参数
JobDataMap jobDataMap = jobExecutionContext
.getJobDetail()
.getJobDataMap();
// ScriptScheduleDTO
ScriptSchedulerDTO scriptSchedulerDTO = (ScriptSchedulerDTO) jobDataMap.get("scriptSchedulerPO");
// 调用实际任务执行器
agentApplyScheduledScript.apply(scriptSchedulerDTO);
}
}

View File

@@ -1,7 +1,7 @@
package io.wdd.rpc.scheduler.job; package io.wdd.rpc.scheduler.job;
import io.wdd.rpc.scheduler.config.QuartzLogOperator; import io.wdd.rpc.scheduler.config.QuartzLogOperator;
import io.wdd.rpc.status.MonitorAllAgentStatus; import io.wdd.rpc.scheduler.service.status.MonitorAllAgentStatus;
import org.quartz.JobExecutionContext; import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException; import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean; import org.springframework.scheduling.quartz.QuartzJobBean;

View File

@@ -14,8 +14,8 @@ import java.text.ParseException;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import static io.wdd.rpc.status.AgentRuntimeMetricStatus.METRIC_REPORT_TIMES_COUNT; import static io.wdd.rpc.scheduler.service.status.AgentRuntimeMetricStatus.METRIC_REPORT_TIMES_COUNT;
import static io.wdd.rpc.status.AgentRuntimeMetricStatus.METRIC_REPORT_TIME_PINCH; import static io.wdd.rpc.scheduler.service.status.AgentRuntimeMetricStatus.METRIC_REPORT_TIME_PINCH;
@Component @Component
@Slf4j @Slf4j
@@ -80,7 +80,7 @@ public class BuildStatusScheduleTask {
// build the Job 只发送一次消息然后让Agent获取消息 (重复间隔,重复次数) 进行相应的处理! // build the Job 只发送一次消息然后让Agent获取消息 (重复间隔,重复次数) 进行相应的处理!
// todo 解决创建太多对象的问题,需要缓存相应的内容 // todo 解决创建太多对象的问题,需要缓存相应的内容
octopusQuartzService.addJob( octopusQuartzService.addMission(
AgentRunMetricStatusJob.class, AgentRunMetricStatusJob.class,
"agentRunMetricStatusJob", "agentRunMetricStatusJob",
JOB_GROUP_NAME, JOB_GROUP_NAME,
@@ -101,7 +101,7 @@ public class BuildStatusScheduleTask {
private void buildMonitorAllAgentStatusScheduleTask() { private void buildMonitorAllAgentStatusScheduleTask() {
// build the Job // build the Job
octopusQuartzService.addJob( octopusQuartzService.addMission(
AgentStatusMonitorJob.class, AgentStatusMonitorJob.class,
"monitorAllAgentStatusJob", "monitorAllAgentStatusJob",
JOB_GROUP_NAME, JOB_GROUP_NAME,

View File

@@ -1,6 +1,7 @@
package io.wdd.rpc.scheduler.service; package io.wdd.rpc.scheduler.service;
import io.wdd.rpc.scheduler.beans.OctopusQuartzJob; import io.wdd.rpc.scheduler.beans.OctopusQuartzJob;
import io.wdd.rpc.scheduler.beans.ScriptSchedulerVO;
import org.quartz.Trigger; import org.quartz.Trigger;
import org.springframework.scheduling.quartz.QuartzJobBean; import org.springframework.scheduling.quartz.QuartzJobBean;
import java.util.List; import java.util.List;
@@ -10,7 +11,7 @@ import java.util.Map;
public interface QuartzSchedulerService { public interface QuartzSchedulerService {
boolean addJob(OctopusQuartzJob quartzJob); boolean addMission(OctopusQuartzJob quartzJob);
/** /**
* 增加一个任务job * 增加一个任务job
@@ -21,7 +22,7 @@ public interface QuartzSchedulerService {
* @param jobRunRepeatTimes 任务运行次数(若<0则不限次数 * @param jobRunRepeatTimes 任务运行次数(若<0则不限次数
* @param jobData 任务参数 * @param jobData 任务参数
*/ */
void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, int jobRunTimePinch, int jobRunRepeatTimes, Map jobData); void addMission(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, int jobRunTimePinch, int jobRunRepeatTimes, Map jobData);
/** /**
* 增加一个任务job * 增加一个任务job
@@ -33,7 +34,7 @@ public interface QuartzSchedulerService {
* @param cronJobExpression 任务时间表达式 * @param cronJobExpression 任务时间表达式
* @param jobData 任务参数 * @param jobData 任务参数
*/ */
void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, int startTime, String cronJobExpression, Map jobData); void addMission(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, int startTime, String cronJobExpression, Map jobData);
/** /**
* 修改一个任务job * 修改一个任务job
@@ -41,7 +42,7 @@ public interface QuartzSchedulerService {
* @param jobGroupName 任务组名 * @param jobGroupName 任务组名
* @param jobTime cron时间表达式 * @param jobTime cron时间表达式
*/ */
void updateJob(String jobName, String jobGroupName, String jobTime); void updateMission(String jobName, String jobGroupName, String jobTime);
/** /**
@@ -51,21 +52,21 @@ public interface QuartzSchedulerService {
* @param jobGroupName * @param jobGroupName
* @return * @return
*/ */
boolean deleteJob(String jobName, String jobGroupName); boolean deleteMission(String jobName, String jobGroupName);
/** /**
* 暂停一个任务job * 暂停一个任务job
* @param jobName * @param jobName
* @param jobGroupName * @param jobGroupName
*/ */
void pauseJob(String jobName, String jobGroupName); void pauseMission(String jobName, String jobGroupName);
/** /**
* 恢复一个任务job * 恢复一个任务job
* @param jobName * @param jobName
* @param jobGroupName * @param jobGroupName
*/ */
void resumeJob(String jobName, String jobGroupName); void resumeMission(String jobName, String jobGroupName);
/** /**
* 立即执行一个任务job * 立即执行一个任务job
@@ -78,13 +79,13 @@ public interface QuartzSchedulerService {
* 获取所有任务job * 获取所有任务job
* @return * @return
*/ */
List<Map<String, Object>> queryAllJob(); List<Map<String, Object>> queryAllMission();
/** /**
* 获取正在运行的任务job * 获取正在运行的任务job
* @return * @return
*/ */
List<Map<String, Object>> queryRunJob(); List<Map<String, Object>> queryRunMission();
/** /**
@@ -94,4 +95,12 @@ public interface QuartzSchedulerService {
List<Trigger> queryAllTrigger(); List<Trigger> queryAllTrigger();
/**
* --------------------------------------------------------------
* 页面定时任务部分
* 应该只有脚本功能才可以定时,目前一阶段的功能
* */
void createScriptScheduledMission(ScriptSchedulerVO scriptSchedulerVO);
} }

View File

@@ -1,9 +1,19 @@
package io.wdd.rpc.scheduler.service; package io.wdd.rpc.scheduler.service;
import io.wdd.common.handler.MyRuntimeException; import io.wdd.common.handler.MyRuntimeException;
import io.wdd.common.utils.FunctionReader;
import io.wdd.rpc.scheduler.beans.OctopusQuartzJob; import io.wdd.rpc.scheduler.beans.OctopusQuartzJob;
import io.wdd.rpc.scheduler.beans.ScriptSchedulerDTO;
import io.wdd.rpc.scheduler.beans.ScriptSchedulerVO;
import io.wdd.rpc.scheduler.job.AgentScriptSchedulerJob;
import io.wdd.server.beans.po.ScriptSchedulerPO;
import io.wdd.server.service.ScriptSchedulerService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*; import org.quartz.*;
import org.quartz.DateBuilder.IntervalUnit; import org.quartz.DateBuilder.IntervalUnit;
import org.quartz.impl.matchers.GroupMatcher; import org.quartz.impl.matchers.GroupMatcher;
@@ -12,9 +22,11 @@ import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static io.wdd.rpc.scheduler.beans.ScriptSchedulerVO.SCHEDULE_MISSION_GROUP_NAME;
import static io.wdd.rpc.scheduler.service.BuildStatusScheduleTask.JOB_GROUP_NAME; import static io.wdd.rpc.scheduler.service.BuildStatusScheduleTask.JOB_GROUP_NAME;
import static org.quartz.TriggerBuilder.newTrigger; import static org.quartz.TriggerBuilder.newTrigger;
@@ -29,6 +41,113 @@ public class QuartzSchedulerServiceImpl implements QuartzSchedulerService {
@Autowired @Autowired
private Scheduler scheduler; private Scheduler scheduler;
@Resource
ScriptSchedulerService scriptSchedulerService;
/**
* --------------------------------------------------------------
* 页面定时任务部分
* 应该只有脚本功能才可以定时,目前一阶段的功能
* */
/**
*
*
* */
@Override
public void createScriptScheduledMission(ScriptSchedulerVO scriptSchedulerVO) {
// validate important value
ScriptSchedulerDTO scriptSchedulerDTO = validateAndConvertToScriptSchedulerDTO(scriptSchedulerVO);
// build the job
// build the trigger
// bind job and trigger
HashMap<String, Object> dataMap = new HashMap<>();
dataMap.put("scriptSchedulerDTO", scriptSchedulerDTO);
this.addMission(
AgentScriptSchedulerJob.class,
scriptSchedulerVO.getName(),
SCHEDULE_MISSION_GROUP_NAME,
1,
scriptSchedulerVO.getScriptContent(),
dataMap
);
// persistent the script scheduled mission
// todo dto should store more info
ScriptSchedulerPO scriptSchedulerPO = convertToScriptSchedulerPO(scriptSchedulerDTO);
scriptSchedulerService.save(scriptSchedulerPO);
}
/**
* 转换 中间层 --> 持久层
*
* @param dto 定时脚本任务-中间转换状态-实体类
* @return 定时脚本任务-持久化-实体类
*/
private ScriptSchedulerPO convertToScriptSchedulerPO(ScriptSchedulerDTO dto) {
// todo should be a static method
return ScriptSchedulerPO
.builder()
.cronExpress(dto.getCronExpress())
.schedulerUuid(dto.getSchedulerUuid())
.scriptContent(String.valueOf(dto.getCommandList()))
.targetMachine(String.valueOf(dto.getTargetMachineList()))
.build();
}
/**
* 转换 视图层 --> 中间层
* @param scriptSchedulerVO 定时脚本任务-前端页面-实体类
* @return 定时脚本任务-中间转换状态-实体类
*/
@SneakyThrows
private ScriptSchedulerDTO validateAndConvertToScriptSchedulerDTO(ScriptSchedulerVO scriptSchedulerVO) {
// 验证cron表达式
if (!CronExpression.isValidExpression(scriptSchedulerVO.getCronExpress())) {
throw new MyRuntimeException("cron express wrong !");
}
// 归一化 commandList
List<List<String>> commandList = FunctionReader.ReadStringToCommandList(scriptSchedulerVO.getScriptContent());
if (commandList.size() == 0) {
throw new MyRuntimeException("commandList parse wrong !");
}
// 执行机器目标归一化
String[] targetMachineSplit = scriptSchedulerVO
.getTargetMachine()
.split(",");
if (targetMachineSplit.length == 0) {
throw new MyRuntimeException("target machine wrong !");
}
ArrayList<String> targetMachineList = new ArrayList<>();
targetMachineList.stream().forEach(
targetMachineList::add
);
// 生成DTO对象
ScriptSchedulerDTO dto = new ScriptSchedulerDTO();
BeanUtils.copyProperties(dto,scriptSchedulerVO);
// 设置属性值
dto.setCommandList(null);
dto.setTargetMachineList(targetMachineList);
// 生成 scheduler uuid
String uuid = RandomStringUtils.randomAlphabetic(32);
dto.setSchedulerUuid(uuid);
return dto;
}
@PostConstruct @PostConstruct
public void startScheduler() { public void startScheduler() {
try { try {
@@ -39,7 +158,7 @@ public class QuartzSchedulerServiceImpl implements QuartzSchedulerService {
} }
@Override @Override
public boolean addJob(OctopusQuartzJob quartzJob) { public boolean addMission(OctopusQuartzJob quartzJob) {
return false; return false;
@@ -56,7 +175,7 @@ public class QuartzSchedulerServiceImpl implements QuartzSchedulerService {
* @param jobData 参数 * @param jobData 参数
*/ */
@Override @Override
public void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, int jobRunTimePinch, int jobRunRepeatTimes, Map jobData) { public void addMission(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, int jobRunTimePinch, int jobRunRepeatTimes, Map jobData) {
try { try {
// 任务名称和组构成任务key // 任务名称和组构成任务key
JobDetail jobDetail = JobBuilder JobDetail jobDetail = JobBuilder
@@ -119,7 +238,7 @@ public class QuartzSchedulerServiceImpl implements QuartzSchedulerService {
* @param jobData 参数 * @param jobData 参数
*/ */
@Override @Override
public void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, int startTime, String cronJobExpression, Map jobData) { public void addMission(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, int startTime, String cronJobExpression, Map jobData) {
try { try {
// 创建jobDetail实例绑定Job实现类 // 创建jobDetail实例绑定Job实现类
// 指明job的名称所在组的名称以及绑定job类 // 指明job的名称所在组的名称以及绑定job类
@@ -188,7 +307,7 @@ public class QuartzSchedulerServiceImpl implements QuartzSchedulerService {
* @param jobTime * @param jobTime
*/ */
@Override @Override
public void updateJob(String jobName, String jobGroupName, String jobTime) { public void updateMission(String jobName, String jobGroupName, String jobTime) {
try { try {
TriggerKey triggerKey = TriggerKey.triggerKey( TriggerKey triggerKey = TriggerKey.triggerKey(
jobName, jobName,
@@ -223,7 +342,7 @@ public class QuartzSchedulerServiceImpl implements QuartzSchedulerService {
* @return * @return
*/ */
@Override @Override
public boolean deleteJob(String jobName, String jobGroupName) { public boolean deleteMission(String jobName, String jobGroupName) {
try { try {
scheduler.deleteJob(new JobKey( scheduler.deleteJob(new JobKey(
@@ -245,7 +364,7 @@ public class QuartzSchedulerServiceImpl implements QuartzSchedulerService {
* @param jobGroupName * @param jobGroupName
*/ */
@Override @Override
public void pauseJob(String jobName, String jobGroupName) { public void pauseMission(String jobName, String jobGroupName) {
try { try {
JobKey jobKey = JobKey.jobKey( JobKey jobKey = JobKey.jobKey(
jobName, jobName,
@@ -265,7 +384,7 @@ public class QuartzSchedulerServiceImpl implements QuartzSchedulerService {
* @param jobGroupName * @param jobGroupName
*/ */
@Override @Override
public void resumeJob(String jobName, String jobGroupName) { public void resumeMission(String jobName, String jobGroupName) {
try { try {
JobKey jobKey = JobKey.jobKey( JobKey jobKey = JobKey.jobKey(
jobName, jobName,
@@ -304,7 +423,7 @@ public class QuartzSchedulerServiceImpl implements QuartzSchedulerService {
* @return * @return
*/ */
@Override @Override
public List<Map<String, Object>> queryAllJob() { public List<Map<String, Object>> queryAllMission() {
List<Map<String, Object>> jobList = null; List<Map<String, Object>> jobList = null;
try { try {
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup(); GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
@@ -362,7 +481,7 @@ public class QuartzSchedulerServiceImpl implements QuartzSchedulerService {
* @return * @return
*/ */
@Override @Override
public List<Map<String, Object>> queryRunJob() { public List<Map<String, Object>> queryRunMission() {
List<Map<String, Object>> jobList = null; List<Map<String, Object>> jobList = null;
try { try {
List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs(); List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
@@ -432,4 +551,7 @@ public class QuartzSchedulerServiceImpl implements QuartzSchedulerService {
} }
} }
} }

View File

@@ -0,0 +1,39 @@
package io.wdd.rpc.scheduler.service.script;
import io.wdd.rpc.execute.service.CoreExecutionService;
import io.wdd.rpc.scheduler.beans.ScriptSchedulerDTO;
import io.wdd.server.beans.po.ScriptSchedulerPO;
import lombok.extern.log4j.Log4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* 1.
*/
@Service
@Log4j
public class AgentApplyScheduledScript {
@Resource
CoreExecutionService coreExecutionService;
public void apply(ScriptSchedulerDTO scriptSchedulerDTO) {
List<List<String>> commandList = scriptSchedulerDTO.getCommandList();
List<String> targetMachineList = scriptSchedulerDTO.getTargetMachineList();
targetMachineList
.stream()
.map(
targetMachine -> {
coreExecutionService.SendCommandToAgent()
}
)
}
}

View File

@@ -1,4 +1,4 @@
package io.wdd.rpc.status; package io.wdd.rpc.scheduler.service.status;
import io.wdd.common.beans.status.OctopusStatusMessage; import io.wdd.common.beans.status.OctopusStatusMessage;

View File

@@ -1,4 +1,4 @@
package io.wdd.rpc.status; package io.wdd.rpc.scheduler.service.status;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;

View File

@@ -1,4 +1,4 @@
package io.wdd.rpc.status; package io.wdd.rpc.scheduler.service.status;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;

View File

@@ -1,4 +1,4 @@
package io.wdd.rpc.status; package io.wdd.rpc.scheduler.service.status;
import io.wdd.common.beans.status.AgentHealthyStatusEnum; import io.wdd.common.beans.status.AgentHealthyStatusEnum;
import io.wdd.common.beans.status.OctopusStatusMessage; import io.wdd.common.beans.status.OctopusStatusMessage;
@@ -18,7 +18,7 @@ import java.util.stream.Collectors;
import static io.wdd.common.beans.status.OctopusStatusMessage.ALL_AGENT_STATUS_REDIS_KEY; import static io.wdd.common.beans.status.OctopusStatusMessage.ALL_AGENT_STATUS_REDIS_KEY;
import static io.wdd.common.beans.status.OctopusStatusMessage.HEALTHY_STATUS_MESSAGE_TYPE; import static io.wdd.common.beans.status.OctopusStatusMessage.HEALTHY_STATUS_MESSAGE_TYPE;
import static io.wdd.rpc.status.AgentRuntimeMetricStatus.ALL_HEALTHY_AGENT_TOPIC_NAMES; import static io.wdd.rpc.scheduler.service.status.AgentRuntimeMetricStatus.ALL_HEALTHY_AGENT_TOPIC_NAMES;
/** /**
* 更新频率被类 BuildStatusScheduleTask.class控制 * 更新频率被类 BuildStatusScheduleTask.class控制

View File

@@ -0,0 +1,107 @@
package io.wdd.server.beans.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDateTime;
import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
*
* @TableName script_scheduler
*/
@TableName(value ="script_scheduler")
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder(toBuilder = true)
@ApiModel("定时脚本任务-持久化-实体类")
public class ScriptSchedulerPO implements Serializable {
/**
*
*/
@TableId(value = "scheduler_uuid")
private String schedulerUuid;
/**
*
*/
@TableField(value = "name")
private String name;
/**
*
*/
@TableField(value = "cron_express")
private String cronExpress;
/**
*
*/
@TableField(value = "description")
private String description;
/**
* 脚本任务的内容
*/
@TableField(value = "script_content")
private String scriptContent;
/**
* 执行目标机器agent_topic_name列表使用, 分隔
*/
@TableField(value = "target_machine")
private String targetMachine;
/**
* 与 execution_log表的主键对应,方便查询执行日志
*/
@TableField(value = "last_execution_id")
private Long lastExecutionId;
/**
* 与 execution_log表的 result_key 对应,方便查询执行日志
*/
@TableField(value = "last_execution_result_key")
private String lastExecutionResultKey;
/**
* 任务上次执行状态
*/
@TableField(value = "last_execution_status")
private Integer lastExecutionStatus;
/**
* 定时脚本任务创建时间
*/
@TableField(value = "create_time")
private LocalDateTime createTime;
/**
* 上次更新时间
*/
@TableField(value = "update_time")
private LocalDateTime updateTime;
/**
* 任务下次计划执行时间
*/
@TableField(value = "next_schedule_time")
private LocalDateTime nextScheduleTime;
/**
* 任务上次计划执行时间
*/
@TableField(value = "last_schedule_time")
private LocalDateTime lastScheduleTime;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

View File

@@ -0,0 +1,18 @@
package io.wdd.server.mapper;
import io.wdd.server.beans.po.ScriptSchedulerPO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author wdd
* @description 针对表【script_scheduler】的数据库操作Mapper
* @createDate 2023-01-17 10:29:22
* @Entity io.wdd.server.beans.po.ScriptSchedulerPO
*/
public interface ScriptSchedulerMapper extends BaseMapper<ScriptSchedulerPO> {
}

View File

@@ -0,0 +1,13 @@
package io.wdd.server.service;
import io.wdd.server.beans.po.ScriptSchedulerPO;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author wdd
* @description 针对表【script_scheduler】的数据库操作Service
* @createDate 2023-01-17 10:29:22
*/
public interface ScriptSchedulerService extends IService<ScriptSchedulerPO> {
}

View File

@@ -0,0 +1,22 @@
package io.wdd.server.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.wdd.server.beans.po.ScriptSchedulerPO;
import io.wdd.server.service.ScriptSchedulerService;
import io.wdd.server.mapper.ScriptSchedulerMapper;
import org.springframework.stereotype.Service;
/**
* @author wdd
* @description 针对表【script_scheduler】的数据库操作Service实现
* @createDate 2023-01-17 10:29:22
*/
@Service
public class ScriptSchedulerServiceImpl extends ServiceImpl<ScriptSchedulerMapper, ScriptSchedulerPO>
implements ScriptSchedulerService{
}

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.wdd.server.mapper.ScriptSchedulerMapper">
<resultMap id="BaseResultMap" type="io.wdd.server.beans.po.ScriptSchedulerPO">
<id property="schedulerUuid" column="scheduler_uuid" jdbcType="VARCHAR"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<result property="cronExpress" column="cron_express" jdbcType="VARCHAR"/>
<result property="description" column="description" jdbcType="VARCHAR"/>
<result property="scriptContent" column="script_content" jdbcType="VARCHAR"/>
<result property="targetMachine" column="target_machine" jdbcType="VARCHAR"/>
<result property="lastExecutionId" column="last_execution_id" jdbcType="BIGINT"/>
<result property="lastExecutionResultKey" column="last_execution_result_key" jdbcType="VARCHAR"/>
<result property="lastExecutionStatus" column="last_execution_status" jdbcType="TINYINT"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
<result property="nextScheduleTime" column="next_schedule_time" jdbcType="TIMESTAMP"/>
<result property="lastScheduleTime" column="last_schedule_time" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">
scheduler_uuid,name,cron_express,
description,script_content,target_machine,
last_execution_id,last_execution_result_key,last_execution_status,
create_time,update_time,next_schedule_time,
last_schedule_time
</sql>
</mapper>