mirror of
https://github.com/PowerJob/PowerJob.git
synced 2025-07-17 00:00:04 +08:00
chore: merge master
This commit is contained in:
commit
59121684a8
@ -69,11 +69,9 @@ CREATE TABLE `instance_info`
|
|||||||
`gmt_create` datetime not NULL COMMENT '创建时间',
|
`gmt_create` datetime not NULL COMMENT '创建时间',
|
||||||
`gmt_modified` datetime not NULL COMMENT '更新时间',
|
`gmt_modified` datetime not NULL COMMENT '更新时间',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `idx01_instance_info` (`job_id`),
|
KEY `idx01_instance_info` (`job_id`, 'status'),
|
||||||
KEY `idx02_instance_info` (`app_id`),
|
KEY `idx02_instance_info` (`app_id`, `status`),
|
||||||
KEY `idx03_instance_info` (`instance_id`),
|
KEY `idx03_instance_info` (`instance_id`, `status`)
|
||||||
KEY `idx04_instance_info` (`wf_instance_id`),
|
|
||||||
KEY `idx05_instance_info` (`expected_trigger_time`)
|
|
||||||
) ENGINE = InnoDB
|
) ENGINE = InnoDB
|
||||||
AUTO_INCREMENT = 1
|
AUTO_INCREMENT = 1
|
||||||
DEFAULT CHARSET = utf8mb4
|
DEFAULT CHARSET = utf8mb4
|
||||||
@ -116,9 +114,7 @@ CREATE TABLE `job_info`
|
|||||||
`gmt_create` datetime not NULL COMMENT '创建时间',
|
`gmt_create` datetime not NULL COMMENT '创建时间',
|
||||||
`gmt_modified` datetime not NULL COMMENT '更新时间',
|
`gmt_modified` datetime not NULL COMMENT '更新时间',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `idx01_job_info` (`app_id`),
|
KEY `idx01_job_info` (`app_id`, `status`, `time_expression_type`, `next_trigger_time`)
|
||||||
KEY `idx02_job_info` (`job_name`),
|
|
||||||
KEY `idx03_job_info` (`next_trigger_time`)
|
|
||||||
) ENGINE = InnoDB
|
) ENGINE = InnoDB
|
||||||
AUTO_INCREMENT = 1
|
AUTO_INCREMENT = 1
|
||||||
DEFAULT CHARSET = utf8mb4
|
DEFAULT CHARSET = utf8mb4
|
||||||
@ -154,7 +150,8 @@ CREATE TABLE `server_info`
|
|||||||
`gmt_modified` datetime DEFAULT NULL COMMENT '更新时间',
|
`gmt_modified` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
`ip` varchar(128) DEFAULT NULL COMMENT '服务器IP地址',
|
`ip` varchar(128) DEFAULT NULL COMMENT '服务器IP地址',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
UNIQUE KEY `uidx01_server_info` (`ip`)
|
UNIQUE KEY `uidx01_server_info` (`ip`),
|
||||||
|
KEY `idx01_server_info` (`gmt_modified`)
|
||||||
) ENGINE = InnoDB
|
) ENGINE = InnoDB
|
||||||
AUTO_INCREMENT = 1
|
AUTO_INCREMENT = 1
|
||||||
DEFAULT CHARSET = utf8mb4
|
DEFAULT CHARSET = utf8mb4
|
||||||
@ -204,7 +201,7 @@ CREATE TABLE `workflow_info`
|
|||||||
`gmt_create` datetime DEFAULT NULL COMMENT '创建时间',
|
`gmt_create` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
`gmt_modified` datetime DEFAULT NULL COMMENT '更新时间',
|
`gmt_modified` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `idx01_workflow_info` (`app_id`)
|
KEY `idx01_workflow_info` (`app_id`, `status`, `time_expression_type`, next_trigger_time)
|
||||||
) ENGINE = InnoDB
|
) ENGINE = InnoDB
|
||||||
AUTO_INCREMENT = 1
|
AUTO_INCREMENT = 1
|
||||||
DEFAULT CHARSET = utf8mb4
|
DEFAULT CHARSET = utf8mb4
|
||||||
@ -231,9 +228,9 @@ CREATE TABLE `workflow_instance_info`
|
|||||||
`gmt_create` datetime DEFAULT NULL COMMENT '创建时间',
|
`gmt_create` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
`gmt_modified` datetime DEFAULT NULL COMMENT '更新时间',
|
`gmt_modified` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
unique index uidx01_wf_instance (wf_instance_id),
|
unique index uidx01_wf_instance (`wf_instance_id`),
|
||||||
index idx01_wf_instance (workflow_id),
|
index idx01_wf_instance (`workflow_id`, `status`),
|
||||||
index idx02_wf_instance (app_id, status)
|
index idx02_wf_instance (`app_id`, `status`, `expected_trigger_time`)
|
||||||
) ENGINE = InnoDB
|
) ENGINE = InnoDB
|
||||||
AUTO_INCREMENT = 1
|
AUTO_INCREMENT = 1
|
||||||
DEFAULT CHARSET = utf8mb4
|
DEFAULT CHARSET = utf8mb4
|
||||||
@ -258,9 +255,7 @@ CREATE TABLE `workflow_node_info`
|
|||||||
`type` int DEFAULT NULL COMMENT '节点类型,1:任务JOB',
|
`type` int DEFAULT NULL COMMENT '节点类型,1:任务JOB',
|
||||||
`workflow_id` bigint DEFAULT NULL COMMENT '工作流ID',
|
`workflow_id` bigint DEFAULT NULL COMMENT '工作流ID',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `idx01_workflow_node_info` (`app_id`),
|
KEY `idx01_workflow_node_info` (`workflow_id`,`gmt_create`)
|
||||||
KEY `idx02_workflow_node_info` (`workflow_id`),
|
|
||||||
KEY `idx03_workflow_node_info` (`job_id`)
|
|
||||||
) ENGINE = InnoDB
|
) ENGINE = InnoDB
|
||||||
AUTO_INCREMENT = 1
|
AUTO_INCREMENT = 1
|
||||||
DEFAULT CHARSET = utf8mb4
|
DEFAULT CHARSET = utf8mb4
|
||||||
|
@ -10,13 +10,13 @@
|
|||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>powerjob-client</artifactId>
|
<artifactId>powerjob-client</artifactId>
|
||||||
<version>4.2.0</version>
|
<version>4.2.1</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<junit.version>5.6.1</junit.version>
|
<junit.version>5.9.1</junit.version>
|
||||||
<fastjson.version>1.2.83</fastjson.version>
|
<fastjson.version>1.2.83</fastjson.version>
|
||||||
<powerjob.common.version>4.2.0</powerjob.common.version>
|
<powerjob.common.version>4.2.1</powerjob.common.version>
|
||||||
|
|
||||||
<mvn.shade.plugin.version>3.2.4</mvn.shade.plugin.version>
|
<mvn.shade.plugin.version>3.2.4</mvn.shade.plugin.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
@ -10,18 +10,18 @@
|
|||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>powerjob-common</artifactId>
|
<artifactId>powerjob-common</artifactId>
|
||||||
<version>4.2.0</version>
|
<version>4.2.1</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<slf4j.version>1.7.30</slf4j.version>
|
<slf4j.version>1.7.36</slf4j.version>
|
||||||
<commons.lang.version>3.12.0</commons.lang.version>
|
<commons.lang.version>3.12.0</commons.lang.version>
|
||||||
<commons.io.version>2.7</commons.io.version>
|
<commons.io.version>2.11.0</commons.io.version>
|
||||||
<guava.version>31.1-jre</guava.version>
|
<guava.version>31.1-jre</guava.version>
|
||||||
<okhttp.version>3.14.9</okhttp.version>
|
<okhttp.version>3.14.9</okhttp.version>
|
||||||
<akka.version>2.6.20</akka.version>
|
<akka.version>2.6.20</akka.version>
|
||||||
<kryo.version>5.0.4</kryo.version>
|
<kryo.version>5.3.0</kryo.version>
|
||||||
<jackson.version>2.12.2</jackson.version>
|
<jackson.version>2.14.0-rc1</jackson.version>
|
||||||
<junit.version>5.9.0</junit.version>
|
<junit.version>5.9.0</junit.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
@ -35,6 +35,12 @@ public class PowerJobDKey {
|
|||||||
public static final String TRANSPORTER_KEEP_ALIVE_TIMEOUT = "powerjob.transporter.keepalive.timeout";
|
public static final String TRANSPORTER_KEEP_ALIVE_TIMEOUT = "powerjob.transporter.keepalive.timeout";
|
||||||
|
|
||||||
public static final String WORKER_STATUS_CHECK_PERIOD = "powerjob.worker.status-check.normal.period";
|
public static final String WORKER_STATUS_CHECK_PERIOD = "powerjob.worker.status-check.normal.period";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* allowed PowerJob to invoke Thread#stop to kill a thread when PowerJob can't interrupt the thread
|
||||||
|
* <a href="https://stackoverflow.com/questions/16504140/thread-stop-deprecated">It's VERY dangerous</a>
|
||||||
|
*/
|
||||||
|
public static final String WORKER_ALLOWED_FORCE_STOP_THREAD = "powerjob.worker.allowed-force-stop-thread";
|
||||||
/**
|
/**
|
||||||
* ms
|
* ms
|
||||||
*/
|
*/
|
||||||
|
@ -26,6 +26,23 @@ public class SystemInstanceResult {
|
|||||||
* 任务执行超时
|
* 任务执行超时
|
||||||
*/
|
*/
|
||||||
public static final String INSTANCE_EXECUTE_TIMEOUT = "instance execute timeout";
|
public static final String INSTANCE_EXECUTE_TIMEOUT = "instance execute timeout";
|
||||||
|
/**
|
||||||
|
* 任务执行超时,成功打断任务
|
||||||
|
*/
|
||||||
|
public static final String INSTANCE_EXECUTE_TIMEOUT_INTERRUPTED = "instance execute timeout,interrupted success";
|
||||||
|
/**
|
||||||
|
* 任务执行超时,强制终止任务
|
||||||
|
*/
|
||||||
|
public static final String INSTANCE_EXECUTE_TIMEOUT_FORCE_STOP= "instance execute timeout,force stop success";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户手动停止任务,成功打断任务
|
||||||
|
*/
|
||||||
|
public static final String USER_STOP_INSTANCE_INTERRUPTED= "user stop instance,interrupted success";
|
||||||
|
/**
|
||||||
|
* 用户手动停止任务,被系统强制终止
|
||||||
|
*/
|
||||||
|
public static final String USER_STOP_INSTANCE_FORCE_STOP= "user stop instance,force stop success";
|
||||||
/**
|
/**
|
||||||
* 创建根任务失败
|
* 创建根任务失败
|
||||||
*/
|
*/
|
||||||
|
@ -26,8 +26,8 @@ public enum ExecuteType {
|
|||||||
MAP_REDUCE(3, "MapReduce"),
|
MAP_REDUCE(3, "MapReduce"),
|
||||||
MAP(4, "Map");
|
MAP(4, "Map");
|
||||||
|
|
||||||
int v;
|
private final int v;
|
||||||
String des;
|
private final String des;
|
||||||
|
|
||||||
public static ExecuteType of(int v) {
|
public static ExecuteType of(int v) {
|
||||||
for (ExecuteType type : values()) {
|
for (ExecuteType type : values()) {
|
||||||
|
@ -5,6 +5,7 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,13 +25,13 @@ public enum TimeExpressionType {
|
|||||||
FIXED_DELAY(4),
|
FIXED_DELAY(4),
|
||||||
WORKFLOW(5);
|
WORKFLOW(5);
|
||||||
|
|
||||||
int v;
|
private final int v;
|
||||||
|
|
||||||
public static final List<Integer> FREQUENT_TYPES = Lists.newArrayList(FIXED_RATE.v, FIXED_DELAY.v);
|
public static final List<Integer> FREQUENT_TYPES = Collections.unmodifiableList(Lists.newArrayList(FIXED_RATE.v, FIXED_DELAY.v));
|
||||||
/**
|
/**
|
||||||
* 首次计算触发时间时必须计算出一个有效值
|
* 首次计算触发时间时必须计算出一个有效值
|
||||||
*/
|
*/
|
||||||
public static final List<Integer> INSPECT_TYPES = Lists.newArrayList(CRON.v);
|
public static final List<Integer> INSPECT_TYPES = Collections.unmodifiableList(Lists.newArrayList(CRON.v));
|
||||||
|
|
||||||
public static TimeExpressionType of(int v) {
|
public static TimeExpressionType of(int v) {
|
||||||
for (TimeExpressionType type : values()) {
|
for (TimeExpressionType type : values()) {
|
||||||
|
@ -4,6 +4,7 @@ import com.google.common.collect.Lists;
|
|||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,11 +28,11 @@ public enum WorkflowInstanceStatus {
|
|||||||
/**
|
/**
|
||||||
* 广义的运行状态
|
* 广义的运行状态
|
||||||
*/
|
*/
|
||||||
public static final List<Integer> GENERALIZED_RUNNING_STATUS = Lists.newArrayList(WAITING.v, RUNNING.v);
|
public static final List<Integer> GENERALIZED_RUNNING_STATUS = Collections.unmodifiableList(Lists.newArrayList(WAITING.v, RUNNING.v));
|
||||||
/**
|
/**
|
||||||
* 结束状态
|
* 结束状态
|
||||||
*/
|
*/
|
||||||
public static final List<Integer> FINISHED_STATUS = Lists.newArrayList(FAILED.v, SUCCEED.v, STOPPED.v);
|
public static final List<Integer> FINISHED_STATUS = Collections.unmodifiableList(Lists.newArrayList(FAILED.v, SUCCEED.v, STOPPED.v));
|
||||||
|
|
||||||
private final int v;
|
private final int v;
|
||||||
|
|
||||||
|
@ -46,6 +46,8 @@ public class TaskTrackerReportInstanceStatusReq implements PowerSerializable {
|
|||||||
|
|
||||||
private long startTime;
|
private long startTime;
|
||||||
|
|
||||||
|
private Long endTime;
|
||||||
|
|
||||||
private long reportTime;
|
private long reportTime;
|
||||||
|
|
||||||
private String sourceAddress;
|
private String sourceAddress;
|
||||||
|
@ -17,26 +17,55 @@ import java.util.List;
|
|||||||
@Data
|
@Data
|
||||||
public class WorkerHeartbeat implements PowerSerializable {
|
public class WorkerHeartbeat implements PowerSerializable {
|
||||||
|
|
||||||
// 本机地址 -> IP:port
|
/**
|
||||||
|
* 本机地址 -> IP:port
|
||||||
|
*/
|
||||||
private String workerAddress;
|
private String workerAddress;
|
||||||
// 当前 appName
|
/**
|
||||||
|
* 当前 appName
|
||||||
|
*/
|
||||||
private String appName;
|
private String appName;
|
||||||
// 当前 appId
|
/**
|
||||||
|
* 当前 appId
|
||||||
|
*/
|
||||||
private Long appId;
|
private Long appId;
|
||||||
// 当前时间
|
/**
|
||||||
|
* 当前时间
|
||||||
|
*/
|
||||||
private long heartbeatTime;
|
private long heartbeatTime;
|
||||||
// 当前加载的容器(容器名称 -> 容器版本)
|
/**
|
||||||
|
* 当前加载的容器(容器名称 -> 容器版本)
|
||||||
|
*/
|
||||||
private List<DeployedContainerInfo> containerInfos;
|
private List<DeployedContainerInfo> containerInfos;
|
||||||
// worker 版本信息
|
/**
|
||||||
|
* worker 版本信息
|
||||||
|
*/
|
||||||
private String version;
|
private String version;
|
||||||
// 使用的通讯协议 AKKA / HTTP
|
/**
|
||||||
|
* 使用的通讯协议 AKKA / HTTP
|
||||||
|
*/
|
||||||
private String protocol;
|
private String protocol;
|
||||||
// worker tag,标识同一个 worker 下的一类集群 ISSUE: 226
|
/**
|
||||||
|
* worker tag,标识同一个 worker 下的一类集群 ISSUE: 226
|
||||||
|
*/
|
||||||
private String tag;
|
private String tag;
|
||||||
// 客户端名称
|
/**
|
||||||
|
* 客户端名称
|
||||||
|
*/
|
||||||
private String client;
|
private String client;
|
||||||
// 扩展字段
|
/**
|
||||||
|
* 扩展字段
|
||||||
|
*/
|
||||||
private String extra;
|
private String extra;
|
||||||
|
/**
|
||||||
|
* 是否已经超载,超载的情况下 Server 一段时间内不会再向其派发任务
|
||||||
|
*/
|
||||||
|
private boolean isOverload;
|
||||||
|
|
||||||
|
private int lightTaskTrackerNum;
|
||||||
|
|
||||||
|
private int heavyTaskTrackerNum;
|
||||||
|
|
||||||
|
|
||||||
private SystemMetrics systemMetrics;
|
private SystemMetrics systemMetrics;
|
||||||
}
|
}
|
||||||
|
@ -4,40 +4,44 @@ import com.fasterxml.jackson.core.JsonParser;
|
|||||||
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;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import tech.powerjob.common.exception.PowerJobException;
|
import tech.powerjob.common.exception.PowerJobException;
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSON工具类
|
* JSON工具类
|
||||||
*
|
*
|
||||||
* @author tjq
|
* @author tjq
|
||||||
* @since 2020/4/16
|
* @since 2020/4/16
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class JsonUtils {
|
public class JsonUtils {
|
||||||
|
|
||||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
|
OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
|
||||||
|
//
|
||||||
|
OBJECT_MAPPER.configure(JsonParser.Feature.IGNORE_UNDEFINED, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonUtils(){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toJSONString(Object obj) {
|
public static String toJSONString(Object obj) {
|
||||||
if (obj instanceof String) {
|
|
||||||
return (String) obj;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
return objectMapper.writeValueAsString(obj);
|
return OBJECT_MAPPER.writeValueAsString(obj);
|
||||||
}catch (Exception ignore) {
|
}catch (Exception ignore) {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toJSONStringUnsafe(Object obj) {
|
public static String toJSONStringUnsafe(Object obj) {
|
||||||
if (obj instanceof String) {
|
|
||||||
return (String) obj;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
return objectMapper.writeValueAsString(obj);
|
return OBJECT_MAPPER.writeValueAsString(obj);
|
||||||
}catch (Exception e) {
|
}catch (Exception e) {
|
||||||
throw new PowerJobException(e);
|
throw new PowerJobException(e);
|
||||||
}
|
}
|
||||||
@ -45,27 +49,41 @@ public class JsonUtils {
|
|||||||
|
|
||||||
public static byte[] toBytes(Object obj) {
|
public static byte[] toBytes(Object obj) {
|
||||||
try {
|
try {
|
||||||
return objectMapper.writeValueAsBytes(obj);
|
return OBJECT_MAPPER.writeValueAsBytes(obj);
|
||||||
}catch (Exception ignore) {
|
}catch (Exception ignore) {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T parseObject(String json, Class<T> clz) throws JsonProcessingException {
|
public static <T> T parseObject(String json, Class<T> clz) throws JsonProcessingException {
|
||||||
return objectMapper.readValue(json, clz);
|
return OBJECT_MAPPER.readValue(json, clz);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T parseObject(byte[] b, Class<T> clz) throws Exception {
|
public static <T> T parseObject(byte[] b, Class<T> clz) throws IOException {
|
||||||
return objectMapper.readValue(b, clz);
|
return OBJECT_MAPPER.readValue(b, clz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T parseObject(byte[] b, TypeReference<T> typeReference) throws IOException {
|
||||||
|
return OBJECT_MAPPER.readValue(b, typeReference);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T parseObject(String json, TypeReference<T> typeReference) throws IOException {
|
||||||
|
return OBJECT_MAPPER.readValue(json, typeReference);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T parseObjectIgnoreException(String json, Class<T> clz) {
|
||||||
|
try {
|
||||||
|
return OBJECT_MAPPER.readValue(json, clz);
|
||||||
|
}catch (Exception e) {
|
||||||
|
log.error("unable to parse json string to object,current string:{}",json,e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T parseObject(byte[] b, TypeReference<T> typeReference) throws Exception {
|
|
||||||
return objectMapper.readValue(b, typeReference);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T parseObjectUnsafe(String json, Class<T> clz) {
|
public static <T> T parseObjectUnsafe(String json, Class<T> clz) {
|
||||||
try {
|
try {
|
||||||
return objectMapper.readValue(json, clz);
|
return OBJECT_MAPPER.readValue(json, clz);
|
||||||
}catch (Exception e) {
|
}catch (Exception e) {
|
||||||
ExceptionUtils.rethrow(e);
|
ExceptionUtils.rethrow(e);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
package tech.powerjob.common.utils;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.JarURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.security.CodeSource;
|
||||||
|
import java.util.jar.Attributes;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 语言相关的工具
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2022/10/23
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class JavaUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取类所在 Jar 包的版本
|
||||||
|
* @param clz 类
|
||||||
|
* @return 包版本
|
||||||
|
*/
|
||||||
|
public static String determinePackageVersion(Class<?> clz) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
String implementationVersion = clz.getPackage().getImplementationVersion();
|
||||||
|
if (implementationVersion != null) {
|
||||||
|
return implementationVersion;
|
||||||
|
}
|
||||||
|
CodeSource codeSource = clz.getProtectionDomain().getCodeSource();
|
||||||
|
if (codeSource == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
URL codeSourceLocation = codeSource.getLocation();
|
||||||
|
|
||||||
|
URLConnection connection = codeSourceLocation.openConnection();
|
||||||
|
if (connection instanceof JarURLConnection) {
|
||||||
|
return getImplementationVersion(((JarURLConnection) connection).getJarFile());
|
||||||
|
}
|
||||||
|
try (JarFile jarFile = new JarFile(new File(codeSourceLocation.toURI()))) {
|
||||||
|
return getImplementationVersion(jarFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
log.warn("[JavaUtils] determinePackageVersion for clz[{}] failed, msg: {}", clz.getSimpleName(), t.toString());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
private static String getImplementationVersion(JarFile jarFile) throws IOException {
|
||||||
|
return jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package tech.powerjob.common.utils;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 语言相关的工具测试
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2022/10/23
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
class JavaUtilsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void determinePackageVersion() {
|
||||||
|
|
||||||
|
String packageVersion = JavaUtils.determinePackageVersion(LoggerFactory.class);
|
||||||
|
log.info("[determinePackageVersion] LoggerFactory's package version: {}", packageVersion);
|
||||||
|
}
|
||||||
|
}
|
@ -18,18 +18,18 @@
|
|||||||
<mvn.shade.plugin.version>3.2.4</mvn.shade.plugin.version>
|
<mvn.shade.plugin.version>3.2.4</mvn.shade.plugin.version>
|
||||||
|
|
||||||
<!-- 不会被打包的部分,scope 只能是 test 或 provide -->
|
<!-- 不会被打包的部分,scope 只能是 test 或 provide -->
|
||||||
<junit.version>5.6.1</junit.version>
|
<junit.version>5.9.1</junit.version>
|
||||||
<logback.version>1.2.3</logback.version>
|
<logback.version>1.2.9</logback.version>
|
||||||
<powerjob.worker.version>4.2.0</powerjob.worker.version>
|
<powerjob.worker.version>4.2.1</powerjob.worker.version>
|
||||||
<spring.jdbc.version>5.2.9.RELEASE</spring.jdbc.version>
|
<spring.jdbc.version>5.2.9.RELEASE</spring.jdbc.version>
|
||||||
<h2.db.version>1.4.200</h2.db.version>
|
<h2.db.version>2.1.214</h2.db.version>
|
||||||
<mysql.version>8.0.28</mysql.version>
|
<mysql.version>8.0.28</mysql.version>
|
||||||
|
|
||||||
<!-- 全部 shade 化,避免依赖冲突 -->
|
<!-- 全部 shade 化,避免依赖冲突 -->
|
||||||
<fastjson.version>1.2.83</fastjson.version>
|
<fastjson.version>1.2.83</fastjson.version>
|
||||||
<okhttp.version>3.14.9</okhttp.version>
|
<okhttp.version>3.14.9</okhttp.version>
|
||||||
<guava.version>30.1.1-jre</guava.version>
|
<guava.version>30.1.1-jre</guava.version>
|
||||||
<commons.io.version>2.6</commons.io.version>
|
<commons.io.version>2.11.0</commons.io.version>
|
||||||
<commons.lang.version>3.10</commons.lang.version>
|
<commons.lang.version>3.10</commons.lang.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import tech.powerjob.worker.core.processor.TaskContext;
|
|||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>powerjob-server</artifactId>
|
<artifactId>powerjob-server</artifactId>
|
||||||
<version>4.2.0</version>
|
<version>4.2.1</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
@ -28,19 +28,19 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<swagger.version>2.9.2</swagger.version>
|
<swagger.version>2.9.2</swagger.version>
|
||||||
<springboot.version>2.7.4</springboot.version>
|
<springboot.version>2.7.4</springboot.version>
|
||||||
<powerjob.common.version>4.2.0</powerjob.common.version>
|
<powerjob.common.version>4.2.1</powerjob.common.version>
|
||||||
<!-- MySQL version that corresponds to spring-boot-dependencies version. -->
|
<!-- MySQL version that corresponds to spring-boot-dependencies version. -->
|
||||||
<mysql.version>8.0.28</mysql.version>
|
<mysql.version>8.0.30</mysql.version>
|
||||||
<ojdbc.version>19.7.0.0</ojdbc.version>
|
<ojdbc.version>19.7.0.0</ojdbc.version>
|
||||||
<mssql-jdbc.version>7.4.1.jre8</mssql-jdbc.version>
|
<mssql-jdbc.version>7.4.1.jre8</mssql-jdbc.version>
|
||||||
<db2-jdbc.version>11.5.0.0</db2-jdbc.version>
|
<db2-jdbc.version>11.5.0.0</db2-jdbc.version>
|
||||||
<postgresql.version>42.2.14</postgresql.version>
|
<postgresql.version>42.2.14</postgresql.version>
|
||||||
<h2.db.version>1.4.200</h2.db.version>
|
<h2.db.version>2.1.214</h2.db.version>
|
||||||
|
|
||||||
<zip4j.version>2.5.2</zip4j.version>
|
<zip4j.version>2.11.2</zip4j.version>
|
||||||
<jgit.version>5.7.0.202003110725-r</jgit.version>
|
<jgit.version>5.7.0.202003110725-r</jgit.version>
|
||||||
<mvn.invoker.version>3.0.1</mvn.invoker.version>
|
<mvn.invoker.version>3.0.1</mvn.invoker.version>
|
||||||
<commons.net.version>3.6</commons.net.version>
|
<commons.net.version>3.8.0</commons.net.version>
|
||||||
<fastjson.version>1.2.83</fastjson.version>
|
<fastjson.version>1.2.83</fastjson.version>
|
||||||
<dingding.version>1.0.1</dingding.version>
|
<dingding.version>1.0.1</dingding.version>
|
||||||
<vertx-web.version>4.0.2</vertx-web.version>
|
<vertx-web.version>4.0.2</vertx-web.version>
|
||||||
@ -48,6 +48,8 @@
|
|||||||
<!-- skip this module when deploying. -->
|
<!-- skip this module when deploying. -->
|
||||||
<maven.deploy.skip>true</maven.deploy.skip>
|
<maven.deploy.skip>true</maven.deploy.skip>
|
||||||
|
|
||||||
|
<groovy.version>3.0.10</groovy.version>
|
||||||
|
<cron-utils.version>9.1.6</cron-utils.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@ -257,7 +259,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.cronutils</groupId>
|
<groupId>com.cronutils</groupId>
|
||||||
<artifactId>cron-utils</artifactId>
|
<artifactId>cron-utils</artifactId>
|
||||||
<version>9.1.6</version>
|
<version>${cron-utils.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- swagger2 -->
|
<!-- swagger2 -->
|
||||||
@ -277,13 +279,13 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.codehaus.groovy</groupId>
|
<groupId>org.codehaus.groovy</groupId>
|
||||||
<artifactId>groovy-jsr223</artifactId>
|
<artifactId>groovy-jsr223</artifactId>
|
||||||
<version>3.0.10</version>
|
<version>${groovy.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- https://mvnrepository.com/artifact/org.codehaus.groovy/groovy-json -->
|
<!-- https://mvnrepository.com/artifact/org.codehaus.groovy/groovy-json -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.codehaus.groovy</groupId>
|
<groupId>org.codehaus.groovy</groupId>
|
||||||
<artifactId>groovy-json</artifactId>
|
<artifactId>groovy-json</artifactId>
|
||||||
<version>3.0.10</version>
|
<version>${groovy.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>powerjob-server</artifactId>
|
<artifactId>powerjob-server</artifactId>
|
||||||
<groupId>tech.powerjob</groupId>
|
<groupId>tech.powerjob</groupId>
|
||||||
<version>4.2.0</version>
|
<version>4.2.1</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package tech.powerjob.server.common;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Echo009
|
||||||
|
* @since 2022/10/2
|
||||||
|
*/
|
||||||
|
public class Holder<T> {
|
||||||
|
|
||||||
|
private T value;
|
||||||
|
|
||||||
|
public Holder(T value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T get() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(T value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
package tech.powerjob.server.common.module;
|
package tech.powerjob.server.common.module;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import tech.powerjob.common.model.DeployedContainerInfo;
|
import tech.powerjob.common.model.DeployedContainerInfo;
|
||||||
import tech.powerjob.common.model.SystemMetrics;
|
import tech.powerjob.common.model.SystemMetrics;
|
||||||
import tech.powerjob.common.request.WorkerHeartbeat;
|
import tech.powerjob.common.request.WorkerHeartbeat;
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -14,6 +15,7 @@ import java.util.List;
|
|||||||
* @since 2021/2/7
|
* @since 2021/2/7
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
|
@Slf4j
|
||||||
public class WorkerInfo {
|
public class WorkerInfo {
|
||||||
|
|
||||||
private String address;
|
private String address;
|
||||||
@ -26,6 +28,14 @@ public class WorkerInfo {
|
|||||||
|
|
||||||
private String tag;
|
private String tag;
|
||||||
|
|
||||||
|
private int lightTaskTrackerNum;
|
||||||
|
|
||||||
|
private int heavyTaskTrackerNum;
|
||||||
|
|
||||||
|
private long lastOverloadTime;
|
||||||
|
|
||||||
|
private boolean overloading;
|
||||||
|
|
||||||
private SystemMetrics systemMetrics;
|
private SystemMetrics systemMetrics;
|
||||||
|
|
||||||
private List<DeployedContainerInfo> containerInfos;
|
private List<DeployedContainerInfo> containerInfos;
|
||||||
@ -40,10 +50,25 @@ public class WorkerInfo {
|
|||||||
tag = workerHeartbeat.getTag();
|
tag = workerHeartbeat.getTag();
|
||||||
systemMetrics = workerHeartbeat.getSystemMetrics();
|
systemMetrics = workerHeartbeat.getSystemMetrics();
|
||||||
containerInfos = workerHeartbeat.getContainerInfos();
|
containerInfos = workerHeartbeat.getContainerInfos();
|
||||||
|
|
||||||
|
lightTaskTrackerNum = workerHeartbeat.getLightTaskTrackerNum();
|
||||||
|
heavyTaskTrackerNum = workerHeartbeat.getHeavyTaskTrackerNum();
|
||||||
|
|
||||||
|
if (workerHeartbeat.isOverload()) {
|
||||||
|
overloading = true;
|
||||||
|
lastOverloadTime = workerHeartbeat.getHeartbeatTime();
|
||||||
|
log.warn("[WorkerInfo] worker {} is overload!", getAddress());
|
||||||
|
} else {
|
||||||
|
overloading = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean timeout() {
|
public boolean timeout() {
|
||||||
long timeout = System.currentTimeMillis() - lastActiveTime;
|
long timeout = System.currentTimeMillis() - lastActiveTime;
|
||||||
return timeout > WORKER_TIMEOUT_MS;
|
return timeout > WORKER_TIMEOUT_MS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean overload() {
|
||||||
|
return overloading;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package tech.powerjob.server.common.thread;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.util.concurrent.RejectedExecutionHandler;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Echo009
|
||||||
|
* @since 2022/10/12
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class NewThreadRunRejectedExecutionHandler implements RejectedExecutionHandler {
|
||||||
|
|
||||||
|
private static final AtomicLong COUNTER = new AtomicLong();
|
||||||
|
|
||||||
|
private final String source;
|
||||||
|
|
||||||
|
public NewThreadRunRejectedExecutionHandler(String source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void rejectedExecution(Runnable r, ThreadPoolExecutor p) {
|
||||||
|
log.error("[{}] ThreadPool[{}] overload, the task[{}] will run by a new thread!, Maybe you need to adjust the ThreadPool config!", source, p, r);
|
||||||
|
if (!p.isShutdown()) {
|
||||||
|
String threadName = source + "-T-" + COUNTER.getAndIncrement();
|
||||||
|
log.info("[{}] create new thread[{}] to run job", source, threadName);
|
||||||
|
new Thread(r, threadName).start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -65,9 +65,11 @@ public class HashedWheelTimer implements Timer {
|
|||||||
taskProcessPool = null;
|
taskProcessPool = null;
|
||||||
}else {
|
}else {
|
||||||
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("HashedWheelTimer-Executor-%d").build();
|
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("HashedWheelTimer-Executor-%d").build();
|
||||||
BlockingQueue<Runnable> queue = Queues.newLinkedBlockingQueue(16);
|
// 这里需要调整一下队列大小
|
||||||
|
BlockingQueue<Runnable> queue = Queues.newLinkedBlockingQueue(8192);
|
||||||
int core = Math.max(Runtime.getRuntime().availableProcessors(), processThreadNum);
|
int core = Math.max(Runtime.getRuntime().availableProcessors(), processThreadNum);
|
||||||
taskProcessPool = new ThreadPoolExecutor(core, 4 * core,
|
// 基本都是 io 密集型任务
|
||||||
|
taskProcessPool = new ThreadPoolExecutor(core, 2 * core,
|
||||||
60, TimeUnit.SECONDS,
|
60, TimeUnit.SECONDS,
|
||||||
queue, threadFactory, RejectedExecutionHandlerFactory.newCallerRun("PowerJobTimeWheelPool"));
|
queue, threadFactory, RejectedExecutionHandlerFactory.newCallerRun("PowerJobTimeWheelPool"));
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,9 @@ import tech.powerjob.server.common.timewheel.Timer;
|
|||||||
*/
|
*/
|
||||||
public class HashedWheelTimerHolder {
|
public class HashedWheelTimerHolder {
|
||||||
|
|
||||||
// 非精确时间轮,每 5S 走一格
|
/**
|
||||||
|
* 非精确时间轮,每 5S 走一格
|
||||||
|
*/
|
||||||
public static final Timer INACCURATE_TIMER = new HashedWheelTimer(5000, 16, 0);
|
public static final Timer INACCURATE_TIMER = new HashedWheelTimer(5000, 16, 0);
|
||||||
|
|
||||||
private HashedWheelTimerHolder() {
|
private HashedWheelTimerHolder() {
|
||||||
|
@ -19,14 +19,22 @@ public class InstanceTimeWheelService {
|
|||||||
|
|
||||||
private static final Map<Long, TimerFuture> CARGO = Maps.newConcurrentMap();
|
private static final Map<Long, TimerFuture> CARGO = Maps.newConcurrentMap();
|
||||||
|
|
||||||
// 精确调度时间轮,每 1MS 走一格
|
/**
|
||||||
|
* 精确调度时间轮,每 1MS 走一格
|
||||||
|
*/
|
||||||
private static final Timer TIMER = new HashedWheelTimer(1, 4096, Runtime.getRuntime().availableProcessors() * 4);
|
private static final Timer TIMER = new HashedWheelTimer(1, 4096, Runtime.getRuntime().availableProcessors() * 4);
|
||||||
// 非精确调度时间轮,用于处理高延迟任务,每 10S 走一格
|
/**
|
||||||
|
* 非精确调度时间轮,用于处理高延迟任务,每 10S 走一格
|
||||||
|
*/
|
||||||
private static final Timer SLOW_TIMER = new HashedWheelTimer(10000, 12, 0);
|
private static final Timer SLOW_TIMER = new HashedWheelTimer(10000, 12, 0);
|
||||||
|
|
||||||
// 支持取消的时间间隔,低于该阈值则不会放进 CARGO
|
/**
|
||||||
|
* 支持取消的时间间隔,低于该阈值则不会放进 CARGO
|
||||||
|
*/
|
||||||
private static final long MIN_INTERVAL_MS = 1000;
|
private static final long MIN_INTERVAL_MS = 1000;
|
||||||
// 长延迟阈值
|
/**
|
||||||
|
* 长延迟阈值
|
||||||
|
*/
|
||||||
private static final long LONG_DELAY_THRESHOLD_MS = 60000;
|
private static final long LONG_DELAY_THRESHOLD_MS = 60000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,8 +25,8 @@ import java.lang.reflect.Method;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class AOPUtils {
|
public class AOPUtils {
|
||||||
|
|
||||||
private static final ExpressionParser parser = new SpelExpressionParser();
|
private static final ExpressionParser PARSER = new SpelExpressionParser();
|
||||||
private static final ParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
|
private static final ParameterNameDiscoverer DISCOVERER = new LocalVariableTableParameterNameDiscoverer();
|
||||||
|
|
||||||
public static String parseRealClassName(JoinPoint joinPoint) {
|
public static String parseRealClassName(JoinPoint joinPoint) {
|
||||||
return joinPoint.getSignature().getDeclaringType().getSimpleName();
|
return joinPoint.getSignature().getDeclaringType().getSimpleName();
|
||||||
@ -50,7 +50,7 @@ public class AOPUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T parseSpEl(Method method, Object[] arguments, String spEl, Class<T> clazz, T defaultResult) {
|
public static <T> T parseSpEl(Method method, Object[] arguments, String spEl, Class<T> clazz, T defaultResult) {
|
||||||
String[] params = discoverer.getParameterNames(method);
|
String[] params = DISCOVERER.getParameterNames(method);
|
||||||
assert params != null;
|
assert params != null;
|
||||||
|
|
||||||
EvaluationContext context = new StandardEvaluationContext();
|
EvaluationContext context = new StandardEvaluationContext();
|
||||||
@ -58,7 +58,7 @@ public class AOPUtils {
|
|||||||
context.setVariable(params[len], arguments[len]);
|
context.setVariable(params[len], arguments[len]);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Expression expression = parser.parseExpression(spEl);
|
Expression expression = PARSER.parseExpression(spEl);
|
||||||
return expression.getValue(context, clazz);
|
return expression.getValue(context, clazz);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[AOPUtils] parse SpEL failed for method[{}], please concat @tjq to fix the bug!", method.getName(), e);
|
log.error("[AOPUtils] parse SpEL failed for method[{}], please concat @tjq to fix the bug!", method.getName(), e);
|
||||||
|
@ -19,9 +19,13 @@ import java.util.List;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class TimeUtils {
|
public class TimeUtils {
|
||||||
|
|
||||||
// NTP 授时服务器(阿里云 -> 交大 -> 水果)
|
/**
|
||||||
|
* NTP 授时服务器(阿里云 -> 交大 -> 水果)
|
||||||
|
*/
|
||||||
private static final List<String> NTP_SERVER_LIST = Lists.newArrayList("ntp.aliyun.com", "ntp.sjtu.edu.cn", "time1.apple.com");
|
private static final List<String> NTP_SERVER_LIST = Lists.newArrayList("ntp.aliyun.com", "ntp.sjtu.edu.cn", "time1.apple.com");
|
||||||
// 最大误差 5S
|
/**
|
||||||
|
* 最大误差 5S
|
||||||
|
*/
|
||||||
private static final long MAX_OFFSET = 5000;
|
private static final long MAX_OFFSET = 5000;
|
||||||
|
|
||||||
public static void check() throws TimeCheckException {
|
public static void check() throws TimeCheckException {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>powerjob-server</artifactId>
|
<artifactId>powerjob-server</artifactId>
|
||||||
<groupId>tech.powerjob</groupId>
|
<groupId>tech.powerjob</groupId>
|
||||||
<version>4.2.0</version>
|
<version>4.2.1</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
@ -1,9 +1,18 @@
|
|||||||
package tech.powerjob.server.core;
|
package tech.powerjob.server.core;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
import tech.powerjob.common.RemoteConstant;
|
import tech.powerjob.common.RemoteConstant;
|
||||||
import tech.powerjob.common.SystemInstanceResult;
|
import tech.powerjob.common.SystemInstanceResult;
|
||||||
import tech.powerjob.common.enums.*;
|
import tech.powerjob.common.enums.*;
|
||||||
import tech.powerjob.common.request.ServerScheduleJobReq;
|
import tech.powerjob.common.request.ServerScheduleJobReq;
|
||||||
|
import tech.powerjob.server.common.Holder;
|
||||||
|
import tech.powerjob.server.common.module.WorkerInfo;
|
||||||
import tech.powerjob.server.core.instance.InstanceManager;
|
import tech.powerjob.server.core.instance.InstanceManager;
|
||||||
import tech.powerjob.server.core.instance.InstanceMetadataService;
|
import tech.powerjob.server.core.instance.InstanceMetadataService;
|
||||||
import tech.powerjob.server.core.lock.UseCacheLock;
|
import tech.powerjob.server.core.lock.UseCacheLock;
|
||||||
@ -12,18 +21,11 @@ import tech.powerjob.server.persistence.remote.model.JobInfoDO;
|
|||||||
import tech.powerjob.server.persistence.remote.repository.InstanceInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.InstanceInfoRepository;
|
||||||
import tech.powerjob.server.remote.transport.TransportService;
|
import tech.powerjob.server.remote.transport.TransportService;
|
||||||
import tech.powerjob.server.remote.worker.WorkerClusterQueryService;
|
import tech.powerjob.server.remote.worker.WorkerClusterQueryService;
|
||||||
import tech.powerjob.server.common.module.WorkerInfo;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.beans.BeanUtils;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.util.CollectionUtils;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static tech.powerjob.common.enums.InstanceStatus.*;
|
import static tech.powerjob.common.enums.InstanceStatus.*;
|
||||||
@ -38,35 +40,39 @@ import static tech.powerjob.common.enums.InstanceStatus.*;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class DispatchService {
|
public class DispatchService {
|
||||||
|
|
||||||
@Resource
|
private final TransportService transportService;
|
||||||
private TransportService transportService;
|
|
||||||
@Resource
|
private final WorkerClusterQueryService workerClusterQueryService;
|
||||||
private WorkerClusterQueryService workerClusterQueryService;
|
|
||||||
@Resource
|
private final InstanceManager instanceManager;
|
||||||
private InstanceManager instanceManager;
|
|
||||||
@Resource
|
private final InstanceMetadataService instanceMetadataService;
|
||||||
private InstanceMetadataService instanceMetadataService;
|
|
||||||
@Resource
|
private final InstanceInfoRepository instanceInfoRepository;
|
||||||
private InstanceInfoRepository instanceInfoRepository;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重新派发任务实例(不考虑实例当前的状态)
|
* 异步重新派发
|
||||||
*
|
*
|
||||||
* @param jobInfo 任务信息(注意,这里传入的任务信息有可能为“空”)
|
|
||||||
* @param instanceId 实例 ID
|
* @param instanceId 实例 ID
|
||||||
*/
|
*/
|
||||||
@UseCacheLock(type = "processJobInstance", key = "#jobInfo.getMaxInstanceNum() > 0 || T(tech.powerjob.common.enums.TimeExpressionType).FREQUENT_TYPES.contains(#jobInfo.getTimeExpressionType()) ? #jobInfo.getId() : #instanceId", concurrencyLevel = 1024)
|
@UseCacheLock(type = "processJobInstance", key = "#instanceId", concurrencyLevel = 1024)
|
||||||
public void redispatch(JobInfoDO jobInfo, Long instanceId) {
|
public void redispatchAsync(Long instanceId, int originStatus) {
|
||||||
InstanceInfoDO instance = instanceInfoRepository.findByInstanceId(instanceId);
|
|
||||||
// 将状态重置为等待派发
|
// 将状态重置为等待派发
|
||||||
instance.setStatus(InstanceStatus.WAITING_DISPATCH.getV());
|
instanceInfoRepository.updateStatusAndGmtModifiedByInstanceIdAndOriginStatus(instanceId, originStatus, InstanceStatus.WAITING_DISPATCH.getV(), new Date());
|
||||||
instance.setGmtModified(new Date());
|
|
||||||
instanceInfoRepository.saveAndFlush(instance);
|
|
||||||
dispatch(jobInfo, instanceId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步批量重新派发,不加锁
|
||||||
|
*/
|
||||||
|
public void redispatchBatchAsyncLockFree(List<Long> instanceIdList, int originStatus) {
|
||||||
|
// 将状态重置为等待派发
|
||||||
|
instanceInfoRepository.updateStatusAndGmtModifiedByInstanceIdListAndOriginStatus(instanceIdList, originStatus, InstanceStatus.WAITING_DISPATCH.getV(), new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将任务从Server派发到Worker(TaskTracker)
|
* 将任务从Server派发到Worker(TaskTracker)
|
||||||
* 只会派发当前状态为等待派发的任务实例
|
* 只会派发当前状态为等待派发的任务实例
|
||||||
@ -80,11 +86,14 @@ public class DispatchService {
|
|||||||
*
|
*
|
||||||
* @param jobInfo 任务的元信息
|
* @param jobInfo 任务的元信息
|
||||||
* @param instanceId 任务实例ID
|
* @param instanceId 任务实例ID
|
||||||
|
* @param instanceInfoOptional 任务实例信息,可选
|
||||||
|
* @param overloadOptional 超载信息,可选
|
||||||
*/
|
*/
|
||||||
@UseCacheLock(type = "processJobInstance", key = "#jobInfo.getMaxInstanceNum() > 0 || T(tech.powerjob.common.enums.TimeExpressionType).FREQUENT_TYPES.contains(#jobInfo.getTimeExpressionType()) ? #jobInfo.getId() : #instanceId", concurrencyLevel = 1024)
|
@UseCacheLock(type = "processJobInstance", key = "#jobInfo.getMaxInstanceNum() > 0 || T(tech.powerjob.common.enums.TimeExpressionType).FREQUENT_TYPES.contains(#jobInfo.getTimeExpressionType()) ? #jobInfo.getId() : #instanceId", concurrencyLevel = 1024)
|
||||||
public void dispatch(JobInfoDO jobInfo, Long instanceId) {
|
public void dispatch(JobInfoDO jobInfo, Long instanceId, Optional<InstanceInfoDO> instanceInfoOptional, Optional<Holder<Boolean>> overloadOptional) {
|
||||||
|
// 允许从外部传入实例信息,减少 io 次数
|
||||||
// 检查当前任务是否被取消
|
// 检查当前任务是否被取消
|
||||||
InstanceInfoDO instanceInfo = instanceInfoRepository.findByInstanceId(instanceId);
|
InstanceInfoDO instanceInfo = instanceInfoOptional.orElseGet(() -> instanceInfoRepository.findByInstanceId(instanceId));
|
||||||
Long jobId = instanceInfo.getJobId();
|
Long jobId = instanceInfo.getJobId();
|
||||||
if (CANCELED.getV() == instanceInfo.getStatus()) {
|
if (CANCELED.getV() == instanceInfo.getStatus()) {
|
||||||
log.info("[Dispatcher-{}|{}] cancel dispatch due to instance has been canceled", jobId, instanceId);
|
log.info("[Dispatcher-{}|{}] cancel dispatch due to instance has been canceled", jobId, instanceId);
|
||||||
@ -125,7 +134,6 @@ public class DispatchService {
|
|||||||
String result = String.format(SystemInstanceResult.TOO_MANY_INSTANCES, runningInstanceCount, maxInstanceNum);
|
String result = String.format(SystemInstanceResult.TOO_MANY_INSTANCES, runningInstanceCount, maxInstanceNum);
|
||||||
log.warn("[Dispatcher-{}|{}] cancel dispatch job due to too much instance is running ({} > {}).", jobId, instanceId, runningInstanceCount, maxInstanceNum);
|
log.warn("[Dispatcher-{}|{}] cancel dispatch job due to too much instance is running ({} > {}).", jobId, instanceId, runningInstanceCount, maxInstanceNum);
|
||||||
instanceInfoRepository.update4TriggerFailed(instanceId, FAILED.getV(), current, current, RemoteConstant.EMPTY_ADDRESS, result, now);
|
instanceInfoRepository.update4TriggerFailed(instanceId, FAILED.getV(), current, current, RemoteConstant.EMPTY_ADDRESS, result, now);
|
||||||
|
|
||||||
instanceManager.processFinishedInstance(instanceId, instanceInfo.getWfInstanceId(), FAILED, result);
|
instanceManager.processFinishedInstance(instanceId, instanceInfo.getWfInstanceId(), FAILED, result);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -141,8 +149,15 @@ public class DispatchService {
|
|||||||
instanceManager.processFinishedInstance(instanceId, instanceInfo.getWfInstanceId(), FAILED, SystemInstanceResult.NO_WORKER_AVAILABLE);
|
instanceManager.processFinishedInstance(instanceId, instanceInfo.getWfInstanceId(), FAILED, SystemInstanceResult.NO_WORKER_AVAILABLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 判断是否超载,在所有可用 worker 超载的情况下直接跳过当前任务
|
||||||
|
suitableWorkers = filterOverloadWorker(suitableWorkers);
|
||||||
|
if (suitableWorkers.isEmpty()) {
|
||||||
|
// 直接取消派发,减少一次数据库 io
|
||||||
|
overloadOptional.ifPresent(booleanHolder -> booleanHolder.set(true));
|
||||||
|
log.warn("[Dispatcher-{}|{}] cancel to dispatch job due to all worker is overload", jobId, instanceId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
List<String> workerIpList = suitableWorkers.stream().map(WorkerInfo::getAddress).collect(Collectors.toList());
|
List<String> workerIpList = suitableWorkers.stream().map(WorkerInfo::getAddress).collect(Collectors.toList());
|
||||||
|
|
||||||
// 构造任务调度请求
|
// 构造任务调度请求
|
||||||
ServerScheduleJobReq req = constructServerScheduleJobReq(jobInfo, instanceInfo, workerIpList);
|
ServerScheduleJobReq req = constructServerScheduleJobReq(jobInfo, instanceInfo, workerIpList);
|
||||||
|
|
||||||
@ -154,12 +169,23 @@ public class DispatchService {
|
|||||||
log.info("[Dispatcher-{}|{}] send schedule request to TaskTracker[protocol:{},address:{}] successfully: {}.", jobId, instanceId, taskTracker.getProtocol(), taskTrackerAddress, req);
|
log.info("[Dispatcher-{}|{}] send schedule request to TaskTracker[protocol:{},address:{}] successfully: {}.", jobId, instanceId, taskTracker.getProtocol(), taskTrackerAddress, req);
|
||||||
|
|
||||||
// 修改状态
|
// 修改状态
|
||||||
instanceInfoRepository.update4TriggerSucceed(instanceId, WAITING_WORKER_RECEIVE.getV(), current, taskTrackerAddress, now);
|
instanceInfoRepository.update4TriggerSucceed(instanceId, WAITING_WORKER_RECEIVE.getV(), current, taskTrackerAddress, now, instanceInfo.getStatus());
|
||||||
|
|
||||||
// 装载缓存
|
// 装载缓存
|
||||||
instanceMetadataService.loadJobInfo(instanceId, jobInfo);
|
instanceMetadataService.loadJobInfo(instanceId, jobInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<WorkerInfo> filterOverloadWorker(List<WorkerInfo> suitableWorkers) {
|
||||||
|
|
||||||
|
List<WorkerInfo> res = new ArrayList<>(suitableWorkers.size());
|
||||||
|
for (WorkerInfo suitableWorker : suitableWorkers) {
|
||||||
|
if (suitableWorker.overload()){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
res.add(suitableWorker);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造任务调度请求
|
* 构造任务调度请求
|
||||||
*/
|
*/
|
||||||
|
@ -43,7 +43,7 @@ import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
|
|||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
@ -84,7 +84,9 @@ public class ContainerService {
|
|||||||
// 并发部署的机器数量
|
// 并发部署的机器数量
|
||||||
private static final int DEPLOY_BATCH_NUM = 50;
|
private static final int DEPLOY_BATCH_NUM = 50;
|
||||||
// 部署间隔
|
// 部署间隔
|
||||||
private static final long DEPLOY_MIN_INTERVAL = 10 * 60 * 1000;
|
private static final long DEPLOY_MIN_INTERVAL = 10 * 60 * 1000L;
|
||||||
|
// 最长部署时间
|
||||||
|
private static final long DEPLOY_MAX_COST_TIME = 10 * 60 * 1000L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存容器
|
* 保存容器
|
||||||
@ -208,14 +210,13 @@ public class ContainerService {
|
|||||||
String deployLock = "containerDeployLock-" + containerId;
|
String deployLock = "containerDeployLock-" + containerId;
|
||||||
RemoteEndpoint.Async remote = session.getAsyncRemote();
|
RemoteEndpoint.Async remote = session.getAsyncRemote();
|
||||||
// 最长部署时间:10分钟
|
// 最长部署时间:10分钟
|
||||||
boolean lock = lockService.tryLock(deployLock, 10 * 60 * 1000);
|
boolean lock = lockService.tryLock(deployLock, DEPLOY_MAX_COST_TIME);
|
||||||
if (!lock) {
|
if (!lock) {
|
||||||
remote.sendText("SYSTEM: acquire deploy lock failed, maybe other user is deploying, please wait until the running deploy task finished.");
|
remote.sendText("SYSTEM: acquire deploy lock failed, maybe other user is deploying, please wait until the running deploy task finished.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
Optional<ContainerInfoDO> containerInfoOpt = containerInfoRepository.findById(containerId);
|
Optional<ContainerInfoDO> containerInfoOpt = containerInfoRepository.findById(containerId);
|
||||||
if (!containerInfoOpt.isPresent()) {
|
if (!containerInfoOpt.isPresent()) {
|
||||||
remote.sendText("SYSTEM: can't find container by id: " + containerId);
|
remote.sendText("SYSTEM: can't find container by id: " + containerId);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package tech.powerjob.server.core.handler;
|
package tech.powerjob.server.core.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
@ -22,7 +23,6 @@ import tech.powerjob.server.persistence.remote.repository.ContainerInfoRepositor
|
|||||||
import tech.powerjob.server.persistence.remote.repository.JobInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.JobInfoRepository;
|
||||||
import tech.powerjob.server.remote.worker.WorkerClusterQueryService;
|
import tech.powerjob.server.remote.worker.WorkerClusterQueryService;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
@ -34,17 +34,18 @@ import java.util.stream.Collectors;
|
|||||||
* @author tjq
|
* @author tjq
|
||||||
* @since 2022/9/11
|
* @since 2022/9/11
|
||||||
*/
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class AbWorkerRequestHandler implements IWorkerRequestHandler {
|
public abstract class AbWorkerRequestHandler implements IWorkerRequestHandler {
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected MonitorService monitorService;
|
protected final MonitorService monitorService;
|
||||||
@Resource
|
|
||||||
protected Environment environment;
|
protected final Environment environment;
|
||||||
@Resource
|
|
||||||
protected ContainerInfoRepository containerInfoRepository;
|
protected final ContainerInfoRepository containerInfoRepository;
|
||||||
@Resource
|
|
||||||
private WorkerClusterQueryService workerClusterQueryService;
|
private final WorkerClusterQueryService workerClusterQueryService;
|
||||||
|
|
||||||
protected abstract void processWorkerHeartbeat0(WorkerHeartbeat heartbeat, WorkerHeartbeatEvent event);
|
protected abstract void processWorkerHeartbeat0(WorkerHeartbeat heartbeat, WorkerHeartbeatEvent event);
|
||||||
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package tech.powerjob.server.core.handler;
|
package tech.powerjob.server.core.handler;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WorkerRequestHandlerHolder
|
* WorkerRequestHandlerHolder
|
||||||
@ -16,13 +14,14 @@ public class WorkerRequestHandlerHolder {
|
|||||||
|
|
||||||
private static IWorkerRequestHandler workerRequestHandler;
|
private static IWorkerRequestHandler workerRequestHandler;
|
||||||
|
|
||||||
|
public WorkerRequestHandlerHolder(IWorkerRequestHandler injectedWorkerRequestHandler) {
|
||||||
|
workerRequestHandler = injectedWorkerRequestHandler;
|
||||||
|
}
|
||||||
|
|
||||||
public static IWorkerRequestHandler fetchWorkerRequestHandler() {
|
public static IWorkerRequestHandler fetchWorkerRequestHandler() {
|
||||||
|
if (workerRequestHandler == null){
|
||||||
|
throw new IllegalStateException("WorkerRequestHandlerHolder not initialized!");
|
||||||
|
}
|
||||||
return workerRequestHandler;
|
return workerRequestHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public void setWorkerRequestHandler(IWorkerRequestHandler workerRequestHandler) {
|
|
||||||
WorkerRequestHandlerHolder.workerRequestHandler = workerRequestHandler;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package tech.powerjob.server.core.handler;
|
package tech.powerjob.server.core.handler;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import tech.powerjob.common.enums.InstanceStatus;
|
import tech.powerjob.common.enums.InstanceStatus;
|
||||||
@ -12,12 +12,14 @@ import tech.powerjob.common.response.AskResponse;
|
|||||||
import tech.powerjob.server.core.instance.InstanceLogService;
|
import tech.powerjob.server.core.instance.InstanceLogService;
|
||||||
import tech.powerjob.server.core.instance.InstanceManager;
|
import tech.powerjob.server.core.instance.InstanceManager;
|
||||||
import tech.powerjob.server.core.workflow.WorkflowInstanceManager;
|
import tech.powerjob.server.core.workflow.WorkflowInstanceManager;
|
||||||
|
import tech.powerjob.server.monitor.MonitorService;
|
||||||
import tech.powerjob.server.monitor.events.w2s.TtReportInstanceStatusEvent;
|
import tech.powerjob.server.monitor.events.w2s.TtReportInstanceStatusEvent;
|
||||||
import tech.powerjob.server.monitor.events.w2s.WorkerHeartbeatEvent;
|
import tech.powerjob.server.monitor.events.w2s.WorkerHeartbeatEvent;
|
||||||
import tech.powerjob.server.monitor.events.w2s.WorkerLogReportEvent;
|
import tech.powerjob.server.monitor.events.w2s.WorkerLogReportEvent;
|
||||||
|
import tech.powerjob.server.persistence.remote.repository.ContainerInfoRepository;
|
||||||
import tech.powerjob.server.remote.worker.WorkerClusterManagerService;
|
import tech.powerjob.server.remote.worker.WorkerClusterManagerService;
|
||||||
|
import tech.powerjob.server.remote.worker.WorkerClusterQueryService;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,12 +32,19 @@ import java.util.Optional;
|
|||||||
@Component
|
@Component
|
||||||
public class WorkerRequestHandlerImpl extends AbWorkerRequestHandler {
|
public class WorkerRequestHandlerImpl extends AbWorkerRequestHandler {
|
||||||
|
|
||||||
@Resource
|
private final InstanceManager instanceManager;
|
||||||
private InstanceManager instanceManager;
|
|
||||||
@Resource
|
private final WorkflowInstanceManager workflowInstanceManager;
|
||||||
private WorkflowInstanceManager workflowInstanceManager;
|
|
||||||
@Resource
|
private final InstanceLogService instanceLogService;
|
||||||
private InstanceLogService instanceLogService;
|
|
||||||
|
public WorkerRequestHandlerImpl(InstanceManager instanceManager, WorkflowInstanceManager workflowInstanceManager, InstanceLogService instanceLogService,
|
||||||
|
MonitorService monitorService, Environment environment, ContainerInfoRepository containerInfoRepository, WorkerClusterQueryService workerClusterQueryService) {
|
||||||
|
super(monitorService, environment, containerInfoRepository, workerClusterQueryService);
|
||||||
|
this.instanceManager = instanceManager;
|
||||||
|
this.workflowInstanceManager = workflowInstanceManager;
|
||||||
|
this.instanceLogService = instanceLogService;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void processWorkerHeartbeat0(WorkerHeartbeat heartbeat, WorkerHeartbeatEvent event) {
|
protected void processWorkerHeartbeat0(WorkerHeartbeat heartbeat, WorkerHeartbeatEvent event) {
|
||||||
|
@ -56,6 +56,7 @@ public class InstanceLogService {
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private InstanceMetadataService instanceMetadataService;
|
private InstanceMetadataService instanceMetadataService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private GridFsManager gridFsManager;
|
private GridFsManager gridFsManager;
|
||||||
/**
|
/**
|
||||||
@ -63,6 +64,7 @@ public class InstanceLogService {
|
|||||||
*/
|
*/
|
||||||
@Resource(name = "localTransactionTemplate")
|
@Resource(name = "localTransactionTemplate")
|
||||||
private TransactionTemplate localTransactionTemplate;
|
private TransactionTemplate localTransactionTemplate;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private LocalInstanceLogRepository localInstanceLogRepository;
|
private LocalInstanceLogRepository localInstanceLogRepository;
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package tech.powerjob.server.core.instance;
|
package tech.powerjob.server.core.instance;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import tech.powerjob.common.enums.InstanceStatus;
|
import tech.powerjob.common.enums.InstanceStatus;
|
||||||
import tech.powerjob.common.enums.Protocol;
|
import tech.powerjob.common.enums.Protocol;
|
||||||
import tech.powerjob.common.enums.TimeExpressionType;
|
import tech.powerjob.common.enums.TimeExpressionType;
|
||||||
@ -39,22 +40,22 @@ import java.util.concurrent.TimeUnit;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class InstanceManager {
|
public class InstanceManager {
|
||||||
|
|
||||||
@Resource
|
private final AlarmCenter alarmCenter;
|
||||||
private AlarmCenter alarmCenter;
|
|
||||||
@Resource
|
private final InstanceLogService instanceLogService;
|
||||||
private InstanceLogService instanceLogService;
|
|
||||||
@Resource
|
private final InstanceMetadataService instanceMetadataService;
|
||||||
private InstanceMetadataService instanceMetadataService;
|
|
||||||
@Resource
|
private final InstanceInfoRepository instanceInfoRepository;
|
||||||
private InstanceInfoRepository instanceInfoRepository;
|
|
||||||
@Resource
|
private final WorkflowInstanceManager workflowInstanceManager;
|
||||||
private WorkflowInstanceManager workflowInstanceManager;
|
|
||||||
@Resource
|
private final TransportService transportService;
|
||||||
private TransportService transportService;
|
|
||||||
@Resource
|
private final WorkerClusterQueryService workerClusterQueryService;
|
||||||
private WorkerClusterQueryService workerClusterQueryService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新任务状态
|
* 更新任务状态
|
||||||
@ -69,7 +70,6 @@ public class InstanceManager {
|
|||||||
public void updateStatus(TaskTrackerReportInstanceStatusReq req) throws ExecutionException {
|
public void updateStatus(TaskTrackerReportInstanceStatusReq req) throws ExecutionException {
|
||||||
|
|
||||||
Long instanceId = req.getInstanceId();
|
Long instanceId = req.getInstanceId();
|
||||||
|
|
||||||
// 获取相关数据
|
// 获取相关数据
|
||||||
JobInfoDO jobInfo = instanceMetadataService.fetchJobInfoByInstanceId(req.getInstanceId());
|
JobInfoDO jobInfo = instanceMetadataService.fetchJobInfoByInstanceId(req.getInstanceId());
|
||||||
InstanceInfoDO instanceInfo = instanceInfoRepository.findByInstanceId(instanceId);
|
InstanceInfoDO instanceInfo = instanceInfoRepository.findByInstanceId(instanceId);
|
||||||
@ -77,6 +77,7 @@ public class InstanceManager {
|
|||||||
log.warn("[InstanceManager-{}] can't find InstanceInfo from database", instanceId);
|
log.warn("[InstanceManager-{}] can't find InstanceInfo from database", instanceId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
int originStatus = instanceInfo.getStatus();
|
||||||
// 丢弃过期的上报数据
|
// 丢弃过期的上报数据
|
||||||
if (req.getReportTime() <= instanceInfo.getLastReportTime()) {
|
if (req.getReportTime() <= instanceInfo.getLastReportTime()) {
|
||||||
log.warn("[InstanceManager-{}] receive the expired status report request: {}, this report will be dropped.", instanceId, req);
|
log.warn("[InstanceManager-{}] receive the expired status report request: {}, this report will be dropped.", instanceId, req);
|
||||||
@ -134,8 +135,7 @@ public class InstanceManager {
|
|||||||
boolean finished = false;
|
boolean finished = false;
|
||||||
if (receivedInstanceStatus == InstanceStatus.SUCCEED) {
|
if (receivedInstanceStatus == InstanceStatus.SUCCEED) {
|
||||||
instanceInfo.setResult(req.getResult());
|
instanceInfo.setResult(req.getResult());
|
||||||
instanceInfo.setFinishedTime(System.currentTimeMillis());
|
instanceInfo.setFinishedTime(req.getEndTime() == null ? System.currentTimeMillis() : req.getEndTime());
|
||||||
|
|
||||||
finished = true;
|
finished = true;
|
||||||
} else if (receivedInstanceStatus == InstanceStatus.FAILED) {
|
} else if (receivedInstanceStatus == InstanceStatus.FAILED) {
|
||||||
|
|
||||||
@ -152,21 +152,23 @@ public class InstanceManager {
|
|||||||
instanceInfo.setStatus(InstanceStatus.WAITING_DISPATCH.getV());
|
instanceInfo.setStatus(InstanceStatus.WAITING_DISPATCH.getV());
|
||||||
} else {
|
} else {
|
||||||
instanceInfo.setResult(req.getResult());
|
instanceInfo.setResult(req.getResult());
|
||||||
instanceInfo.setFinishedTime(System.currentTimeMillis());
|
instanceInfo.setFinishedTime(req.getEndTime() == null ? System.currentTimeMillis() : req.getEndTime());
|
||||||
finished = true;
|
finished = true;
|
||||||
log.info("[InstanceManager-{}] instance execute failed and have no chance to retry.", instanceId);
|
log.info("[InstanceManager-{}] instance execute failed and have no chance to retry.", instanceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同步状态变更信息到数据库
|
|
||||||
instanceInfoRepository.saveAndFlush(instanceInfo);
|
|
||||||
|
|
||||||
if (finished) {
|
if (finished) {
|
||||||
|
// 最终状态允许直接覆盖更新
|
||||||
|
instanceInfoRepository.saveAndFlush(instanceInfo);
|
||||||
// 这里的 InstanceStatus 只有 成功/失败 两种,手动停止不会由 TaskTracker 上报
|
// 这里的 InstanceStatus 只有 成功/失败 两种,手动停止不会由 TaskTracker 上报
|
||||||
processFinishedInstance(instanceId, req.getWfInstanceId(), receivedInstanceStatus, req.getResult());
|
processFinishedInstance(instanceId, req.getWfInstanceId(), receivedInstanceStatus, req.getResult());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 带条件更新
|
||||||
|
final int i = instanceInfoRepository.updateStatusChangeInfoByInstanceIdAndStatus(instanceInfo.getLastReportTime(), instanceInfo.getGmtModified(), instanceInfo.getRunningTimes(), instanceInfo.getStatus(), instanceInfo.getInstanceId(), originStatus);
|
||||||
|
if (i == 0) {
|
||||||
|
log.warn("[InstanceManager-{}] update instance status failed, maybe the instance status has been changed by other thread. discard this status change,{}", instanceId, instanceInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopInstance(Long instanceId, InstanceInfoDO instanceInfo) {
|
private void stopInstance(Long instanceId, InstanceInfoDO instanceInfo) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package tech.powerjob.server.core.instance;
|
package tech.powerjob.server.core.instance;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import tech.powerjob.server.persistence.remote.model.InstanceInfoDO;
|
import tech.powerjob.server.persistence.remote.model.InstanceInfoDO;
|
||||||
import tech.powerjob.server.persistence.remote.model.JobInfoDO;
|
import tech.powerjob.server.persistence.remote.model.JobInfoDO;
|
||||||
import tech.powerjob.server.persistence.remote.repository.InstanceInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.InstanceInfoRepository;
|
||||||
@ -21,14 +22,16 @@ import java.util.concurrent.ExecutionException;
|
|||||||
* @since 2020/6/23
|
* @since 2020/6/23
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class InstanceMetadataService implements InitializingBean {
|
public class InstanceMetadataService implements InitializingBean {
|
||||||
|
|
||||||
@Resource
|
private final JobInfoRepository jobInfoRepository;
|
||||||
private JobInfoRepository jobInfoRepository;
|
|
||||||
@Resource
|
|
||||||
private InstanceInfoRepository instanceInfoRepository;
|
|
||||||
|
|
||||||
// 缓存,一旦生成任务实例,其对应的 JobInfo 不应该再改变(即使源数据改变)
|
private final InstanceInfoRepository instanceInfoRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存,一旦生成任务实例,其对应的 JobInfo 不应该再改变(即使源数据改变)
|
||||||
|
*/
|
||||||
private Cache<Long, JobInfoDO> instanceId2JobInfoCache;
|
private Cache<Long, JobInfoDO> instanceId2JobInfoCache;
|
||||||
|
|
||||||
@Value("${oms.instance.metadata.cache.size}")
|
@Value("${oms.instance.metadata.cache.size}")
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
package tech.powerjob.server.core.instance;
|
package tech.powerjob.server.core.instance;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import tech.powerjob.common.exception.PowerJobException;
|
|
||||||
import tech.powerjob.common.PowerQuery;
|
import tech.powerjob.common.PowerQuery;
|
||||||
import tech.powerjob.common.SystemInstanceResult;
|
import tech.powerjob.common.SystemInstanceResult;
|
||||||
import tech.powerjob.common.enums.InstanceStatus;
|
import tech.powerjob.common.enums.InstanceStatus;
|
||||||
import tech.powerjob.common.enums.Protocol;
|
import tech.powerjob.common.enums.Protocol;
|
||||||
|
import tech.powerjob.common.exception.PowerJobException;
|
||||||
import tech.powerjob.common.model.InstanceDetail;
|
import tech.powerjob.common.model.InstanceDetail;
|
||||||
import tech.powerjob.common.request.ServerQueryInstanceStatusReq;
|
import tech.powerjob.common.request.ServerQueryInstanceStatusReq;
|
||||||
import tech.powerjob.common.request.ServerStopInstanceReq;
|
import tech.powerjob.common.request.ServerStopInstanceReq;
|
||||||
@ -28,7 +29,6 @@ import tech.powerjob.server.remote.server.redirector.DesignateServer;
|
|||||||
import tech.powerjob.server.remote.transport.TransportService;
|
import tech.powerjob.server.remote.transport.TransportService;
|
||||||
import tech.powerjob.server.remote.worker.WorkerClusterQueryService;
|
import tech.powerjob.server.remote.worker.WorkerClusterQueryService;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -45,23 +45,22 @@ import static tech.powerjob.common.enums.InstanceStatus.STOPPED;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class InstanceService {
|
public class InstanceService {
|
||||||
|
|
||||||
@Resource
|
private final TransportService transportService;
|
||||||
private TransportService transportService;
|
|
||||||
@Resource
|
|
||||||
private DispatchService dispatchService;
|
|
||||||
@Resource
|
|
||||||
private IdGenerateService idGenerateService;
|
|
||||||
@Resource
|
|
||||||
private InstanceManager instanceManager;
|
|
||||||
@Resource
|
|
||||||
private JobInfoRepository jobInfoRepository;
|
|
||||||
@Resource
|
|
||||||
private InstanceInfoRepository instanceInfoRepository;
|
|
||||||
|
|
||||||
@Resource
|
private final DispatchService dispatchService;
|
||||||
private WorkerClusterQueryService workerClusterQueryService;
|
|
||||||
|
private final IdGenerateService idGenerateService;
|
||||||
|
|
||||||
|
private final InstanceManager instanceManager;
|
||||||
|
|
||||||
|
private final JobInfoRepository jobInfoRepository;
|
||||||
|
|
||||||
|
private final InstanceInfoRepository instanceInfoRepository;
|
||||||
|
|
||||||
|
private final WorkerClusterQueryService workerClusterQueryService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建任务实例(注意,该方法并不调用 saveAndFlush,如果有需要立即同步到DB的需求,请在方法结束后手动调用 flush)
|
* 创建任务实例(注意,该方法并不调用 saveAndFlush,如果有需要立即同步到DB的需求,请在方法结束后手动调用 flush)
|
||||||
@ -78,7 +77,7 @@ public class InstanceService {
|
|||||||
* @param expectTriggerTime 预期执行时间
|
* @param expectTriggerTime 预期执行时间
|
||||||
* @return 任务实例ID
|
* @return 任务实例ID
|
||||||
*/
|
*/
|
||||||
public Long create(Long jobId, Long appId, String jobParams, String instanceParams, Long wfInstanceId, Long expectTriggerTime) {
|
public InstanceInfoDO create(Long jobId, Long appId, String jobParams, String instanceParams, Long wfInstanceId, Long expectTriggerTime) {
|
||||||
|
|
||||||
Long instanceId = idGenerateService.allocate();
|
Long instanceId = idGenerateService.allocate();
|
||||||
Date now = new Date();
|
Date now = new Date();
|
||||||
@ -100,7 +99,7 @@ public class InstanceService {
|
|||||||
newInstanceInfo.setGmtModified(now);
|
newInstanceInfo.setGmtModified(now);
|
||||||
|
|
||||||
instanceInfoRepository.save(newInstanceInfo);
|
instanceInfoRepository.save(newInstanceInfo);
|
||||||
return instanceId;
|
return newInstanceInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -181,7 +180,7 @@ public class InstanceService {
|
|||||||
// 派发任务
|
// 派发任务
|
||||||
Long jobId = instanceInfo.getJobId();
|
Long jobId = instanceInfo.getJobId();
|
||||||
JobInfoDO jobInfo = jobInfoRepository.findById(jobId).orElseThrow(() -> new PowerJobException("can't find job info by jobId: " + jobId));
|
JobInfoDO jobInfo = jobInfoRepository.findById(jobId).orElseThrow(() -> new PowerJobException("can't find job info by jobId: " + jobId));
|
||||||
dispatchService.redispatch(jobInfo, instanceId);
|
dispatchService.dispatch(jobInfo, instanceId,Optional.of(instanceInfo),Optional.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
|
|||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.aspectj.lang.ProceedingJoinPoint;
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
import org.aspectj.lang.annotation.Around;
|
import org.aspectj.lang.annotation.Around;
|
||||||
@ -30,10 +31,10 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
@Aspect
|
@Aspect
|
||||||
@Component
|
@Component
|
||||||
@Order(1)
|
@Order(1)
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class UseCacheLockAspect {
|
public class UseCacheLockAspect {
|
||||||
|
|
||||||
@Resource
|
private final MonitorService monitorService;
|
||||||
private MonitorService monitorService;
|
|
||||||
|
|
||||||
private final Map<String, Cache<String, ReentrantLock>> lockContainer = Maps.newConcurrentMap();
|
private final Map<String, Cache<String, ReentrantLock>> lockContainer = Maps.newConcurrentMap();
|
||||||
|
|
||||||
|
@ -1,15 +1,5 @@
|
|||||||
package tech.powerjob.server.core.scheduler;
|
package tech.powerjob.server.core.scheduler;
|
||||||
|
|
||||||
import tech.powerjob.common.enums.InstanceStatus;
|
|
||||||
import tech.powerjob.common.enums.WorkflowInstanceStatus;
|
|
||||||
import tech.powerjob.server.common.constants.PJThreadPool;
|
|
||||||
import tech.powerjob.server.common.utils.OmsFileUtils;
|
|
||||||
import tech.powerjob.server.persistence.remote.repository.InstanceInfoRepository;
|
|
||||||
import tech.powerjob.server.persistence.remote.repository.WorkflowInstanceInfoRepository;
|
|
||||||
import tech.powerjob.server.persistence.mongodb.GridFsManager;
|
|
||||||
import tech.powerjob.server.persistence.remote.repository.WorkflowNodeInfoRepository;
|
|
||||||
import tech.powerjob.server.remote.worker.WorkerClusterManagerService;
|
|
||||||
import tech.powerjob.server.extension.LockService;
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Stopwatch;
|
import com.google.common.base.Stopwatch;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -18,8 +8,17 @@ import org.springframework.beans.factory.annotation.Value;
|
|||||||
import org.springframework.scheduling.annotation.Async;
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import tech.powerjob.common.enums.InstanceStatus;
|
||||||
|
import tech.powerjob.common.enums.WorkflowInstanceStatus;
|
||||||
|
import tech.powerjob.server.common.constants.PJThreadPool;
|
||||||
|
import tech.powerjob.server.common.utils.OmsFileUtils;
|
||||||
|
import tech.powerjob.server.extension.LockService;
|
||||||
|
import tech.powerjob.server.persistence.mongodb.GridFsManager;
|
||||||
|
import tech.powerjob.server.persistence.remote.repository.InstanceInfoRepository;
|
||||||
|
import tech.powerjob.server.persistence.remote.repository.WorkflowInstanceInfoRepository;
|
||||||
|
import tech.powerjob.server.persistence.remote.repository.WorkflowNodeInfoRepository;
|
||||||
|
import tech.powerjob.server.remote.worker.WorkerClusterManagerService;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
@ -33,25 +32,21 @@ import java.util.Date;
|
|||||||
@Service
|
@Service
|
||||||
public class CleanService {
|
public class CleanService {
|
||||||
|
|
||||||
@Resource
|
private final GridFsManager gridFsManager;
|
||||||
private GridFsManager gridFsManager;
|
|
||||||
@Resource
|
|
||||||
private InstanceInfoRepository instanceInfoRepository;
|
|
||||||
@Resource
|
|
||||||
private WorkflowInstanceInfoRepository workflowInstanceInfoRepository;
|
|
||||||
@Resource
|
|
||||||
private WorkflowNodeInfoRepository workflowNodeInfoRepository;
|
|
||||||
@Resource
|
|
||||||
private LockService lockService;
|
|
||||||
|
|
||||||
@Value("${oms.instanceinfo.retention}")
|
private final InstanceInfoRepository instanceInfoRepository;
|
||||||
private int instanceInfoRetentionDay;
|
|
||||||
|
|
||||||
@Value("${oms.container.retention.local}")
|
private final WorkflowInstanceInfoRepository workflowInstanceInfoRepository;
|
||||||
private int localContainerRetentionDay;
|
|
||||||
@Value("${oms.container.retention.remote}")
|
|
||||||
private int remoteContainerRetentionDay;
|
|
||||||
|
|
||||||
|
private final WorkflowNodeInfoRepository workflowNodeInfoRepository;
|
||||||
|
|
||||||
|
private final LockService lockService;
|
||||||
|
|
||||||
|
private final int instanceInfoRetentionDay;
|
||||||
|
|
||||||
|
private final int localContainerRetentionDay;
|
||||||
|
|
||||||
|
private final int remoteContainerRetentionDay;
|
||||||
|
|
||||||
private static final int TEMPORARY_RETENTION_DAY = 3;
|
private static final int TEMPORARY_RETENTION_DAY = 3;
|
||||||
|
|
||||||
@ -62,6 +57,21 @@ public class CleanService {
|
|||||||
|
|
||||||
private static final String HISTORY_DELETE_LOCK = "history_delete_lock";
|
private static final String HISTORY_DELETE_LOCK = "history_delete_lock";
|
||||||
|
|
||||||
|
public CleanService(GridFsManager gridFsManager, InstanceInfoRepository instanceInfoRepository, WorkflowInstanceInfoRepository workflowInstanceInfoRepository,
|
||||||
|
WorkflowNodeInfoRepository workflowNodeInfoRepository, LockService lockService,
|
||||||
|
@Value("${oms.instanceinfo.retention}") int instanceInfoRetentionDay,
|
||||||
|
@Value("${oms.container.retention.local}") int localContainerRetentionDay,
|
||||||
|
@Value("${oms.container.retention.remote}") int remoteContainerRetentionDay) {
|
||||||
|
this.gridFsManager = gridFsManager;
|
||||||
|
this.instanceInfoRepository = instanceInfoRepository;
|
||||||
|
this.workflowInstanceInfoRepository = workflowInstanceInfoRepository;
|
||||||
|
this.workflowNodeInfoRepository = workflowNodeInfoRepository;
|
||||||
|
this.lockService = lockService;
|
||||||
|
this.instanceInfoRetentionDay = instanceInfoRetentionDay;
|
||||||
|
this.localContainerRetentionDay = localContainerRetentionDay;
|
||||||
|
this.remoteContainerRetentionDay = remoteContainerRetentionDay;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Async(PJThreadPool.TIMING_POOL)
|
@Async(PJThreadPool.TIMING_POOL)
|
||||||
@Scheduled(cron = CLEAN_TIME_EXPRESSION)
|
@Scheduled(cron = CLEAN_TIME_EXPRESSION)
|
||||||
@ -84,7 +94,7 @@ public class CleanService {
|
|||||||
*/
|
*/
|
||||||
private void cleanByOneServer() {
|
private void cleanByOneServer() {
|
||||||
// 只要第一个server抢到锁其他server就会返回,所以锁10分钟应该足够了
|
// 只要第一个server抢到锁其他server就会返回,所以锁10分钟应该足够了
|
||||||
boolean lock = lockService.tryLock(HISTORY_DELETE_LOCK, 10 * 60 * 1000);
|
boolean lock = lockService.tryLock(HISTORY_DELETE_LOCK, 10 * 60 * 1000L);
|
||||||
if (!lock) {
|
if (!lock) {
|
||||||
log.info("[CleanService] clean job is already running, just return.");
|
log.info("[CleanService] clean job is already running, just return.");
|
||||||
return;
|
return;
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
package tech.powerjob.server.core.scheduler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.DisposableBean;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Echo009
|
||||||
|
* @since 2022/10/12
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CoreScheduleTaskManager implements InitializingBean, DisposableBean {
|
||||||
|
|
||||||
|
|
||||||
|
private final PowerScheduleService powerScheduleService;
|
||||||
|
|
||||||
|
private final InstanceStatusCheckService instanceStatusCheckService;
|
||||||
|
|
||||||
|
private final List<Thread> coreThreadContainer = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("AlibabaAvoidManuallyCreateThread")
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() {
|
||||||
|
// 定时调度
|
||||||
|
coreThreadContainer.add(new Thread(new LoopRunnable("ScheduleCronJob", PowerScheduleService.SCHEDULE_RATE, powerScheduleService::scheduleCronJob), "Thread-ScheduleCronJob"));
|
||||||
|
coreThreadContainer.add(new Thread(new LoopRunnable("ScheduleCronWorkflow", PowerScheduleService.SCHEDULE_RATE, powerScheduleService::scheduleCronWorkflow), "Thread-ScheduleCronWorkflow"));
|
||||||
|
coreThreadContainer.add(new Thread(new LoopRunnable("ScheduleFrequentJob", PowerScheduleService.SCHEDULE_RATE, powerScheduleService::scheduleFrequentJob), "Thread-ScheduleFrequentJob"));
|
||||||
|
// 数据清理
|
||||||
|
coreThreadContainer.add(new Thread(new LoopRunnable("CleanWorkerData", PowerScheduleService.SCHEDULE_RATE, powerScheduleService::cleanData), "Thread-CleanWorkerData"));
|
||||||
|
// 状态检查
|
||||||
|
coreThreadContainer.add(new Thread(new LoopRunnable("CheckRunningInstance", InstanceStatusCheckService.CHECK_INTERVAL, instanceStatusCheckService::checkRunningInstance), "Thread-CheckRunningInstance"));
|
||||||
|
coreThreadContainer.add(new Thread(new LoopRunnable("CheckWaitingDispatchInstance", InstanceStatusCheckService.CHECK_INTERVAL, instanceStatusCheckService::checkWaitingDispatchInstance), "Thread-CheckWaitingDispatchInstance"));
|
||||||
|
coreThreadContainer.add(new Thread(new LoopRunnable("CheckWaitingWorkerReceiveInstance", InstanceStatusCheckService.CHECK_INTERVAL, instanceStatusCheckService::checkWaitingWorkerReceiveInstance), "Thread-CheckWaitingWorkerReceiveInstance"));
|
||||||
|
coreThreadContainer.add(new Thread(new LoopRunnable("CheckWorkflowInstance", InstanceStatusCheckService.CHECK_INTERVAL, instanceStatusCheckService::checkWorkflowInstance), "Thread-CheckWorkflowInstance"));
|
||||||
|
|
||||||
|
coreThreadContainer.forEach(Thread::start);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
coreThreadContainer.forEach(Thread::interrupt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
private static class LoopRunnable implements Runnable {
|
||||||
|
|
||||||
|
private final String taskName;
|
||||||
|
|
||||||
|
private final Long runningInterval;
|
||||||
|
|
||||||
|
private final Runnable innerRunnable;
|
||||||
|
|
||||||
|
@SuppressWarnings("BusyWait")
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
log.info("start task : {}.", taskName);
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
innerRunnable.run();
|
||||||
|
Thread.sleep(runningInterval);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.warn("[{}] task has been interrupted!", taskName, e);
|
||||||
|
break;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[{}] task failed!", taskName, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,29 +1,31 @@
|
|||||||
package tech.powerjob.server.core.scheduler;
|
package tech.powerjob.server.core.scheduler;
|
||||||
|
|
||||||
import tech.powerjob.common.enums.InstanceStatus;
|
import com.google.common.base.Stopwatch;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
import tech.powerjob.common.SystemInstanceResult;
|
import tech.powerjob.common.SystemInstanceResult;
|
||||||
|
import tech.powerjob.common.enums.InstanceStatus;
|
||||||
import tech.powerjob.common.enums.TimeExpressionType;
|
import tech.powerjob.common.enums.TimeExpressionType;
|
||||||
import tech.powerjob.common.enums.WorkflowInstanceStatus;
|
import tech.powerjob.common.enums.WorkflowInstanceStatus;
|
||||||
import tech.powerjob.server.common.constants.PJThreadPool;
|
import tech.powerjob.server.common.Holder;
|
||||||
import tech.powerjob.server.common.constants.SwitchableStatus;
|
import tech.powerjob.server.common.constants.SwitchableStatus;
|
||||||
import tech.powerjob.server.remote.transport.starter.AkkaStarter;
|
|
||||||
import tech.powerjob.server.persistence.remote.model.*;
|
|
||||||
import tech.powerjob.server.persistence.remote.repository.*;
|
|
||||||
import tech.powerjob.server.core.DispatchService;
|
import tech.powerjob.server.core.DispatchService;
|
||||||
import tech.powerjob.server.core.instance.InstanceManager;
|
import tech.powerjob.server.core.instance.InstanceManager;
|
||||||
import tech.powerjob.server.core.workflow.WorkflowInstanceManager;
|
import tech.powerjob.server.core.workflow.WorkflowInstanceManager;
|
||||||
import com.google.common.base.Stopwatch;
|
import tech.powerjob.server.persistence.remote.model.InstanceInfoDO;
|
||||||
import com.google.common.collect.Lists;
|
import tech.powerjob.server.persistence.remote.model.JobInfoDO;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import tech.powerjob.server.persistence.remote.model.WorkflowInfoDO;
|
||||||
import org.springframework.scheduling.annotation.Async;
|
import tech.powerjob.server.persistence.remote.model.WorkflowInstanceInfoDO;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import tech.powerjob.server.persistence.remote.model.brief.BriefInstanceInfo;
|
||||||
import org.springframework.stereotype.Service;
|
import tech.powerjob.server.persistence.remote.repository.*;
|
||||||
import org.springframework.util.CollectionUtils;
|
import tech.powerjob.server.remote.transport.starter.AkkaStarter;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.Date;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,135 +36,220 @@ import java.util.stream.Collectors;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class InstanceStatusCheckService {
|
public class InstanceStatusCheckService {
|
||||||
|
|
||||||
private static final int MAX_BATCH_NUM = 10;
|
private static final int MAX_BATCH_NUM_APP = 10;
|
||||||
|
private static final int MAX_BATCH_NUM_INSTANCE = 3000;
|
||||||
|
private static final int MAX_BATCH_UPDATE_NUM = 500;
|
||||||
private static final long DISPATCH_TIMEOUT_MS = 30000;
|
private static final long DISPATCH_TIMEOUT_MS = 30000;
|
||||||
private static final long RECEIVE_TIMEOUT_MS = 60000;
|
private static final long RECEIVE_TIMEOUT_MS = 60000;
|
||||||
private static final long RUNNING_TIMEOUT_MS = 60000;
|
private static final long RUNNING_TIMEOUT_MS = 60000;
|
||||||
private static final long WORKFLOW_WAITING_TIMEOUT_MS = 60000;
|
private static final long WORKFLOW_WAITING_TIMEOUT_MS = 60000;
|
||||||
|
|
||||||
@Resource
|
public static final long CHECK_INTERVAL = 10000;
|
||||||
private DispatchService dispatchService;
|
|
||||||
@Resource
|
|
||||||
private InstanceManager instanceManager;
|
|
||||||
@Resource
|
|
||||||
private WorkflowInstanceManager workflowInstanceManager;
|
|
||||||
|
|
||||||
@Resource
|
private final DispatchService dispatchService;
|
||||||
private AppInfoRepository appInfoRepository;
|
|
||||||
@Resource
|
|
||||||
private JobInfoRepository jobInfoRepository;
|
|
||||||
@Resource
|
|
||||||
private InstanceInfoRepository instanceInfoRepository;
|
|
||||||
@Resource
|
|
||||||
private WorkflowInfoRepository workflowInfoRepository;
|
|
||||||
@Resource
|
|
||||||
private WorkflowInstanceInfoRepository workflowInstanceInfoRepository;
|
|
||||||
|
|
||||||
@Async(PJThreadPool.TIMING_POOL)
|
private final InstanceManager instanceManager;
|
||||||
@Scheduled(fixedDelay = 10000)
|
|
||||||
public void timingStatusCheck() {
|
private final WorkflowInstanceManager workflowInstanceManager;
|
||||||
|
|
||||||
|
private final AppInfoRepository appInfoRepository;
|
||||||
|
|
||||||
|
private final JobInfoRepository jobInfoRepository;
|
||||||
|
|
||||||
|
private final InstanceInfoRepository instanceInfoRepository;
|
||||||
|
|
||||||
|
|
||||||
|
private final WorkflowInfoRepository workflowInfoRepository;
|
||||||
|
|
||||||
|
private final WorkflowInstanceInfoRepository workflowInstanceInfoRepository;
|
||||||
|
|
||||||
|
public void checkWorkflowInstance() {
|
||||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||||
|
|
||||||
// 查询 DB 获取该 Server 需要负责的 AppGroup
|
// 查询 DB 获取该 Server 需要负责的 AppGroup
|
||||||
List<AppInfoDO> appInfoList = appInfoRepository.findAllByCurrentServer(AkkaStarter.getActorSystemAddress());
|
List<Long> allAppIds = appInfoRepository.listAppIdByCurrentServer(AkkaStarter.getActorSystemAddress());
|
||||||
if (CollectionUtils.isEmpty(appInfoList)) {
|
if (CollectionUtils.isEmpty(allAppIds)) {
|
||||||
log.info("[InstanceStatusChecker] current server has no app's job to check");
|
log.info("[InstanceStatusChecker] current server has no app's job to check");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<Long> allAppIds = appInfoList.stream().map(AppInfoDO::getId).collect(Collectors.toList());
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
checkInstance(allAppIds);
|
|
||||||
checkWorkflowInstance(allAppIds);
|
checkWorkflowInstance(allAppIds);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[InstanceStatusChecker] status check failed.", e);
|
log.error("[InstanceStatusChecker] WorkflowInstance status check failed.", e);
|
||||||
}
|
}
|
||||||
log.info("[InstanceStatusChecker] status check using {}.", stopwatch.stop());
|
log.info("[InstanceStatusChecker] WorkflowInstance status check using {}.", stopwatch.stop());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查任务实例的状态,发现异常及时重试,包括
|
* 检查等待派发的实例
|
||||||
* WAITING_DISPATCH 超时:写入时间轮但未调度前 server down
|
* WAITING_DISPATCH 超时:写入时间轮但未调度前 server down
|
||||||
* WAITING_WORKER_RECEIVE 超时:由于网络错误导致 worker 未接受成功
|
|
||||||
* RUNNING 超时:TaskTracker down,断开与 server 的心跳连接
|
|
||||||
*
|
|
||||||
* @param allAppIds 本系统所承担的所有 appIds
|
|
||||||
*/
|
*/
|
||||||
private void checkInstance(List<Long> allAppIds) {
|
public void checkWaitingDispatchInstance() {
|
||||||
|
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||||
Lists.partition(allAppIds, MAX_BATCH_NUM).forEach(partAppIds -> {
|
// 查询 DB 获取该 Server 需要负责的 AppGroup
|
||||||
// 1. 检查等待 WAITING_DISPATCH 状态的任务
|
List<Long> allAppIds = appInfoRepository.listAppIdByCurrentServer(AkkaStarter.getActorSystemAddress());
|
||||||
handleWaitingDispatchInstance(partAppIds);
|
if (CollectionUtils.isEmpty(allAppIds)) {
|
||||||
// 2. 检查 WAITING_WORKER_RECEIVE 状态的任务
|
log.info("[InstanceStatusChecker] current server has no app's job to check");
|
||||||
handleWaitingWorkerReceiveInstance(partAppIds);
|
return;
|
||||||
// 3. 检查 RUNNING 状态的任务(一定时间内没收到 TaskTracker 的状态报告,视为失败)
|
}
|
||||||
handleRunningInstance(partAppIds);
|
try {
|
||||||
});
|
// 检查等待 WAITING_DISPATCH 状态的任务
|
||||||
|
Lists.partition(allAppIds, MAX_BATCH_NUM_APP).forEach(this::handleWaitingDispatchInstance);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[InstanceStatusChecker] WaitingDispatchInstance status check failed.", e);
|
||||||
|
}
|
||||||
|
log.info("[InstanceStatusChecker] WaitingDispatchInstance status check using {}.", stopwatch.stop());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查等待 worker 接收的实例
|
||||||
|
* WAITING_WORKER_RECEIVE 超时:由于网络错误导致 worker 未接受成功
|
||||||
|
*/
|
||||||
|
public void checkWaitingWorkerReceiveInstance() {
|
||||||
|
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||||
|
// 查询 DB 获取该 Server 需要负责的 AppGroup
|
||||||
|
List<Long> allAppIds = appInfoRepository.listAppIdByCurrentServer(AkkaStarter.getActorSystemAddress());
|
||||||
|
if (CollectionUtils.isEmpty(allAppIds)) {
|
||||||
|
log.info("[InstanceStatusChecker] current server has no app's job to check");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 检查 WAITING_WORKER_RECEIVE 状态的任务
|
||||||
|
Lists.partition(allAppIds, MAX_BATCH_NUM_APP).forEach(this::handleWaitingWorkerReceiveInstance);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[InstanceStatusChecker] WaitingWorkerReceiveInstance status check failed.", e);
|
||||||
|
}
|
||||||
|
log.info("[InstanceStatusChecker] WaitingWorkerReceiveInstance status check using {}.", stopwatch.stop());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查运行中的实例
|
||||||
|
* RUNNING 超时:TaskTracker down,断开与 server 的心跳连接
|
||||||
|
*/
|
||||||
|
public void checkRunningInstance() {
|
||||||
|
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||||
|
// 查询 DB 获取该 Server 需要负责的 AppGroup
|
||||||
|
List<Long> allAppIds = appInfoRepository.listAppIdByCurrentServer(AkkaStarter.getActorSystemAddress());
|
||||||
|
if (CollectionUtils.isEmpty(allAppIds)) {
|
||||||
|
log.info("[InstanceStatusChecker] current server has no app's job to check");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 检查 RUNNING 状态的任务(一定时间没收到 TaskTracker 的状态报告,视为失败)
|
||||||
|
Lists.partition(allAppIds, MAX_BATCH_NUM_APP).forEach(this::handleRunningInstance);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[InstanceStatusChecker] RunningInstance status check failed.", e);
|
||||||
|
}
|
||||||
|
log.info("[InstanceStatusChecker] RunningInstance status check using {}.", stopwatch.stop());
|
||||||
|
}
|
||||||
|
|
||||||
private void handleWaitingDispatchInstance(List<Long> partAppIds) {
|
private void handleWaitingDispatchInstance(List<Long> partAppIds) {
|
||||||
// 1. 检查等待 WAITING_DISPATCH 状态的任务
|
// 1. 检查等待 WAITING_DISPATCH 状态的任务
|
||||||
long threshold = System.currentTimeMillis() - DISPATCH_TIMEOUT_MS;
|
long threshold = System.currentTimeMillis() - DISPATCH_TIMEOUT_MS;
|
||||||
List<InstanceInfoDO> waitingDispatchInstances = instanceInfoRepository.findByAppIdInAndStatusAndExpectedTriggerTimeLessThan(partAppIds, InstanceStatus.WAITING_DISPATCH.getV(), threshold);
|
List<InstanceInfoDO> waitingDispatchInstances = instanceInfoRepository.findAllByAppIdInAndStatusAndExpectedTriggerTimeLessThan(partAppIds, InstanceStatus.WAITING_DISPATCH.getV(), threshold, PageRequest.of(0, MAX_BATCH_NUM_INSTANCE));
|
||||||
if (!CollectionUtils.isEmpty(waitingDispatchInstances)) {
|
while (!waitingDispatchInstances.isEmpty()) {
|
||||||
log.warn("[InstanceStatusChecker] find some instance which is not triggered as expected: {}", waitingDispatchInstances);
|
List<Long> overloadAppIdList = new ArrayList<>();
|
||||||
waitingDispatchInstances.forEach(instance -> {
|
long startTime = System.currentTimeMillis();
|
||||||
|
// 按照 appId 分组处理,方便处理超载的逻辑
|
||||||
Optional<JobInfoDO> jobInfoOpt = jobInfoRepository.findById(instance.getJobId());
|
Map<Long, List<InstanceInfoDO>> waitingDispatchInstancesMap = waitingDispatchInstances.stream().collect(Collectors.groupingBy(InstanceInfoDO::getAppId));
|
||||||
|
for (Map.Entry<Long, List<InstanceInfoDO>> entry : waitingDispatchInstancesMap.entrySet()) {
|
||||||
|
final Long currentAppId = entry.getKey();
|
||||||
|
final List<InstanceInfoDO> currentAppWaitingDispatchInstances = entry.getValue();
|
||||||
|
// collect job id
|
||||||
|
Set<Long> jobIds = currentAppWaitingDispatchInstances.stream().map(InstanceInfoDO::getJobId).collect(Collectors.toSet());
|
||||||
|
// query job info and map
|
||||||
|
Map<Long, JobInfoDO> jobInfoMap = jobInfoRepository.findByIdIn(jobIds).stream().collect(Collectors.toMap(JobInfoDO::getId, e -> e));
|
||||||
|
log.warn("[InstanceStatusChecker] find some instance in app({}) which is not triggered as expected: {}", currentAppId, currentAppWaitingDispatchInstances.stream().map(InstanceInfoDO::getInstanceId).collect(Collectors.toList()));
|
||||||
|
final Holder<Boolean> overloadFlag = new Holder<>(false);
|
||||||
|
// 先这么简单处理没问题,毕竟只有这一个地方用了 parallelStream
|
||||||
|
currentAppWaitingDispatchInstances.parallelStream().forEach(instance -> {
|
||||||
|
if (overloadFlag.get()) {
|
||||||
|
// 直接忽略
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Optional<JobInfoDO> jobInfoOpt = Optional.ofNullable(jobInfoMap.get(instance.getJobId()));
|
||||||
if (jobInfoOpt.isPresent()) {
|
if (jobInfoOpt.isPresent()) {
|
||||||
dispatchService.redispatch(jobInfoOpt.get(), instance.getInstanceId());
|
// 处理等待派发的任务没有必要再重置一次状态,减少 io 次数
|
||||||
|
dispatchService.dispatch(jobInfoOpt.get(), instance.getInstanceId(), Optional.of(instance), Optional.of(overloadFlag));
|
||||||
} else {
|
} else {
|
||||||
log.warn("[InstanceStatusChecker] can't find job by jobId[{}], so redispatch failed, failed instance: {}", instance.getJobId(), instance);
|
log.warn("[InstanceStatusChecker] can't find job by jobId[{}], so redispatch failed, failed instance: {}", instance.getJobId(), instance);
|
||||||
updateFailedInstance(instance, SystemInstanceResult.CAN_NOT_FIND_JOB_INFO);
|
final Optional<InstanceInfoDO> opt = instanceInfoRepository.findById(instance.getId());
|
||||||
|
opt.ifPresent(instanceInfoDO -> updateFailedInstance(instanceInfoDO, SystemInstanceResult.CAN_NOT_FIND_JOB_INFO));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
threshold = System.currentTimeMillis() - DISPATCH_TIMEOUT_MS;
|
||||||
|
if (overloadFlag.get()) {
|
||||||
|
overloadAppIdList.add(currentAppId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.info("[InstanceStatusChecker] process {} task,use {} ms", waitingDispatchInstances.size(), System.currentTimeMillis() - startTime);
|
||||||
|
if (!overloadAppIdList.isEmpty()) {
|
||||||
|
log.warn("[InstanceStatusChecker] app[{}] is overload, so skip check waiting dispatch instance", overloadAppIdList);
|
||||||
|
partAppIds.removeAll(overloadAppIdList);
|
||||||
|
}
|
||||||
|
if (partAppIds.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
waitingDispatchInstances = instanceInfoRepository.findAllByAppIdInAndStatusAndExpectedTriggerTimeLessThan(partAppIds, InstanceStatus.WAITING_DISPATCH.getV(), threshold, PageRequest.of(0, MAX_BATCH_NUM_INSTANCE));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private void handleWaitingWorkerReceiveInstance(List<Long> partAppIds) {
|
private void handleWaitingWorkerReceiveInstance(List<Long> partAppIds) {
|
||||||
// 2. 检查 WAITING_WORKER_RECEIVE 状态的任务
|
// 2. 检查 WAITING_WORKER_RECEIVE 状态的任务
|
||||||
long threshold = System.currentTimeMillis() - RECEIVE_TIMEOUT_MS;
|
long threshold = System.currentTimeMillis() - RECEIVE_TIMEOUT_MS;
|
||||||
List<InstanceInfoDO> waitingWorkerReceiveInstances = instanceInfoRepository.findByAppIdInAndStatusAndActualTriggerTimeLessThan(partAppIds, InstanceStatus.WAITING_WORKER_RECEIVE.getV(), threshold);
|
List<BriefInstanceInfo> waitingWorkerReceiveInstances = instanceInfoRepository.selectBriefInfoByAppIdInAndStatusAndActualTriggerTimeLessThan(partAppIds, InstanceStatus.WAITING_WORKER_RECEIVE.getV(), threshold, PageRequest.of(0, MAX_BATCH_NUM_INSTANCE));
|
||||||
if (!CollectionUtils.isEmpty(waitingWorkerReceiveInstances)) {
|
while (!waitingWorkerReceiveInstances.isEmpty()) {
|
||||||
log.warn("[InstanceStatusChecker] find one instance didn't receive any reply from worker, try to redispatch: {}", waitingWorkerReceiveInstances);
|
log.warn("[InstanceStatusChecker] find some instance didn't receive any reply from worker, try to redispatch: {}", waitingWorkerReceiveInstances.stream().map(BriefInstanceInfo::getInstanceId).collect(Collectors.toList()));
|
||||||
waitingWorkerReceiveInstances.forEach(instance -> {
|
final List<List<BriefInstanceInfo>> partitions = Lists.partition(waitingWorkerReceiveInstances, MAX_BATCH_UPDATE_NUM);
|
||||||
// 重新派发
|
for (List<BriefInstanceInfo> partition : partitions) {
|
||||||
JobInfoDO jobInfoDO = jobInfoRepository.findById(instance.getJobId()).orElseGet(JobInfoDO::new);
|
dispatchService.redispatchBatchAsyncLockFree(partition.stream().map(BriefInstanceInfo::getInstanceId).collect(Collectors.toList()), InstanceStatus.WAITING_WORKER_RECEIVE.getV());
|
||||||
dispatchService.redispatch(jobInfoDO, instance.getInstanceId());
|
}
|
||||||
});
|
// 重新查询
|
||||||
|
threshold = System.currentTimeMillis() - RECEIVE_TIMEOUT_MS;
|
||||||
|
waitingWorkerReceiveInstances = instanceInfoRepository.selectBriefInfoByAppIdInAndStatusAndActualTriggerTimeLessThan(partAppIds, InstanceStatus.WAITING_WORKER_RECEIVE.getV(), threshold, PageRequest.of(0, MAX_BATCH_NUM_INSTANCE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleRunningInstance(List<Long> partAppIds) {
|
private void handleRunningInstance(List<Long> partAppIds) {
|
||||||
// 3. 检查 RUNNING 状态的任务(一定时间没收到 TaskTracker 的状态报告,视为失败)
|
// 3. 检查 RUNNING 状态的任务(一定时间没收到 TaskTracker 的状态报告,视为失败)
|
||||||
long threshold = System.currentTimeMillis() - RUNNING_TIMEOUT_MS;
|
long threshold = System.currentTimeMillis() - RUNNING_TIMEOUT_MS;
|
||||||
List<InstanceInfoDO> failedInstances = instanceInfoRepository.findByAppIdInAndStatusAndGmtModifiedBefore(partAppIds, InstanceStatus.RUNNING.getV(), new Date(threshold));
|
List<BriefInstanceInfo> failedInstances = instanceInfoRepository.selectBriefInfoByAppIdInAndStatusAndGmtModifiedBefore(partAppIds, InstanceStatus.RUNNING.getV(), new Date(threshold), PageRequest.of(0, MAX_BATCH_NUM_INSTANCE));
|
||||||
if (!CollectionUtils.isEmpty(failedInstances)) {
|
while (!failedInstances.isEmpty()) {
|
||||||
log.warn("[InstanceStatusCheckService] instances({}) has not received status report for a long time.", failedInstances);
|
// collect job id
|
||||||
|
Set<Long> jobIds = failedInstances.stream().map(BriefInstanceInfo::getJobId).collect(Collectors.toSet());
|
||||||
|
// query job info and map
|
||||||
|
Map<Long, JobInfoDO> jobInfoMap = jobInfoRepository.findByIdIn(jobIds).stream().collect(Collectors.toMap(JobInfoDO::getId, e -> e));
|
||||||
|
log.warn("[InstanceStatusCheckService] find some instances have not received status report for a long time : {}", failedInstances.stream().map(BriefInstanceInfo::getInstanceId).collect(Collectors.toList()));
|
||||||
failedInstances.forEach(instance -> {
|
failedInstances.forEach(instance -> {
|
||||||
|
Optional<JobInfoDO> jobInfoOpt = Optional.ofNullable(jobInfoMap.get(instance.getJobId()));
|
||||||
JobInfoDO jobInfoDO = jobInfoRepository.findById(instance.getJobId()).orElseGet(JobInfoDO::new);
|
if (!jobInfoOpt.isPresent()) {
|
||||||
TimeExpressionType timeExpressionType = TimeExpressionType.of(jobInfoDO.getTimeExpressionType());
|
final Optional<InstanceInfoDO> opt = instanceInfoRepository.findById(instance.getId());
|
||||||
SwitchableStatus switchableStatus = SwitchableStatus.of(jobInfoDO.getStatus());
|
opt.ifPresent(e -> updateFailedInstance(e, SystemInstanceResult.REPORT_TIMEOUT));
|
||||||
|
return;
|
||||||
// 如果任务已关闭,则不进行重试,将任务置为失败即可;秒级任务也直接置为失败,由派发器重新调度
|
}
|
||||||
if (switchableStatus != SwitchableStatus.ENABLE || TimeExpressionType.FREQUENT_TYPES.contains(timeExpressionType.getV())) {
|
TimeExpressionType timeExpressionType = TimeExpressionType.of(jobInfoOpt.get().getTimeExpressionType());
|
||||||
updateFailedInstance(instance, SystemInstanceResult.REPORT_TIMEOUT);
|
SwitchableStatus switchableStatus = SwitchableStatus.of(jobInfoOpt.get().getStatus());
|
||||||
|
// 如果任务已关闭,则不进行重试,将任务置为失败即可;秒级任务也直接置为失败,由派发器重新调度
|
||||||
|
if (switchableStatus != SwitchableStatus.ENABLE || TimeExpressionType.FREQUENT_TYPES.contains(timeExpressionType.getV())) {
|
||||||
|
final Optional<InstanceInfoDO> opt = instanceInfoRepository.findById(instance.getId());
|
||||||
|
opt.ifPresent(e -> updateFailedInstance(e, SystemInstanceResult.REPORT_TIMEOUT));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CRON 和 API一样,失败次数 + 1,根据重试配置进行重试
|
// CRON 和 API一样,失败次数 + 1,根据重试配置进行重试
|
||||||
if (instance.getRunningTimes() < jobInfoDO.getInstanceRetryNum()) {
|
if (instance.getRunningTimes() < jobInfoOpt.get().getInstanceRetryNum()) {
|
||||||
dispatchService.redispatch(jobInfoDO, instance.getInstanceId());
|
dispatchService.redispatchAsync(instance.getInstanceId(), InstanceStatus.RUNNING.getV());
|
||||||
} else {
|
} else {
|
||||||
updateFailedInstance(instance, SystemInstanceResult.REPORT_TIMEOUT);
|
final Optional<InstanceInfoDO> opt = instanceInfoRepository.findById(instance.getId());
|
||||||
|
opt.ifPresent(e -> updateFailedInstance(e, SystemInstanceResult.REPORT_TIMEOUT));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
threshold = System.currentTimeMillis() - RUNNING_TIMEOUT_MS;
|
||||||
|
failedInstances = instanceInfoRepository.selectBriefInfoByAppIdInAndStatusAndGmtModifiedBefore(partAppIds, InstanceStatus.RUNNING.getV(), new Date(threshold), PageRequest.of(0, MAX_BATCH_NUM_INSTANCE));
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -175,7 +262,7 @@ public class InstanceStatusCheckService {
|
|||||||
|
|
||||||
// 重试长时间处于 WAITING 状态的工作流实例
|
// 重试长时间处于 WAITING 状态的工作流实例
|
||||||
long threshold = System.currentTimeMillis() - WORKFLOW_WAITING_TIMEOUT_MS;
|
long threshold = System.currentTimeMillis() - WORKFLOW_WAITING_TIMEOUT_MS;
|
||||||
Lists.partition(allAppIds, MAX_BATCH_NUM).forEach(partAppIds -> {
|
Lists.partition(allAppIds, MAX_BATCH_NUM_APP).forEach(partAppIds -> {
|
||||||
List<WorkflowInstanceInfoDO> waitingWfInstanceList = workflowInstanceInfoRepository.findByAppIdInAndStatusAndExpectedTriggerTimeLessThan(partAppIds, WorkflowInstanceStatus.WAITING.getV(), threshold);
|
List<WorkflowInstanceInfoDO> waitingWfInstanceList = workflowInstanceInfoRepository.findByAppIdInAndStatusAndExpectedTriggerTimeLessThan(partAppIds, WorkflowInstanceStatus.WAITING.getV(), threshold);
|
||||||
if (!CollectionUtils.isEmpty(waitingWfInstanceList)) {
|
if (!CollectionUtils.isEmpty(waitingWfInstanceList)) {
|
||||||
|
|
||||||
|
@ -1,38 +1,33 @@
|
|||||||
package tech.powerjob.server.core.scheduler;
|
package tech.powerjob.server.core.scheduler;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
import tech.powerjob.common.enums.InstanceStatus;
|
import tech.powerjob.common.enums.InstanceStatus;
|
||||||
import tech.powerjob.common.enums.TimeExpressionType;
|
import tech.powerjob.common.enums.TimeExpressionType;
|
||||||
import tech.powerjob.common.model.LifeCycle;
|
import tech.powerjob.common.model.LifeCycle;
|
||||||
import tech.powerjob.server.common.constants.PJThreadPool;
|
|
||||||
import tech.powerjob.server.remote.transport.starter.AkkaStarter;
|
|
||||||
import tech.powerjob.server.common.constants.SwitchableStatus;
|
import tech.powerjob.server.common.constants.SwitchableStatus;
|
||||||
import tech.powerjob.server.persistence.remote.model.AppInfoDO;
|
import tech.powerjob.server.common.timewheel.holder.InstanceTimeWheelService;
|
||||||
|
import tech.powerjob.server.core.DispatchService;
|
||||||
|
import tech.powerjob.server.core.instance.InstanceService;
|
||||||
|
import tech.powerjob.server.core.service.JobService;
|
||||||
|
import tech.powerjob.server.core.workflow.WorkflowInstanceManager;
|
||||||
import tech.powerjob.server.persistence.remote.model.JobInfoDO;
|
import tech.powerjob.server.persistence.remote.model.JobInfoDO;
|
||||||
import tech.powerjob.server.persistence.remote.model.WorkflowInfoDO;
|
import tech.powerjob.server.persistence.remote.model.WorkflowInfoDO;
|
||||||
import tech.powerjob.server.persistence.remote.repository.AppInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.AppInfoRepository;
|
||||||
import tech.powerjob.server.persistence.remote.repository.InstanceInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.InstanceInfoRepository;
|
||||||
import tech.powerjob.server.persistence.remote.repository.JobInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.JobInfoRepository;
|
||||||
import tech.powerjob.server.persistence.remote.repository.WorkflowInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.WorkflowInfoRepository;
|
||||||
import tech.powerjob.server.core.DispatchService;
|
import tech.powerjob.server.remote.transport.starter.AkkaStarter;
|
||||||
import tech.powerjob.server.core.service.JobService;
|
|
||||||
import tech.powerjob.server.remote.worker.WorkerClusterManagerService;
|
import tech.powerjob.server.remote.worker.WorkerClusterManagerService;
|
||||||
import tech.powerjob.server.core.instance.InstanceService;
|
|
||||||
import tech.powerjob.server.common.timewheel.holder.InstanceTimeWheelService;
|
|
||||||
import tech.powerjob.server.core.workflow.WorkflowInstanceManager;
|
|
||||||
import com.google.common.base.Stopwatch;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.collect.Sets;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.beans.BeanUtils;
|
|
||||||
import org.springframework.scheduling.annotation.Async;
|
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.util.CollectionUtils;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 任务调度执行服务(调度 CRON 表达式的任务进行执行)
|
* 任务调度执行服务(调度 CRON 表达式的任务进行执行)
|
||||||
@ -44,6 +39,7 @@ import java.util.stream.Collectors;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class PowerScheduleService {
|
public class PowerScheduleService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,83 +47,105 @@ public class PowerScheduleService {
|
|||||||
*/
|
*/
|
||||||
private static final int MAX_APP_NUM = 10;
|
private static final int MAX_APP_NUM = 10;
|
||||||
|
|
||||||
@Resource
|
private final DispatchService dispatchService;
|
||||||
private DispatchService dispatchService;
|
|
||||||
@Resource
|
|
||||||
private InstanceService instanceService;
|
|
||||||
@Resource
|
|
||||||
private WorkflowInstanceManager workflowInstanceManager;
|
|
||||||
|
|
||||||
@Resource
|
private final InstanceService instanceService;
|
||||||
private AppInfoRepository appInfoRepository;
|
|
||||||
@Resource
|
|
||||||
private JobInfoRepository jobInfoRepository;
|
|
||||||
@Resource
|
|
||||||
private WorkflowInfoRepository workflowInfoRepository;
|
|
||||||
@Resource
|
|
||||||
private InstanceInfoRepository instanceInfoRepository;
|
|
||||||
|
|
||||||
@Resource
|
private final WorkflowInstanceManager workflowInstanceManager;
|
||||||
private JobService jobService;
|
|
||||||
@Resource
|
|
||||||
private TimingStrategyService timingStrategyService;
|
|
||||||
|
|
||||||
private static final long SCHEDULE_RATE = 15000;
|
private final AppInfoRepository appInfoRepository;
|
||||||
|
|
||||||
@Async(PJThreadPool.TIMING_POOL)
|
private final JobInfoRepository jobInfoRepository;
|
||||||
@Scheduled(fixedDelay = SCHEDULE_RATE)
|
|
||||||
public void timingSchedule() {
|
|
||||||
|
|
||||||
|
private final WorkflowInfoRepository workflowInfoRepository;
|
||||||
|
|
||||||
|
private final InstanceInfoRepository instanceInfoRepository;
|
||||||
|
|
||||||
|
private final JobService jobService;
|
||||||
|
|
||||||
|
private final TimingStrategyService timingStrategyService;
|
||||||
|
|
||||||
|
public static final long SCHEDULE_RATE = 15000;
|
||||||
|
|
||||||
|
|
||||||
|
public void scheduleCronJob() {
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
|
||||||
|
|
||||||
// 先查询DB,查看本机需要负责的任务
|
|
||||||
List<AppInfoDO> allAppInfos = appInfoRepository.findAllByCurrentServer(AkkaStarter.getActorSystemAddress());
|
|
||||||
if (CollectionUtils.isEmpty(allAppInfos)) {
|
|
||||||
log.info("[JobScheduleService] current server has no app's job to schedule.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
List<Long> allAppIds = allAppInfos.stream().map(AppInfoDO::getId).collect(Collectors.toList());
|
|
||||||
// 清理不需要维护的数据
|
|
||||||
WorkerClusterManagerService.clean(allAppIds);
|
|
||||||
|
|
||||||
// 调度 CRON 表达式 JOB
|
// 调度 CRON 表达式 JOB
|
||||||
try {
|
try {
|
||||||
scheduleCronJob(allAppIds);
|
final List<Long> allAppIds = appInfoRepository.listAppIdByCurrentServer(AkkaStarter.getActorSystemAddress());
|
||||||
} catch (Exception e) {
|
if (CollectionUtils.isEmpty(allAppIds)) {
|
||||||
log.error("[CronScheduler] schedule cron job failed.", e);
|
log.info("[CronJobSchedule] current server has no app's job to schedule.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
String cronTime = stopwatch.toString();
|
scheduleCronJobCore(allAppIds);
|
||||||
stopwatch.reset().start();
|
|
||||||
|
|
||||||
// 调度 workflow 任务
|
|
||||||
try {
|
|
||||||
scheduleWorkflow(allAppIds);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[WorkflowScheduler] schedule workflow job failed.", e);
|
log.error("[CronJobSchedule] schedule cron job failed.", e);
|
||||||
}
|
}
|
||||||
String wfTime = stopwatch.toString();
|
|
||||||
stopwatch.reset().start();
|
|
||||||
|
|
||||||
// 调度 秒级任务
|
|
||||||
try {
|
|
||||||
scheduleFrequentJob(allAppIds);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("[FrequentScheduler] schedule frequent job failed.", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("[JobScheduleService] cron schedule: {}, workflow schedule: {}, frequent schedule: {}.", cronTime, wfTime, stopwatch.stop());
|
|
||||||
|
|
||||||
long cost = System.currentTimeMillis() - start;
|
long cost = System.currentTimeMillis() - start;
|
||||||
|
log.info("[CronJobSchedule] cron job schedule use {} ms.", cost);
|
||||||
if (cost > SCHEDULE_RATE) {
|
if (cost > SCHEDULE_RATE) {
|
||||||
log.warn("[JobScheduleService] The database query is using too much time({}ms), please check if the database load is too high!", cost);
|
log.warn("[CronJobSchedule] The database query is using too much time({}ms), please check if the database load is too high!", cost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scheduleCronWorkflow() {
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
// 调度 CRON 表达式 WORKFLOW
|
||||||
|
try {
|
||||||
|
final List<Long> allAppIds = appInfoRepository.listAppIdByCurrentServer(AkkaStarter.getActorSystemAddress());
|
||||||
|
if (CollectionUtils.isEmpty(allAppIds)) {
|
||||||
|
log.info("[CronWorkflowSchedule] current server has no app's workflow to schedule.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scheduleWorkflowCore(allAppIds);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[CronWorkflowSchedule] schedule cron workflow failed.", e);
|
||||||
|
}
|
||||||
|
long cost = System.currentTimeMillis() - start;
|
||||||
|
log.info("[CronWorkflowSchedule] cron workflow schedule use {} ms.", cost);
|
||||||
|
if (cost > SCHEDULE_RATE) {
|
||||||
|
log.warn("[CronWorkflowSchedule] The database query is using too much time({}ms), please check if the database load is too high!", cost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void scheduleFrequentJob() {
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
// 调度 FIX_RATE/FIX_DELAY 表达式 JOB
|
||||||
|
try {
|
||||||
|
final List<Long> allAppIds = appInfoRepository.listAppIdByCurrentServer(AkkaStarter.getActorSystemAddress());
|
||||||
|
if (CollectionUtils.isEmpty(allAppIds)) {
|
||||||
|
log.info("[FrequentJobSchedule] current server has no app's job to schedule.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scheduleFrequentJobCore(allAppIds);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[FrequentJobSchedule] schedule frequent job failed.", e);
|
||||||
|
}
|
||||||
|
long cost = System.currentTimeMillis() - start;
|
||||||
|
log.info("[FrequentJobSchedule] frequent job schedule use {} ms.", cost);
|
||||||
|
if (cost > SCHEDULE_RATE) {
|
||||||
|
log.warn("[FrequentJobSchedule] The database query is using too much time({}ms), please check if the database load is too high!", cost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void cleanData() {
|
||||||
|
try {
|
||||||
|
final List<Long> allAppIds = appInfoRepository.listAppIdByCurrentServer(AkkaStarter.getActorSystemAddress());
|
||||||
|
if (allAppIds.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WorkerClusterManagerService.clean(allAppIds);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[CleanData] clean data failed.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 调度 CRON 表达式类型的任务
|
* 调度 CRON 表达式类型的任务
|
||||||
*/
|
*/
|
||||||
private void scheduleCronJob(List<Long> appIds) {
|
private void scheduleCronJobCore(List<Long> appIds) {
|
||||||
|
|
||||||
long nowTime = System.currentTimeMillis();
|
long nowTime = System.currentTimeMillis();
|
||||||
long timeThreshold = nowTime + 2 * SCHEDULE_RATE;
|
long timeThreshold = nowTime + 2 * SCHEDULE_RATE;
|
||||||
@ -147,7 +165,7 @@ public class PowerScheduleService {
|
|||||||
log.info("[CronScheduler] These cron jobs will be scheduled: {}.", jobInfos);
|
log.info("[CronScheduler] These cron jobs will be scheduled: {}.", jobInfos);
|
||||||
|
|
||||||
jobInfos.forEach(jobInfo -> {
|
jobInfos.forEach(jobInfo -> {
|
||||||
Long instanceId = instanceService.create(jobInfo.getId(), jobInfo.getAppId(), jobInfo.getJobParams(), null, null, jobInfo.getNextTriggerTime());
|
Long instanceId = instanceService.create(jobInfo.getId(), jobInfo.getAppId(), jobInfo.getJobParams(), null, null, jobInfo.getNextTriggerTime()).getInstanceId();
|
||||||
jobId2InstanceId.put(jobInfo.getId(), instanceId);
|
jobId2InstanceId.put(jobInfo.getId(), instanceId);
|
||||||
});
|
});
|
||||||
instanceInfoRepository.flush();
|
instanceInfoRepository.flush();
|
||||||
@ -165,7 +183,7 @@ public class PowerScheduleService {
|
|||||||
delay = targetTriggerTime - nowTime;
|
delay = targetTriggerTime - nowTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstanceTimeWheelService.schedule(instanceId, delay, () -> dispatchService.dispatch(jobInfoDO, instanceId));
|
InstanceTimeWheelService.schedule(instanceId, delay, () -> dispatchService.dispatch(jobInfoDO, instanceId, Optional.empty(), Optional.empty()));
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3. 计算下一次调度时间(忽略5S内的重复执行,即CRON模式下最小的连续执行间隔为 SCHEDULE_RATE ms)
|
// 3. 计算下一次调度时间(忽略5S内的重复执行,即CRON模式下最小的连续执行间隔为 SCHEDULE_RATE ms)
|
||||||
@ -185,7 +203,7 @@ public class PowerScheduleService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleWorkflow(List<Long> appIds) {
|
private void scheduleWorkflowCore(List<Long> appIds) {
|
||||||
|
|
||||||
long nowTime = System.currentTimeMillis();
|
long nowTime = System.currentTimeMillis();
|
||||||
long timeThreshold = nowTime + 2 * SCHEDULE_RATE;
|
long timeThreshold = nowTime + 2 * SCHEDULE_RATE;
|
||||||
@ -220,7 +238,7 @@ public class PowerScheduleService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleFrequentJob(List<Long> appIds) {
|
private void scheduleFrequentJobCore(List<Long> appIds) {
|
||||||
|
|
||||||
Lists.partition(appIds, MAX_APP_NUM).forEach(partAppIds -> {
|
Lists.partition(appIds, MAX_APP_NUM).forEach(partAppIds -> {
|
||||||
try {
|
try {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package tech.powerjob.server.core.service;
|
package tech.powerjob.server.core.service;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import tech.powerjob.common.exception.PowerJobException;
|
import tech.powerjob.common.exception.PowerJobException;
|
||||||
import tech.powerjob.server.persistence.remote.model.AppInfoDO;
|
import tech.powerjob.server.persistence.remote.model.AppInfoDO;
|
||||||
import tech.powerjob.server.persistence.remote.repository.AppInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.AppInfoRepository;
|
||||||
@ -15,10 +16,10 @@ import java.util.Objects;
|
|||||||
* @since 2020/6/20
|
* @since 2020/6/20
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class AppInfoService {
|
public class AppInfoService {
|
||||||
|
|
||||||
@Resource
|
private final AppInfoRepository appInfoRepository;
|
||||||
private AppInfoRepository appInfoRepository;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证应用访问权限
|
* 验证应用访问权限
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
package tech.powerjob.server.core.service;
|
package tech.powerjob.server.core.service;
|
||||||
|
|
||||||
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
import tech.powerjob.server.persistence.remote.model.InstanceInfoDO;
|
import tech.powerjob.server.persistence.remote.model.InstanceInfoDO;
|
||||||
import tech.powerjob.server.persistence.remote.model.JobInfoDO;
|
import tech.powerjob.server.persistence.remote.model.JobInfoDO;
|
||||||
import tech.powerjob.server.persistence.remote.model.WorkflowInfoDO;
|
import tech.powerjob.server.persistence.remote.model.WorkflowInfoDO;
|
||||||
import tech.powerjob.server.persistence.remote.repository.InstanceInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.InstanceInfoRepository;
|
||||||
import tech.powerjob.server.persistence.remote.repository.JobInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.JobInfoRepository;
|
||||||
import tech.powerjob.server.persistence.remote.repository.WorkflowInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.WorkflowInfoRepository;
|
||||||
import com.google.common.cache.Cache;
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@ -25,19 +24,23 @@ import java.util.Optional;
|
|||||||
@Service
|
@Service
|
||||||
public class CacheService {
|
public class CacheService {
|
||||||
|
|
||||||
@Resource
|
private final JobInfoRepository jobInfoRepository;
|
||||||
private JobInfoRepository jobInfoRepository;
|
|
||||||
@Resource
|
private final WorkflowInfoRepository workflowInfoRepository;
|
||||||
private WorkflowInfoRepository workflowInfoRepository;
|
|
||||||
@Resource
|
private final InstanceInfoRepository instanceInfoRepository;
|
||||||
private InstanceInfoRepository instanceInfoRepository;
|
|
||||||
|
|
||||||
private final Cache<Long, String> jobId2JobNameCache;
|
private final Cache<Long, String> jobId2JobNameCache;
|
||||||
private final Cache<Long, String> workflowId2WorkflowNameCache;
|
private final Cache<Long, String> workflowId2WorkflowNameCache;
|
||||||
private final Cache<Long, Long> instanceId2AppId;
|
private final Cache<Long, Long> instanceId2AppId;
|
||||||
private final Cache<Long, Long> jobId2AppId;
|
private final Cache<Long, Long> jobId2AppId;
|
||||||
|
|
||||||
public CacheService() {
|
public CacheService(JobInfoRepository jobInfoRepository, WorkflowInfoRepository workflowInfoRepository, InstanceInfoRepository instanceInfoRepository) {
|
||||||
|
|
||||||
|
this.jobInfoRepository = jobInfoRepository;
|
||||||
|
this.workflowInfoRepository = workflowInfoRepository;
|
||||||
|
this.instanceInfoRepository = instanceInfoRepository;
|
||||||
|
|
||||||
jobId2JobNameCache = CacheBuilder.newBuilder()
|
jobId2JobNameCache = CacheBuilder.newBuilder()
|
||||||
.expireAfterWrite(Duration.ofMinutes(1))
|
.expireAfterWrite(Duration.ofMinutes(1))
|
||||||
.maximumSize(512)
|
.maximumSize(512)
|
||||||
|
@ -2,6 +2,7 @@ package tech.powerjob.server.core.service;
|
|||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.data.jpa.domain.Specification;
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
@ -43,20 +44,18 @@ import java.util.stream.Collectors;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class JobService {
|
public class JobService {
|
||||||
|
|
||||||
@Resource
|
private final InstanceService instanceService;
|
||||||
private InstanceService instanceService;
|
|
||||||
|
|
||||||
@Resource
|
private final DispatchService dispatchService;
|
||||||
private DispatchService dispatchService;
|
|
||||||
@Resource
|
|
||||||
private JobInfoRepository jobInfoRepository;
|
|
||||||
@Resource
|
|
||||||
private InstanceInfoRepository instanceInfoRepository;
|
|
||||||
@Resource
|
|
||||||
private TimingStrategyService timingStrategyService;
|
|
||||||
|
|
||||||
|
private final JobInfoRepository jobInfoRepository;
|
||||||
|
|
||||||
|
private final InstanceInfoRepository instanceInfoRepository;
|
||||||
|
|
||||||
|
private final TimingStrategyService timingStrategyService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存/修改任务
|
* 保存/修改任务
|
||||||
@ -173,15 +172,15 @@ public class JobService {
|
|||||||
JobInfoDO jobInfo = jobInfoRepository.findById(jobId).orElseThrow(() -> new IllegalArgumentException("can't find job by id:" + jobId));
|
JobInfoDO jobInfo = jobInfoRepository.findById(jobId).orElseThrow(() -> new IllegalArgumentException("can't find job by id:" + jobId));
|
||||||
|
|
||||||
log.info("[Job-{}] try to run job in app[{}], instanceParams={},delay={} ms.", jobInfo.getId(), appId, instanceParams, delay);
|
log.info("[Job-{}] try to run job in app[{}], instanceParams={},delay={} ms.", jobInfo.getId(), appId, instanceParams, delay);
|
||||||
Long instanceId = instanceService.create(jobInfo.getId(), jobInfo.getAppId(), jobInfo.getJobParams(), instanceParams, null, System.currentTimeMillis() + Math.max(delay, 0));
|
final InstanceInfoDO instanceInfo = instanceService.create(jobInfo.getId(), jobInfo.getAppId(), jobInfo.getJobParams(), instanceParams, null, System.currentTimeMillis() + Math.max(delay, 0));
|
||||||
instanceInfoRepository.flush();
|
instanceInfoRepository.flush();
|
||||||
if (delay <= 0) {
|
if (delay <= 0) {
|
||||||
dispatchService.dispatch(jobInfo, instanceId);
|
dispatchService.dispatch(jobInfo, instanceInfo.getInstanceId(), Optional.of(instanceInfo),Optional.empty());
|
||||||
} else {
|
} else {
|
||||||
InstanceTimeWheelService.schedule(instanceId, delay, () -> dispatchService.dispatch(jobInfo, instanceId));
|
InstanceTimeWheelService.schedule(instanceInfo.getInstanceId(), delay, () -> dispatchService.dispatch(jobInfo, instanceInfo.getInstanceId(), Optional.empty(),Optional.empty()));
|
||||||
}
|
}
|
||||||
log.info("[Job-{}|{}] execute 'runJob' successfully, params={}", jobInfo.getId(), instanceId, instanceParams);
|
log.info("[Job-{}|{}] execute 'runJob' successfully, params={}", jobInfo.getId(), instanceInfo.getInstanceId(), instanceParams);
|
||||||
return instanceId;
|
return instanceInfo.getInstanceId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -205,9 +204,8 @@ public class JobService {
|
|||||||
* 启用某个任务
|
* 启用某个任务
|
||||||
*
|
*
|
||||||
* @param jobId 任务ID
|
* @param jobId 任务ID
|
||||||
* @throws ParseException 异常(CRON表达式错误)
|
|
||||||
*/
|
*/
|
||||||
public void enableJob(Long jobId) throws ParseException {
|
public void enableJob(Long jobId) {
|
||||||
JobInfoDO jobInfoDO = jobInfoRepository.findById(jobId).orElseThrow(() -> new IllegalArgumentException("can't find job by jobId:" + jobId));
|
JobInfoDO jobInfoDO = jobInfoRepository.findById(jobId).orElseThrow(() -> new IllegalArgumentException("can't find job by jobId:" + jobId));
|
||||||
|
|
||||||
jobInfoDO.setStatus(SwitchableStatus.ENABLE.getV());
|
jobInfoDO.setStatus(SwitchableStatus.ENABLE.getV());
|
||||||
|
@ -5,7 +5,7 @@ import tech.powerjob.server.persistence.remote.repository.UserInfoRepository;
|
|||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -18,11 +18,10 @@ import tech.powerjob.server.remote.server.self.ServerInfoService;
|
|||||||
public class IdGenerateService {
|
public class IdGenerateService {
|
||||||
|
|
||||||
private final SnowFlakeIdGenerator snowFlakeIdGenerator;
|
private final SnowFlakeIdGenerator snowFlakeIdGenerator;
|
||||||
|
|
||||||
private static final int DATA_CENTER_ID = 0;
|
private static final int DATA_CENTER_ID = 0;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public IdGenerateService(ServerInfoService serverInfoService) {
|
public IdGenerateService(ServerInfoService serverInfoService) {
|
||||||
|
|
||||||
long id = serverInfoService.fetchServiceInfo().getId();
|
long id = serverInfoService.fetchServiceInfo().getId();
|
||||||
snowFlakeIdGenerator = new SnowFlakeIdGenerator(DATA_CENTER_ID, id);
|
snowFlakeIdGenerator = new SnowFlakeIdGenerator(DATA_CENTER_ID, id);
|
||||||
log.info("[IdGenerateService] initialize IdGenerateService successfully, ID:{}", id);
|
log.info("[IdGenerateService] initialize IdGenerateService successfully, ID:{}", id);
|
||||||
|
@ -7,37 +7,50 @@ package tech.powerjob.server.core.uid;
|
|||||||
* @since 2020/4/6
|
* @since 2020/4/6
|
||||||
*/
|
*/
|
||||||
public class SnowFlakeIdGenerator {
|
public class SnowFlakeIdGenerator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 起始的时间戳(a special day for me)
|
* 起始的时间戳(a special day for me)
|
||||||
*/
|
*/
|
||||||
private final static long START_STAMP = 1555776000000L;
|
private final static long START_STAMP = 1555776000000L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 每一部分占用的位数
|
* 序列号占用的位数
|
||||||
*/
|
*/
|
||||||
private final static long SEQUENCE_BIT = 6; //序列号占用的位数
|
private final static long SEQUENCE_BIT = 6;
|
||||||
private final static long MACHINE_BIT = 14; //机器标识占用的位数
|
/**
|
||||||
private final static long DATA_CENTER_BIT = 2;//数据中心占用的位数
|
* 机器标识占用的位数
|
||||||
|
*/
|
||||||
|
private final static long MACHINE_BIT = 14;
|
||||||
|
/**
|
||||||
|
* 数据中心占用的位数
|
||||||
|
*/
|
||||||
|
private final static long DATA_CENTER_BIT = 2;
|
||||||
/**
|
/**
|
||||||
* 每一部分的最大值
|
* 每一部分的最大值
|
||||||
*/
|
*/
|
||||||
private final static long MAX_DATA_CENTER_NUM = ~(-1L << DATA_CENTER_BIT);
|
private final static long MAX_DATA_CENTER_NUM = ~(-1L << DATA_CENTER_BIT);
|
||||||
private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
|
private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
|
||||||
private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
|
private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 每一部分向左的位移
|
* 每一部分向左的位移
|
||||||
*/
|
*/
|
||||||
private final static long MACHINE_LEFT = SEQUENCE_BIT;
|
private final static long MACHINE_LEFT = SEQUENCE_BIT;
|
||||||
private final static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
|
private final static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
|
||||||
private final static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT;
|
private final static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT;
|
||||||
|
/**
|
||||||
private final long dataCenterId; //数据中心
|
* 数据中心
|
||||||
private final long machineId; //机器标识
|
*/
|
||||||
private long sequence = 0L; //序列号
|
private final long dataCenterId;
|
||||||
private long lastTimestamp = -1L;//上一次时间戳
|
/**
|
||||||
|
* 机器标识
|
||||||
|
*/
|
||||||
|
private final long machineId;
|
||||||
|
/**
|
||||||
|
* 序列号
|
||||||
|
*/
|
||||||
|
private long sequence = 0L;
|
||||||
|
/**
|
||||||
|
* 上一次时间戳
|
||||||
|
*/
|
||||||
|
private long lastTimestamp = -1L;
|
||||||
|
|
||||||
public SnowFlakeIdGenerator(long dataCenterId, long machineId) {
|
public SnowFlakeIdGenerator(long dataCenterId, long machineId) {
|
||||||
if (dataCenterId > MAX_DATA_CENTER_NUM || dataCenterId < 0) {
|
if (dataCenterId > MAX_DATA_CENTER_NUM || dataCenterId < 0) {
|
||||||
@ -56,7 +69,7 @@ public class SnowFlakeIdGenerator {
|
|||||||
public synchronized long nextId() {
|
public synchronized long nextId() {
|
||||||
long currStamp = getNewStamp();
|
long currStamp = getNewStamp();
|
||||||
if (currStamp < lastTimestamp) {
|
if (currStamp < lastTimestamp) {
|
||||||
throw new RuntimeException("clock moved backwards, refusing to generate id");
|
return futureId();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currStamp == lastTimestamp) {
|
if (currStamp == lastTimestamp) {
|
||||||
@ -79,6 +92,22 @@ public class SnowFlakeIdGenerator {
|
|||||||
| sequence; //序列号部分
|
| sequence; //序列号部分
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发生时钟回拨时借用未来时间生成Id,避免运行过程中任务调度和工作流直接进入不可用状态
|
||||||
|
* 注:该方式不可解决原算法中停服状态下时钟回拨导致的重复id问题
|
||||||
|
*/
|
||||||
|
private long futureId() {
|
||||||
|
sequence = (sequence + 1) & MAX_SEQUENCE;
|
||||||
|
if (sequence == 0L) {
|
||||||
|
lastTimestamp = lastTimestamp + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (lastTimestamp - START_STAMP) << TIMESTAMP_LEFT //时间戳部分
|
||||||
|
| dataCenterId << DATA_CENTER_LEFT //数据中心部分
|
||||||
|
| machineId << MACHINE_LEFT //机器标识部分
|
||||||
|
| sequence; //序列号部分
|
||||||
|
}
|
||||||
|
|
||||||
private long getNextMill() {
|
private long getNextMill() {
|
||||||
long mill = getNewStamp();
|
long mill = getNewStamp();
|
||||||
while (mill <= lastTimestamp) {
|
while (mill <= lastTimestamp) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package tech.powerjob.server.core.validator;
|
package tech.powerjob.server.core.validator;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import tech.powerjob.common.enums.WorkflowNodeType;
|
import tech.powerjob.common.enums.WorkflowNodeType;
|
||||||
@ -18,10 +19,10 @@ import javax.annotation.Resource;
|
|||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class JobNodeValidator implements NodeValidator {
|
public class JobNodeValidator implements NodeValidator {
|
||||||
|
|
||||||
@Resource
|
private final JobInfoRepository jobInfoRepository;
|
||||||
private JobInfoRepository jobInfoRepository;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void complexValidate(WorkflowNodeInfoDO node, WorkflowDAG dag) {
|
public void complexValidate(WorkflowNodeInfoDO node, WorkflowDAG dag) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package tech.powerjob.server.core.validator;
|
package tech.powerjob.server.core.validator;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import tech.powerjob.common.enums.WorkflowNodeType;
|
import tech.powerjob.common.enums.WorkflowNodeType;
|
||||||
@ -13,7 +14,6 @@ import tech.powerjob.server.persistence.remote.model.WorkflowNodeInfoDO;
|
|||||||
import tech.powerjob.server.persistence.remote.repository.WorkflowInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.WorkflowInfoRepository;
|
||||||
import tech.powerjob.server.persistence.remote.repository.WorkflowNodeInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.WorkflowNodeInfoRepository;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@ -23,12 +23,12 @@ import java.util.Optional;
|
|||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class NestedWorkflowNodeValidator implements NodeValidator {
|
public class NestedWorkflowNodeValidator implements NodeValidator {
|
||||||
|
|
||||||
@Resource
|
private final WorkflowInfoRepository workflowInfoRepository;
|
||||||
private WorkflowInfoRepository workflowInfoRepository;
|
|
||||||
@Resource
|
private final WorkflowNodeInfoRepository workflowNodeInfoRepository;
|
||||||
private WorkflowNodeInfoRepository workflowNodeInfoRepository;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void complexValidate(WorkflowNodeInfoDO node, WorkflowDAG dag) {
|
public void complexValidate(WorkflowNodeInfoDO node, WorkflowDAG dag) {
|
||||||
|
@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
|
|||||||
import com.alibaba.fastjson.TypeReference;
|
import com.alibaba.fastjson.TypeReference;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
@ -18,6 +19,7 @@ import tech.powerjob.common.model.PEWorkflowDAG;
|
|||||||
import tech.powerjob.common.serialize.JsonUtils;
|
import tech.powerjob.common.serialize.JsonUtils;
|
||||||
import tech.powerjob.common.utils.CommonUtils;
|
import tech.powerjob.common.utils.CommonUtils;
|
||||||
import tech.powerjob.server.common.constants.SwitchableStatus;
|
import tech.powerjob.server.common.constants.SwitchableStatus;
|
||||||
|
import tech.powerjob.server.common.utils.SpringUtils;
|
||||||
import tech.powerjob.server.core.helper.StatusMappingHelper;
|
import tech.powerjob.server.core.helper.StatusMappingHelper;
|
||||||
import tech.powerjob.server.core.lock.UseCacheLock;
|
import tech.powerjob.server.core.lock.UseCacheLock;
|
||||||
import tech.powerjob.server.core.service.UserService;
|
import tech.powerjob.server.core.service.UserService;
|
||||||
@ -32,7 +34,6 @@ import tech.powerjob.server.persistence.remote.repository.WorkflowInfoRepository
|
|||||||
import tech.powerjob.server.persistence.remote.repository.WorkflowInstanceInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.WorkflowInstanceInfoRepository;
|
||||||
import tech.powerjob.server.persistence.remote.repository.WorkflowNodeInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.WorkflowNodeInfoRepository;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -47,25 +48,25 @@ import static tech.powerjob.server.core.workflow.algorithm.WorkflowDAGUtils.isNo
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
@SuppressWarnings("squid:S1192")
|
@SuppressWarnings("squid:S1192")
|
||||||
public class WorkflowInstanceManager {
|
public class WorkflowInstanceManager {
|
||||||
|
|
||||||
@Resource
|
private final AlarmCenter alarmCenter;
|
||||||
private AlarmCenter alarmCenter;
|
|
||||||
@Resource
|
private final IdGenerateService idGenerateService;
|
||||||
private IdGenerateService idGenerateService;
|
|
||||||
@Resource
|
private final JobInfoRepository jobInfoRepository;
|
||||||
private JobInfoRepository jobInfoRepository;
|
|
||||||
@Resource
|
private final UserService userService;
|
||||||
private UserService userService;
|
|
||||||
@Resource
|
private final WorkflowInfoRepository workflowInfoRepository;
|
||||||
private WorkflowInfoRepository workflowInfoRepository;
|
|
||||||
@Resource
|
private final WorkflowInstanceInfoRepository workflowInstanceInfoRepository;
|
||||||
private WorkflowInstanceInfoRepository workflowInstanceInfoRepository;
|
|
||||||
@Resource
|
private final WorkflowNodeInfoRepository workflowNodeInfoRepository;
|
||||||
private WorkflowNodeInfoRepository workflowNodeInfoRepository;
|
|
||||||
@Resource
|
private final WorkflowNodeHandleService workflowNodeHandleService;
|
||||||
private WorkflowNodeHandleService workflowNodeHandleService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建工作流任务实例
|
* 创建工作流任务实例
|
||||||
@ -440,10 +441,10 @@ public class WorkflowInstanceManager {
|
|||||||
if (workflowInstanceStatus == WorkflowInstanceStatus.SUCCEED){
|
if (workflowInstanceStatus == WorkflowInstanceStatus.SUCCEED){
|
||||||
HashMap<String, String> wfContext = JSON.parseObject(wfInstance.getWfContext(), new TypeReference<HashMap<String, String>>() {
|
HashMap<String, String> wfContext = JSON.parseObject(wfInstance.getWfContext(), new TypeReference<HashMap<String, String>>() {
|
||||||
});
|
});
|
||||||
updateWorkflowContext(wfInstance.getParentWfInstanceId(),wfContext);
|
SpringUtils.getBean(this.getClass()).updateWorkflowContext(wfInstance.getParentWfInstanceId(), wfContext);
|
||||||
}
|
}
|
||||||
// 处理父工作流
|
// 处理父工作流, fix https://github.com/PowerJob/PowerJob/issues/465
|
||||||
move(wfInstance.getParentWfInstanceId(), wfInstance.getWfInstanceId(), StatusMappingHelper.toInstanceStatus(workflowInstanceStatus), result);
|
SpringUtils.getBean(this.getClass()).move(wfInstance.getParentWfInstanceId(), wfInstance.getWfInstanceId(), StatusMappingHelper.toInstanceStatus(workflowInstanceStatus), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 报警
|
// 报警
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
package tech.powerjob.server.core.workflow;
|
package tech.powerjob.server.core.workflow;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import tech.powerjob.common.SystemInstanceResult;
|
||||||
import tech.powerjob.common.enums.InstanceStatus;
|
import tech.powerjob.common.enums.InstanceStatus;
|
||||||
|
import tech.powerjob.common.enums.WorkflowInstanceStatus;
|
||||||
import tech.powerjob.common.enums.WorkflowNodeType;
|
import tech.powerjob.common.enums.WorkflowNodeType;
|
||||||
import tech.powerjob.common.exception.PowerJobException;
|
import tech.powerjob.common.exception.PowerJobException;
|
||||||
import tech.powerjob.common.SystemInstanceResult;
|
|
||||||
import tech.powerjob.common.enums.WorkflowInstanceStatus;
|
|
||||||
import tech.powerjob.common.model.PEWorkflowDAG;
|
import tech.powerjob.common.model.PEWorkflowDAG;
|
||||||
import tech.powerjob.common.response.WorkflowInstanceInfoDTO;
|
import tech.powerjob.common.response.WorkflowInstanceInfoDTO;
|
||||||
import tech.powerjob.server.common.constants.SwitchableStatus;
|
import tech.powerjob.server.common.constants.SwitchableStatus;
|
||||||
|
import tech.powerjob.server.common.utils.SpringUtils;
|
||||||
|
import tech.powerjob.server.core.instance.InstanceService;
|
||||||
import tech.powerjob.server.core.lock.UseCacheLock;
|
import tech.powerjob.server.core.lock.UseCacheLock;
|
||||||
import tech.powerjob.server.core.workflow.algorithm.WorkflowDAGUtils;
|
import tech.powerjob.server.core.workflow.algorithm.WorkflowDAGUtils;
|
||||||
import tech.powerjob.server.persistence.remote.model.WorkflowInfoDO;
|
import tech.powerjob.server.persistence.remote.model.WorkflowInfoDO;
|
||||||
@ -16,12 +22,7 @@ import tech.powerjob.server.persistence.remote.model.WorkflowInstanceInfoDO;
|
|||||||
import tech.powerjob.server.persistence.remote.repository.WorkflowInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.WorkflowInfoRepository;
|
||||||
import tech.powerjob.server.persistence.remote.repository.WorkflowInstanceInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.WorkflowInstanceInfoRepository;
|
||||||
import tech.powerjob.server.remote.server.redirector.DesignateServer;
|
import tech.powerjob.server.remote.server.redirector.DesignateServer;
|
||||||
import tech.powerjob.server.core.instance.InstanceService;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.beans.BeanUtils;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -35,18 +36,16 @@ import java.util.Optional;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class WorkflowInstanceService {
|
public class WorkflowInstanceService {
|
||||||
|
|
||||||
@Resource
|
private final InstanceService instanceService;
|
||||||
private InstanceService instanceService;
|
|
||||||
@Resource
|
private final WorkflowInstanceInfoRepository wfInstanceInfoRepository;
|
||||||
private WorkflowInstanceInfoRepository wfInstanceInfoRepository;
|
|
||||||
@Resource
|
private final WorkflowInstanceManager workflowInstanceManager;
|
||||||
private WorkflowInstanceManager workflowInstanceManager;
|
|
||||||
@Resource
|
private final WorkflowInfoRepository workflowInfoRepository;
|
||||||
private WorkflowInfoRepository workflowInfoRepository;
|
|
||||||
@Resource
|
|
||||||
private WorkflowInstanceService self;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 停止工作流实例(入口)
|
* 停止工作流实例(入口)
|
||||||
@ -61,10 +60,10 @@ public class WorkflowInstanceService {
|
|||||||
}
|
}
|
||||||
// 如果这是一个被嵌套的工作流,则终止父工作流
|
// 如果这是一个被嵌套的工作流,则终止父工作流
|
||||||
if (wfInstance.getParentWfInstanceId() != null) {
|
if (wfInstance.getParentWfInstanceId() != null) {
|
||||||
self.stopWorkflowInstance(wfInstance.getParentWfInstanceId(), appId);
|
SpringUtils.getBean(this.getClass()).stopWorkflowInstance(wfInstance.getParentWfInstanceId(), appId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.stopWorkflowInstance(wfInstanceId, appId);
|
SpringUtils.getBean(this.getClass()).stopWorkflowInstance(wfInstanceId, appId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,7 +7,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import tech.powerjob.common.enums.TimeExpressionType;
|
import tech.powerjob.common.enums.TimeExpressionType;
|
||||||
import tech.powerjob.common.exception.PowerJobException;
|
import tech.powerjob.common.exception.PowerJobException;
|
||||||
import tech.powerjob.common.model.LifeCycle;
|
import tech.powerjob.common.model.LifeCycle;
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
package tech.powerjob.server.core.workflow.hanlder.impl;
|
package tech.powerjob.server.core.workflow.hanlder.impl;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import tech.powerjob.common.enums.InstanceStatus;
|
import tech.powerjob.common.enums.InstanceStatus;
|
||||||
import tech.powerjob.common.enums.TimeExpressionType;
|
import tech.powerjob.common.enums.TimeExpressionType;
|
||||||
import tech.powerjob.common.enums.WorkflowNodeType;
|
import tech.powerjob.common.enums.WorkflowNodeType;
|
||||||
import tech.powerjob.common.model.PEWorkflowDAG;
|
import tech.powerjob.common.model.PEWorkflowDAG;
|
||||||
|
import tech.powerjob.server.common.utils.SpringUtils;
|
||||||
import tech.powerjob.server.core.DispatchService;
|
import tech.powerjob.server.core.DispatchService;
|
||||||
import tech.powerjob.server.core.instance.InstanceService;
|
import tech.powerjob.server.core.instance.InstanceService;
|
||||||
import tech.powerjob.server.core.workflow.hanlder.TaskNodeHandler;
|
import tech.powerjob.server.core.workflow.hanlder.TaskNodeHandler;
|
||||||
@ -13,7 +15,7 @@ import tech.powerjob.server.persistence.remote.model.JobInfoDO;
|
|||||||
import tech.powerjob.server.persistence.remote.model.WorkflowInstanceInfoDO;
|
import tech.powerjob.server.persistence.remote.model.WorkflowInstanceInfoDO;
|
||||||
import tech.powerjob.server.persistence.remote.repository.JobInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.JobInfoRepository;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Echo009
|
* @author Echo009
|
||||||
@ -21,21 +23,15 @@ import javax.annotation.Resource;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class JobNodeHandler implements TaskNodeHandler {
|
public class JobNodeHandler implements TaskNodeHandler {
|
||||||
|
|
||||||
@Resource
|
private final JobInfoRepository jobInfoRepository;
|
||||||
private InstanceService instanceService;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private JobInfoRepository jobInfoRepository;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private DispatchService dispatchService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createTaskInstance(PEWorkflowDAG.Node node, PEWorkflowDAG dag, WorkflowInstanceInfoDO wfInstanceInfo) {
|
public void createTaskInstance(PEWorkflowDAG.Node node, PEWorkflowDAG dag, WorkflowInstanceInfoDO wfInstanceInfo) {
|
||||||
// instanceParam 传递的是工作流实例的 wfContext
|
// instanceParam 传递的是工作流实例的 wfContext
|
||||||
Long instanceId = instanceService.create(node.getJobId(), wfInstanceInfo.getAppId(), node.getNodeParams(), wfInstanceInfo.getWfContext(), wfInstanceInfo.getWfInstanceId(), System.currentTimeMillis());
|
Long instanceId = SpringUtils.getBean(InstanceService.class).create(node.getJobId(), wfInstanceInfo.getAppId(), node.getNodeParams(), wfInstanceInfo.getWfContext(), wfInstanceInfo.getWfInstanceId(), System.currentTimeMillis()).getInstanceId();
|
||||||
node.setInstanceId(instanceId);
|
node.setInstanceId(instanceId);
|
||||||
node.setStatus(InstanceStatus.RUNNING.getV());
|
node.setStatus(InstanceStatus.RUNNING.getV());
|
||||||
log.info("[Workflow-{}|{}] create readyNode(JOB) instance(nodeId={},jobId={},instanceId={}) successfully~", wfInstanceInfo.getWorkflowId(), wfInstanceInfo.getWfInstanceId(), node.getNodeId(), node.getJobId(), instanceId);
|
log.info("[Workflow-{}|{}] create readyNode(JOB) instance(nodeId={},jobId={},instanceId={}) successfully~", wfInstanceInfo.getWorkflowId(), wfInstanceInfo.getWfInstanceId(), node.getNodeId(), node.getJobId(), instanceId);
|
||||||
@ -46,7 +42,7 @@ public class JobNodeHandler implements TaskNodeHandler {
|
|||||||
JobInfoDO jobInfo = jobInfoRepository.findById(node.getJobId()).orElseGet(JobInfoDO::new);
|
JobInfoDO jobInfo = jobInfoRepository.findById(node.getJobId()).orElseGet(JobInfoDO::new);
|
||||||
// 洗去时间表达式类型
|
// 洗去时间表达式类型
|
||||||
jobInfo.setTimeExpressionType(TimeExpressionType.WORKFLOW.getV());
|
jobInfo.setTimeExpressionType(TimeExpressionType.WORKFLOW.getV());
|
||||||
dispatchService.dispatch(jobInfo, node.getInstanceId());
|
SpringUtils.getBean(DispatchService.class).dispatch(jobInfo, node.getInstanceId(), Optional.empty(), Optional.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package tech.powerjob.server.core.workflow.hanlder.impl;
|
package tech.powerjob.server.core.workflow.hanlder.impl;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import tech.powerjob.common.SystemInstanceResult;
|
import tech.powerjob.common.SystemInstanceResult;
|
||||||
@ -11,6 +12,7 @@ import tech.powerjob.common.exception.PowerJobException;
|
|||||||
import tech.powerjob.common.model.PEWorkflowDAG;
|
import tech.powerjob.common.model.PEWorkflowDAG;
|
||||||
import tech.powerjob.common.utils.CommonUtils;
|
import tech.powerjob.common.utils.CommonUtils;
|
||||||
import tech.powerjob.server.common.constants.SwitchableStatus;
|
import tech.powerjob.server.common.constants.SwitchableStatus;
|
||||||
|
import tech.powerjob.server.common.utils.SpringUtils;
|
||||||
import tech.powerjob.server.core.workflow.WorkflowInstanceManager;
|
import tech.powerjob.server.core.workflow.WorkflowInstanceManager;
|
||||||
import tech.powerjob.server.core.workflow.algorithm.WorkflowDAGUtils;
|
import tech.powerjob.server.core.workflow.algorithm.WorkflowDAGUtils;
|
||||||
import tech.powerjob.server.core.workflow.hanlder.TaskNodeHandler;
|
import tech.powerjob.server.core.workflow.hanlder.TaskNodeHandler;
|
||||||
@ -19,7 +21,6 @@ import tech.powerjob.server.persistence.remote.model.WorkflowInstanceInfoDO;
|
|||||||
import tech.powerjob.server.persistence.remote.repository.WorkflowInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.WorkflowInfoRepository;
|
||||||
import tech.powerjob.server.persistence.remote.repository.WorkflowInstanceInfoRepository;
|
import tech.powerjob.server.persistence.remote.repository.WorkflowInstanceInfoRepository;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,16 +29,12 @@ import java.util.Date;
|
|||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class NestedWorkflowNodeHandler implements TaskNodeHandler {
|
public class NestedWorkflowNodeHandler implements TaskNodeHandler {
|
||||||
|
|
||||||
@Resource
|
private final WorkflowInfoRepository workflowInfoRepository;
|
||||||
private WorkflowInfoRepository workflowInfoRepository;
|
|
||||||
|
|
||||||
@Resource
|
private final WorkflowInstanceInfoRepository workflowInstanceInfoRepository;
|
||||||
private WorkflowInstanceInfoRepository workflowInstanceInfoRepository;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private WorkflowInstanceManager workflowInstanceManager;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createTaskInstance(PEWorkflowDAG.Node node, PEWorkflowDAG dag, WorkflowInstanceInfoDO wfInstanceInfo) {
|
public void createTaskInstance(PEWorkflowDAG.Node node, PEWorkflowDAG dag, WorkflowInstanceInfoDO wfInstanceInfo) {
|
||||||
@ -78,7 +75,7 @@ public class NestedWorkflowNodeHandler implements TaskNodeHandler {
|
|||||||
} else {
|
} else {
|
||||||
// 透传当前的上下文创建新的工作流实例
|
// 透传当前的上下文创建新的工作流实例
|
||||||
String wfContext = wfInstanceInfo.getWfContext();
|
String wfContext = wfInstanceInfo.getWfContext();
|
||||||
Long instanceId = workflowInstanceManager.create(targetWf, wfContext, System.currentTimeMillis(), wfInstanceInfo.getWfInstanceId());
|
Long instanceId = SpringUtils.getBean(WorkflowInstanceManager.class).create(targetWf, wfContext, System.currentTimeMillis(), wfInstanceInfo.getWfInstanceId());
|
||||||
node.setInstanceId(instanceId);
|
node.setInstanceId(instanceId);
|
||||||
}
|
}
|
||||||
node.setStartTime(CommonUtils.formatTime(System.currentTimeMillis()));
|
node.setStartTime(CommonUtils.formatTime(System.currentTimeMillis()));
|
||||||
@ -89,7 +86,7 @@ public class NestedWorkflowNodeHandler implements TaskNodeHandler {
|
|||||||
public void startTaskInstance(PEWorkflowDAG.Node node) {
|
public void startTaskInstance(PEWorkflowDAG.Node node) {
|
||||||
Long wfId = node.getJobId();
|
Long wfId = node.getJobId();
|
||||||
WorkflowInfoDO targetWf = workflowInfoRepository.findById(wfId).orElse(null);
|
WorkflowInfoDO targetWf = workflowInfoRepository.findById(wfId).orElse(null);
|
||||||
workflowInstanceManager.start(targetWf, node.getInstanceId());
|
SpringUtils.getBean(WorkflowInstanceManager.class).start(targetWf, node.getInstanceId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>powerjob-server</artifactId>
|
<artifactId>powerjob-server</artifactId>
|
||||||
<groupId>tech.powerjob</groupId>
|
<groupId>tech.powerjob</groupId>
|
||||||
<version>4.2.0</version>
|
<version>4.2.1</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
@ -21,6 +21,7 @@ import org.springframework.stereotype.Service;
|
|||||||
public class DatabaseLockService implements LockService {
|
public class DatabaseLockService implements LockService {
|
||||||
|
|
||||||
private final String ownerIp;
|
private final String ownerIp;
|
||||||
|
|
||||||
private final OmsLockRepository omsLockRepository;
|
private final OmsLockRepository omsLockRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -24,9 +24,9 @@ import java.util.concurrent.*;
|
|||||||
public class AlarmCenter {
|
public class AlarmCenter {
|
||||||
|
|
||||||
private final ExecutorService POOL;
|
private final ExecutorService POOL;
|
||||||
|
|
||||||
private final List<Alarmable> BEANS = Lists.newLinkedList();
|
private final List<Alarmable> BEANS = Lists.newLinkedList();
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public AlarmCenter(List<Alarmable> alarmables) {
|
public AlarmCenter(List<Alarmable> alarmables) {
|
||||||
int cores = Runtime.getRuntime().availableProcessors();
|
int cores = Runtime.getRuntime().availableProcessors();
|
||||||
ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("AlarmPool-%d").build();
|
ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("AlarmPool-%d").build();
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package tech.powerjob.server.extension.defaultimpl.alarm.impl;
|
package tech.powerjob.server.extension.defaultimpl.alarm.impl;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import tech.powerjob.common.OmsConstant;
|
import tech.powerjob.common.OmsConstant;
|
||||||
import tech.powerjob.common.exception.PowerJobException;
|
import tech.powerjob.common.exception.PowerJobException;
|
||||||
import tech.powerjob.common.utils.NetUtils;
|
import tech.powerjob.common.utils.NetUtils;
|
||||||
@ -30,17 +31,19 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class DingTalkAlarmService implements Alarmable {
|
public class DingTalkAlarmService implements Alarmable {
|
||||||
|
|
||||||
@Resource
|
private final Environment environment;
|
||||||
private Environment environment;
|
|
||||||
|
|
||||||
private Long agentId;
|
private Long agentId;
|
||||||
private DingTalkUtils dingTalkUtils;
|
private DingTalkUtils dingTalkUtils;
|
||||||
private Cache<String, String> mobile2UserIdCache;
|
private Cache<String, String> mobile2UserIdCache;
|
||||||
|
|
||||||
private static final int CACHE_SIZE = 8192;
|
private static final int CACHE_SIZE = 8192;
|
||||||
// 防止缓存击穿
|
/**
|
||||||
|
* 防止缓存击穿
|
||||||
|
*/
|
||||||
private static final String EMPTY_TAG = "EMPTY";
|
private static final String EMPTY_TAG = "EMPTY";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -125,7 +125,7 @@ public class DingTalkUtils implements Closeable {
|
|||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public static final class MarkdownEntity {
|
public static final class MarkdownEntity {
|
||||||
private String title;
|
private final String title;
|
||||||
private String detail;
|
private final String detail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package tech.powerjob.server.extension.defaultimpl.alarm.impl;
|
package tech.powerjob.server.extension.defaultimpl.alarm.impl;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import tech.powerjob.server.persistence.remote.model.UserInfoDO;
|
import tech.powerjob.server.persistence.remote.model.UserInfoDO;
|
||||||
import tech.powerjob.server.extension.defaultimpl.alarm.module.Alarm;
|
import tech.powerjob.server.extension.defaultimpl.alarm.module.Alarm;
|
||||||
import tech.powerjob.server.extension.Alarmable;
|
import tech.powerjob.server.extension.Alarmable;
|
||||||
@ -10,7 +12,6 @@ import org.springframework.mail.SimpleMailMessage;
|
|||||||
import org.springframework.mail.javamail.JavaMailSender;
|
import org.springframework.mail.javamail.JavaMailSender;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -31,12 +32,11 @@ public class MailAlarmService implements Alarmable {
|
|||||||
|
|
||||||
private JavaMailSender javaMailSender;
|
private JavaMailSender javaMailSender;
|
||||||
|
|
||||||
|
@Value("${spring.mail.username:''}")
|
||||||
private String from;
|
private String from;
|
||||||
private static final String FROM_KEY = "spring.mail.username";
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailed(Alarm alarm, List<UserInfoDO> targetUserList) {
|
public void onFailed(Alarm alarm, List<UserInfoDO> targetUserList) {
|
||||||
initFrom();
|
|
||||||
if (CollectionUtils.isEmpty(targetUserList) || javaMailSender == null || StringUtils.isEmpty(from)) {
|
if (CollectionUtils.isEmpty(targetUserList) || javaMailSender == null || StringUtils.isEmpty(from)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -59,10 +59,4 @@ public class MailAlarmService implements Alarmable {
|
|||||||
this.javaMailSender = javaMailSender;
|
this.javaMailSender = javaMailSender;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 不能直接使用 @Value 注入,不存在的时候会报错
|
|
||||||
private void initFrom() {
|
|
||||||
if (StringUtils.isEmpty(from)) {
|
|
||||||
from = environment.getProperty(FROM_KEY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import okhttp3.MediaType;
|
|||||||
import okhttp3.RequestBody;
|
import okhttp3.RequestBody;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -10,38 +10,70 @@ import lombok.Data;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class JobInstanceAlarm implements Alarm {
|
public class JobInstanceAlarm implements Alarm {
|
||||||
// 应用ID
|
/**
|
||||||
|
* 应用ID
|
||||||
|
*/
|
||||||
private long appId;
|
private long appId;
|
||||||
// 任务ID
|
/**
|
||||||
|
* 任务ID
|
||||||
|
*/
|
||||||
private long jobId;
|
private long jobId;
|
||||||
// 任务实例ID
|
/**
|
||||||
|
* 任务实例ID
|
||||||
|
*/
|
||||||
private long instanceId;
|
private long instanceId;
|
||||||
// 任务名称
|
/**
|
||||||
|
* 任务名称
|
||||||
|
*/
|
||||||
private String jobName;
|
private String jobName;
|
||||||
// 任务自带的参数
|
/**
|
||||||
|
* 任务自带的参数
|
||||||
|
*/
|
||||||
private String jobParams;
|
private String jobParams;
|
||||||
// 时间表达式类型(CRON/API/FIX_RATE/FIX_DELAY)
|
/**
|
||||||
|
* 时间表达式类型(CRON/API/FIX_RATE/FIX_DELAY)
|
||||||
|
*/
|
||||||
private Integer timeExpressionType;
|
private Integer timeExpressionType;
|
||||||
// 时间表达式,CRON/NULL/LONG/LONG
|
/**
|
||||||
|
* 时间表达式,CRON/NULL/LONG/LONG
|
||||||
|
*/
|
||||||
private String timeExpression;
|
private String timeExpression;
|
||||||
// 执行类型,单机/广播/MR
|
/**
|
||||||
|
* 执行类型,单机/广播/MR
|
||||||
|
*/
|
||||||
private Integer executeType;
|
private Integer executeType;
|
||||||
// 执行器类型,Java/Shell
|
/**
|
||||||
|
* 执行器类型,Java/Shell
|
||||||
|
*/
|
||||||
private Integer processorType;
|
private Integer processorType;
|
||||||
// 执行器信息
|
/**
|
||||||
|
* 执行器信息
|
||||||
|
*/
|
||||||
private String processorInfo;
|
private String processorInfo;
|
||||||
|
|
||||||
// 任务实例参数
|
/**
|
||||||
|
* 任务实例参数
|
||||||
|
*/
|
||||||
private String instanceParams;
|
private String instanceParams;
|
||||||
// 执行结果
|
/**
|
||||||
|
* 执行结果
|
||||||
|
*/
|
||||||
private String result;
|
private String result;
|
||||||
// 预计触发时间
|
/**
|
||||||
|
* 预计触发时间
|
||||||
|
*/
|
||||||
private Long expectedTriggerTime;
|
private Long expectedTriggerTime;
|
||||||
// 实际触发时间
|
/**
|
||||||
|
* 实际触发时间
|
||||||
|
*/
|
||||||
private Long actualTriggerTime;
|
private Long actualTriggerTime;
|
||||||
// 结束时间
|
/**
|
||||||
|
* 结束时间
|
||||||
|
*/
|
||||||
private Long finishedTime;
|
private Long finishedTime;
|
||||||
// TaskTracker地址
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
private String taskTrackerAddress;
|
private String taskTrackerAddress;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -14,25 +14,39 @@ public class WorkflowInstanceAlarm implements Alarm {
|
|||||||
|
|
||||||
private String workflowName;
|
private String workflowName;
|
||||||
|
|
||||||
// 任务所属应用的ID,冗余提高查询效率
|
/**
|
||||||
|
* 任务所属应用的ID,冗余提高查询效率
|
||||||
|
*/
|
||||||
private Long appId;
|
private Long appId;
|
||||||
private Long workflowId;
|
private Long workflowId;
|
||||||
// workflowInstanceId(任务实例表都使用单独的ID作为主键以支持潜在的分表需求)
|
/**
|
||||||
|
* workflowInstanceId(任务实例表都使用单独的ID作为主键以支持潜在的分表需求)
|
||||||
|
*/
|
||||||
private Long wfInstanceId;
|
private Long wfInstanceId;
|
||||||
// workflow 状态(WorkflowInstanceStatus)
|
/**
|
||||||
|
* workflow 状态(WorkflowInstanceStatus)
|
||||||
|
*/
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
private PEWorkflowDAG peWorkflowDAG;
|
private PEWorkflowDAG peWorkflowDAG;
|
||||||
private String result;
|
private String result;
|
||||||
|
|
||||||
// 实际触发时间
|
/**
|
||||||
|
* 实际触发时间
|
||||||
|
*/
|
||||||
private Long actualTriggerTime;
|
private Long actualTriggerTime;
|
||||||
// 结束时间
|
/**
|
||||||
|
* 结束时间
|
||||||
|
*/
|
||||||
private Long finishedTime;
|
private Long finishedTime;
|
||||||
|
|
||||||
// 时间表达式类型(CRON/API/FIX_RATE/FIX_DELAY)
|
/**
|
||||||
|
* 时间表达式类型(CRON/API/FIX_RATE/FIX_DELAY)
|
||||||
|
*/
|
||||||
private Integer timeExpressionType;
|
private Integer timeExpressionType;
|
||||||
// 时间表达式,CRON/NULL/LONG/LONG
|
/**
|
||||||
|
* 时间表达式,CRON/NULL/LONG/LONG
|
||||||
|
*/
|
||||||
private String timeExpression;
|
private String timeExpression;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>powerjob-server</artifactId>
|
<artifactId>powerjob-server</artifactId>
|
||||||
<groupId>tech.powerjob</groupId>
|
<groupId>tech.powerjob</groupId>
|
||||||
<version>4.2.0</version>
|
<version>4.2.1</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
@ -2,9 +2,11 @@ package tech.powerjob.server.migrate;
|
|||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import tech.powerjob.common.exception.PowerJobException;
|
import tech.powerjob.common.exception.PowerJobException;
|
||||||
import tech.powerjob.common.enums.ProcessorType;
|
import tech.powerjob.common.enums.ProcessorType;
|
||||||
import tech.powerjob.common.model.PEWorkflowDAG;
|
import tech.powerjob.common.model.PEWorkflowDAG;
|
||||||
|
import tech.powerjob.server.common.utils.SpringUtils;
|
||||||
import tech.powerjob.server.extension.LockService;
|
import tech.powerjob.server.extension.LockService;
|
||||||
import tech.powerjob.server.persistence.remote.model.JobInfoDO;
|
import tech.powerjob.server.persistence.remote.model.JobInfoDO;
|
||||||
import tech.powerjob.server.persistence.remote.model.WorkflowInfoDO;
|
import tech.powerjob.server.persistence.remote.model.WorkflowInfoDO;
|
||||||
@ -20,7 +22,6 @@ import org.springframework.data.jpa.domain.Specification;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.persistence.criteria.Predicate;
|
import javax.persistence.criteria.Predicate;
|
||||||
import javax.transaction.Transactional;
|
import javax.transaction.Transactional;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -35,23 +36,18 @@ import java.util.concurrent.TimeUnit;
|
|||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class V3ToV4MigrateService {
|
public class V3ToV4MigrateService {
|
||||||
|
|
||||||
private static final String MIGRATE_LOCK_TEMPLATE = "v3to4MigrateLock-%s-%s";
|
private static final String MIGRATE_LOCK_TEMPLATE = "v3to4MigrateLock-%s-%s";
|
||||||
|
|
||||||
@Resource
|
private final LockService lockService;
|
||||||
private LockService lockService;
|
|
||||||
@Resource
|
private final JobInfoRepository jobInfoRepository;
|
||||||
private JobInfoRepository jobInfoRepository;
|
|
||||||
@Resource
|
private final WorkflowInfoRepository workflowInfoRepository;
|
||||||
private WorkflowInfoRepository workflowInfoRepository;
|
|
||||||
@Resource
|
private final WorkflowNodeInfoRepository workflowNodeInfoRepository;
|
||||||
private WorkflowNodeInfoRepository workflowNodeInfoRepository;
|
|
||||||
/**
|
|
||||||
* 避免内部方法调用导致事务不生效
|
|
||||||
*/
|
|
||||||
@Resource
|
|
||||||
private V3ToV4MigrateService self;
|
|
||||||
|
|
||||||
/* ********************** 3.x => 4.x ********************** */
|
/* ********************** 3.x => 4.x ********************** */
|
||||||
|
|
||||||
@ -149,7 +145,7 @@ public class V3ToV4MigrateService {
|
|||||||
for (WorkflowInfoDO workflowInfo : workflowInfoList) {
|
for (WorkflowInfoDO workflowInfo : workflowInfoList) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
boolean fixed = self.fixWorkflowInfoCoreFromV3ToV4(workflowInfo, jobId2NodeIdMap);
|
boolean fixed = SpringUtils.getBean(this.getClass()).fixWorkflowInfoCoreFromV3ToV4(workflowInfo, jobId2NodeIdMap);
|
||||||
if (fixed) {
|
if (fixed) {
|
||||||
fixedWorkflowIds.add(workflowInfo.getId());
|
fixedWorkflowIds.add(workflowInfo.getId());
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>powerjob-server</artifactId>
|
<artifactId>powerjob-server</artifactId>
|
||||||
<groupId>tech.powerjob</groupId>
|
<groupId>tech.powerjob</groupId>
|
||||||
<version>4.2.0</version>
|
<version>4.2.1</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
@ -2,7 +2,6 @@ package tech.powerjob.server.monitor;
|
|||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -19,9 +18,7 @@ public class PowerJobMonitorService implements MonitorService {
|
|||||||
|
|
||||||
private final List<Monitor> monitors = Lists.newLinkedList();
|
private final List<Monitor> monitors = Lists.newLinkedList();
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public PowerJobMonitorService(List<Monitor> monitors) {
|
public PowerJobMonitorService(List<Monitor> monitors) {
|
||||||
|
|
||||||
monitors.forEach(m -> {
|
monitors.forEach(m -> {
|
||||||
log.info("[MonitorService] register monitor: {}", m.getClass().getName());
|
log.info("[MonitorService] register monitor: {}", m.getClass().getName());
|
||||||
this.monitors.add(m);
|
this.monitors.add(m);
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>powerjob-server</artifactId>
|
<artifactId>powerjob-server</artifactId>
|
||||||
<groupId>tech.powerjob</groupId>
|
<groupId>tech.powerjob</groupId>
|
||||||
<version>4.2.0</version>
|
<version>4.2.1</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
@ -7,6 +7,7 @@ import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
|
|||||||
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
|
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.DependsOn;
|
||||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||||
@ -38,9 +39,6 @@ import java.util.Objects;
|
|||||||
)
|
)
|
||||||
public class LocalJpaConfig {
|
public class LocalJpaConfig {
|
||||||
|
|
||||||
@Resource(name = "omsLocalDatasource")
|
|
||||||
private DataSource omsLocalDatasource;
|
|
||||||
|
|
||||||
public static final String LOCAL_PACKAGES = "tech.powerjob.server.persistence.local";
|
public static final String LOCAL_PACKAGES = "tech.powerjob.server.persistence.local";
|
||||||
|
|
||||||
private static Map<String, Object> genDatasourceProperties() {
|
private static Map<String, Object> genDatasourceProperties() {
|
||||||
@ -56,8 +54,7 @@ public class LocalJpaConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "localEntityManagerFactory")
|
@Bean(name = "localEntityManagerFactory")
|
||||||
public LocalContainerEntityManagerFactoryBean initLocalEntityManagerFactory(EntityManagerFactoryBuilder builder) {
|
public LocalContainerEntityManagerFactoryBean initLocalEntityManagerFactory(@Qualifier("omsLocalDatasource") DataSource omsLocalDatasource,EntityManagerFactoryBuilder builder) {
|
||||||
|
|
||||||
return builder
|
return builder
|
||||||
.dataSource(omsLocalDatasource)
|
.dataSource(omsLocalDatasource)
|
||||||
.properties(genDatasourceProperties())
|
.properties(genDatasourceProperties())
|
||||||
@ -66,10 +63,9 @@ public class LocalJpaConfig {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Bean(name = "localTransactionManager")
|
@Bean(name = "localTransactionManager")
|
||||||
public PlatformTransactionManager initLocalTransactionManager(EntityManagerFactoryBuilder builder) {
|
public PlatformTransactionManager initLocalTransactionManager(@Qualifier("localEntityManagerFactory") LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean) {
|
||||||
return new JpaTransactionManager(Objects.requireNonNull(initLocalEntityManagerFactory(builder).getObject()));
|
return new JpaTransactionManager(Objects.requireNonNull(localContainerEntityManagerFactoryBean.getObject()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "localTransactionTemplate")
|
@Bean(name = "localTransactionTemplate")
|
||||||
|
@ -5,7 +5,7 @@ import tech.powerjob.server.common.utils.PropertyUtils;
|
|||||||
import org.hibernate.boot.model.naming.Identifier;
|
import org.hibernate.boot.model.naming.Identifier;
|
||||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||||
import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy;
|
import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy;
|
||||||
import org.springframework.util.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ public class PowerJobPhysicalNamingStrategy extends SpringPhysicalNamingStrategy
|
|||||||
|
|
||||||
String text = name.getText();
|
String text = name.getText();
|
||||||
String noDOText = StringUtils.endsWithIgnoreCase(text, "do") ? text.substring(0, text.length() - 2) : text;
|
String noDOText = StringUtils.endsWithIgnoreCase(text, "do") ? text.substring(0, text.length() - 2) : text;
|
||||||
String newText = StringUtils.hasLength(tablePrefix) ? tablePrefix + noDOText : noDOText;
|
String newText = StringUtils.isEmpty(tablePrefix) ? tablePrefix + noDOText : noDOText;
|
||||||
return super.toPhysicalTableName(new Identifier(newText, name.isQuoted()), jdbcEnvironment);
|
return super.toPhysicalTableName(new Identifier(newText, name.isQuoted()), jdbcEnvironment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package tech.powerjob.server.persistence.config;
|
package tech.powerjob.server.persistence.config;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
|
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
|
||||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
|
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
|
||||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
|
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
|
||||||
@ -13,7 +14,6 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
|||||||
import org.springframework.transaction.PlatformTransactionManager;
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -36,12 +36,6 @@ import java.util.Objects;
|
|||||||
)
|
)
|
||||||
public class RemoteJpaConfig {
|
public class RemoteJpaConfig {
|
||||||
|
|
||||||
@Resource(name = "omsRemoteDatasource")
|
|
||||||
private DataSource omsRemoteDatasource;
|
|
||||||
|
|
||||||
@Resource(name = "multiDatasourceProperties")
|
|
||||||
private MultiDatasourceProperties properties;
|
|
||||||
|
|
||||||
public static final String CORE_PACKAGES = "tech.powerjob.server.persistence.remote";
|
public static final String CORE_PACKAGES = "tech.powerjob.server.persistence.remote";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,7 +63,7 @@ public class RemoteJpaConfig {
|
|||||||
|
|
||||||
@Primary
|
@Primary
|
||||||
@Bean(name = "remoteEntityManagerFactory")
|
@Bean(name = "remoteEntityManagerFactory")
|
||||||
public LocalContainerEntityManagerFactoryBean initRemoteEntityManagerFactory(EntityManagerFactoryBuilder builder) {
|
public LocalContainerEntityManagerFactoryBean initRemoteEntityManagerFactory(@Qualifier("omsRemoteDatasource") DataSource omsRemoteDatasource,@Qualifier("multiDatasourceProperties") MultiDatasourceProperties properties, EntityManagerFactoryBuilder builder) {
|
||||||
Map<String, Object> datasourceProperties = genDatasourceProperties();
|
Map<String, Object> datasourceProperties = genDatasourceProperties();
|
||||||
datasourceProperties.putAll(properties.getRemote().getHibernate().getProperties());
|
datasourceProperties.putAll(properties.getRemote().getHibernate().getProperties());
|
||||||
return builder
|
return builder
|
||||||
@ -83,7 +77,7 @@ public class RemoteJpaConfig {
|
|||||||
|
|
||||||
@Primary
|
@Primary
|
||||||
@Bean(name = "remoteTransactionManager")
|
@Bean(name = "remoteTransactionManager")
|
||||||
public PlatformTransactionManager initRemoteTransactionManager(EntityManagerFactoryBuilder builder) {
|
public PlatformTransactionManager initRemoteTransactionManager(@Qualifier("remoteEntityManagerFactory") LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean) {
|
||||||
return new JpaTransactionManager(Objects.requireNonNull(initRemoteEntityManagerFactory(builder).getObject()));
|
return new JpaTransactionManager(Objects.requireNonNull(localContainerEntityManagerFactoryBean.getObject()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,16 +16,20 @@ import java.util.stream.Stream;
|
|||||||
*/
|
*/
|
||||||
public interface LocalInstanceLogRepository extends JpaRepository<LocalInstanceLogDO, Long> {
|
public interface LocalInstanceLogRepository extends JpaRepository<LocalInstanceLogDO, Long> {
|
||||||
|
|
||||||
// 流式查询
|
/**
|
||||||
|
* 流式查询
|
||||||
|
*/
|
||||||
Stream<LocalInstanceLogDO> findByInstanceIdOrderByLogTime(Long instanceId);
|
Stream<LocalInstanceLogDO> findByInstanceIdOrderByLogTime(Long instanceId);
|
||||||
|
|
||||||
// 删除数据
|
/**
|
||||||
|
* 删除数据
|
||||||
|
*/
|
||||||
@Modifying
|
@Modifying
|
||||||
@Transactional
|
@Transactional(rollbackOn = Exception.class)
|
||||||
long deleteByInstanceId(Long instanceId);
|
long deleteByInstanceId(Long instanceId);
|
||||||
|
|
||||||
@Modifying
|
@Modifying
|
||||||
@Transactional
|
@Transactional(rollbackOn = Exception.class)
|
||||||
@CanIgnoreReturnValue
|
@CanIgnoreReturnValue
|
||||||
long deleteByInstanceIdInAndLogTimeLessThan(List<Long> instanceIds, Long t);
|
long deleteByInstanceIdInAndLogTimeLessThan(List<Long> instanceIds, Long t);
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package tech.powerjob.server.persistence.mongodb;
|
package tech.powerjob.server.persistence.mongodb;
|
||||||
|
|
||||||
import tech.powerjob.server.common.PowerJobServerConfigKey;
|
|
||||||
import com.google.common.base.Stopwatch;
|
import com.google.common.base.Stopwatch;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.mongodb.client.MongoDatabase;
|
import com.mongodb.client.MongoDatabase;
|
||||||
@ -19,8 +18,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import tech.powerjob.server.common.PowerJobServerConfigKey;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -36,21 +35,24 @@ import java.util.function.Consumer;
|
|||||||
@Service
|
@Service
|
||||||
public class GridFsManager implements InitializingBean {
|
public class GridFsManager implements InitializingBean {
|
||||||
|
|
||||||
@Resource
|
private final Environment environment;
|
||||||
private Environment environment;
|
|
||||||
|
private final MongoDatabase db;
|
||||||
|
|
||||||
private MongoDatabase db;
|
|
||||||
private boolean available;
|
private boolean available;
|
||||||
|
|
||||||
private final Map<String, GridFSBucket> bucketCache = Maps.newConcurrentMap();
|
private final Map<String, GridFSBucket> bucketCache = Maps.newConcurrentMap();
|
||||||
|
|
||||||
public static final String LOG_BUCKET = "log";
|
public static final String LOG_BUCKET = "log";
|
||||||
|
|
||||||
public static final String CONTAINER_BUCKET = "container";
|
public static final String CONTAINER_BUCKET = "container";
|
||||||
|
|
||||||
@Autowired(required = false)
|
public GridFsManager(Environment environment, @Autowired(required = false) MongoTemplate mongoTemplate) {
|
||||||
public void setMongoTemplate(MongoTemplate mongoTemplate) {
|
this.environment = environment;
|
||||||
if (mongoTemplate != null) {
|
if (mongoTemplate != null) {
|
||||||
this.db = mongoTemplate.getDb();
|
this.db = mongoTemplate.getDb();
|
||||||
|
} else {
|
||||||
|
this.db = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package tech.powerjob.server.persistence.monitor;
|
package tech.powerjob.server.persistence.monitor;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.aspectj.lang.ProceedingJoinPoint;
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
import org.aspectj.lang.annotation.Around;
|
import org.aspectj.lang.annotation.Around;
|
||||||
@ -11,7 +12,6 @@ import tech.powerjob.server.monitor.MonitorService;
|
|||||||
import tech.powerjob.server.monitor.events.db.DatabaseEvent;
|
import tech.powerjob.server.monitor.events.db.DatabaseEvent;
|
||||||
import tech.powerjob.server.monitor.events.db.DatabaseType;
|
import tech.powerjob.server.monitor.events.db.DatabaseType;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@ -25,10 +25,10 @@ import java.util.stream.Stream;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@Aspect
|
@Aspect
|
||||||
@Component
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class DatabaseMonitorAspect {
|
public class DatabaseMonitorAspect {
|
||||||
|
|
||||||
@Resource
|
private final MonitorService monitorService;
|
||||||
private MonitorService monitorService;
|
|
||||||
|
|
||||||
@Around("execution(* tech.powerjob.server.persistence.remote.repository..*.*(..))")
|
@Around("execution(* tech.powerjob.server.persistence.remote.repository..*.*(..))")
|
||||||
public Object monitorCoreDB(ProceedingJoinPoint joinPoint) throws Throwable {
|
public Object monitorCoreDB(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||||
|
@ -14,7 +14,7 @@ import java.util.Date;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Entity
|
@Entity
|
||||||
@Table(uniqueConstraints = {@UniqueConstraint(name = "appNameUK", columnNames = {"appName"})})
|
@Table(uniqueConstraints = {@UniqueConstraint(name = "uidx01_app_info", columnNames = {"appName"})})
|
||||||
public class AppInfoDO {
|
public class AppInfoDO {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
@ -14,7 +14,7 @@ import java.util.Date;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Entity
|
@Entity
|
||||||
@Table(indexes = {@Index(columnList = "appId")})
|
@Table(indexes = {@Index(name = "idx01_container_info", columnList = "appId")})
|
||||||
public class ContainerInfoDO {
|
public class ContainerInfoDO {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
@ -19,7 +19,11 @@ import java.util.Date;
|
|||||||
@Entity
|
@Entity
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Table(indexes = {@Index(columnList = "jobId"), @Index(columnList = "appId"), @Index(columnList = "instanceId")})
|
@Table(indexes = {
|
||||||
|
@Index(name = "idx01_instance_info", columnList = "jobId,status"),
|
||||||
|
@Index(name = "idx02_instance_info", columnList = "appId,status"),
|
||||||
|
@Index(name = "idx03_instance_info", columnList = "instanceId,status")
|
||||||
|
})
|
||||||
public class InstanceInfoDO {
|
public class InstanceInfoDO {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@ -40,6 +44,7 @@ public class InstanceInfoDO {
|
|||||||
private Long instanceId;
|
private Long instanceId;
|
||||||
/**
|
/**
|
||||||
* 任务参数(静态)
|
* 任务参数(静态)
|
||||||
|
*
|
||||||
* @since 2021/2/01
|
* @since 2021/2/01
|
||||||
*/
|
*/
|
||||||
@Lob
|
@Lob
|
||||||
|
@ -19,7 +19,9 @@ import java.util.Date;
|
|||||||
@Entity
|
@Entity
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Table(indexes = {@Index(columnList = "appId")})
|
@Table(indexes = {
|
||||||
|
@Index(name = "idx01_job_info", columnList = "appId,status,timeExpressionType,nextTriggerTime"),
|
||||||
|
})
|
||||||
public class JobInfoDO {
|
public class JobInfoDO {
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ import java.util.Date;
|
|||||||
@Data
|
@Data
|
||||||
@Entity
|
@Entity
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@Table(uniqueConstraints = {@UniqueConstraint(name = "lockNameUK", columnNames = {"lockName"})})
|
@Table(uniqueConstraints = {@UniqueConstraint(name = "uidx01_oms_lock", columnNames = {"lockName"})})
|
||||||
public class OmsLockDO {
|
public class OmsLockDO {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
@ -16,7 +16,10 @@ import java.util.Date;
|
|||||||
@Data
|
@Data
|
||||||
@Entity
|
@Entity
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = "ip")})
|
@Table(
|
||||||
|
uniqueConstraints = {@UniqueConstraint(name = "uidx01_server_info", columnNames = "ip")},
|
||||||
|
indexes = {@Index(name = "idx01_server_info", columnList = "gmtModified")}
|
||||||
|
)
|
||||||
public class ServerInfoDO {
|
public class ServerInfoDO {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
@ -14,7 +14,10 @@ import java.util.Date;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Entity
|
@Entity
|
||||||
@Table
|
@Table(indexes = {
|
||||||
|
@Index(name = "uidx01_user_info", columnList = "username"),
|
||||||
|
@Index(name = "uidx02_user_info", columnList = "email")
|
||||||
|
})
|
||||||
public class UserInfoDO {
|
public class UserInfoDO {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
@ -18,7 +18,9 @@ import java.util.Date;
|
|||||||
@Entity
|
@Entity
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Table(indexes = {@Index(columnList = "appId")})
|
@Table(indexes = {
|
||||||
|
@Index(name = "idx01_workflow_info",columnList = "appId,status,timeExpressionType,nextTriggerTime")
|
||||||
|
})
|
||||||
public class WorkflowInfoDO {
|
public class WorkflowInfoDO {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
@ -18,7 +18,13 @@ import java.util.Date;
|
|||||||
@Entity
|
@Entity
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Table
|
@Table(
|
||||||
|
uniqueConstraints = {@UniqueConstraint(name = "uidx01_wf_instance", columnNames = {"wfInstanceId"})},
|
||||||
|
indexes = {
|
||||||
|
@Index(name = "idx01_wf_instance", columnList = "workflowId,status"),
|
||||||
|
@Index(name = "idx01_wf_instance", columnList = "appId,status,expectedTriggerTime")
|
||||||
|
}
|
||||||
|
)
|
||||||
public class WorkflowInstanceInfoDO {
|
public class WorkflowInstanceInfoDO {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
@ -21,7 +21,9 @@ import java.util.Date;
|
|||||||
@Entity
|
@Entity
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Table(indexes = {@Index(columnList = "appId"), @Index(columnList = "workflowId")})
|
@Table(indexes = {
|
||||||
|
@Index(name = "idx01_workflow_node_info", columnList = "workflowId,gmtCreate")
|
||||||
|
})
|
||||||
public class WorkflowNodeInfoDO {
|
public class WorkflowNodeInfoDO {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
package tech.powerjob.server.persistence.remote.model.brief;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Echo009
|
||||||
|
* @since 2022/9/13
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class BriefInstanceInfo {
|
||||||
|
|
||||||
|
|
||||||
|
private Long appId;
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
/**
|
||||||
|
* 任务ID
|
||||||
|
*/
|
||||||
|
private Long jobId;
|
||||||
|
/**
|
||||||
|
* 任务所属应用的ID,冗余提高查询效率
|
||||||
|
*/
|
||||||
|
private Long instanceId;
|
||||||
|
/**
|
||||||
|
* 总共执行的次数(用于重试判断)
|
||||||
|
*/
|
||||||
|
private Long runningTimes;
|
||||||
|
|
||||||
|
|
||||||
|
public BriefInstanceInfo(Long appId, Long id, Long jobId, Long instanceId) {
|
||||||
|
this.appId = appId;
|
||||||
|
this.id = id;
|
||||||
|
this.jobId = jobId;
|
||||||
|
this.instanceId = instanceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BriefInstanceInfo(Long appId, Long id, Long jobId, Long instanceId, Long runningTimes) {
|
||||||
|
this.appId = appId;
|
||||||
|
this.id = id;
|
||||||
|
this.jobId = jobId;
|
||||||
|
this.instanceId = instanceId;
|
||||||
|
this.runningTimes = runningTimes;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
package tech.powerjob.server.persistence.remote.repository;
|
package tech.powerjob.server.persistence.remote.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
import tech.powerjob.server.persistence.remote.model.AppInfoDO;
|
import tech.powerjob.server.persistence.remote.model.AppInfoDO;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
@ -25,4 +27,8 @@ public interface AppInfoRepository extends JpaRepository<AppInfoDO, Long> {
|
|||||||
* 其实只需要 id,处于性能考虑可以直接写SQL只返回ID
|
* 其实只需要 id,处于性能考虑可以直接写SQL只返回ID
|
||||||
*/
|
*/
|
||||||
List<AppInfoDO> findAllByCurrentServer(String currentServer);
|
List<AppInfoDO> findAllByCurrentServer(String currentServer);
|
||||||
|
|
||||||
|
@Query(value = "select id from AppInfoDO where currentServer = :currentServer")
|
||||||
|
List<Long> listAppIdByCurrentServer(@Param("currentServer")String currentServer);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
package tech.powerjob.server.persistence.remote.repository;
|
package tech.powerjob.server.persistence.remote.repository;
|
||||||
|
|
||||||
import tech.powerjob.server.persistence.remote.model.InstanceInfoDO;
|
|
||||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
import org.springframework.data.jpa.repository.Modifying;
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import tech.powerjob.server.persistence.remote.model.InstanceInfoDO;
|
||||||
|
import tech.powerjob.server.persistence.remote.model.brief.BriefInstanceInfo;
|
||||||
|
|
||||||
import javax.transaction.Transactional;
|
import javax.transaction.Transactional;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -23,10 +25,26 @@ public interface InstanceInfoRepository extends JpaRepository<InstanceInfoDO, Lo
|
|||||||
/**
|
/**
|
||||||
* 统计当前JOB有多少实例正在运行
|
* 统计当前JOB有多少实例正在运行
|
||||||
*/
|
*/
|
||||||
|
@Query(value = "select count(*) from InstanceInfoDO where jobId = ?1 and status in ?2")
|
||||||
long countByJobIdAndStatusIn(long jobId, List<Integer> status);
|
long countByJobIdAndStatusIn(long jobId, List<Integer> status);
|
||||||
|
|
||||||
List<InstanceInfoDO> findByJobIdAndStatusIn(long jobId, List<Integer> status);
|
List<InstanceInfoDO> findByJobIdAndStatusIn(long jobId, List<Integer> status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新状态变更信息
|
||||||
|
*
|
||||||
|
* @param lastReportTime 最近一次上报时间
|
||||||
|
* @param modifyTime 更新时间
|
||||||
|
* @param runningTimes 运行次数
|
||||||
|
* @param instanceId 实例 ID
|
||||||
|
* @param status 目标状态
|
||||||
|
* @param oldStatus 旧状态
|
||||||
|
* @return 更新记录数
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackOn = Exception.class)
|
||||||
|
@Modifying
|
||||||
|
@Query(value = "update InstanceInfoDO set lastReportTime = :lastReportTime, gmtModified = :modifyTime, runningTimes = :runningTimes, status = :status where instanceId = :instanceId and status = :oldStatus")
|
||||||
|
int updateStatusChangeInfoByInstanceIdAndStatus(@Param("lastReportTime") long lastReportTime, @Param("modifyTime") Date modifyTime, @Param("runningTimes") long runningTimes, @Param("status") int status, @Param("instanceId") long instanceId, @Param("oldStatus") int oldStatus);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新任务执行记录内容(DispatchService专用)
|
* 更新任务执行记录内容(DispatchService专用)
|
||||||
@ -46,6 +64,7 @@ public interface InstanceInfoRepository extends JpaRepository<InstanceInfoDO, Lo
|
|||||||
@Query(value = "update InstanceInfoDO set status = :status, actualTriggerTime = :actualTriggerTime, finishedTime = :finishedTime, taskTrackerAddress = :taskTrackerAddress, result = :result, gmtModified = :modifyTime where instanceId = :instanceId")
|
@Query(value = "update InstanceInfoDO set status = :status, actualTriggerTime = :actualTriggerTime, finishedTime = :finishedTime, taskTrackerAddress = :taskTrackerAddress, result = :result, gmtModified = :modifyTime where instanceId = :instanceId")
|
||||||
int update4TriggerFailed(@Param("instanceId") long instanceId, @Param("status") int status, @Param("actualTriggerTime") long actualTriggerTime, @Param("finishedTime") long finishedTime, @Param("taskTrackerAddress") String taskTrackerAddress, @Param("result") String result, @Param("modifyTime") Date modifyTime);
|
int update4TriggerFailed(@Param("instanceId") long instanceId, @Param("status") int status, @Param("actualTriggerTime") long actualTriggerTime, @Param("finishedTime") long finishedTime, @Param("taskTrackerAddress") String taskTrackerAddress, @Param("result") String result, @Param("modifyTime") Date modifyTime);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新任务执行记录内容(DispatchService专用)
|
* 更新任务执行记录内容(DispatchService专用)
|
||||||
*
|
*
|
||||||
@ -54,13 +73,28 @@ public interface InstanceInfoRepository extends JpaRepository<InstanceInfoDO, Lo
|
|||||||
* @param actualTriggerTime 实际调度时间
|
* @param actualTriggerTime 实际调度时间
|
||||||
* @param taskTrackerAddress taskTracker 地址
|
* @param taskTrackerAddress taskTracker 地址
|
||||||
* @param modifyTime 更新时间
|
* @param modifyTime 更新时间
|
||||||
|
* @param oldStatus 旧状态
|
||||||
* @return 更新记录数量
|
* @return 更新记录数量
|
||||||
*/
|
*/
|
||||||
@Transactional(rollbackOn = Exception.class)
|
@Transactional(rollbackOn = Exception.class)
|
||||||
@Modifying
|
@Modifying
|
||||||
@CanIgnoreReturnValue
|
@CanIgnoreReturnValue
|
||||||
@Query(value = "update InstanceInfoDO set status = :status, actualTriggerTime = :actualTriggerTime, taskTrackerAddress = :taskTrackerAddress, gmtModified = :modifyTime where instanceId = :instanceId")
|
@Query(value = "update InstanceInfoDO set status = :status, actualTriggerTime = :actualTriggerTime, taskTrackerAddress = :taskTrackerAddress, gmtModified = :modifyTime where instanceId = :instanceId and status = :oldStatus")
|
||||||
int update4TriggerSucceed(@Param("instanceId") long instanceId, @Param("status") int status, @Param("actualTriggerTime") long actualTriggerTime, @Param("taskTrackerAddress") String taskTrackerAddress, @Param("modifyTime") Date modifyTime);
|
int update4TriggerSucceed(@Param("instanceId") long instanceId, @Param("status") int status, @Param("actualTriggerTime") long actualTriggerTime, @Param("taskTrackerAddress") String taskTrackerAddress, @Param("modifyTime") Date modifyTime, @Param("oldStatus") int oldStatus);
|
||||||
|
|
||||||
|
|
||||||
|
@Transactional(rollbackOn = Exception.class)
|
||||||
|
@Modifying
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
@Query(value = "update InstanceInfoDO set status = :status, gmtModified = :modifyTime where instanceId = :instanceId and status = :originStatus ")
|
||||||
|
int updateStatusAndGmtModifiedByInstanceIdAndOriginStatus(@Param("instanceId") long instanceId, @Param("originStatus") int originStatus, @Param("status") int status, @Param("modifyTime") Date modifyTime);
|
||||||
|
|
||||||
|
|
||||||
|
@Transactional(rollbackOn = Exception.class)
|
||||||
|
@Modifying
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
@Query(value = "update InstanceInfoDO set status = :status, gmtModified = :modifyTime where instanceId in (:instanceIdList) and status = :originStatus ")
|
||||||
|
int updateStatusAndGmtModifiedByInstanceIdListAndOriginStatus(@Param("instanceIdList") List<Long> instanceIdList, @Param("originStatus") int originStatus, @Param("status") int status, @Param("modifyTime") Date modifyTime);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新固定频率任务的执行记录
|
* 更新固定频率任务的执行记录
|
||||||
@ -77,19 +111,20 @@ public interface InstanceInfoRepository extends JpaRepository<InstanceInfoDO, Lo
|
|||||||
@Query(value = "update InstanceInfoDO set status = :status, runningTimes = :runningTimes, gmtModified = :modifyTime where instanceId = :instanceId")
|
@Query(value = "update InstanceInfoDO set status = :status, runningTimes = :runningTimes, gmtModified = :modifyTime where instanceId = :instanceId")
|
||||||
int update4FrequentJob(@Param("instanceId") long instanceId, @Param("status") int status, @Param("runningTimes") long runningTimes, @Param("modifyTime") Date modifyTime);
|
int update4FrequentJob(@Param("instanceId") long instanceId, @Param("status") int status, @Param("runningTimes") long runningTimes, @Param("modifyTime") Date modifyTime);
|
||||||
|
|
||||||
/* --状态检查三兄弟,对应 WAITING_DISPATCH 、 WAITING_WORKER_RECEIVE 和 RUNNING 三阶段,数据量一般不大,就不单独写SQL优化 IO 了-- */
|
List<InstanceInfoDO> findAllByAppIdInAndStatusAndExpectedTriggerTimeLessThan(@Param("appIds") List<Long> appIds, @Param("status") int status, @Param("time") long time, Pageable pageable);
|
||||||
|
|
||||||
List<InstanceInfoDO> findByAppIdInAndStatusAndExpectedTriggerTimeLessThan(List<Long> jobIds, int status, long time);
|
@Query(value = "select new tech.powerjob.server.persistence.remote.model.brief.BriefInstanceInfo(i.appId,i.id,i.jobId,i.instanceId) from InstanceInfoDO i where i.appId in (:appIds) and i.status = :status and i.actualTriggerTime < :time")
|
||||||
|
List<BriefInstanceInfo> selectBriefInfoByAppIdInAndStatusAndActualTriggerTimeLessThan(@Param("appIds") List<Long> appIds, @Param("status") int status, @Param("time") long time, Pageable pageable);
|
||||||
|
|
||||||
List<InstanceInfoDO> findByAppIdInAndStatusAndActualTriggerTimeLessThan(List<Long> jobIds, int status, long time);
|
@Query(value = "select new tech.powerjob.server.persistence.remote.model.brief.BriefInstanceInfo(i.appId,i.id,i.jobId,i.instanceId,i.runningTimes) from InstanceInfoDO i where i.appId in (:appIds) and i.status = :status and i.gmtModified < :time")
|
||||||
|
List<BriefInstanceInfo> selectBriefInfoByAppIdInAndStatusAndGmtModifiedBefore(@Param("appIds") List<Long> appIds, @Param("status") int status, @Param("time") Date time, Pageable pageable);
|
||||||
List<InstanceInfoDO> findByAppIdInAndStatusAndGmtModifiedBefore(List<Long> jobIds, int status, Date time);
|
|
||||||
|
|
||||||
|
|
||||||
InstanceInfoDO findByInstanceId(long instanceId);
|
InstanceInfoDO findByInstanceId(long instanceId);
|
||||||
|
|
||||||
/* --数据统计-- */
|
/* --数据统计-- */
|
||||||
|
|
||||||
|
@Query(value = "select count(*) from InstanceInfoDO where appId = ?1 and status = ?2")
|
||||||
long countByAppIdAndStatus(long appId, int status);
|
long countByAppIdAndStatus(long appId, int status);
|
||||||
|
|
||||||
long countByAppIdAndStatusAndGmtCreateAfter(long appId, int status, Date time);
|
long countByAppIdAndStatusAndGmtCreateAfter(long appId, int status, Date time);
|
||||||
|
@ -7,6 +7,7 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
|||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@ -44,4 +45,6 @@ public interface JobInfoRepository extends JpaRepository<JobInfoDO, Long>, JpaSp
|
|||||||
|
|
||||||
List<JobInfoDO> findByAppId(Long appId);
|
List<JobInfoDO> findByAppId(Long appId);
|
||||||
|
|
||||||
|
List<JobInfoDO> findByIdIn(Collection<Long> jobIds);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,13 +16,13 @@ import javax.transaction.Transactional;
|
|||||||
public interface OmsLockRepository extends JpaRepository<OmsLockDO, Long> {
|
public interface OmsLockRepository extends JpaRepository<OmsLockDO, Long> {
|
||||||
|
|
||||||
@Modifying
|
@Modifying
|
||||||
@Transactional
|
@Transactional(rollbackOn = Exception.class)
|
||||||
@Query(value = "delete from OmsLockDO where lockName = ?1")
|
@Query(value = "delete from OmsLockDO where lockName = ?1")
|
||||||
int deleteByLockName(String lockName);
|
int deleteByLockName(String lockName);
|
||||||
|
|
||||||
OmsLockDO findByLockName(String lockName);
|
OmsLockDO findByLockName(String lockName);
|
||||||
|
|
||||||
@Modifying
|
@Modifying
|
||||||
@Transactional
|
@Transactional(rollbackOn = Exception.class)
|
||||||
int deleteByOwnerIP(String ip);
|
int deleteByOwnerIP(String ip);
|
||||||
}
|
}
|
||||||
|
@ -20,13 +20,18 @@ public interface WorkflowInfoRepository extends JpaRepository<WorkflowInfoDO, Lo
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询指定 APP 下所有的工作流信息
|
* 查询指定 APP 下所有的工作流信息
|
||||||
|
*
|
||||||
* @param appId APP ID
|
* @param appId APP ID
|
||||||
* @return 该 APP 下的所有工作流信息
|
* @return 该 APP 下的所有工作流信息
|
||||||
*/
|
*/
|
||||||
List<WorkflowInfoDO> findByAppId(Long appId);
|
List<WorkflowInfoDO> findByAppId(Long appId);
|
||||||
|
|
||||||
/** 对外查询(list)三兄弟 */
|
/**
|
||||||
|
* 对外查询(list)三兄弟
|
||||||
|
*/
|
||||||
Page<WorkflowInfoDO> findByAppIdAndStatusNot(Long appId, int nStatus, Pageable pageable);
|
Page<WorkflowInfoDO> findByAppIdAndStatusNot(Long appId, int nStatus, Pageable pageable);
|
||||||
|
|
||||||
Page<WorkflowInfoDO> findByIdAndStatusNot(Long id, int nStatus, Pageable pageable);
|
Page<WorkflowInfoDO> findByIdAndStatusNot(Long id, int nStatus, Pageable pageable);
|
||||||
|
|
||||||
Page<WorkflowInfoDO> findByAppIdAndStatusNotAndWfNameLike(Long appId, int nStatus, String condition, Pageable pageable);
|
Page<WorkflowInfoDO> findByAppIdAndStatusNotAndWfNameLike(Long appId, int nStatus, String condition, Pageable pageable);
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>powerjob-server</artifactId>
|
<artifactId>powerjob-server</artifactId>
|
||||||
<groupId>tech.powerjob</groupId>
|
<groupId>tech.powerjob</groupId>
|
||||||
<version>4.2.0</version>
|
<version>4.2.1</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
@ -37,20 +37,25 @@ import java.util.concurrent.TimeUnit;
|
|||||||
@Service
|
@Service
|
||||||
public class ServerElectionService {
|
public class ServerElectionService {
|
||||||
|
|
||||||
@Resource
|
private final LockService lockService;
|
||||||
private LockService lockService;
|
|
||||||
@Resource
|
|
||||||
private TransportService transportService;
|
|
||||||
@Resource
|
|
||||||
private AppInfoRepository appInfoRepository;
|
|
||||||
|
|
||||||
@Value("${oms.accurate.select.server.percentage}")
|
private final TransportService transportService;
|
||||||
private int accurateSelectServerPercentage;
|
|
||||||
|
private final AppInfoRepository appInfoRepository;
|
||||||
|
|
||||||
|
private final int accurateSelectServerPercentage;
|
||||||
|
|
||||||
private static final int RETRY_TIMES = 10;
|
private static final int RETRY_TIMES = 10;
|
||||||
private static final long PING_TIMEOUT_MS = 1000;
|
private static final long PING_TIMEOUT_MS = 1000;
|
||||||
private static final String SERVER_ELECT_LOCK = "server_elect_%d";
|
private static final String SERVER_ELECT_LOCK = "server_elect_%d";
|
||||||
|
|
||||||
|
public ServerElectionService(LockService lockService, TransportService transportService, AppInfoRepository appInfoRepository,@Value("${oms.accurate.select.server.percentage}") int accurateSelectServerPercentage) {
|
||||||
|
this.lockService = lockService;
|
||||||
|
this.transportService = transportService;
|
||||||
|
this.appInfoRepository = appInfoRepository;
|
||||||
|
this.accurateSelectServerPercentage = accurateSelectServerPercentage;
|
||||||
|
}
|
||||||
|
|
||||||
public String elect(Long appId, String protocol, String currentServer) {
|
public String elect(Long appId, String protocol, String currentServer) {
|
||||||
if (!accurate()) {
|
if (!accurate()) {
|
||||||
// 如果是本机,就不需要查数据库那么复杂的操作了,直接返回成功
|
// 如果是本机,就不需要查数据库那么复杂的操作了,直接返回成功
|
||||||
|
@ -4,6 +4,7 @@ import akka.pattern.Patterns;
|
|||||||
import com.fasterxml.jackson.databind.JavaType;
|
import com.fasterxml.jackson.databind.JavaType;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.type.TypeFactory;
|
import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import tech.powerjob.common.exception.PowerJobException;
|
import tech.powerjob.common.exception.PowerJobException;
|
||||||
import tech.powerjob.common.RemoteConstant;
|
import tech.powerjob.common.RemoteConstant;
|
||||||
import tech.powerjob.common.response.AskResponse;
|
import tech.powerjob.common.response.AskResponse;
|
||||||
@ -39,10 +40,10 @@ import java.util.concurrent.CompletionStage;
|
|||||||
@Aspect
|
@Aspect
|
||||||
@Component
|
@Component
|
||||||
@Order(0)
|
@Order(0)
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class DesignateServerAspect {
|
public class DesignateServerAspect {
|
||||||
|
|
||||||
@Resource
|
private final AppInfoRepository appInfoRepository;
|
||||||
private AppInfoRepository appInfoRepository;
|
|
||||||
|
|
||||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
@ -70,7 +71,7 @@ public class DesignateServerAspect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (appId == null) {
|
if (appId == null) {
|
||||||
throw new PowerJobException("can't find appId in params for:" + signature.toString());
|
throw new PowerJobException("can't find appId in params for:" + signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取执行机器
|
// 获取执行机器
|
||||||
|
@ -21,11 +21,17 @@ import java.util.Map;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class ClusterStatusHolder {
|
public class ClusterStatusHolder {
|
||||||
|
|
||||||
// 集群所属的应用名称
|
/**
|
||||||
|
* 集群所属的应用名称
|
||||||
|
*/
|
||||||
private final String appName;
|
private final String appName;
|
||||||
// 集群中所有机器的信息
|
/**
|
||||||
|
* 集群中所有机器的信息
|
||||||
|
*/
|
||||||
private final Map<String, WorkerInfo> address2WorkerInfo;
|
private final Map<String, WorkerInfo> address2WorkerInfo;
|
||||||
// 集群中所有机器的容器部署状态 containerId -> (workerAddress -> containerInfo)
|
/**
|
||||||
|
* 集群中所有机器的容器部署状态 containerId -> (workerAddress -> containerInfo)
|
||||||
|
*/
|
||||||
private Map<Long, Map<String, DeployedContainerInfo>> containerId2Infos;
|
private Map<Long, Map<String, DeployedContainerInfo>> containerId2Infos;
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,8 +18,10 @@ import java.util.Set;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class WorkerClusterManagerService {
|
public class WorkerClusterManagerService {
|
||||||
|
|
||||||
// 存储Worker健康信息,appId -> ClusterStatusHolder
|
/**
|
||||||
private static final Map<Long, ClusterStatusHolder> appId2ClusterStatus = Maps.newConcurrentMap();
|
* 存储Worker健康信息,appId -> ClusterStatusHolder
|
||||||
|
*/
|
||||||
|
private static final Map<Long, ClusterStatusHolder> APP_ID_2_CLUSTER_STATUS = Maps.newConcurrentMap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新状态
|
* 更新状态
|
||||||
@ -28,7 +30,7 @@ public class WorkerClusterManagerService {
|
|||||||
public static void updateStatus(WorkerHeartbeat heartbeat) {
|
public static void updateStatus(WorkerHeartbeat heartbeat) {
|
||||||
Long appId = heartbeat.getAppId();
|
Long appId = heartbeat.getAppId();
|
||||||
String appName = heartbeat.getAppName();
|
String appName = heartbeat.getAppName();
|
||||||
ClusterStatusHolder clusterStatusHolder = appId2ClusterStatus.computeIfAbsent(appId, ignore -> new ClusterStatusHolder(appName));
|
ClusterStatusHolder clusterStatusHolder = APP_ID_2_CLUSTER_STATUS.computeIfAbsent(appId, ignore -> new ClusterStatusHolder(appName));
|
||||||
clusterStatusHolder.updateStatus(heartbeat);
|
clusterStatusHolder.updateStatus(heartbeat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +40,7 @@ public class WorkerClusterManagerService {
|
|||||||
*/
|
*/
|
||||||
public static void clean(List<Long> usingAppIds) {
|
public static void clean(List<Long> usingAppIds) {
|
||||||
Set<Long> keys = Sets.newHashSet(usingAppIds);
|
Set<Long> keys = Sets.newHashSet(usingAppIds);
|
||||||
appId2ClusterStatus.entrySet().removeIf(entry -> !keys.contains(entry.getKey()));
|
APP_ID_2_CLUSTER_STATUS.entrySet().removeIf(entry -> !keys.contains(entry.getKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -46,11 +48,11 @@ public class WorkerClusterManagerService {
|
|||||||
* 清理缓存信息,防止 OOM
|
* 清理缓存信息,防止 OOM
|
||||||
*/
|
*/
|
||||||
public static void cleanUp() {
|
public static void cleanUp() {
|
||||||
appId2ClusterStatus.values().forEach(ClusterStatusHolder::release);
|
APP_ID_2_CLUSTER_STATUS.values().forEach(ClusterStatusHolder::release);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static Map<Long, ClusterStatusHolder> getAppId2ClusterStatus() {
|
protected static Map<Long, ClusterStatusHolder> getAppId2ClusterStatus() {
|
||||||
return appId2ClusterStatus;
|
return APP_ID_2_CLUSTER_STATUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,9 +26,8 @@ import java.util.Optional;
|
|||||||
@Service
|
@Service
|
||||||
public class WorkerClusterQueryService {
|
public class WorkerClusterQueryService {
|
||||||
|
|
||||||
private List<WorkerFilter> workerFilters;
|
private final List<WorkerFilter> workerFilters;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public WorkerClusterQueryService(List<WorkerFilter> workerFilters) {
|
public WorkerClusterQueryService(List<WorkerFilter> workerFilters) {
|
||||||
this.workerFilters = workerFilters;
|
this.workerFilters = workerFilters;
|
||||||
}
|
}
|
||||||
@ -92,7 +91,6 @@ public class WorkerClusterQueryService {
|
|||||||
*/
|
*/
|
||||||
public Optional<WorkerInfo> getWorkerInfoByAddress(Long appId, String address) {
|
public Optional<WorkerInfo> getWorkerInfoByAddress(Long appId, String address) {
|
||||||
// this may cause NPE while address value is null .
|
// this may cause NPE while address value is null .
|
||||||
//return Optional.ofNullable(getWorkerInfosByAppId(appId).get(address));
|
|
||||||
final Map<String, WorkerInfo> workerInfosByAppId = getWorkerInfosByAppId(appId);
|
final Map<String, WorkerInfo> workerInfosByAppId = getWorkerInfosByAppId(appId);
|
||||||
//add null check for both workerInfos Map and address
|
//add null check for both workerInfos Map and address
|
||||||
if (null != workerInfosByAppId && null != address) {
|
if (null != workerInfosByAppId && null != address) {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>powerjob-server</artifactId>
|
<artifactId>powerjob-server</artifactId>
|
||||||
<groupId>tech.powerjob</groupId>
|
<groupId>tech.powerjob</groupId>
|
||||||
<version>4.2.0</version>
|
<version>4.2.1</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package tech.powerjob.server.config;
|
package tech.powerjob.server.config;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@ -25,10 +26,10 @@ import static springfox.documentation.builders.PathSelectors.any;
|
|||||||
@Configuration
|
@Configuration
|
||||||
@EnableSwagger2
|
@EnableSwagger2
|
||||||
@ConditionalOnProperty(name = PowerJobServerConfigKey.SWAGGER_UI_ENABLE, havingValue = "true")
|
@ConditionalOnProperty(name = PowerJobServerConfigKey.SWAGGER_UI_ENABLE, havingValue = "true")
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class SwaggerConfig {
|
public class SwaggerConfig {
|
||||||
|
|
||||||
@Resource
|
private final ServerInfoService serverInfoService;
|
||||||
private ServerInfoService serverInfoService;
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Docket createRestApi() {
|
public Docket createRestApi() {
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
package tech.powerjob.server.config;
|
package tech.powerjob.server.config;
|
||||||
|
|
||||||
import org.springframework.core.task.AsyncTaskExecutor;
|
|
||||||
import org.springframework.core.task.TaskExecutor;
|
|
||||||
import tech.powerjob.server.common.RejectedExecutionHandlerFactory;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.task.AsyncTaskExecutor;
|
||||||
|
import org.springframework.core.task.TaskExecutor;
|
||||||
import org.springframework.scheduling.TaskScheduler;
|
import org.springframework.scheduling.TaskScheduler;
|
||||||
import org.springframework.scheduling.annotation.EnableAsync;
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||||
|
import tech.powerjob.server.common.RejectedExecutionHandlerFactory;
|
||||||
import tech.powerjob.server.common.constants.PJThreadPool;
|
import tech.powerjob.server.common.constants.PJThreadPool;
|
||||||
|
import tech.powerjob.server.common.thread.NewThreadRunRejectedExecutionHandler;
|
||||||
import java.util.concurrent.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 公用线程池配置
|
* 公用线程池配置
|
||||||
@ -34,7 +33,7 @@ public class ThreadPoolConfig {
|
|||||||
executor.setQueueCapacity(0);
|
executor.setQueueCapacity(0);
|
||||||
executor.setKeepAliveSeconds(60);
|
executor.setKeepAliveSeconds(60);
|
||||||
executor.setThreadNamePrefix("PJ-TIMING-");
|
executor.setThreadNamePrefix("PJ-TIMING-");
|
||||||
executor.setRejectedExecutionHandler(RejectedExecutionHandlerFactory.newThreadRun(PJThreadPool.TIMING_POOL));
|
executor.setRejectedExecutionHandler(new NewThreadRunRejectedExecutionHandler(PJThreadPool.TIMING_POOL));
|
||||||
return executor;
|
return executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,12 +61,14 @@ public class ThreadPoolConfig {
|
|||||||
return executor;
|
return executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 引入 WebSocket 支持后需要手动初始化调度线程池
|
/**
|
||||||
|
* 引入 WebSocket 支持后需要手动初始化调度线程池
|
||||||
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public TaskScheduler taskScheduler() {
|
public TaskScheduler taskScheduler() {
|
||||||
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
||||||
scheduler.setPoolSize(Runtime.getRuntime().availableProcessors());
|
scheduler.setPoolSize(Math.max(Runtime.getRuntime().availableProcessors() * 8, 32));
|
||||||
scheduler.setThreadNamePrefix("PJ-WS-");
|
scheduler.setThreadNamePrefix("PJ-DEFAULT-");
|
||||||
scheduler.setDaemon(true);
|
scheduler.setDaemon(true);
|
||||||
return scheduler;
|
return scheduler;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package tech.powerjob.server.web.controller;
|
package tech.powerjob.server.web.controller;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import tech.powerjob.common.exception.PowerJobException;
|
import tech.powerjob.common.exception.PowerJobException;
|
||||||
import tech.powerjob.common.response.ResultDTO;
|
import tech.powerjob.common.response.ResultDTO;
|
||||||
import tech.powerjob.server.persistence.remote.model.AppInfoDO;
|
import tech.powerjob.server.persistence.remote.model.AppInfoDO;
|
||||||
@ -31,12 +32,12 @@ import java.util.stream.Collectors;
|
|||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/appInfo")
|
@RequestMapping("/appInfo")
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class AppInfoController {
|
public class AppInfoController {
|
||||||
|
|
||||||
@Resource
|
private final AppInfoService appInfoService;
|
||||||
private AppInfoService appInfoService;
|
|
||||||
@Resource
|
private final AppInfoRepository appInfoRepository;
|
||||||
private AppInfoRepository appInfoRepository;
|
|
||||||
|
|
||||||
private static final int MAX_APP_NUM = 200;
|
private static final int MAX_APP_NUM = 200;
|
||||||
|
|
||||||
|
@ -41,15 +41,21 @@ import java.util.stream.Collectors;
|
|||||||
@RequestMapping("/container")
|
@RequestMapping("/container")
|
||||||
public class ContainerController {
|
public class ContainerController {
|
||||||
|
|
||||||
@Value("${server.port}")
|
|
||||||
private int port;
|
|
||||||
|
|
||||||
@Resource
|
private final int port;
|
||||||
private ContainerService containerService;
|
|
||||||
@Resource
|
private final ContainerService containerService;
|
||||||
private AppInfoRepository appInfoRepository;
|
|
||||||
@Resource
|
private final AppInfoRepository appInfoRepository;
|
||||||
private ContainerInfoRepository containerInfoRepository;
|
|
||||||
|
private final ContainerInfoRepository containerInfoRepository;
|
||||||
|
|
||||||
|
public ContainerController(@Value("${server.port}") int port, ContainerService containerService, AppInfoRepository appInfoRepository, ContainerInfoRepository containerInfoRepository) {
|
||||||
|
this.port = port;
|
||||||
|
this.containerService = containerService;
|
||||||
|
this.appInfoRepository = appInfoRepository;
|
||||||
|
this.containerInfoRepository = containerInfoRepository;
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/downloadJar")
|
@GetMapping("/downloadJar")
|
||||||
public void downloadJar(String version, HttpServletResponse response) throws IOException {
|
public void downloadJar(String version, HttpServletResponse response) throws IOException {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user