package com.vip.saturn.job.console.service.impl;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.vip.saturn.job.console.domain.JobStatus;
import com.vip.saturn.job.console.domain.RestApiJobConfig;
import com.vip.saturn.job.console.domain.RestApiJobInfo;
import com.vip.saturn.job.console.domain.RestApiJobStatistics;
import com.vip.saturn.job.console.exception.SaturnJobConsoleException;
import com.vip.saturn.job.console.repository.zookeeper.CuratorRepository;
import com.vip.saturn.job.console.service.JobDimensionService;
import com.vip.saturn.job.console.service.RegistryCenterService;
import com.vip.saturn.job.console.service.RestApiService;
import com.vip.saturn.job.console.service.impl.helper.ReuseCallBack;
import com.vip.saturn.job.console.service.impl.helper.ReuseUtils;
import com.vip.saturn.job.console.utils.JobNodePath;
import com.vip.saturn.job.console.utils.SaturnConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author hebelala
*/
@Service
public class RestApiServiceImpl implements RestApiService {
private final static Logger logger = LoggerFactory.getLogger(RestApiServiceImpl.class);
private final long threeSecondsMillis = 3 * 1000L;
@Resource
private RegistryCenterService registryCenterService;
@Resource
private CuratorRepository curatorRepository;
@Resource
private JobDimensionService jobDimensionService;
@Override
public List<RestApiJobInfo> getRestApiJobInfos(String namespace) throws SaturnJobConsoleException {
return ReuseUtils.reuse(namespace, registryCenterService, curatorRepository, new ReuseCallBack<List<RestApiJobInfo>>() {
@Override
public List<RestApiJobInfo> call(CuratorRepository.CuratorFrameworkOp curatorFrameworkOp) throws SaturnJobConsoleException {
List<RestApiJobInfo> restApiJobInfos = new ArrayList<>();
List<String> jobs = jobDimensionService.getAllUnSystemJobs(curatorFrameworkOp);
if (jobs != null) {
for (String job : jobs) {
try {
RestApiJobInfo restApiJobInfo = new RestApiJobInfo();
restApiJobInfo.setJobName(job);
// 设置作业配置信息
setJobConfig(curatorFrameworkOp, restApiJobInfo, job);
// 设置运行状态
setRunningStatus(curatorFrameworkOp, restApiJobInfo, job);
// 设置统计信息
RestApiJobStatistics restApiJobStatistics = new RestApiJobStatistics();
setStatics(curatorFrameworkOp, restApiJobStatistics, job);
restApiJobInfo.setStatistics(restApiJobStatistics);
restApiJobInfos.add(restApiJobInfo);
} catch (Exception e) {
logger.error("getRestApiJobInfos exception:", e);
continue;
}
}
}
return restApiJobInfos;
}
});
}
private void setRunningStatus(CuratorRepository.CuratorFrameworkOp curatorFrameworkOp, RestApiJobInfo restApiJobInfo, String jobName) {
String executionNodePath = JobNodePath.getExecutionNodePath(jobName);
List<String> items = curatorFrameworkOp.getChildren(executionNodePath);
boolean isRunning = false;
if (items != null) {
for (String item : items) {
String runningNodePath = JobNodePath.getExecutionNodePath(jobName, item, "running");
if (curatorFrameworkOp.checkExists(runningNodePath)) {
isRunning = true;
break;
}
}
}
String enabledNodePath = JobNodePath.getConfigNodePath(jobName, "enabled");
if (Boolean.valueOf(curatorFrameworkOp.getData(enabledNodePath))) {
if (isRunning) {
restApiJobInfo.setRunningStatus(JobStatus.RUNNING.name());
} else {
restApiJobInfo.setRunningStatus(JobStatus.READY.name());
}
} else {
if (isRunning) {
restApiJobInfo.setRunningStatus(JobStatus.STOPPING.name());
} else {
restApiJobInfo.setRunningStatus(JobStatus.STOPPED.name());
}
}
}
private void setJobConfig(CuratorRepository.CuratorFrameworkOp curatorFrameworkOp, RestApiJobInfo restApiJobInfo, String jobName) {
String configNodePath = JobNodePath.getConfigNodePath(jobName);
if (curatorFrameworkOp.checkExists(configNodePath)) {
restApiJobInfo.setDescription(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "description")));
restApiJobInfo.setEnabled(Boolean.valueOf(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "enabled"))));
RestApiJobConfig restApiJobConfig = new RestApiJobConfig();
restApiJobConfig.setJobClass(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "jobClass")));
restApiJobConfig.setJobType(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "jobType")));
restApiJobConfig.setShardingTotalCount(Integer.valueOf(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "shardingTotalCount"))));
restApiJobConfig.setShardingItemParameters(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "shardingItemParameters")));
restApiJobConfig.setJobParameter(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "jobParameter")));
String timeZone = curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "timeZone"));
if (timeZone == null || timeZone.trim().length() == 0) {
timeZone = SaturnConstants.TIME_ZONE_ID_DEFAULT;
}
restApiJobConfig.setTimeZone(timeZone);
restApiJobConfig.setCron(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "cron")));
restApiJobConfig.setPausePeriodDate(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "pausePeriodDate")));
restApiJobConfig.setPausePeriodTime(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "pausePeriodTime")));
String timeout4AlarmSecondsStr = curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "timeout4AlarmSeconds"));
if (timeout4AlarmSecondsStr != null) {
restApiJobConfig.setTimeout4AlarmSeconds(Integer.valueOf(timeout4AlarmSecondsStr));
} else {
restApiJobConfig.setTimeout4AlarmSeconds(0);
}
restApiJobConfig.setTimeoutSeconds(Integer.valueOf(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "timeoutSeconds"))));
restApiJobConfig.setChannelName(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "channelName")));
restApiJobConfig.setQueueName(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "queueName")));
restApiJobConfig.setLoadLevel(Integer.valueOf(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "loadLevel"))));
String jobDegree = curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "jobDegree"));
if (!Strings.isNullOrEmpty(jobDegree)) {
restApiJobConfig.setJobDegree(Integer.valueOf(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "jobDegree"))));
}
restApiJobConfig.setEnabledReport(Boolean.valueOf(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "enabledReport"))));
restApiJobConfig.setPreferList(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "preferList")));
restApiJobConfig.setUseDispreferList(Boolean.valueOf(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "useDispreferList"))));
restApiJobConfig.setLocalMode(Boolean.valueOf(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "localMode"))));
restApiJobConfig.setUseSerial(Boolean.valueOf(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "useSerial"))));
restApiJobConfig.setDependencies(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "dependencies")));
restApiJobConfig.setGroups(curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "groups")));
restApiJobInfo.setJobConfig(restApiJobConfig);
}
}
private void setStatics(CuratorRepository.CuratorFrameworkOp curatorFrameworkOp, RestApiJobStatistics restApiJobStatistics, String jobName) {
setProcessCount(curatorFrameworkOp, restApiJobStatistics, jobName);
setTimes(curatorFrameworkOp, restApiJobStatistics, jobName);
}
private void setProcessCount(CuratorRepository.CuratorFrameworkOp curatorFrameworkOp, RestApiJobStatistics restApiJobStatistics, String jobName) {
String processCountPath = JobNodePath.getProcessCountPath(jobName);
String processCountStr = curatorFrameworkOp.getData(processCountPath);
if (processCountStr != null) {
try {
restApiJobStatistics.setProcessCount(Long.valueOf(processCountStr));
} catch (NumberFormatException e) {
logger.error(e.getMessage(), e);
}
}
String errorCountPath = JobNodePath.getErrorCountPath(jobName);
String errorCountPathStr = curatorFrameworkOp.getData(errorCountPath);
if (errorCountPathStr != null) {
try {
restApiJobStatistics.setProcessErrorCount(Long.valueOf(errorCountPathStr));
} catch (NumberFormatException e) {
logger.error(e.getMessage(), e);
}
}
}
private void setTimes(CuratorRepository.CuratorFrameworkOp curatorFrameworkOp, RestApiJobStatistics restApiJobStatistics, String jobName) {
String executionNodePath = JobNodePath.getExecutionNodePath(jobName);
List<String> items = curatorFrameworkOp.getChildren(executionNodePath);
if (items != null) {
List<String> lastBeginTimeList = new ArrayList<>();
List<String> lastCompleteTimeList = new ArrayList<>();
List<String> nextFireTimeList = new ArrayList<>();
int runningItemSize = 0;
for (String item : items) {
if (getRunningIP(item, jobName, curatorFrameworkOp) == null) {
continue;
}
++runningItemSize;
String lastBeginTime = curatorFrameworkOp.getData(JobNodePath.getExecutionNodePath(jobName, item, "lastBeginTime"));
if (null != lastBeginTime) {
lastBeginTimeList.add(lastBeginTime);
}
String lastCompleteTime = curatorFrameworkOp.getData(JobNodePath.getExecutionNodePath(jobName, item, "lastCompleteTime"));
if (null != lastCompleteTime) {
boolean isItemCompleted = curatorFrameworkOp.checkExists(JobNodePath.getExecutionNodePath(jobName, item, "completed"));
boolean isItemRunning = curatorFrameworkOp.checkExists(JobNodePath.getExecutionNodePath(jobName, item, "running"));
if (isItemCompleted && !isItemRunning) { // 如果作业分片已执行完毕,则添加该完成时间到集合中进行排序
lastCompleteTimeList.add(lastCompleteTime);
}
}
String nextFireTime = curatorFrameworkOp.getData(JobNodePath.getExecutionNodePath(jobName, item, "nextFireTime"));
if (null != nextFireTime) {
nextFireTimeList.add(nextFireTime);
}
}
if (!CollectionUtils.isEmpty(lastBeginTimeList)) {
Collections.sort(lastBeginTimeList);
try {
restApiJobStatistics.setLastBeginTime(Long.parseLong(lastBeginTimeList.get(0))); // 所有分片中最近最早的开始时间
} catch (NumberFormatException e) {
logger.error(e.getMessage(), e);
}
}
if (!CollectionUtils.isEmpty(lastCompleteTimeList) && lastCompleteTimeList.size() == runningItemSize) { // 所有分配都完成才显示最近最晚的完成时间
Collections.sort(lastCompleteTimeList);
try {
restApiJobStatistics.setLastCompleteTime(Long.parseLong(lastCompleteTimeList.get(lastCompleteTimeList.size() - 1))); // 所有分片中最近最晚的完成时间
} catch (NumberFormatException e) {
logger.error(e.getMessage(), e);
}
}
if (!CollectionUtils.isEmpty(nextFireTimeList)) {
Collections.sort(nextFireTimeList);
try {
restApiJobStatistics.setNextFireTime(Long.parseLong(nextFireTimeList.get(0))); // 所有分片中下次最早的开始时间
} catch (NumberFormatException e) {
logger.error(e.getMessage(), e);
}
}
}
}
private String getRunningIP(String item, String jobName, CuratorRepository.CuratorFrameworkOp curatorFrameworkOp) {
String runningIp = null;
String serverNodePath = JobNodePath.getServerNodePath(jobName);
if (!curatorFrameworkOp.checkExists(serverNodePath)) {
return runningIp;
}
List<String> servers = curatorFrameworkOp.getChildren(serverNodePath);
for (String server : servers) {
String sharding = curatorFrameworkOp.getData(JobNodePath.getServerNodePath(jobName, server, "sharding"));
String toFind = "";
if (Strings.isNullOrEmpty(sharding)) {
continue;
}
for (String itemKey : Splitter.on(',').split(sharding)) {
if (item.equals(itemKey)) {
toFind = itemKey;
break;
}
}
if (!Strings.isNullOrEmpty(toFind)) {
runningIp = server;
break;
}
}
return runningIp;
}
@Override
public int enableJob(String namespace, final String jobName) throws SaturnJobConsoleException {
return ReuseUtils.reuse(namespace, jobName, registryCenterService, curatorRepository, new ReuseCallBack<Integer>() {
@Override
public Integer call(CuratorRepository.CuratorFrameworkOp curatorFrameworkOp) throws SaturnJobConsoleException {
String enabledNodePath = JobNodePath.getConfigNodePath(jobName, "enabled");
String enabled = curatorFrameworkOp.getData(enabledNodePath);
if (Boolean.valueOf(enabled)) {
return 201;
} else {
long mtime = curatorFrameworkOp.getMtime(enabledNodePath);
if (updateIntervalLessThanThreeSeconds(mtime)) {
return 403;
}
curatorFrameworkOp.update(enabledNodePath, "true");
return 200;
}
}
});
}
@Override
public int disableJob(String namespace, final String jobName) throws SaturnJobConsoleException {
return ReuseUtils.reuse(namespace, jobName, registryCenterService, curatorRepository, new ReuseCallBack<Integer>() {
@Override
public Integer call(CuratorRepository.CuratorFrameworkOp curatorFrameworkOp) throws SaturnJobConsoleException {
String enabledNodePath = JobNodePath.getConfigNodePath(jobName, "enabled");
String enabled = curatorFrameworkOp.getData(enabledNodePath);
if (Boolean.valueOf(enabled)) {
long mtime = curatorFrameworkOp.getMtime(enabledNodePath);
if (updateIntervalLessThanThreeSeconds(mtime)) {
return 403;
}
curatorFrameworkOp.update(enabledNodePath, "false");
return 200;
} else {
return 201;
}
}
});
}
private boolean updateIntervalLessThanThreeSeconds(long lastMtime) {
return Math.abs(System.currentTimeMillis() - lastMtime) < threeSecondsMillis;
}
}