mirror of
https://github.com/PowerJob/PowerJob.git
synced 2025-07-17 00:00:04 +08:00
prepare to go to my brother's birthday party
This commit is contained in:
parent
e99292f027
commit
a42bc496df
@ -24,6 +24,7 @@ public class RemoteConstant {
|
|||||||
public static final String SERVER_ACTOR_SYSTEM_NAME = "oms-server";
|
public static final String SERVER_ACTOR_SYSTEM_NAME = "oms-server";
|
||||||
|
|
||||||
public static final String SERVER_ACTOR_NAME = "server_actor";
|
public static final String SERVER_ACTOR_NAME = "server_actor";
|
||||||
|
public static final String SERVER_FRIEND_ACTOR_NAME = "friend_actor";
|
||||||
public static final String SERVER_AKKA_CONFIG_NAME = "oms-server.akka.conf";
|
public static final String SERVER_AKKA_CONFIG_NAME = "oms-server.akka.conf";
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,4 +65,7 @@ public class ServerScheduleJobReq implements Serializable {
|
|||||||
// 时间表达式,CRON/NULL/LONG/LONG(单位MS)
|
// 时间表达式,CRON/NULL/LONG/LONG(单位MS)
|
||||||
private String timeExpression;
|
private String timeExpression;
|
||||||
|
|
||||||
|
// 最大同时运行任务数,默认 1
|
||||||
|
private Integer maxInstanceNum;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.github.kfcfans.common.request;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务器要求任务实例停止执行请求
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/4/9
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class ServerStopInstanceReq implements Serializable {
|
||||||
|
private Long instanceId;
|
||||||
|
}
|
@ -26,4 +26,5 @@ public class TaskTrackerReportInstanceStatusReq implements Serializable {
|
|||||||
private long failedTaskNum;
|
private long failedTaskNum;
|
||||||
|
|
||||||
private long reportTime;
|
private long reportTime;
|
||||||
|
private String sourceAddress;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package com.github.kfcfans.oms.server;
|
package com.github.kfcfans.oms.server;
|
||||||
|
|
||||||
import com.github.kfcfans.oms.server.core.akka.OhMyServer;
|
import com.github.kfcfans.oms.server.akka.OhMyServer;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package com.github.kfcfans.oms.server.core.akka;
|
package com.github.kfcfans.oms.server.akka;
|
||||||
|
|
||||||
import akka.actor.ActorSelection;
|
import akka.actor.ActorSelection;
|
||||||
import akka.actor.ActorSystem;
|
import akka.actor.ActorSystem;
|
||||||
import akka.actor.Props;
|
import akka.actor.Props;
|
||||||
import com.github.kfcfans.common.RemoteConstant;
|
import com.github.kfcfans.common.RemoteConstant;
|
||||||
import com.github.kfcfans.common.utils.NetUtils;
|
import com.github.kfcfans.common.utils.NetUtils;
|
||||||
|
import com.github.kfcfans.oms.server.akka.actors.FriendActor;
|
||||||
|
import com.github.kfcfans.oms.server.akka.actors.ServerActor;
|
||||||
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.typesafe.config.Config;
|
import com.typesafe.config.Config;
|
||||||
@ -47,6 +49,7 @@ public class OhMyServer {
|
|||||||
actorSystem = ActorSystem.create(RemoteConstant.SERVER_ACTOR_SYSTEM_NAME, akkaFinalConfig);
|
actorSystem = ActorSystem.create(RemoteConstant.SERVER_ACTOR_SYSTEM_NAME, akkaFinalConfig);
|
||||||
|
|
||||||
actorSystem.actorOf(Props.create(ServerActor.class), RemoteConstant.SERVER_ACTOR_NAME);
|
actorSystem.actorOf(Props.create(ServerActor.class), RemoteConstant.SERVER_ACTOR_NAME);
|
||||||
|
actorSystem.actorOf(Props.create(FriendActor.class), RemoteConstant.SERVER_FRIEND_ACTOR_NAME);
|
||||||
|
|
||||||
log.info("[OhMyServer] OhMyServer's akka system start successfully, using time {}.", stopwatch);
|
log.info("[OhMyServer] OhMyServer's akka system start successfully, using time {}.", stopwatch);
|
||||||
}
|
}
|
||||||
@ -57,7 +60,7 @@ public class OhMyServer {
|
|||||||
* @return ActorSelection
|
* @return ActorSelection
|
||||||
*/
|
*/
|
||||||
public static ActorSelection getServerActor(String address) {
|
public static ActorSelection getServerActor(String address) {
|
||||||
String path = String.format(AKKA_PATH, RemoteConstant.SERVER_ACTOR_SYSTEM_NAME, address, RemoteConstant.SERVER_ACTOR_NAME);
|
String path = String.format(AKKA_PATH, RemoteConstant.SERVER_ACTOR_SYSTEM_NAME, address, RemoteConstant.SERVER_FRIEND_ACTOR_NAME);
|
||||||
return actorSystem.actorSelection(path);
|
return actorSystem.actorSelection(path);
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
|||||||
|
package com.github.kfcfans.oms.server.akka.actors;
|
||||||
|
|
||||||
|
import akka.actor.AbstractActor;
|
||||||
|
import com.github.kfcfans.common.request.TaskTrackerReportInstanceStatusReq;
|
||||||
|
import com.github.kfcfans.common.request.WorkerHeartbeat;
|
||||||
|
import com.github.kfcfans.common.response.AskResponse;
|
||||||
|
import com.github.kfcfans.oms.server.akka.OhMyServer;
|
||||||
|
import com.github.kfcfans.oms.server.akka.requests.Ping;
|
||||||
|
import com.github.kfcfans.oms.server.akka.requests.RedirectServerStopInstanceReq;
|
||||||
|
import com.github.kfcfans.oms.server.service.ha.WorkerManagerService;
|
||||||
|
import com.github.kfcfans.oms.server.service.instance.InstanceManager;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理朋友们的信息(处理服务器与服务器之间的通讯)
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/4/9
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class FriendActor extends AbstractActor {
|
||||||
|
@Override
|
||||||
|
public Receive createReceive() {
|
||||||
|
return receiveBuilder()
|
||||||
|
.match(Ping.class, this::onReceivePing)
|
||||||
|
.match(RedirectServerStopInstanceReq.class, this::onReceiveRedirectServerStopInstanceReq)
|
||||||
|
.matchAny(obj -> log.warn("[ServerActor] receive unknown request: {}.", obj))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理存活检测的请求
|
||||||
|
* @param ping 存活检测请求
|
||||||
|
*/
|
||||||
|
private void onReceivePing(Ping ping) {
|
||||||
|
AskResponse askResponse = new AskResponse();
|
||||||
|
askResponse.setSuccess(true);
|
||||||
|
askResponse.setExtra(System.currentTimeMillis() - ping.getCurrentTime());
|
||||||
|
getSender().tell(askResponse, getSelf());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理停止任务实例的请求
|
||||||
|
* @param req 停止运行任务实例
|
||||||
|
*/
|
||||||
|
private void onReceiveRedirectServerStopInstanceReq(RedirectServerStopInstanceReq req) {
|
||||||
|
|
||||||
|
Long instanceId = req.getServerStopInstanceReq().getInstanceId();
|
||||||
|
String taskTrackerAddress = InstanceManager.getTaskTrackerAddress(instanceId);
|
||||||
|
|
||||||
|
// 非空,发请求停止任务实例
|
||||||
|
if (StringUtils.isNotEmpty(taskTrackerAddress)) {
|
||||||
|
OhMyServer.getTaskTrackerActor(taskTrackerAddress).tell(req.getServerStopInstanceReq(), getSelf());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 空,可能刚经历 Server 变更 或 TaskTracker 宕机。先忽略吧,打条日志压压惊
|
||||||
|
log.warn("[FriendActor] can't find TaskTracker's address for instance(instanceId={}), so stop instance may fail.", instanceId);
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,11 @@
|
|||||||
package com.github.kfcfans.oms.server.core.akka;
|
package com.github.kfcfans.oms.server.akka.actors;
|
||||||
|
|
||||||
import akka.actor.AbstractActor;
|
import akka.actor.AbstractActor;
|
||||||
import com.github.kfcfans.common.request.TaskTrackerReportInstanceStatusReq;
|
import com.github.kfcfans.common.request.TaskTrackerReportInstanceStatusReq;
|
||||||
import com.github.kfcfans.common.request.WorkerHeartbeat;
|
import com.github.kfcfans.common.request.WorkerHeartbeat;
|
||||||
import com.github.kfcfans.common.response.AskResponse;
|
import com.github.kfcfans.common.response.AskResponse;
|
||||||
import com.github.kfcfans.oms.server.core.InstanceManager;
|
import com.github.kfcfans.oms.server.akka.requests.Ping;
|
||||||
|
import com.github.kfcfans.oms.server.service.instance.InstanceManager;
|
||||||
import com.github.kfcfans.oms.server.service.ha.WorkerManagerService;
|
import com.github.kfcfans.oms.server.service.ha.WorkerManagerService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@ -22,21 +23,11 @@ public class ServerActor extends AbstractActor {
|
|||||||
return receiveBuilder()
|
return receiveBuilder()
|
||||||
.match(WorkerHeartbeat.class, this::onReceiveWorkerHeartbeat)
|
.match(WorkerHeartbeat.class, this::onReceiveWorkerHeartbeat)
|
||||||
.match(TaskTrackerReportInstanceStatusReq.class, this::onReceiveTaskTrackerReportInstanceStatusReq)
|
.match(TaskTrackerReportInstanceStatusReq.class, this::onReceiveTaskTrackerReportInstanceStatusReq)
|
||||||
.match(Ping.class, this::onReceivePing)
|
|
||||||
.matchAny(obj -> log.warn("[ServerActor] receive unknown request: {}.", obj))
|
.matchAny(obj -> log.warn("[ServerActor] receive unknown request: {}.", obj))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理存活检测的请求
|
|
||||||
* @param ping 存活检测请求
|
|
||||||
*/
|
|
||||||
private void onReceivePing(Ping ping) {
|
|
||||||
AskResponse askResponse = new AskResponse();
|
|
||||||
askResponse.setSuccess(true);
|
|
||||||
askResponse.setExtra(System.currentTimeMillis() - ping.getCurrentTime());
|
|
||||||
getSender().tell(askResponse, getSelf());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理 Worker 的心跳请求
|
* 处理 Worker 的心跳请求
|
@ -1,4 +1,4 @@
|
|||||||
package com.github.kfcfans.oms.server.core.akka;
|
package com.github.kfcfans.oms.server.akka.requests;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.github.kfcfans.oms.server.akka.requests;
|
||||||
|
|
||||||
|
import com.github.kfcfans.common.request.ServerStopInstanceReq;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重定向 ServerStopInstanceReq
|
||||||
|
* 被HTTP请求停止任务实例的机器需要将请求转发到该实例对应的Server上处理,由该Server下发到Worker(只有该Server持有Worker的地址信息)
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/4/9
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class RedirectServerStopInstanceReq implements Serializable {
|
||||||
|
private ServerStopInstanceReq serverStopInstanceReq;
|
||||||
|
}
|
@ -19,4 +19,12 @@ public enum JobStatus {
|
|||||||
|
|
||||||
private int v;
|
private int v;
|
||||||
|
|
||||||
|
public static JobStatus of(int v) {
|
||||||
|
for (JobStatus type : values()) {
|
||||||
|
if (type.v == v) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("unknown JobStatus of " + v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ public class ExecuteLogDO {
|
|||||||
// 结束时间
|
// 结束时间
|
||||||
private Long finishedTime;
|
private Long finishedTime;
|
||||||
|
|
||||||
// 总共执行的次数(CRON任务 -> 代表重试次数,FREQUENT -> 代表总执行次数)
|
// 总共执行的次数(用于重试判断)
|
||||||
private Long runningTimes;
|
private Long runningTimes;
|
||||||
|
|
||||||
private Date gmtCreate;
|
private Date gmtCreate;
|
||||||
|
@ -5,7 +5,7 @@ import com.github.kfcfans.common.ExecuteType;
|
|||||||
import com.github.kfcfans.common.ProcessorType;
|
import com.github.kfcfans.common.ProcessorType;
|
||||||
import com.github.kfcfans.common.TimeExpressionType;
|
import com.github.kfcfans.common.TimeExpressionType;
|
||||||
import com.github.kfcfans.common.request.ServerScheduleJobReq;
|
import com.github.kfcfans.common.request.ServerScheduleJobReq;
|
||||||
import com.github.kfcfans.oms.server.core.akka.OhMyServer;
|
import com.github.kfcfans.oms.server.akka.OhMyServer;
|
||||||
import com.github.kfcfans.oms.server.persistence.model.JobInfoDO;
|
import com.github.kfcfans.oms.server.persistence.model.JobInfoDO;
|
||||||
import com.github.kfcfans.oms.server.persistence.repository.ExecuteLogRepository;
|
import com.github.kfcfans.oms.server.persistence.repository.ExecuteLogRepository;
|
||||||
import com.github.kfcfans.oms.server.service.ha.WorkerManagerService;
|
import com.github.kfcfans.oms.server.service.ha.WorkerManagerService;
|
||||||
|
@ -3,8 +3,8 @@ package com.github.kfcfans.oms.server.service.ha;
|
|||||||
import akka.actor.ActorSelection;
|
import akka.actor.ActorSelection;
|
||||||
import akka.pattern.Patterns;
|
import akka.pattern.Patterns;
|
||||||
import com.github.kfcfans.common.response.AskResponse;
|
import com.github.kfcfans.common.response.AskResponse;
|
||||||
import com.github.kfcfans.oms.server.core.akka.OhMyServer;
|
import com.github.kfcfans.oms.server.akka.OhMyServer;
|
||||||
import com.github.kfcfans.oms.server.core.akka.Ping;
|
import com.github.kfcfans.oms.server.akka.requests.Ping;
|
||||||
import com.github.kfcfans.oms.server.persistence.model.AppInfoDO;
|
import com.github.kfcfans.oms.server.persistence.model.AppInfoDO;
|
||||||
import com.github.kfcfans.oms.server.persistence.repository.AppInfoRepository;
|
import com.github.kfcfans.oms.server.persistence.repository.AppInfoRepository;
|
||||||
import com.github.kfcfans.oms.server.service.lock.LockService;
|
import com.github.kfcfans.oms.server.service.lock.LockService;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package com.github.kfcfans.oms.server.core;
|
package com.github.kfcfans.oms.server.service.instance;
|
||||||
|
|
||||||
import com.github.kfcfans.common.InstanceStatus;
|
import com.github.kfcfans.common.InstanceStatus;
|
||||||
import com.github.kfcfans.common.request.TaskTrackerReportInstanceStatusReq;
|
import com.github.kfcfans.common.request.TaskTrackerReportInstanceStatusReq;
|
||||||
@ -119,11 +119,25 @@ public class InstanceManager {
|
|||||||
|
|
||||||
// 清除已完成的实例信息
|
// 清除已完成的实例信息
|
||||||
if (finished) {
|
if (finished) {
|
||||||
instanceId2JobInfo.remove(instanceId);
|
|
||||||
instanceId2StatusHolder.remove(instanceId);
|
instanceId2StatusHolder.remove(instanceId);
|
||||||
|
// 这一步也可能导致后面取不到 JobInfoDO
|
||||||
|
instanceId2JobInfo.remove(instanceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取某个任务实例对应的 TaskTracker 地址
|
||||||
|
* @param instanceId 任务实例ID
|
||||||
|
* @return TaskTracker地址,IP:Port
|
||||||
|
*/
|
||||||
|
public static String getTaskTrackerAddress(Long instanceId) {
|
||||||
|
InstanceStatusHolder statusHolder = instanceId2StatusHolder.get(instanceId);
|
||||||
|
if (statusHolder == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return statusHolder.getSourceAddress();
|
||||||
|
}
|
||||||
|
|
||||||
private static ExecuteLogRepository getExecuteLogRepository() {
|
private static ExecuteLogRepository getExecuteLogRepository() {
|
||||||
while (executeLogRepository == null) {
|
while (executeLogRepository == null) {
|
||||||
try {
|
try {
|
@ -1,4 +1,4 @@
|
|||||||
package com.github.kfcfans.oms.server.core;
|
package com.github.kfcfans.oms.server.service.instance;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@ -22,4 +22,6 @@ public class InstanceStatusHolder {
|
|||||||
|
|
||||||
// 上次上报时间
|
// 上次上报时间
|
||||||
private long lastReportTime;
|
private long lastReportTime;
|
||||||
|
// 源地址(TaskTracker 地址)
|
||||||
|
private String sourceAddress;
|
||||||
}
|
}
|
@ -1,7 +1,9 @@
|
|||||||
package com.github.kfcfans.oms.server.service.timing;
|
package com.github.kfcfans.oms.server.service.timing;
|
||||||
|
|
||||||
import com.github.kfcfans.common.InstanceStatus;
|
import com.github.kfcfans.common.InstanceStatus;
|
||||||
import com.github.kfcfans.oms.server.core.akka.OhMyServer;
|
import com.github.kfcfans.common.TimeExpressionType;
|
||||||
|
import com.github.kfcfans.oms.server.common.constans.JobStatus;
|
||||||
|
import com.github.kfcfans.oms.server.akka.OhMyServer;
|
||||||
import com.github.kfcfans.oms.server.persistence.model.AppInfoDO;
|
import com.github.kfcfans.oms.server.persistence.model.AppInfoDO;
|
||||||
import com.github.kfcfans.oms.server.persistence.model.ExecuteLogDO;
|
import com.github.kfcfans.oms.server.persistence.model.ExecuteLogDO;
|
||||||
import com.github.kfcfans.oms.server.persistence.model.JobInfoDO;
|
import com.github.kfcfans.oms.server.persistence.model.JobInfoDO;
|
||||||
@ -98,12 +100,40 @@ public class InstanceStatusCheckService {
|
|||||||
if (!CollectionUtils.isEmpty(failedInstances)) {
|
if (!CollectionUtils.isEmpty(failedInstances)) {
|
||||||
log.warn("[InstanceStatusCheckService] instances({}) has not received status report for a long time.", failedInstances);
|
log.warn("[InstanceStatusCheckService] instances({}) has not received status report for a long time.", failedInstances);
|
||||||
failedInstances.forEach(instance -> {
|
failedInstances.forEach(instance -> {
|
||||||
// 重新派发
|
|
||||||
JobInfoDO jobInfoDO = jobInfoRepository.findById(instance.getJobId()).orElseGet(JobInfoDO::new);
|
JobInfoDO jobInfoDO = jobInfoRepository.findById(instance.getJobId()).orElseGet(JobInfoDO::new);
|
||||||
dispatchService.dispatch(jobInfoDO, instance.getInstanceId(), instance.getRunningTimes());
|
TimeExpressionType timeExpressionType = TimeExpressionType.of(jobInfoDO.getTimeExpressionType());
|
||||||
|
JobStatus jobStatus = JobStatus.of(jobInfoDO.getStatus());
|
||||||
|
|
||||||
|
// 如果任务已关闭,则不进行重试,将任务置为失败即可
|
||||||
|
if (jobStatus != JobStatus.ENABLE) {
|
||||||
|
updateFailedInstance(instance);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 秒级任务,无限重试,直接派发
|
||||||
|
if (timeExpressionType == TimeExpressionType.FIX_RATE || timeExpressionType == TimeExpressionType.FIX_DELAY) {
|
||||||
|
dispatchService.dispatch(jobInfoDO, instance.getInstanceId(), instance.getRunningTimes());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRON 和 API一样,失败次数 + 1,根据重试配置进行重试
|
||||||
|
if (instance.getRunningTimes() > jobInfoDO.getInstanceRetryNum()) {
|
||||||
|
dispatchService.dispatch(jobInfoDO, instance.getInstanceId(), instance.getRunningTimes());
|
||||||
|
}else {
|
||||||
|
updateFailedInstance(instance);
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateFailedInstance(ExecuteLogDO instance) {
|
||||||
|
instance.setStatus(InstanceStatus.FAILED.getV());
|
||||||
|
instance.setFinishedTime(System.currentTimeMillis());
|
||||||
|
instance.setGmtModified(new Date());
|
||||||
|
instance.setResult("worker report timeout, maybe all worker down");
|
||||||
|
executeLogRepository.saveAndFlush(instance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ import com.github.kfcfans.common.InstanceStatus;
|
|||||||
import com.github.kfcfans.oms.server.common.constans.JobStatus;
|
import com.github.kfcfans.oms.server.common.constans.JobStatus;
|
||||||
import com.github.kfcfans.common.TimeExpressionType;
|
import com.github.kfcfans.common.TimeExpressionType;
|
||||||
import com.github.kfcfans.oms.server.common.utils.CronExpression;
|
import com.github.kfcfans.oms.server.common.utils.CronExpression;
|
||||||
import com.github.kfcfans.oms.server.core.InstanceManager;
|
import com.github.kfcfans.oms.server.service.instance.InstanceManager;
|
||||||
import com.github.kfcfans.oms.server.core.akka.OhMyServer;
|
import com.github.kfcfans.oms.server.akka.OhMyServer;
|
||||||
import com.github.kfcfans.oms.server.persistence.model.AppInfoDO;
|
import com.github.kfcfans.oms.server.persistence.model.AppInfoDO;
|
||||||
import com.github.kfcfans.oms.server.persistence.model.ExecuteLogDO;
|
import com.github.kfcfans.oms.server.persistence.model.ExecuteLogDO;
|
||||||
import com.github.kfcfans.oms.server.persistence.model.JobInfoDO;
|
import com.github.kfcfans.oms.server.persistence.model.JobInfoDO;
|
||||||
@ -29,11 +29,11 @@ import java.util.Date;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 任务调度执行服务
|
* 任务调度执行服务(调度 CRON 表达式的任务进行执行)
|
||||||
|
* FIX_RATE和FIX_DELAY任务不需要被调度,创建后直接被派发到Worker执行,只需要失败重试机制(在InstanceStatusCheckService中完成)
|
||||||
*
|
*
|
||||||
* @author tjq
|
* @author tjq
|
||||||
* @since 2020/4/5
|
* @since 2020/4/5
|
||||||
@ -75,13 +75,6 @@ public class JobScheduleService {
|
|||||||
}catch (Exception e) {
|
}catch (Exception e) {
|
||||||
log.error("[JobScheduleService] schedule cron job failed.", e);
|
log.error("[JobScheduleService] schedule cron job failed.", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调度 FIX_RATE 和 FIX_DELAY JOB
|
|
||||||
try {
|
|
||||||
scheduleFrequentJob(allAppIds);
|
|
||||||
}catch (Exception e) {
|
|
||||||
log.error("[JobScheduleService] schedule frequent job failed.", e);
|
|
||||||
}
|
|
||||||
log.info("[JobScheduleService] finished job schedule, using time {}.", stopwatch.stop());
|
log.info("[JobScheduleService] finished job schedule, using time {}.", stopwatch.stop());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,40 +170,4 @@ public class JobScheduleService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 调度 FIX_RATE 和 FIX_DELAY 的任务
|
|
||||||
*/
|
|
||||||
private void scheduleFrequentJob(List<Long> appIds) {
|
|
||||||
|
|
||||||
List<JobInfoDO> fixDelayJobs = jobInfoRepository.findByAppIdInAndStatusAndTimeExpressionType(appIds, JobStatus.ENABLE.getV(), TimeExpressionType.FIX_DELAY.getV());
|
|
||||||
List<JobInfoDO> fixRateJobs = jobInfoRepository.findByAppIdInAndStatusAndTimeExpressionType(appIds, JobStatus.ENABLE.getV(), TimeExpressionType.FIX_RATE.getV());
|
|
||||||
|
|
||||||
List<Long> jobIds = Lists.newLinkedList();
|
|
||||||
Map<Long, JobInfoDO> jobId2JobInfo = Maps.newHashMap();
|
|
||||||
Consumer<JobInfoDO> consumer = jobInfo -> {
|
|
||||||
jobIds.add(jobInfo.getId());
|
|
||||||
jobId2JobInfo.put(jobInfo.getId(), jobInfo);
|
|
||||||
};
|
|
||||||
fixDelayJobs.forEach(consumer);
|
|
||||||
fixRateJobs.forEach(consumer);
|
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(jobIds)) {
|
|
||||||
log.info("[JobScheduleService] no frequent job need to schedule for appIds in {}.", appIds);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询 ExecuteLog 表,不存在或非运行状态则重新调度
|
|
||||||
List<ExecuteLogDO> executeLogDOS = executeLogRepository.findByJobIdIn(jobIds);
|
|
||||||
executeLogDOS.forEach(executeLogDO -> {
|
|
||||||
if (executeLogDO.getStatus() == InstanceStatus.RUNNING.getV()) {
|
|
||||||
jobId2JobInfo.remove(executeLogDO.getJobId());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 重新Dispatch
|
|
||||||
jobId2JobInfo.values().forEach(jobInfoDO -> {
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.github.kfcfans.oms.server.web.controller;
|
||||||
|
|
||||||
|
import akka.actor.ActorSelection;
|
||||||
|
import com.github.kfcfans.common.request.ServerStopInstanceReq;
|
||||||
|
import com.github.kfcfans.common.response.ResultDTO;
|
||||||
|
import com.github.kfcfans.oms.server.akka.OhMyServer;
|
||||||
|
import com.github.kfcfans.oms.server.akka.requests.RedirectServerStopInstanceReq;
|
||||||
|
import com.github.kfcfans.oms.server.persistence.model.AppInfoDO;
|
||||||
|
import com.github.kfcfans.oms.server.persistence.model.ExecuteLogDO;
|
||||||
|
import com.github.kfcfans.oms.server.persistence.model.JobInfoDO;
|
||||||
|
import com.github.kfcfans.oms.server.persistence.repository.AppInfoRepository;
|
||||||
|
import com.github.kfcfans.oms.server.persistence.repository.ExecuteLogRepository;
|
||||||
|
import com.github.kfcfans.oms.server.persistence.repository.JobInfoRepository;
|
||||||
|
import com.github.kfcfans.oms.server.service.ha.ServerSelectService;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务实例 Controller
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/4/9
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/instance")
|
||||||
|
public class InstanceController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ExecuteLogRepository executeLogRepository;
|
||||||
|
@Resource
|
||||||
|
private JobInfoRepository jobInfoRepository;
|
||||||
|
@Resource
|
||||||
|
private AppInfoRepository appInfoRepository;
|
||||||
|
|
||||||
|
@GetMapping("/stop")
|
||||||
|
public ResultDTO<Void> stopInstance(Long instanceId) throws Exception {
|
||||||
|
|
||||||
|
// 联级查询:instanceId -> jobId -> appId -> serverAddress
|
||||||
|
ExecuteLogDO executeLogDO = executeLogRepository.findByInstanceId(instanceId);
|
||||||
|
if (executeLogDO == null) {
|
||||||
|
return ResultDTO.failed("invalid instanceId: " + instanceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
JobInfoDO jobInfoDO = jobInfoRepository.findById(executeLogDO.getJobId()).orElseThrow(() -> {
|
||||||
|
throw new RuntimeException("impossible");
|
||||||
|
});
|
||||||
|
|
||||||
|
AppInfoDO appInfoDO = appInfoRepository.findById(jobInfoDO.getAppId()).orElseThrow(() -> {
|
||||||
|
throw new RuntimeException("impossible");
|
||||||
|
});
|
||||||
|
|
||||||
|
String serverAddress = appInfoDO.getCurrentServer();
|
||||||
|
|
||||||
|
// 将请求转发给目标Server(HTTP -> AKKA)
|
||||||
|
ActorSelection serverActor = OhMyServer.getServerActor(serverAddress);
|
||||||
|
RedirectServerStopInstanceReq req = new RedirectServerStopInstanceReq();
|
||||||
|
req.setServerStopInstanceReq(new ServerStopInstanceReq(instanceId));
|
||||||
|
serverActor.tell(req, null);
|
||||||
|
|
||||||
|
return ResultDTO.success(null);
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,18 @@
|
|||||||
package com.github.kfcfans.oms.server.web.controller;
|
package com.github.kfcfans.oms.server.web.controller;
|
||||||
|
|
||||||
import com.github.kfcfans.common.ExecuteType;
|
import com.github.kfcfans.common.ExecuteType;
|
||||||
|
import com.github.kfcfans.common.InstanceStatus;
|
||||||
import com.github.kfcfans.common.ProcessorType;
|
import com.github.kfcfans.common.ProcessorType;
|
||||||
import com.github.kfcfans.common.TimeExpressionType;
|
import com.github.kfcfans.common.TimeExpressionType;
|
||||||
|
import com.github.kfcfans.oms.server.common.constans.JobStatus;
|
||||||
import com.github.kfcfans.oms.server.common.utils.CronExpression;
|
import com.github.kfcfans.oms.server.common.utils.CronExpression;
|
||||||
|
import com.github.kfcfans.oms.server.persistence.model.ExecuteLogDO;
|
||||||
|
import com.github.kfcfans.oms.server.persistence.repository.ExecuteLogRepository;
|
||||||
import com.github.kfcfans.oms.server.persistence.repository.JobInfoRepository;
|
import com.github.kfcfans.oms.server.persistence.repository.JobInfoRepository;
|
||||||
import com.github.kfcfans.common.response.ResultDTO;
|
import com.github.kfcfans.common.response.ResultDTO;
|
||||||
import com.github.kfcfans.oms.server.persistence.model.JobInfoDO;
|
import com.github.kfcfans.oms.server.persistence.model.JobInfoDO;
|
||||||
|
import com.github.kfcfans.oms.server.service.DispatchService;
|
||||||
|
import com.github.kfcfans.oms.server.service.IdGenerateService;
|
||||||
import com.github.kfcfans.oms.server.web.request.ModifyJobInfoRequest;
|
import com.github.kfcfans.oms.server.web.request.ModifyJobInfoRequest;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@ -16,6 +22,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 任务信息管理 Controller
|
* 任务信息管理 Controller
|
||||||
@ -23,12 +30,16 @@ import java.util.Date;
|
|||||||
* @author tjq
|
* @author tjq
|
||||||
* @since 2020/3/30
|
* @since 2020/3/30
|
||||||
*/
|
*/
|
||||||
@RestController()
|
@RestController
|
||||||
@RequestMapping("job")
|
@RequestMapping("job")
|
||||||
public class JobController {
|
public class JobController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private DispatchService dispatchService;
|
||||||
@Resource
|
@Resource
|
||||||
private JobInfoRepository jobInfoRepository;
|
private JobInfoRepository jobInfoRepository;
|
||||||
|
@Resource
|
||||||
|
private ExecuteLogRepository executeLogRepository;
|
||||||
|
|
||||||
@PostMapping("/save")
|
@PostMapping("/save")
|
||||||
public ResultDTO<Void> saveJobInfo(ModifyJobInfoRequest request) throws Exception {
|
public ResultDTO<Void> saveJobInfo(ModifyJobInfoRequest request) throws Exception {
|
||||||
@ -54,13 +65,46 @@ public class JobController {
|
|||||||
}
|
}
|
||||||
jobInfoDO.setGmtModified(now);
|
jobInfoDO.setGmtModified(now);
|
||||||
jobInfoRepository.saveAndFlush(jobInfoDO);
|
jobInfoRepository.saveAndFlush(jobInfoDO);
|
||||||
|
|
||||||
|
// 秒级任务直接调度执行
|
||||||
|
if (timeExpressionType == TimeExpressionType.FIX_RATE || timeExpressionType == TimeExpressionType.FIX_DELAY) {
|
||||||
|
|
||||||
|
ExecuteLogDO executeLog = new ExecuteLogDO();
|
||||||
|
executeLog.setJobId(jobInfoDO.getId());
|
||||||
|
executeLog.setInstanceId(IdGenerateService.allocate());
|
||||||
|
executeLog.setStatus(InstanceStatus.WAITING_DISPATCH.getV());
|
||||||
|
executeLog.setExpectedTriggerTime(System.currentTimeMillis());
|
||||||
|
executeLog.setGmtCreate(new Date());
|
||||||
|
executeLog.setGmtModified(executeLog.getGmtCreate());
|
||||||
|
|
||||||
|
executeLogRepository.saveAndFlush(executeLog);
|
||||||
|
dispatchService.dispatch(jobInfoDO, executeLog.getInstanceId(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultDTO.success(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/stop")
|
||||||
|
public ResultDTO<Void> stopJob(Long jobId) throws Exception {
|
||||||
|
updateJobStatus(jobId, JobStatus.STOPPED);
|
||||||
return ResultDTO.success(null);
|
return ResultDTO.success(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/delete")
|
@GetMapping("/delete")
|
||||||
public ResultDTO<Void> deleteJobInfo(Long jobId) {
|
public ResultDTO<Void> deleteJob(Long jobId) throws Exception {
|
||||||
jobInfoRepository.deleteById(jobId);
|
updateJobStatus(jobId, JobStatus.DELETED);
|
||||||
return ResultDTO.success(null);
|
return ResultDTO.success(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateJobStatus(Long jobId, JobStatus status) {
|
||||||
|
JobInfoDO jobInfoDO = jobInfoRepository.findById(jobId).orElseThrow(() -> {
|
||||||
|
throw new IllegalArgumentException("can't find job which id is " + jobId);
|
||||||
|
});
|
||||||
|
jobInfoDO.setStatus(status.getV());
|
||||||
|
jobInfoRepository.saveAndFlush(jobInfoDO);
|
||||||
|
|
||||||
|
// TODO: 关闭秒级任务
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package com.github.kfcfans.oms.server.web.controller;
|
package com.github.kfcfans.oms.server.web.controller;
|
||||||
|
|
||||||
import com.github.kfcfans.oms.server.core.akka.OhMyServer;
|
import com.github.kfcfans.oms.server.akka.OhMyServer;
|
||||||
import com.github.kfcfans.oms.server.persistence.model.AppInfoDO;
|
import com.github.kfcfans.oms.server.persistence.model.AppInfoDO;
|
||||||
import com.github.kfcfans.oms.server.persistence.repository.AppInfoRepository;
|
import com.github.kfcfans.oms.server.persistence.repository.AppInfoRepository;
|
||||||
import com.github.kfcfans.oms.server.service.ha.ServerSelectService;
|
import com.github.kfcfans.oms.server.service.ha.ServerSelectService;
|
||||||
|
@ -112,6 +112,8 @@ public class CommonTaskTracker extends TaskTracker {
|
|||||||
req.setSucceedTaskNum(holder.succeedNum);
|
req.setSucceedTaskNum(holder.succeedNum);
|
||||||
req.setFailedTaskNum(holder.failedNum);
|
req.setFailedTaskNum(holder.failedNum);
|
||||||
req.setReportTime(System.currentTimeMillis());
|
req.setReportTime(System.currentTimeMillis());
|
||||||
|
req.setSourceAddress(OhMyWorker.getWorkerAddress());
|
||||||
|
|
||||||
|
|
||||||
// 2. 如果未完成任务数为0,判断是否真正结束,并获取真正结束任务的执行结果
|
// 2. 如果未完成任务数为0,判断是否真正结束,并获取真正结束任务的执行结果
|
||||||
TaskDO resultTask = null;
|
TaskDO resultTask = null;
|
||||||
|
@ -42,6 +42,8 @@ public class FrequentTaskTracker extends TaskTracker {
|
|||||||
// 时间表达式类型
|
// 时间表达式类型
|
||||||
private TimeExpressionType timeExpressionType;
|
private TimeExpressionType timeExpressionType;
|
||||||
private long timeParams;
|
private long timeParams;
|
||||||
|
// 最大同时运行实例数
|
||||||
|
private int maxInstanceNum;
|
||||||
|
|
||||||
// 总运行次数(正常情况不会出现锁竞争,直接用 Atomic 系列,锁竞争验证推荐 LongAdder)
|
// 总运行次数(正常情况不会出现锁竞争,直接用 Atomic 系列,锁竞争验证推荐 LongAdder)
|
||||||
private AtomicLong triggerTimes;
|
private AtomicLong triggerTimes;
|
||||||
@ -68,6 +70,7 @@ public class FrequentTaskTracker extends TaskTracker {
|
|||||||
// 0. 初始化实例变量
|
// 0. 初始化实例变量
|
||||||
timeExpressionType = TimeExpressionType.valueOf(req.getTimeExpressionType());
|
timeExpressionType = TimeExpressionType.valueOf(req.getTimeExpressionType());
|
||||||
timeParams = Long.parseLong(req.getTimeExpression());
|
timeParams = Long.parseLong(req.getTimeExpression());
|
||||||
|
maxInstanceNum = req.getMaxInstanceNum();
|
||||||
|
|
||||||
triggerTimes = new AtomicLong(0);
|
triggerTimes = new AtomicLong(0);
|
||||||
succeedTimes = new AtomicLong(0);
|
succeedTimes = new AtomicLong(0);
|
||||||
@ -133,8 +136,16 @@ public class FrequentTaskTracker extends TaskTracker {
|
|||||||
newRootTask.setCreatedTime(System.currentTimeMillis());
|
newRootTask.setCreatedTime(System.currentTimeMillis());
|
||||||
newRootTask.setLastModifiedTime(System.currentTimeMillis());
|
newRootTask.setLastModifiedTime(System.currentTimeMillis());
|
||||||
|
|
||||||
|
// 判断是否超出最大执行实例数
|
||||||
|
if (timeExpressionType == TimeExpressionType.FIX_RATE) {
|
||||||
|
if (subInstanceId2TimeHolder.size() > maxInstanceNum) {
|
||||||
|
log.error("[TaskTracker-{}] cancel to launch the subInstance({}) due to too much subInstance is running.", instanceId, subInstanceId);
|
||||||
|
processFinishedSubInstance(subInstanceId, false, "TOO_MUCH_INSTANCE");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 必须先持久化,持久化成功才能 Dispatch,否则会导致后续报错(因为DB中没有这个taskId对应的记录,会各种报错)
|
// 必须先持久化,持久化成功才能 dispatch,否则会导致后续报错(因为DB中没有这个taskId对应的记录,会各种报错)
|
||||||
if (!taskPersistenceService.save(newRootTask)) {
|
if (!taskPersistenceService.save(newRootTask)) {
|
||||||
log.error("[TaskTracker-{}] Launcher create new root task failed.", instanceId);
|
log.error("[TaskTracker-{}] Launcher create new root task failed.", instanceId);
|
||||||
processFinishedSubInstance(subInstanceId, false, "LAUNCH_FAILED");
|
processFinishedSubInstance(subInstanceId, false, "LAUNCH_FAILED");
|
||||||
@ -259,6 +270,7 @@ public class FrequentTaskTracker extends TaskTracker {
|
|||||||
req.setSucceedTaskNum(succeedTimes.get());
|
req.setSucceedTaskNum(succeedTimes.get());
|
||||||
req.setFailedTaskNum(failedTimes.get());
|
req.setFailedTaskNum(failedTimes.get());
|
||||||
req.setReportTime(System.currentTimeMillis());
|
req.setReportTime(System.currentTimeMillis());
|
||||||
|
req.setSourceAddress(OhMyWorker.getWorkerAddress());
|
||||||
|
|
||||||
String serverPath = AkkaUtils.getAkkaServerPath(RemoteConstant.SERVER_ACTOR_NAME);
|
String serverPath = AkkaUtils.getAkkaServerPath(RemoteConstant.SERVER_ACTOR_NAME);
|
||||||
ActorSelection serverActor = OhMyWorker.actorSystem.actorSelection(serverPath);
|
ActorSelection serverActor = OhMyWorker.actorSystem.actorSelection(serverPath);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user