package com.vip.saturn.job.console.service.impl;
import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.annotation.Resource;
import com.vip.saturn.job.console.utils.SaturnConstants;
import jxl.Workbook;
import jxl.write.Label;
import jxl.write.WritableCell;
import jxl.write.WritableCellFeatures;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import com.google.common.base.Strings;
import com.vip.saturn.job.console.domain.JobBriefInfo.JobType;
import com.vip.saturn.job.console.domain.JobConfig;
import com.vip.saturn.job.console.domain.RequestResult;
import com.vip.saturn.job.console.exception.SaturnJobConsoleException;
import com.vip.saturn.job.console.repository.zookeeper.CuratorRepository;
import com.vip.saturn.job.console.repository.zookeeper.CuratorRepository.CuratorFrameworkOp;
import com.vip.saturn.job.console.service.ExecutorService;
import com.vip.saturn.job.console.service.JobDimensionService;
import com.vip.saturn.job.console.utils.ExecutorNodePath;
import com.vip.saturn.job.console.utils.JobNodePath;
import com.vip.saturn.job.console.utils.ThreadLocalCuratorClient;
/**
*
* @author xiaopeng.he
*
*/
@Service
public class ExecutorServiceImpl implements ExecutorService {
protected static Logger log = LoggerFactory.getLogger(ExecutorServiceImpl.class);
public static final String ROOT = ExecutorNodePath.get$ExecutorNodePath();
public static final String IP_NODE_NAME = "ip";
@Resource
private CuratorRepository curatorRepository;
@Resource
private JobDimensionService jobDimensionService;
private Random random = new Random();
@Override
public List<String> getAliveExecutorNames() {
CuratorRepository.CuratorFrameworkOp curatorFrameworkOp = curatorRepository.inSessionClient();
if (curatorFrameworkOp.checkExists(ExecutorNodePath.getExecutorNodePath())) {
List<String> executorNames = curatorFrameworkOp.getChildren(ExecutorNodePath.getExecutorNodePath());
if (executorNames != null) {
List<String> aliveExecutorNames = new ArrayList<>(executorNames.size());
for (String executorName : executorNames) {
if (executorName != null) {
String ip = null;
try {
ip = curatorFrameworkOp.getData(ExecutorNodePath.getExecutorNodePath(executorName,IP_NODE_NAME));
} catch (Throwable t) {
log.error(t.getMessage(), t);
}
if (StringUtils.isNotBlank(ip)) {
aliveExecutorNames.add(executorName);
}
}
}
return aliveExecutorNames;
}
}
return null;
}
@Override
public RequestResult addJobs(JobConfig jobConfig) {
RequestResult requestResult = new RequestResult();
requestResult.setMessage("");
requestResult.setSuccess(true);
try {
CuratorRepository.CuratorFrameworkOp curatorFrameworkOp = curatorRepository.inSessionClient();
String jobName = jobConfig.getJobName();
if (!curatorFrameworkOp.checkExists(JobNodePath.getJobNodePath(jobName))) {
if(jobConfig.getIsCopyJob()){// 复制作业
copyAndPersisJobJobConfig(jobConfig);
}else{
persisJobConfig(jobConfig);// 新增作业
}
} else {
requestResult.setSuccess(false);
requestResult.setMessage("作业名" + jobName + "已经存在" );
}
} catch (Throwable t) {
log.error(t.getMessage(), t);
requestResult.setSuccess(false);
requestResult.setMessage(t.getMessage());
}
return requestResult;
}
private void persisJobConfig(JobConfig jobConfig) {
jobConfig.setDefaultValues();
String jobName = jobConfig.getJobName();
CuratorRepository.CuratorFrameworkOp curatorFrameworkOp = curatorRepository.inSessionClient();
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "enabled"), "false");
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "description"), jobConfig.getDescription());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "jobType"), jobConfig.getJobType());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "jobMode"), jobConfig.getJobMode());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "shardingItemParameters"), jobConfig.getShardingItemParameters());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "jobParameter"), jobConfig.getJobParameter());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "queueName"), jobConfig.getQueueName());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "channelName"), jobConfig.getChannelName());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "failover"), "true");
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "timeout4AlarmSeconds"), jobConfig.getTimeout4AlarmSeconds());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "timeoutSeconds"), jobConfig.getTimeoutSeconds());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "timeZone"), jobConfig.getTimeZone());
if(JobType.MSG_JOB.name().equals(jobConfig.getJobType())){// MSG作业没有cron表达式
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "cron"), "");
}else{
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "cron"), jobConfig.getCron());
}
if(!Strings.isNullOrEmpty(jobConfig.getPausePeriodDate())){
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "pausePeriodDate"), jobConfig.getPausePeriodDate());
}
if(!Strings.isNullOrEmpty(jobConfig.getPausePeriodTime())){
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "pausePeriodTime"), jobConfig.getPausePeriodTime());
}
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "processCountIntervalSeconds"), jobConfig.getProcessCountIntervalSeconds());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "shardingTotalCount"), jobConfig.getShardingTotalCount());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "showNormalLog"), jobConfig.getShowNormalLog());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "loadLevel"), jobConfig.getLoadLevel());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "jobDegree"), jobConfig.getJobDegree());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "enabledReport"), jobConfig.getEnabledReport());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "preferList"), jobConfig.getPreferList());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "useDispreferList"), jobConfig.getUseDispreferList());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "localMode"), jobConfig.getLocalMode());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "useSerial"), jobConfig.getUseSerial());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "dependencies"), jobConfig.getDependencies());
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "groups"), jobConfig.getGroups());
if(JobType.SHELL_JOB.name().equals(jobConfig.getJobType())){
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "jobClass"), "");
}else{
curatorFrameworkOp.fillJobNodeIfNotExist(JobNodePath.getConfigNodePath(jobName, "jobClass"), jobConfig.getJobClass());
}
}
private void copyAndPersisJobJobConfig(JobConfig jobConfig) throws Exception {
String originJobName = jobConfig.getOriginJobName();
CuratorRepository.CuratorFrameworkOp curatorFrameworkOp = curatorRepository.inSessionClient();
List<String> jobConfigNodes = curatorFrameworkOp.getChildren(JobNodePath.getConfigNodePath(originJobName));
if(CollectionUtils.isEmpty(jobConfigNodes)){
return;
}
Class<?> cls = jobConfig.getClass();
String jobClassPath = "";
String jobClassValue = "";
for(String jobConfigNode : jobConfigNodes){
String jobConfigPath = JobNodePath.getConfigNodePath(originJobName, jobConfigNode);
String jobConfigValue = curatorFrameworkOp.getData(jobConfigPath);
if("enabled".equals(jobConfigNode)){// enabled固定为false
String fillJobNodePath = JobNodePath.getConfigNodePath(jobConfig.getJobName(), jobConfigNode);
curatorFrameworkOp.fillJobNodeIfNotExist(fillJobNodePath,"false");
continue;
}
if("failover".equals(jobConfigNode)){// failover固定为true
String fillJobNodePath = JobNodePath.getConfigNodePath(jobConfig.getJobName(), jobConfigNode);
curatorFrameworkOp.fillJobNodeIfNotExist(fillJobNodePath,"true");
continue;
}
try{
Field field = cls.getDeclaredField(jobConfigNode);
field.setAccessible(true);
Object fieldValue = field.get(jobConfig);
if(fieldValue != null){
jobConfigValue = fieldValue.toString();
}
if("jobClass".equals(jobConfigNode)){// 持久化jobClass会触发添加作业,待其他节点全部持久化完毕以后再持久化jobClass
jobClassPath = JobNodePath.getConfigNodePath(jobConfig.getJobName(), jobConfigNode);
jobClassValue = jobConfigValue;
}
}catch(NoSuchFieldException e){// 即使JobConfig类中不存在该属性也复制(一般是旧版作业的一些节点,可以在旧版Executor上运行)
continue;
}finally{
if(!"jobClass".equals(jobConfigNode)){// 持久化jobClass会触发添加作业,待其他节点全部持久化完毕以后再持久化jobClass
String fillJobNodePath = JobNodePath.getConfigNodePath(jobConfig.getJobName(), jobConfigNode);
curatorFrameworkOp.fillJobNodeIfNotExist(fillJobNodePath,jobConfigValue);
}
}
}
if(!Strings.isNullOrEmpty(jobClassPath)){
curatorFrameworkOp.fillJobNodeIfNotExist(jobClassPath,jobClassValue);
}
}
@Override
public String removeJob(String jobName) {
try {
Stat itemStat = ThreadLocalCuratorClient.getCuratorClient().checkExists().forPath(JobNodePath.getJobNodePath(jobName));
if(itemStat != null){
long createTimeDiff = System.currentTimeMillis() - itemStat.getCtime();
if(createTimeDiff < SaturnConstants.JOB_CAN_BE_DELETE_TIME_LIMIT){
return "作业【"+jobName+"】创建后"+ (SaturnConstants.JOB_CAN_BE_DELETE_TIME_LIMIT / 60 / 1000) +"分钟内不允许删除";
}
}
CuratorRepository.CuratorFrameworkOp curatorFrameworkOp = curatorRepository.inSessionClient();
//1.作业的executor全online的情况,添加toDelete节点,触发监听器动态删除节点
String toDeleteNodePath = JobNodePath.getConfigNodePath(jobName, "toDelete");
if(curatorFrameworkOp.checkExists(toDeleteNodePath)){
curatorFrameworkOp.deleteRecursive(toDeleteNodePath);
}
curatorFrameworkOp.create(toDeleteNodePath);
for(int i=0;i<20;i++){
// 2.作业的executor全offline的情况,或有几个online,几个offline的情况
String jobServerPath = JobNodePath.getServerNodePath(jobName);
if(!curatorFrameworkOp.checkExists(jobServerPath)){
// (1)如果不存在$Job/JobName/servers节点,说明该作业没有任何executor接管,可直接删除作业节点
curatorFrameworkOp.deleteRecursive(JobNodePath.getJobNodePath(jobName));
return SaturnConstants.DEAL_SUCCESS;
}
// (2)如果该作业servers下没有任何executor,可直接删除作业节点
List<String> executors = curatorFrameworkOp.getChildren(jobServerPath);
if(CollectionUtils.isEmpty(executors)){
curatorFrameworkOp.deleteRecursive(JobNodePath.getJobNodePath(jobName));
return SaturnConstants.DEAL_SUCCESS;
}
// (3)只要该作业没有一个能运行的该作业的executor在线,那么直接删除作业节点
boolean hasOnlineExecutor = false;
for(String executor : executors){
if(curatorFrameworkOp.checkExists(ExecutorNodePath.getExecutorNodePath(executor, "ip"))
&& curatorFrameworkOp.checkExists(JobNodePath.getServerStatus(jobName, executor))){
hasOnlineExecutor = true;
break;
}
}
if(!hasOnlineExecutor){
curatorFrameworkOp.deleteRecursive(JobNodePath.getJobNodePath(jobName));
}
Thread.sleep(200);
}
} catch (Throwable t) {
log.error(t.getMessage(), t);
return t.getMessage();
}
return SaturnConstants.DEAL_SUCCESS;
}
@Override
public File getExportJobFile() throws SaturnJobConsoleException {
try {
String sep = System.getProperty("file.separator");
File tmp = new File(System.getProperty("user.home") + sep + ".saturn" + sep + "saturn_console" + sep + "caches" + sep + "tmp_exportFile_" + System.currentTimeMillis() + "_" + random.nextInt(1000) + ".xls");
if(!tmp.exists()) {
FileUtils.forceMkdir(tmp.getParentFile());
tmp.createNewFile();
}
WritableWorkbook writableWorkbook = Workbook.createWorkbook(tmp);
WritableSheet sheet1 = writableWorkbook.createSheet("Sheet1", 0);
sheet1.addCell(new Label(0, 0, "作业名称"));
sheet1.addCell(new Label(1, 0, "作业类型"));
sheet1.addCell(new Label(2, 0, "作业实现类"));
sheet1.addCell(new Label(3, 0, "cron表达式"));
sheet1.addCell(new Label(4, 0, "作业描述"));
Label localModeLabel = new Label(5, 0, "本地模式");
setCellComment(localModeLabel, "对于非本地模式,默认为false;对于本地模式,该配置无效,固定为true");
sheet1.addCell(localModeLabel);
Label shardingTotalCountLabel = new Label(6, 0, "分片数");
setCellComment(shardingTotalCountLabel, "对本地作业无效");
sheet1.addCell(shardingTotalCountLabel);
Label timeoutSecondsLabel = new Label(7, 0, "超时(Kill线程/进程)时间");
setCellComment(timeoutSecondsLabel, "0表示无超时");
sheet1.addCell(timeoutSecondsLabel);
sheet1.addCell(new Label(8, 0, "自定义参数"));
sheet1.addCell(new Label(9, 0, "分片序列号/参数对照表"));
sheet1.addCell(new Label(10, 0, "Queue名"));
sheet1.addCell(new Label(11, 0, "执行结果发送的Channel"));
Label preferListLabel = new Label(12, 0, "优先Executor");
setCellComment(preferListLabel, "可填executorName,多个元素使用英文逗号隔开");
sheet1.addCell(preferListLabel);
Label usePreferListOnlyLabel = new Label(13, 0, "只使用优先Executor");
setCellComment(usePreferListOnlyLabel, "默认为false");
sheet1.addCell(usePreferListOnlyLabel);
sheet1.addCell(new Label(14, 0, "统计处理数据量的间隔秒数"));
sheet1.addCell(new Label(15, 0, "负荷"));
sheet1.addCell(new Label(16, 0, "显示控制台输出日志"));
sheet1.addCell(new Label(17, 0, "暂停日期段"));
sheet1.addCell(new Label(18, 0, "暂停时间段"));
Label useSerialLabel = new Label(19, 0, "串行消费");
setCellComment(useSerialLabel, "默认为false");
sheet1.addCell(useSerialLabel);
Label jobDegreeLabel = new Label(20, 0, "作业重要等级");
setCellComment(jobDegreeLabel, "0:没有定义,1:非线上业务,2:简单业务,3:一般业务,4:重要业务,5:核心业务");
sheet1.addCell(jobDegreeLabel);
Label enabledReportLabel = new Label(21, 0, "上报运行状态");
setCellComment(enabledReportLabel, "对于定时作业,默认为true;对于消息作业,默认为false");
sheet1.addCell(enabledReportLabel);
Label jobModeLabel = new Label(22, 0, "作业模式");
setCellComment(jobModeLabel, "用户不能添加系统作业");
sheet1.addCell(jobModeLabel);
Label dependenciesLabel = new Label(23, 0, "依赖的作业");
setCellComment(dependenciesLabel, "作业的启用、禁用会检查依赖关系的作业的状态。依赖多个作业,使用英文逗号给开。");
sheet1.addCell(dependenciesLabel);
Label groupsLabel = new Label(24, 0, "所属分组");
setCellComment(groupsLabel, "作业所属分组,一个作业只能属于一个分组,一个分组可以包含多个作业");
sheet1.addCell(groupsLabel);
Label timeout4AlarmSecondsLabel = new Label(25, 0, "超时(告警)时间");
setCellComment(timeout4AlarmSecondsLabel, "0表示无超时");
sheet1.addCell(timeout4AlarmSecondsLabel);
Label timeZoneLabel = new Label(26, 0, "时区");
setCellComment(timeZoneLabel, "作业运行时区");
sheet1.addCell(timeZoneLabel);
CuratorRepository.CuratorFrameworkOp curatorFrameworkOp = curatorRepository.inSessionClient();
List<String> jobNames = jobDimensionService.getAllUnSystemJobs(curatorFrameworkOp);
for (int i=0; i<jobNames.size(); i++) {
try {
String jobName = jobNames.get(i);
sheet1.addCell(new Label(0, i + 1, jobName));
sheet1.addCell(new Label(1, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "jobType"))));
sheet1.addCell(new Label(2, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "jobClass"))));
sheet1.addCell(new Label(3, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "cron"))));
sheet1.addCell(new Label(4, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "description"))));
sheet1.addCell(new Label(5, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "localMode"))));
sheet1.addCell(new Label(6, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "shardingTotalCount"))));
sheet1.addCell(new Label(7, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "timeoutSeconds"))));
sheet1.addCell(new Label(8, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "jobParameter"))));
sheet1.addCell(new Label(9, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "shardingItemParameters"))));
sheet1.addCell(new Label(10, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "queueName"))));
sheet1.addCell(new Label(11, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "channelName"))));
sheet1.addCell(new Label(12, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "preferList"))));
String useDispreferList = curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "useDispreferList"));
if(useDispreferList != null) {
useDispreferList = String.valueOf(!Boolean.valueOf(useDispreferList));
}
sheet1.addCell(new Label(13, i + 1, useDispreferList));
sheet1.addCell(new Label(14, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "processCountIntervalSeconds"))));
sheet1.addCell(new Label(15, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "loadLevel"))));
sheet1.addCell(new Label(16, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "showNormalLog"))));
sheet1.addCell(new Label(17, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "pausePeriodDate"))));
sheet1.addCell(new Label(18, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "pausePeriodTime"))));
sheet1.addCell(new Label(19, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "useSerial"))));
sheet1.addCell(new Label(20, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "jobDegree"))));
sheet1.addCell(new Label(21, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "enabledReport"))));
sheet1.addCell(new Label(22, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "jobMode"))));
sheet1.addCell(new Label(23, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "dependencies"))));
sheet1.addCell(new Label(24, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "groups"))));
sheet1.addCell(new Label(25, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "timeout4AlarmSeconds"))));
sheet1.addCell(new Label(26, i + 1, curatorFrameworkOp.getData(JobNodePath.getConfigNodePath(jobName, "timeZone"))));
} catch (Exception e) {
log.error("export job exception:", e);
continue;
}
}
writableWorkbook.write();
writableWorkbook.close();
return tmp;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new SaturnJobConsoleException(e);
}
}
private void setCellComment(WritableCell cell, String comment) {
WritableCellFeatures cellFeatures = new WritableCellFeatures();
cellFeatures.setComment(comment);
cell.setCellFeatures(cellFeatures);
}
@Override
public RequestResult shardAllAtOnce() throws SaturnJobConsoleException {
try{
RequestResult requestResult = new RequestResult();
CuratorFrameworkOp curatorFrameworkOp = curatorRepository.inSessionClient();
String shardAllAtOnceNodePath = ExecutorNodePath.getExecutorShardingNodePath("shardAllAtOnce");
if(curatorFrameworkOp.checkExists(shardAllAtOnceNodePath)){
curatorFrameworkOp.deleteRecursive(shardAllAtOnceNodePath);
}
curatorFrameworkOp.create(shardAllAtOnceNodePath);
requestResult.setMessage("");
requestResult.setSuccess(true);
return requestResult;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new SaturnJobConsoleException(e);
}
}
}