mirror of
https://github.com/PowerJob/PowerJob.git
synced 2025-07-17 00:00:04 +08:00
[release] merge branch 'v3.1.3'
This commit is contained in:
commit
152524df1e
16
README.md
16
README.md
@ -22,11 +22,21 @@ PowerJob(原OhMyScheduler)是全新一代分布式调度与计算框架,
|
|||||||
* 高可用&高性能:调度服务器经过精心设计,一改其他调度框架基于数据库锁的策略,实现了无锁化调度。部署多个调度服务器可以同时实现高可用和性能的提升(支持无限的水平扩展)。
|
* 高可用&高性能:调度服务器经过精心设计,一改其他调度框架基于数据库锁的策略,实现了无锁化调度。部署多个调度服务器可以同时实现高可用和性能的提升(支持无限的水平扩展)。
|
||||||
* 故障转移与恢复:任务执行失败后,可根据配置的重试策略完成重试,只要执行器集群有足够的计算节点,任务就能顺利完成。
|
* 故障转移与恢复:任务执行失败后,可根据配置的重试策略完成重试,只要执行器集群有足够的计算节点,任务就能顺利完成。
|
||||||
|
|
||||||
[在线试用地址](https://www.yuque.com/powerjob/guidence/hnbskn)
|
|
||||||
### 适用场景
|
### 适用场景
|
||||||
* 有定时执行需求的业务场景:如每天凌晨全量同步数据、生成业务报表等。
|
* 有定时执行需求的业务场景:如每天凌晨全量同步数据、生成业务报表等。
|
||||||
* 有需要全部机器一同执行的业务场景:如使用广播执行模式清理集群日志。
|
* 有需要全部机器一同执行的业务场景:如使用广播执行模式清理集群日志。
|
||||||
* 有需要分布式处理的业务场景:比如需要更新一大批数据,单机执行耗时非常长,可以使用Map/MapReduce处理器完成任务的分发,调动整个集群加速计算。
|
* 有需要分布式处理的业务场景:比如需要更新一大批数据,单机执行耗时非常长,可以使用Map/MapReduce处理器完成任务的分发,调动整个集群加速计算。
|
||||||
|
* 有需要延迟执行某些任务的业务场景:比如订单过期处理等。
|
||||||
|
|
||||||
|
### 设计目标
|
||||||
|
PowerJob 的设计目标为企业级的分布式任务调度平台,即成为公司内部的**任务调度中间件**。整个公司统一部署调度中心 powerjob-server,旗下所有业务线应用只需要依赖 `powerjob-worker` 即可接入调度中心获取任务调度与分布式计算能力。
|
||||||
|
|
||||||
|
### 在线试用
|
||||||
|
试用地址:[try.powerjob.tech](http://try.powerjob.tech/)
|
||||||
|
试用应用名称:powerjob-agent-test
|
||||||
|
控制台密码:123
|
||||||
|
|
||||||
|
[建议点击查看试用文档了解相关操作](https://www.yuque.com/powerjob/guidence/hnbskn)
|
||||||
|
|
||||||
### 同类产品对比
|
### 同类产品对比
|
||||||
| | QuartZ | xxl-job | SchedulerX 2.0 | PowerJob |
|
| | QuartZ | xxl-job | SchedulerX 2.0 | PowerJob |
|
||||||
@ -43,7 +53,9 @@ PowerJob(原OhMyScheduler)是全新一代分布式调度与计算框架,
|
|||||||
|
|
||||||
|
|
||||||
# 文档
|
# 文档
|
||||||
**[超详细中文文档](https://www.yuque.com/powerjob/guidence/ztn4i5)** OR **[备用地址(内容可能更新不及时)](https://kfcfans.github.io/)**
|
**[中文文档](https://www.yuque.com/powerjob/guidence/ztn4i5)**
|
||||||
|
|
||||||
|
**[Document](https://www.yuque.com/powerjob/en/xrdoqw)**
|
||||||
|
|
||||||
PS:感谢文档翻译平台[breword](https://www.breword.com/)对本项目英文文档翻译做出的巨大贡献!
|
PS:感谢文档翻译平台[breword](https://www.breword.com/)对本项目英文文档翻译做出的巨大贡献!
|
||||||
|
|
||||||
|
@ -10,11 +10,11 @@
|
|||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>powerjob-client</artifactId>
|
<artifactId>powerjob-client</artifactId>
|
||||||
<version>3.1.2</version>
|
<version>3.1.3</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<powerjob.common.version>3.1.2</powerjob.common.version>
|
<powerjob.common.version>3.1.3</powerjob.common.version>
|
||||||
<junit.version>5.6.1</junit.version>
|
<junit.version>5.6.1</junit.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
@ -66,9 +66,11 @@ public class OhMyClient {
|
|||||||
appId = Long.parseLong(resultDTO.getData().toString());
|
appId = Long.parseLong(resultDTO.getData().toString());
|
||||||
currentAddress = addr;
|
currentAddress = addr;
|
||||||
break;
|
break;
|
||||||
|
}else {
|
||||||
|
throw new OmsException(resultDTO.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}catch (Exception ignore) {
|
}catch (IOException ignore) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,13 +175,15 @@ public class OhMyClient {
|
|||||||
* 运行某个任务
|
* 运行某个任务
|
||||||
* @param jobId 任务ID
|
* @param jobId 任务ID
|
||||||
* @param instanceParams 任务实例的参数
|
* @param instanceParams 任务实例的参数
|
||||||
|
* @param delayMS 延迟时间,单位毫秒
|
||||||
* @return 任务实例ID(instanceId)
|
* @return 任务实例ID(instanceId)
|
||||||
* @throws Exception 异常
|
* @throws Exception 异常
|
||||||
*/
|
*/
|
||||||
public ResultDTO<Long> runJob(Long jobId, String instanceParams) throws Exception {
|
public ResultDTO<Long> runJob(Long jobId, String instanceParams, long delayMS) throws Exception {
|
||||||
FormBody.Builder builder = new FormBody.Builder()
|
FormBody.Builder builder = new FormBody.Builder()
|
||||||
.add("jobId", jobId.toString())
|
.add("jobId", jobId.toString())
|
||||||
.add("appId", appId.toString());
|
.add("appId", appId.toString())
|
||||||
|
.add("delay", String.valueOf(delayMS));
|
||||||
|
|
||||||
if (StringUtils.isNotEmpty(instanceParams)) {
|
if (StringUtils.isNotEmpty(instanceParams)) {
|
||||||
builder.add("instanceParams", instanceParams);
|
builder.add("instanceParams", instanceParams);
|
||||||
@ -188,7 +192,7 @@ public class OhMyClient {
|
|||||||
return JsonUtils.parseObject(post, ResultDTO.class);
|
return JsonUtils.parseObject(post, ResultDTO.class);
|
||||||
}
|
}
|
||||||
public ResultDTO<Long> runJob(Long jobId) throws Exception {
|
public ResultDTO<Long> runJob(Long jobId) throws Exception {
|
||||||
return runJob(jobId, null);
|
return runJob(jobId, null, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************* Instance 区 ************* */
|
/* ************* Instance 区 ************* */
|
||||||
|
@ -21,7 +21,7 @@ public class TestClient {
|
|||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public static void initClient() throws Exception {
|
public static void initClient() throws Exception {
|
||||||
ohMyClient = new OhMyClient("127.0.0.1:7700", "oms-test2", null);
|
ohMyClient = new OhMyClient("127.0.0.1:7700", "powerjob-agent-test", "123");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -70,7 +70,7 @@ public class TestClient {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRunJob() throws Exception {
|
public void testRunJob() throws Exception {
|
||||||
System.out.println(ohMyClient.runJob(8L, "this is instanceParams"));
|
System.out.println(ohMyClient.runJob(6L, "this is instanceParams", 60000));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>powerjob-common</artifactId>
|
<artifactId>powerjob-common</artifactId>
|
||||||
<version>3.1.2</version>
|
<version>3.1.3</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
@ -15,5 +15,5 @@ import lombok.NoArgsConstructor;
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class ServerDestroyContainerRequest implements OmsSerializable {
|
public class ServerDestroyContainerRequest implements OmsSerializable {
|
||||||
private String containerName;
|
private Long containerId;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package com.github.kfcfans.powerjob.common.request.http;
|
|||||||
import com.github.kfcfans.powerjob.common.ExecuteType;
|
import com.github.kfcfans.powerjob.common.ExecuteType;
|
||||||
import com.github.kfcfans.powerjob.common.ProcessorType;
|
import com.github.kfcfans.powerjob.common.ProcessorType;
|
||||||
import com.github.kfcfans.powerjob.common.TimeExpressionType;
|
import com.github.kfcfans.powerjob.common.TimeExpressionType;
|
||||||
|
import com.github.kfcfans.powerjob.common.utils.CommonUtils;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -76,4 +77,14 @@ public class SaveJobInfoRequest {
|
|||||||
|
|
||||||
// 报警用户ID列表
|
// 报警用户ID列表
|
||||||
private List<Long> notifyUserIds;
|
private List<Long> notifyUserIds;
|
||||||
|
|
||||||
|
|
||||||
|
public void valid() {
|
||||||
|
CommonUtils.requireNonNull(jobName, "jobName can't be empty");
|
||||||
|
CommonUtils.requireNonNull(appId, "appId can't be empty");
|
||||||
|
CommonUtils.requireNonNull(processorInfo, "processorInfo can't be empty");
|
||||||
|
CommonUtils.requireNonNull(executeType, "executeType can't be empty");
|
||||||
|
CommonUtils.requireNonNull(processorType, "processorType can't be empty");
|
||||||
|
CommonUtils.requireNonNull(timeExpressionType, "timeExpressionType can't be empty");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package com.github.kfcfans.powerjob.common.request.http;
|
|||||||
|
|
||||||
import com.github.kfcfans.powerjob.common.TimeExpressionType;
|
import com.github.kfcfans.powerjob.common.TimeExpressionType;
|
||||||
import com.github.kfcfans.powerjob.common.model.PEWorkflowDAG;
|
import com.github.kfcfans.powerjob.common.model.PEWorkflowDAG;
|
||||||
|
import com.github.kfcfans.powerjob.common.utils.CommonUtils;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@ -43,4 +44,11 @@ public class SaveWorkflowRequest {
|
|||||||
|
|
||||||
// 工作流整体失败的报警
|
// 工作流整体失败的报警
|
||||||
private List<Long> notifyUserIds = Lists.newLinkedList();
|
private List<Long> notifyUserIds = Lists.newLinkedList();
|
||||||
|
|
||||||
|
public void valid() {
|
||||||
|
CommonUtils.requireNonNull(wfName, "workflow name can't be empty");
|
||||||
|
CommonUtils.requireNonNull(appId, "appId can't be empty");
|
||||||
|
CommonUtils.requireNonNull(pEWorkflowDAG, "dag can't be empty");
|
||||||
|
CommonUtils.requireNonNull(timeExpressionType, "timeExpressionType can't be empty");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
package com.github.kfcfans.powerjob.common.utils;
|
package com.github.kfcfans.powerjob.common.utils;
|
||||||
|
|
||||||
|
import com.github.kfcfans.powerjob.common.OmsException;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
|
||||||
@ -114,4 +117,16 @@ public class CommonUtils {
|
|||||||
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
|
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> T requireNonNull(T obj, String msg) {
|
||||||
|
if (obj == null) {
|
||||||
|
throw new OmsException(msg);
|
||||||
|
}
|
||||||
|
if (obj instanceof String) {
|
||||||
|
if (StringUtils.isEmpty((String) obj)) {
|
||||||
|
throw new OmsException(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,13 +10,13 @@
|
|||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>powerjob-server</artifactId>
|
<artifactId>powerjob-server</artifactId>
|
||||||
<version>3.1.2</version>
|
<version>3.1.3</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<swagger.version>2.9.2</swagger.version>
|
<swagger.version>2.9.2</swagger.version>
|
||||||
<springboot.version>2.2.6.RELEASE</springboot.version>
|
<springboot.version>2.2.6.RELEASE</springboot.version>
|
||||||
<powerjob.common.version>3.1.2</powerjob.common.version>
|
<powerjob.common.version>3.1.3</powerjob.common.version>
|
||||||
<mysql.version>8.0.19</mysql.version>
|
<mysql.version>8.0.19</mysql.version>
|
||||||
<h2.db.version>1.4.200</h2.db.version>
|
<h2.db.version>1.4.200</h2.db.version>
|
||||||
<zip4j.version>2.5.2</zip4j.version>
|
<zip4j.version>2.5.2</zip4j.version>
|
||||||
|
@ -10,6 +10,7 @@ import com.github.kfcfans.powerjob.common.request.WorkerNeedDeployContainerReque
|
|||||||
import com.github.kfcfans.powerjob.common.response.AskResponse;
|
import com.github.kfcfans.powerjob.common.response.AskResponse;
|
||||||
import com.github.kfcfans.powerjob.common.utils.JsonUtils;
|
import com.github.kfcfans.powerjob.common.utils.JsonUtils;
|
||||||
import com.github.kfcfans.powerjob.common.utils.NetUtils;
|
import com.github.kfcfans.powerjob.common.utils.NetUtils;
|
||||||
|
import com.github.kfcfans.powerjob.server.common.constans.SwitchableStatus;
|
||||||
import com.github.kfcfans.powerjob.server.common.utils.SpringUtils;
|
import com.github.kfcfans.powerjob.server.common.utils.SpringUtils;
|
||||||
import com.github.kfcfans.powerjob.server.persistence.core.model.ContainerInfoDO;
|
import com.github.kfcfans.powerjob.server.persistence.core.model.ContainerInfoDO;
|
||||||
import com.github.kfcfans.powerjob.server.persistence.core.repository.ContainerInfoRepository;
|
import com.github.kfcfans.powerjob.server.persistence.core.repository.ContainerInfoRepository;
|
||||||
@ -91,8 +92,10 @@ public class ServerActor extends AbstractActor {
|
|||||||
|
|
||||||
Optional<ContainerInfoDO> containerInfoOpt = containerInfoRepository.findById(req.getContainerId());
|
Optional<ContainerInfoDO> containerInfoOpt = containerInfoRepository.findById(req.getContainerId());
|
||||||
AskResponse askResponse = new AskResponse();
|
AskResponse askResponse = new AskResponse();
|
||||||
askResponse.setSuccess(false);
|
if (!containerInfoOpt.isPresent() || containerInfoOpt.get().getStatus() != SwitchableStatus.ENABLE.getV()) {
|
||||||
if (containerInfoOpt.isPresent()) {
|
askResponse.setSuccess(false);
|
||||||
|
askResponse.setMessage("can't find container by id: " + req.getContainerId());
|
||||||
|
}else {
|
||||||
ContainerInfoDO containerInfo = containerInfoOpt.get();
|
ContainerInfoDO containerInfo = containerInfoOpt.get();
|
||||||
askResponse.setSuccess(true);
|
askResponse.setSuccess(true);
|
||||||
|
|
||||||
@ -104,7 +107,6 @@ public class ServerActor extends AbstractActor {
|
|||||||
|
|
||||||
askResponse.setData(JsonUtils.toBytes(dpReq));
|
askResponse.setData(JsonUtils.toBytes(dpReq));
|
||||||
}
|
}
|
||||||
|
|
||||||
getSender().tell(askResponse, getSelf());
|
getSender().tell(askResponse, getSelf());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
package com.github.kfcfans.powerjob.server.common.constans;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 容器状态
|
|
||||||
* 由于命名约束,准备采取硬删除策略
|
|
||||||
*
|
|
||||||
* @author tjq
|
|
||||||
* @since 2020/5/15
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
@AllArgsConstructor
|
|
||||||
public enum ContainerStatus {
|
|
||||||
|
|
||||||
ENABLE(1),
|
|
||||||
DISABLE(2),
|
|
||||||
DELETED(99);
|
|
||||||
|
|
||||||
private int v;
|
|
||||||
|
|
||||||
public static ContainerStatus of(int v) {
|
|
||||||
for (ContainerStatus type : values()) {
|
|
||||||
if (type.v == v) {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("unknown ContainerStatus of " + v);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,19 @@
|
|||||||
package com.github.kfcfans.powerjob.server.common.utils;
|
package com.github.kfcfans.powerjob.server.common.utils;
|
||||||
|
/*
|
||||||
|
Copyright [2020] [KFCFans]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
@ -37,8 +37,6 @@ public class HashedWheelTimer implements Timer {
|
|||||||
|
|
||||||
private final ExecutorService taskProcessPool;
|
private final ExecutorService taskProcessPool;
|
||||||
|
|
||||||
private static final int MAXIMUM_CAPACITY = 1 << 30;
|
|
||||||
|
|
||||||
public HashedWheelTimer(long tickDuration, int ticksPerWheel) {
|
public HashedWheelTimer(long tickDuration, int ticksPerWheel) {
|
||||||
this(tickDuration, ticksPerWheel, 0);
|
this(tickDuration, ticksPerWheel, 0);
|
||||||
}
|
}
|
||||||
@ -47,9 +45,9 @@ public class HashedWheelTimer implements Timer {
|
|||||||
* 新建时间轮定时器
|
* 新建时间轮定时器
|
||||||
* @param tickDuration 时间间隔,单位毫秒(ms)
|
* @param tickDuration 时间间隔,单位毫秒(ms)
|
||||||
* @param ticksPerWheel 轮盘个数
|
* @param ticksPerWheel 轮盘个数
|
||||||
* @param processTaskNum 处理任务的线程个数,0代表不启用新线程(如果定时任务需要耗时操作,请启用线程池)
|
* @param processThreadNum 处理任务的线程个数,0代表不启用新线程(如果定时任务需要耗时操作,请启用线程池)
|
||||||
*/
|
*/
|
||||||
public HashedWheelTimer(long tickDuration, int ticksPerWheel, int processTaskNum) {
|
public HashedWheelTimer(long tickDuration, int ticksPerWheel, int processThreadNum) {
|
||||||
|
|
||||||
this.tickDuration = tickDuration;
|
this.tickDuration = tickDuration;
|
||||||
|
|
||||||
@ -62,12 +60,13 @@ public class HashedWheelTimer implements Timer {
|
|||||||
mask = wheel.length - 1;
|
mask = wheel.length - 1;
|
||||||
|
|
||||||
// 初始化执行线程池
|
// 初始化执行线程池
|
||||||
if (processTaskNum <= 0) {
|
if (processThreadNum <= 0) {
|
||||||
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(16);
|
||||||
taskProcessPool = new ThreadPoolExecutor(2, processTaskNum,
|
int core = Math.max(Runtime.getRuntime().availableProcessors(), processThreadNum);
|
||||||
|
taskProcessPool = new ThreadPoolExecutor(core, 2 * core,
|
||||||
60, TimeUnit.SECONDS,
|
60, TimeUnit.SECONDS,
|
||||||
queue, threadFactory, new ThreadPoolExecutor.CallerRunsPolicy());
|
queue, threadFactory, new ThreadPoolExecutor.CallerRunsPolicy());
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package com.github.kfcfans.powerjob.server.common.utils.timewheel;
|
package com.github.kfcfans.powerjob.server.common.utils.timewheel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* description
|
* TimerFuture
|
||||||
*
|
*
|
||||||
* @author tjq
|
* @author tjq
|
||||||
* @since 2020/4/3
|
* @since 2020/4/3
|
||||||
|
@ -13,5 +13,5 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public interface ContainerInfoRepository extends JpaRepository<ContainerInfoDO, Long> {
|
public interface ContainerInfoRepository extends JpaRepository<ContainerInfoDO, Long> {
|
||||||
|
|
||||||
List<ContainerInfoDO> findByAppId(Long appId);
|
List<ContainerInfoDO> findByAppIdAndStatusNot(Long appId, Integer status);
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import com.github.kfcfans.powerjob.common.utils.NetUtils;
|
|||||||
import com.github.kfcfans.powerjob.common.utils.SegmentLock;
|
import com.github.kfcfans.powerjob.common.utils.SegmentLock;
|
||||||
import com.github.kfcfans.powerjob.server.akka.OhMyServer;
|
import com.github.kfcfans.powerjob.server.akka.OhMyServer;
|
||||||
import com.github.kfcfans.powerjob.server.common.constans.ContainerSourceType;
|
import com.github.kfcfans.powerjob.server.common.constans.ContainerSourceType;
|
||||||
|
import com.github.kfcfans.powerjob.server.common.constans.SwitchableStatus;
|
||||||
import com.github.kfcfans.powerjob.server.common.utils.OmsFileUtils;
|
import com.github.kfcfans.powerjob.server.common.utils.OmsFileUtils;
|
||||||
import com.github.kfcfans.powerjob.server.persistence.core.model.ContainerInfoDO;
|
import com.github.kfcfans.powerjob.server.persistence.core.model.ContainerInfoDO;
|
||||||
import com.github.kfcfans.powerjob.server.persistence.core.repository.ContainerInfoRepository;
|
import com.github.kfcfans.powerjob.server.persistence.core.repository.ContainerInfoRepository;
|
||||||
@ -85,6 +86,9 @@ public class ContainerService {
|
|||||||
* @param request 容器保存请求
|
* @param request 容器保存请求
|
||||||
*/
|
*/
|
||||||
public void save(SaveContainerInfoRequest request) {
|
public void save(SaveContainerInfoRequest request) {
|
||||||
|
|
||||||
|
request.valid();
|
||||||
|
|
||||||
ContainerInfoDO container;
|
ContainerInfoDO container;
|
||||||
Long originId = request.getId();
|
Long originId = request.getId();
|
||||||
if (originId != null) {
|
if (originId != null) {
|
||||||
@ -120,15 +124,17 @@ public class ContainerService {
|
|||||||
throw new RuntimeException("Permission Denied!");
|
throw new RuntimeException("Permission Denied!");
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerDestroyContainerRequest destroyRequest = new ServerDestroyContainerRequest(container.getContainerName());
|
ServerDestroyContainerRequest destroyRequest = new ServerDestroyContainerRequest(container.getId());
|
||||||
WorkerManagerService.getActiveWorkerInfo(container.getAppId()).keySet().forEach(akkaAddress -> {
|
WorkerManagerService.getActiveWorkerInfo(container.getAppId()).keySet().forEach(akkaAddress -> {
|
||||||
ActorSelection workerActor = OhMyServer.getWorkerActor(akkaAddress);
|
ActorSelection workerActor = OhMyServer.getWorkerActor(akkaAddress);
|
||||||
workerActor.tell(destroyRequest, null);
|
workerActor.tell(destroyRequest, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
log.info("[ContainerService] delete container: {}.", container);
|
log.info("[ContainerService] delete container: {}.", container);
|
||||||
// 硬删除算了...留着好像也没什么用
|
// 软删除
|
||||||
containerInfoRepository.deleteById(containerId);
|
container.setStatus(SwitchableStatus.DELETED.getV());
|
||||||
|
container.setGmtModified(new Date());
|
||||||
|
containerInfoRepository.saveAndFlush(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,6 +12,7 @@ import com.github.kfcfans.powerjob.server.persistence.core.model.JobInfoDO;
|
|||||||
import com.github.kfcfans.powerjob.server.persistence.core.repository.InstanceInfoRepository;
|
import com.github.kfcfans.powerjob.server.persistence.core.repository.InstanceInfoRepository;
|
||||||
import com.github.kfcfans.powerjob.server.persistence.core.repository.JobInfoRepository;
|
import com.github.kfcfans.powerjob.server.persistence.core.repository.JobInfoRepository;
|
||||||
import com.github.kfcfans.powerjob.server.service.instance.InstanceService;
|
import com.github.kfcfans.powerjob.server.service.instance.InstanceService;
|
||||||
|
import com.github.kfcfans.powerjob.server.service.timing.schedule.HashedWheelTimerHolder;
|
||||||
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;
|
||||||
@ -21,6 +22,7 @@ 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;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 任务服务
|
* 任务服务
|
||||||
@ -50,6 +52,8 @@ public class JobService {
|
|||||||
*/
|
*/
|
||||||
public Long saveJob(SaveJobInfoRequest request) throws Exception {
|
public Long saveJob(SaveJobInfoRequest request) throws Exception {
|
||||||
|
|
||||||
|
request.valid();
|
||||||
|
|
||||||
JobInfoDO jobInfoDO;
|
JobInfoDO jobInfoDO;
|
||||||
if (request.getId() != null) {
|
if (request.getId() != null) {
|
||||||
jobInfoDO = jobInfoRepository.findById(request.getId()).orElseThrow(() -> new IllegalArgumentException("can't find job by jobId: " + request.getId()));
|
jobInfoDO = jobInfoRepository.findById(request.getId()).orElseThrow(() -> new IllegalArgumentException("can't find job by jobId: " + request.getId()));
|
||||||
@ -94,16 +98,22 @@ public class JobService {
|
|||||||
* 手动立即运行某个任务
|
* 手动立即运行某个任务
|
||||||
* @param jobId 任务ID
|
* @param jobId 任务ID
|
||||||
* @param instanceParams 任务实例参数(仅 OpenAPI 存在)
|
* @param instanceParams 任务实例参数(仅 OpenAPI 存在)
|
||||||
|
* @param delay 延迟时间,单位 毫秒
|
||||||
* @return 任务实例ID
|
* @return 任务实例ID
|
||||||
*/
|
*/
|
||||||
public long runJob(Long jobId, String instanceParams) {
|
public long runJob(Long jobId, String instanceParams, long delay) {
|
||||||
|
|
||||||
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));
|
||||||
|
Long instanceId = instanceService.create(jobInfo.getId(), jobInfo.getAppId(), instanceParams, null, System.currentTimeMillis() + Math.max(delay, 0));
|
||||||
Long instanceId = instanceService.create(jobInfo.getId(), jobInfo.getAppId(), instanceParams, null, System.currentTimeMillis());
|
|
||||||
instanceInfoRepository.flush();
|
instanceInfoRepository.flush();
|
||||||
|
|
||||||
dispatchService.dispatch(jobInfo, instanceId, 0, instanceParams, null);
|
if (delay <= 0) {
|
||||||
|
dispatchService.dispatch(jobInfo, instanceId, 0, instanceParams, null);
|
||||||
|
}else {
|
||||||
|
HashedWheelTimerHolder.TIMER.schedule(() -> {
|
||||||
|
dispatchService.dispatch(jobInfo, instanceId, 0, instanceParams, null);
|
||||||
|
}, delay, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
return instanceId;
|
return instanceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ public class ServerSelectService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取某个应用对应的Server
|
* 获取某个应用对应的Server
|
||||||
* 缺点:如果server死而复生,可能造成worker集群脑裂,不过感觉影响不是很大 & 概率极低,就不管了
|
*
|
||||||
* @param appId 应用ID
|
* @param appId 应用ID
|
||||||
* @return 当前可用的Server
|
* @return 当前可用的Server
|
||||||
*/
|
*/
|
||||||
|
@ -258,7 +258,7 @@ public class OmsScheduleService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.info("[FrequentScheduler] These frequent jobs will be scheduled: {}.", notRunningJobIds);
|
log.info("[FrequentScheduler] These frequent jobs will be scheduled: {}.", notRunningJobIds);
|
||||||
notRunningJobIds.forEach(jobId -> jobService.runJob(jobId, null));
|
notRunningJobIds.forEach(jobId -> jobService.runJob(jobId, null, 0));
|
||||||
}catch (Exception e) {
|
}catch (Exception e) {
|
||||||
log.error("[FrequentScheduler] schedule frequent job failed.", e);
|
log.error("[FrequentScheduler] schedule frequent job failed.", e);
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,8 @@ public class WorkflowService {
|
|||||||
*/
|
*/
|
||||||
public Long saveWorkflow(SaveWorkflowRequest req) throws Exception {
|
public Long saveWorkflow(SaveWorkflowRequest req) throws Exception {
|
||||||
|
|
||||||
|
req.valid();
|
||||||
|
|
||||||
if (!WorkflowDAGUtils.valid(req.getPEWorkflowDAG())) {
|
if (!WorkflowDAGUtils.valid(req.getPEWorkflowDAG())) {
|
||||||
throw new OmsException("illegal DAG");
|
throw new OmsException("illegal DAG");
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import com.github.kfcfans.powerjob.common.OmsConstant;
|
|||||||
import com.github.kfcfans.powerjob.common.response.ResultDTO;
|
import com.github.kfcfans.powerjob.common.response.ResultDTO;
|
||||||
import com.github.kfcfans.powerjob.server.akka.OhMyServer;
|
import com.github.kfcfans.powerjob.server.akka.OhMyServer;
|
||||||
import com.github.kfcfans.powerjob.server.common.constans.ContainerSourceType;
|
import com.github.kfcfans.powerjob.server.common.constans.ContainerSourceType;
|
||||||
import com.github.kfcfans.powerjob.server.common.constans.ContainerStatus;
|
import com.github.kfcfans.powerjob.server.common.constans.SwitchableStatus;
|
||||||
import com.github.kfcfans.powerjob.server.common.utils.ContainerTemplateGenerator;
|
import com.github.kfcfans.powerjob.server.common.utils.ContainerTemplateGenerator;
|
||||||
import com.github.kfcfans.powerjob.server.common.utils.OmsFileUtils;
|
import com.github.kfcfans.powerjob.server.common.utils.OmsFileUtils;
|
||||||
import com.github.kfcfans.powerjob.server.persistence.core.model.AppInfoDO;
|
import com.github.kfcfans.powerjob.server.persistence.core.model.AppInfoDO;
|
||||||
@ -87,7 +87,8 @@ public class ContainerController {
|
|||||||
|
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
public ResultDTO<List<ContainerInfoVO>> listContainers(Long appId) {
|
public ResultDTO<List<ContainerInfoVO>> listContainers(Long appId) {
|
||||||
List<ContainerInfoVO> res = containerInfoRepository.findByAppId(appId).stream().map(ContainerController::convert).collect(Collectors.toList());
|
List<ContainerInfoVO> res = containerInfoRepository.findByAppIdAndStatusNot(appId, SwitchableStatus.DELETED.getV())
|
||||||
|
.stream().map(ContainerController::convert).collect(Collectors.toList());
|
||||||
return ResultDTO.success(res);
|
return ResultDTO.success(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +123,7 @@ public class ContainerController {
|
|||||||
}else {
|
}else {
|
||||||
vo.setLastDeployTime(DateFormatUtils.format(containerInfoDO.getLastDeployTime(), OmsConstant.TIME_PATTERN));
|
vo.setLastDeployTime(DateFormatUtils.format(containerInfoDO.getLastDeployTime(), OmsConstant.TIME_PATTERN));
|
||||||
}
|
}
|
||||||
ContainerStatus status = ContainerStatus.of(containerInfoDO.getStatus());
|
SwitchableStatus status = SwitchableStatus.of(containerInfoDO.getStatus());
|
||||||
vo.setStatus(status.name());
|
vo.setStatus(status.name());
|
||||||
ContainerSourceType sourceType = ContainerSourceType.of(containerInfoDO.getSourceType());
|
ContainerSourceType sourceType = ContainerSourceType.of(containerInfoDO.getSourceType());
|
||||||
vo.setSourceType(sourceType.name());
|
vo.setSourceType(sourceType.name());
|
||||||
|
@ -63,7 +63,7 @@ public class JobController {
|
|||||||
|
|
||||||
@GetMapping("/run")
|
@GetMapping("/run")
|
||||||
public ResultDTO<Long> runImmediately(String jobId) {
|
public ResultDTO<Long> runImmediately(String jobId) {
|
||||||
return ResultDTO.success(jobService.runJob(Long.valueOf(jobId), null));
|
return ResultDTO.success(jobService.runJob(Long.valueOf(jobId), null, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/list")
|
@PostMapping("/list")
|
||||||
|
@ -80,9 +80,9 @@ public class OpenAPIController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(OpenAPIConstant.RUN_JOB)
|
@PostMapping(OpenAPIConstant.RUN_JOB)
|
||||||
public ResultDTO<Long> runJob(Long appId, Long jobId, @RequestParam(required = false) String instanceParams) {
|
public ResultDTO<Long> runJob(Long appId, Long jobId, @RequestParam(required = false) String instanceParams, @RequestParam(required = false) Long delay) {
|
||||||
checkJobIdValid(jobId, appId);
|
checkJobIdValid(jobId, appId);
|
||||||
return ResultDTO.success(jobService.runJob(jobId, instanceParams));
|
return ResultDTO.success(jobService.runJob(jobId, instanceParams, delay == null ? 0 : delay));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************* Instance 区 ************* */
|
/* ************* Instance 区 ************* */
|
||||||
|
@ -46,4 +46,9 @@ public class ServerController {
|
|||||||
return ResultDTO.success(server);
|
return ResultDTO.success(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/hello")
|
||||||
|
public ResultDTO<String> ping() {
|
||||||
|
return ResultDTO.success("this is powerjob-server~");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ public class SystemInfoController {
|
|||||||
}
|
}
|
||||||
String server =appInfoOpt.get().getCurrentServer();
|
String server =appInfoOpt.get().getCurrentServer();
|
||||||
|
|
||||||
// 没有Server
|
// 没有 Server,说明从来没有该 appId 的 worker 集群连接过
|
||||||
if (StringUtils.isEmpty(server)) {
|
if (StringUtils.isEmpty(server)) {
|
||||||
return ResultDTO.success(Collections.emptyList());
|
return ResultDTO.success(Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package com.github.kfcfans.powerjob.server.web.request;
|
package com.github.kfcfans.powerjob.server.web.request;
|
||||||
|
|
||||||
|
import com.github.kfcfans.powerjob.common.utils.CommonUtils;
|
||||||
import com.github.kfcfans.powerjob.server.common.constans.ContainerSourceType;
|
import com.github.kfcfans.powerjob.server.common.constans.ContainerSourceType;
|
||||||
import com.github.kfcfans.powerjob.server.common.constans.ContainerStatus;
|
import com.github.kfcfans.powerjob.server.common.constans.SwitchableStatus;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,5 +29,11 @@ public class SaveContainerInfoRequest {
|
|||||||
private String sourceInfo;
|
private String sourceInfo;
|
||||||
|
|
||||||
// 状态,枚举值为 ContainerStatus(ENABLE/DISABLE)
|
// 状态,枚举值为 ContainerStatus(ENABLE/DISABLE)
|
||||||
private ContainerStatus status;
|
private SwitchableStatus status;
|
||||||
|
|
||||||
|
public void valid() {
|
||||||
|
CommonUtils.requireNonNull(containerName, "containerName can't be empty");
|
||||||
|
CommonUtils.requireNonNull(appId, "appId can't be empty");
|
||||||
|
CommonUtils.requireNonNull(sourceInfo, "sourceInfo can't be empty");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -256,7 +256,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _bar
|
|||||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: \"Navbar\",\n data: function data() {\n return {\n changeAppInfoDialogVisible: false,\n appInfo: {\n id: this.$store.state.appInfo.id,\n appName: this.$store.state.appInfo.appName,\n password: undefined,\n password2: undefined\n }\n };\n },\n methods: {\n // 退出当前应用\n onClickCloseConsole: function onClickCloseConsole() {\n window.localStorage.removeItem('oms_auto_login');\n this.$router.push(\"/\");\n },\n // 处理系统设置的指令时间\n handleSettings: function handleSettings(cmd) {\n switch (cmd) {\n case \"logout\":\n this.onClickCloseConsole();\n break;\n\n case \"changeAppInfo\":\n this.changeAppInfoDialogVisible = true;\n break;\n }\n },\n // 更新应用信息\n saveNewAppInfo: function saveNewAppInfo() {\n var _this = this;\n\n if (this.appInfo.password === this.appInfo.password2) {\n var that = this;\n this.axios.post(\"/appInfo/save\", this.appInfo).then(function () {\n that.$message.success(_this.$t('message.success'));\n that.$router.push(\"/\");\n }, function (e) {\n return that.$message.error(e);\n });\n }\n }\n }\n});\n\n//# sourceURL=webpack:///./src/components/bar/Navbar.vue?./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
|
eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: \"Navbar\",\n data: function data() {\n return {\n changeAppInfoDialogVisible: false,\n appInfo: {\n id: this.$store.state.appInfo.id,\n appName: this.$store.state.appInfo.appName,\n password: undefined,\n password2: undefined\n }\n };\n },\n methods: {\n // 退出当前应用\n onClickCloseConsole: function onClickCloseConsole() {\n window.localStorage.removeItem('oms_auto_login');\n this.$router.push(\"/\");\n },\n // 处理系统设置的指令时间\n handleSettings: function handleSettings(cmd) {\n switch (cmd) {\n case \"logout\":\n this.onClickCloseConsole();\n break;\n\n case \"changeAppInfo\":\n this.changeAppInfoDialogVisible = true;\n break;\n }\n },\n // 更新应用信息\n saveNewAppInfo: function saveNewAppInfo() {\n var _this = this;\n\n if (this.appInfo.password === this.appInfo.password2) {\n var that = this;\n this.axios.post(\"/appInfo/save\", this.appInfo).then(function () {\n that.$message.success(_this.$t('message.success'));\n that.$router.push(\"/\");\n }, function (e) {\n return that.$message.error(e);\n });\n } else {\n this.$message.error(\"the password doesn't match\");\n }\n }\n }\n});\n\n//# sourceURL=webpack:///./src/components/bar/Navbar.vue?./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options");
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
|
@ -10,12 +10,12 @@
|
|||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>powerjob-worker-agent</artifactId>
|
<artifactId>powerjob-worker-agent</artifactId>
|
||||||
<version>3.1.2</version>
|
<version>3.1.3</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<powerjob.worker.version>3.1.2</powerjob.worker.version>
|
<powerjob.worker.version>3.1.3</powerjob.worker.version>
|
||||||
<logback.version>1.2.3</logback.version>
|
<logback.version>1.2.3</logback.version>
|
||||||
<picocli.version>4.3.2</picocli.version>
|
<picocli.version>4.3.2</picocli.version>
|
||||||
|
|
||||||
|
@ -10,11 +10,11 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>powerjob-worker-samples</artifactId>
|
<artifactId>powerjob-worker-samples</artifactId>
|
||||||
<version>3.1.2</version>
|
<version>3.1.3</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<springboot.version>2.2.6.RELEASE</springboot.version>
|
<springboot.version>2.2.6.RELEASE</springboot.version>
|
||||||
<powerjob.worker.version>3.1.2</powerjob.worker.version>
|
<powerjob.worker.version>3.1.3</powerjob.worker.version>
|
||||||
<fastjson.version>1.2.68</fastjson.version>
|
<fastjson.version>1.2.68</fastjson.version>
|
||||||
|
|
||||||
<!-- 部署时跳过该module -->
|
<!-- 部署时跳过该module -->
|
||||||
|
@ -31,7 +31,7 @@ public class OhMySchedulerConfig {
|
|||||||
// 1. 创建配置文件
|
// 1. 创建配置文件
|
||||||
OhMyConfig config = new OhMyConfig();
|
OhMyConfig config = new OhMyConfig();
|
||||||
config.setPort(port);
|
config.setPort(port);
|
||||||
config.setAppName("oms-test");
|
config.setAppName("powerjob-agent-test");
|
||||||
config.setServerAddress(serverAddress);
|
config.setServerAddress(serverAddress);
|
||||||
// 如果没有大型 Map/MapReduce 的需求,建议使用内存来加速计算
|
// 如果没有大型 Map/MapReduce 的需求,建议使用内存来加速计算
|
||||||
// 为了本地模拟多个实例,只能使用 MEMORY 启动(文件只能由一个应用占有)
|
// 为了本地模拟多个实例,只能使用 MEMORY 启动(文件只能由一个应用占有)
|
||||||
|
@ -10,12 +10,12 @@
|
|||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>powerjob-worker</artifactId>
|
<artifactId>powerjob-worker</artifactId>
|
||||||
<version>3.1.2</version>
|
<version>3.1.3</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<spring.version>5.2.4.RELEASE</spring.version>
|
<spring.version>5.2.4.RELEASE</spring.version>
|
||||||
<powerjob.common.version>3.1.2</powerjob.common.version>
|
<powerjob.common.version>3.1.3</powerjob.common.version>
|
||||||
<h2.db.version>1.4.200</h2.db.version>
|
<h2.db.version>1.4.200</h2.db.version>
|
||||||
<hikaricp.version>3.4.2</hikaricp.version>
|
<hikaricp.version>3.4.2</hikaricp.version>
|
||||||
<junit.version>5.6.1</junit.version>
|
<junit.version>5.6.1</junit.version>
|
||||||
|
@ -59,9 +59,7 @@ public class ProcessorTrackerActor extends AbstractActor {
|
|||||||
|
|
||||||
Long instanceId = req.getInstanceId();
|
Long instanceId = req.getInstanceId();
|
||||||
List<ProcessorTracker> removedPts = ProcessorTrackerPool.removeProcessorTracker(instanceId);
|
List<ProcessorTracker> removedPts = ProcessorTrackerPool.removeProcessorTracker(instanceId);
|
||||||
if (CollectionUtils.isEmpty(removedPts)) {
|
if (!CollectionUtils.isEmpty(removedPts)) {
|
||||||
log.warn("[ProcessorTrackerActor] ProcessorTracker for instance(instanceId={}) already destroyed.", instanceId);
|
|
||||||
}else {
|
|
||||||
removedPts.forEach(ProcessorTracker::destroy);
|
removedPts.forEach(ProcessorTracker::destroy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package com.github.kfcfans.powerjob.worker.actors;
|
|||||||
|
|
||||||
import akka.actor.AbstractActor;
|
import akka.actor.AbstractActor;
|
||||||
import com.github.kfcfans.powerjob.common.request.ServerDeployContainerRequest;
|
import com.github.kfcfans.powerjob.common.request.ServerDeployContainerRequest;
|
||||||
|
import com.github.kfcfans.powerjob.common.request.ServerDestroyContainerRequest;
|
||||||
import com.github.kfcfans.powerjob.worker.container.OmsContainerFactory;
|
import com.github.kfcfans.powerjob.worker.container.OmsContainerFactory;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ public class WorkerActor extends AbstractActor {
|
|||||||
public Receive createReceive() {
|
public Receive createReceive() {
|
||||||
return receiveBuilder()
|
return receiveBuilder()
|
||||||
.match(ServerDeployContainerRequest.class, this::onReceiveServerDeployContainerRequest)
|
.match(ServerDeployContainerRequest.class, this::onReceiveServerDeployContainerRequest)
|
||||||
|
.match(ServerDestroyContainerRequest.class, this::onReceiveServerDestroyContainerRequest)
|
||||||
.matchAny(obj -> log.warn("[WorkerActor] receive unknown request: {}.", obj))
|
.matchAny(obj -> log.warn("[WorkerActor] receive unknown request: {}.", obj))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
@ -25,4 +27,8 @@ public class WorkerActor extends AbstractActor {
|
|||||||
private void onReceiveServerDeployContainerRequest(ServerDeployContainerRequest request) {
|
private void onReceiveServerDeployContainerRequest(ServerDeployContainerRequest request) {
|
||||||
OmsContainerFactory.deployContainer(request);
|
OmsContainerFactory.deployContainer(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onReceiveServerDestroyContainerRequest(ServerDestroyContainerRequest request) {
|
||||||
|
OmsContainerFactory.destroyContainer(request.getContainerId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,16 +39,21 @@ public class OmsContainerFactory {
|
|||||||
/**
|
/**
|
||||||
* 获取容器
|
* 获取容器
|
||||||
* @param containerId 容器ID
|
* @param containerId 容器ID
|
||||||
|
* @param loadFromServer 当本地不存在时尝试从 server 加载
|
||||||
* @return 容器示例,可能为 null
|
* @return 容器示例,可能为 null
|
||||||
*/
|
*/
|
||||||
public static OmsContainer getContainer(Long containerId) {
|
public static OmsContainer fetchContainer(Long containerId, boolean loadFromServer) {
|
||||||
|
|
||||||
OmsContainer omsContainer = CARGO.get(containerId);
|
OmsContainer omsContainer = CARGO.get(containerId);
|
||||||
if (omsContainer != null) {
|
if (omsContainer != null) {
|
||||||
return omsContainer;
|
return omsContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试下载
|
if (!loadFromServer) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试从 server 加载
|
||||||
log.info("[OmsContainer-{}] can't find the container in factory, try to deploy from server.", containerId);
|
log.info("[OmsContainer-{}] can't find the container in factory, try to deploy from server.", containerId);
|
||||||
WorkerNeedDeployContainerRequest request = new WorkerNeedDeployContainerRequest(containerId);
|
WorkerNeedDeployContainerRequest request = new WorkerNeedDeployContainerRequest(containerId);
|
||||||
|
|
||||||
@ -66,6 +71,8 @@ public class OmsContainerFactory {
|
|||||||
ServerDeployContainerRequest deployRequest = askResponse.getData(ServerDeployContainerRequest.class);
|
ServerDeployContainerRequest deployRequest = askResponse.getData(ServerDeployContainerRequest.class);
|
||||||
log.info("[OmsContainer-{}] fetch containerInfo from server successfully.", containerId);
|
log.info("[OmsContainer-{}] fetch containerInfo from server successfully.", containerId);
|
||||||
deployContainer(deployRequest);
|
deployContainer(deployRequest);
|
||||||
|
}else {
|
||||||
|
log.warn("[OmsContainer-{}] fetch containerInfo failed, reason is {}.", containerId, askResponse.getMessage());
|
||||||
}
|
}
|
||||||
}catch (Exception e) {
|
}catch (Exception e) {
|
||||||
log.error("[OmsContainer-{}] get container failed, exception is {}", containerId, e.toString());
|
log.error("[OmsContainer-{}] get container failed, exception is {}", containerId, e.toString());
|
||||||
@ -133,4 +140,21 @@ public class OmsContainerFactory {
|
|||||||
CARGO.forEach((name, container) -> info.add(new DeployedContainerInfo(container.getContainerId(), container.getVersion(), container.getDeployedTime(), null)));
|
CARGO.forEach((name, container) -> info.add(new DeployedContainerInfo(container.getContainerId(), container.getVersion(), container.getDeployedTime(), null)));
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁指定容器
|
||||||
|
* @param containerId 容器ID
|
||||||
|
*/
|
||||||
|
public static void destroyContainer(Long containerId) {
|
||||||
|
OmsContainer container = CARGO.remove(containerId);
|
||||||
|
if (container == null) {
|
||||||
|
log.info("[OmsContainer-{}] container not exists, so there is no need to destroy the container.", containerId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
container.destroy();
|
||||||
|
}catch (Exception e) {
|
||||||
|
log.warn("[OmsContainer-{}] destroy container failed.", containerId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,7 +190,7 @@ public class OmsJarContainer implements OmsContainer {
|
|||||||
// 需要满足的条件:引用计数器减为0 & 有更新的容器出现
|
// 需要满足的条件:引用计数器减为0 & 有更新的容器出现
|
||||||
if (referenceCount.decrementAndGet() <= 0) {
|
if (referenceCount.decrementAndGet() <= 0) {
|
||||||
|
|
||||||
OmsContainer container = OmsContainerFactory.getContainer(containerId);
|
OmsContainer container = OmsContainerFactory.fetchContainer(containerId, false);
|
||||||
if (container != this) {
|
if (container != this) {
|
||||||
try {
|
try {
|
||||||
destroy();
|
destroy();
|
||||||
|
@ -65,7 +65,7 @@ public class ProcessorTracker {
|
|||||||
|
|
||||||
private static final int THREAD_POOL_QUEUE_MAX_SIZE = 100;
|
private static final int THREAD_POOL_QUEUE_MAX_SIZE = 100;
|
||||||
// 长时间空闲的 ProcessorTracker 会发起销毁请求
|
// 长时间空闲的 ProcessorTracker 会发起销毁请求
|
||||||
private static final long MAX_IDLE_TIME = 120000;
|
private static final long MAX_IDLE_TIME = 300000;
|
||||||
|
|
||||||
// 当 ProcessorTracker 出现根本性错误(比如 Processor 创建失败,所有的任务直接失败)
|
// 当 ProcessorTracker 出现根本性错误(比如 Processor 创建失败,所有的任务直接失败)
|
||||||
private boolean lethal = false;
|
private boolean lethal = false;
|
||||||
@ -179,7 +179,7 @@ public class ProcessorTracker {
|
|||||||
statusReportRetryQueue.clear();
|
statusReportRetryQueue.clear();
|
||||||
ProcessorTrackerPool.removeProcessorTracker(instanceId);
|
ProcessorTrackerPool.removeProcessorTracker(instanceId);
|
||||||
|
|
||||||
log.info("[ProcessorTracker-{}] ProcessorTracker already destroyed!", instanceId);
|
log.info("[ProcessorTracker-{}] ProcessorTracker destroyed successfully!", instanceId);
|
||||||
|
|
||||||
// 3. 关闭定时线程池
|
// 3. 关闭定时线程池
|
||||||
CommonUtils.executeIgnoreException(() -> timingPool.shutdownNow());
|
CommonUtils.executeIgnoreException(() -> timingPool.shutdownNow());
|
||||||
@ -309,9 +309,11 @@ public class ProcessorTracker {
|
|||||||
String[] split = processorInfo.split("#");
|
String[] split = processorInfo.split("#");
|
||||||
log.info("[ProcessorTracker-{}] try to load processor({}) in container({})", instanceId, split[1], split[0]);
|
log.info("[ProcessorTracker-{}] try to load processor({}) in container({})", instanceId, split[1], split[0]);
|
||||||
|
|
||||||
omsContainer = OmsContainerFactory.getContainer(Long.valueOf(split[0]));
|
omsContainer = OmsContainerFactory.fetchContainer(Long.valueOf(split[0]), true);
|
||||||
if (omsContainer != null) {
|
if (omsContainer != null) {
|
||||||
processor = omsContainer.getProcessor(split[1]);
|
processor = omsContainer.getProcessor(split[1]);
|
||||||
|
}else {
|
||||||
|
log.warn("[ProcessorTracker-{}] load container failed.", instanceId);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user