ligang
6 years ago
55 changed files with 9378 additions and 0 deletions
@ -0,0 +1,127 @@ |
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<parent> |
||||||
|
<artifactId>escheduler</artifactId> |
||||||
|
<groupId>cn.analysys</groupId> |
||||||
|
<version>1.0.0</version> |
||||||
|
</parent> |
||||||
|
<artifactId>escheduler-server</artifactId> |
||||||
|
<name>escheduler-server</name> |
||||||
|
<url>http://maven.apache.org</url> |
||||||
|
<packaging>jar</packaging> |
||||||
|
<properties> |
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>cn.analysys</groupId> |
||||||
|
<artifactId>escheduler-common</artifactId> |
||||||
|
<exclusions> |
||||||
|
<exclusion> |
||||||
|
<groupId>io.netty</groupId> |
||||||
|
<artifactId>netty</artifactId> |
||||||
|
</exclusion> |
||||||
|
<exclusion> |
||||||
|
<groupId>io.netty</groupId> |
||||||
|
<artifactId>netty-all</artifactId> |
||||||
|
</exclusion> |
||||||
|
<exclusion> |
||||||
|
<groupId>com.google</groupId> |
||||||
|
<artifactId>netty</artifactId> |
||||||
|
</exclusion> |
||||||
|
<exclusion> |
||||||
|
<artifactId>log4j-slf4j-impl</artifactId> |
||||||
|
<groupId>org.apache.logging.log4j</groupId> |
||||||
|
</exclusion> |
||||||
|
</exclusions> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>cn.analysys</groupId> |
||||||
|
<artifactId>escheduler-dao</artifactId> |
||||||
|
<exclusions> |
||||||
|
<exclusion> |
||||||
|
<artifactId>spring-boot-starter-logging</artifactId> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
</exclusion> |
||||||
|
</exclusions> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>cn.analysys</groupId> |
||||||
|
<artifactId>escheduler-api</artifactId> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>cn.analysys</groupId> |
||||||
|
<artifactId>escheduler-rpc</artifactId> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.apache.curator</groupId> |
||||||
|
<artifactId>curator-framework</artifactId> |
||||||
|
<exclusions> |
||||||
|
<exclusion> |
||||||
|
<groupId>org.apache.zookeeper</groupId> |
||||||
|
<artifactId>zookeeper</artifactId> |
||||||
|
</exclusion> |
||||||
|
</exclusions> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.apache.curator</groupId> |
||||||
|
<artifactId>curator-recipes</artifactId> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>org.apache.httpcomponents</groupId> |
||||||
|
<artifactId>httpclient</artifactId> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.apache.httpcomponents</groupId> |
||||||
|
<artifactId>httpcore</artifactId> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>junit</groupId> |
||||||
|
<artifactId>junit</artifactId> |
||||||
|
<scope>test</scope> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>cn.analysys</groupId> |
||||||
|
<artifactId>escheduler-alert</artifactId> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
</dependencies> |
||||||
|
|
||||||
|
|
||||||
|
<build> |
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<artifactId>maven-assembly-plugin</artifactId> |
||||||
|
<configuration> |
||||||
|
<descriptors> |
||||||
|
<descriptor>src/main/assembly/package.xml</descriptor> |
||||||
|
</descriptors> |
||||||
|
<appendAssemblyId>false</appendAssemblyId> |
||||||
|
</configuration> |
||||||
|
<executions> |
||||||
|
<execution> |
||||||
|
<id>make-assembly</id> |
||||||
|
<phase>package</phase> |
||||||
|
<goals> |
||||||
|
<goal>single</goal> |
||||||
|
</goals> |
||||||
|
</execution> |
||||||
|
</executions> |
||||||
|
</plugin> |
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-compiler-plugin</artifactId> |
||||||
|
<configuration> |
||||||
|
<source>${java.version}</source> |
||||||
|
<target>${java.version}</target> |
||||||
|
<encoding>${project.build.sourceEncoding}</encoding> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,74 @@ |
|||||||
|
<assembly |
||||||
|
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" |
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> |
||||||
|
<id>cluster</id> |
||||||
|
<formats> |
||||||
|
<format>dir</format> |
||||||
|
</formats> |
||||||
|
<includeBaseDirectory>false</includeBaseDirectory> |
||||||
|
<fileSets> |
||||||
|
<fileSet> |
||||||
|
<directory>src/main/resources</directory> |
||||||
|
<includes> |
||||||
|
<include>**/*.properties</include> |
||||||
|
<include>**/*.xml</include> |
||||||
|
<include>**/*.json</include> |
||||||
|
</includes> |
||||||
|
<outputDirectory>conf</outputDirectory> |
||||||
|
</fileSet> |
||||||
|
<fileSet> |
||||||
|
<directory>${project.parent.basedir}/escheduler-common/src/main/resources</directory> |
||||||
|
<includes> |
||||||
|
<include>**/*.properties</include> |
||||||
|
<include>**/*.xml</include> |
||||||
|
<include>**/*.json</include> |
||||||
|
</includes> |
||||||
|
<outputDirectory>conf</outputDirectory> |
||||||
|
</fileSet> |
||||||
|
<fileSet> |
||||||
|
<directory>${project.parent.basedir}/escheduler-common/src/main/resources/bin</directory> |
||||||
|
<includes> |
||||||
|
<include>*.*</include> |
||||||
|
</includes> |
||||||
|
<directoryMode>755</directoryMode> |
||||||
|
<outputDirectory>bin</outputDirectory> |
||||||
|
</fileSet> |
||||||
|
<fileSet> |
||||||
|
<directory>${project.parent.basedir}/escheduler-dao/src/main/resources</directory> |
||||||
|
<includes> |
||||||
|
<include>**/*.properties</include> |
||||||
|
<include>**/*.xml</include> |
||||||
|
<include>**/*.json</include> |
||||||
|
</includes> |
||||||
|
<outputDirectory>conf</outputDirectory> |
||||||
|
</fileSet> |
||||||
|
<fileSet> |
||||||
|
<directory>${project.parent.basedir}/escheduler-api/src/main/resources</directory> |
||||||
|
<includes> |
||||||
|
<include>**/*.properties</include> |
||||||
|
<include>**/*.xml</include> |
||||||
|
<include>**/*.json</include> |
||||||
|
</includes> |
||||||
|
<outputDirectory>conf</outputDirectory> |
||||||
|
</fileSet> |
||||||
|
<fileSet> |
||||||
|
<directory>target/</directory> |
||||||
|
<includes> |
||||||
|
<include>escheduler-server-${project.version}.jar</include> |
||||||
|
</includes> |
||||||
|
<outputDirectory>lib</outputDirectory> |
||||||
|
</fileSet> |
||||||
|
</fileSets> |
||||||
|
<dependencySets> |
||||||
|
<dependencySet> |
||||||
|
<outputDirectory>lib</outputDirectory> |
||||||
|
<useProjectArtifact>true</useProjectArtifact> |
||||||
|
<excludes> |
||||||
|
<exclude>javax.servlet:servlet-api</exclude> |
||||||
|
<exclude>org.eclipse.jetty.aggregate:jetty-all</exclude> |
||||||
|
<exclude>org.slf4j:slf4j-log4j12</exclude> |
||||||
|
</excludes> |
||||||
|
</dependencySet> |
||||||
|
</dependencySets> |
||||||
|
</assembly> |
@ -0,0 +1,51 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server; |
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException; |
||||||
|
import org.springframework.context.ApplicationContext; |
||||||
|
import org.springframework.context.ApplicationContextAware; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
/** |
||||||
|
* bean context |
||||||
|
*/ |
||||||
|
@Component |
||||||
|
public class BeanContext implements ApplicationContextAware { |
||||||
|
private static ApplicationContext applicationContext; |
||||||
|
|
||||||
|
public static ApplicationContext getApplicationContext(){ |
||||||
|
return applicationContext; |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
public static <T> T getBean(String name) throws BeansException { |
||||||
|
return (T)applicationContext.getBean(name); |
||||||
|
} |
||||||
|
|
||||||
|
public static <T> T getBean(Class<T> clz) throws BeansException { |
||||||
|
return applicationContext.getBean(clz); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { |
||||||
|
BeanContext.applicationContext = applicationContext; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,102 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.utils.JSONUtils; |
||||||
|
import cn.escheduler.common.utils.OSUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* heartbeat for ZK reigster res info |
||||||
|
*/ |
||||||
|
public class ResInfo { |
||||||
|
|
||||||
|
/** |
||||||
|
* cpuUsage |
||||||
|
*/ |
||||||
|
private double cpuUsage; |
||||||
|
|
||||||
|
/** |
||||||
|
* memoryUsage |
||||||
|
*/ |
||||||
|
private double memoryUsage; |
||||||
|
|
||||||
|
public ResInfo(){} |
||||||
|
|
||||||
|
public ResInfo(double cpuUsage , double memoryUsage){ |
||||||
|
this.cpuUsage = cpuUsage ; |
||||||
|
this.memoryUsage = memoryUsage; |
||||||
|
} |
||||||
|
|
||||||
|
public double getCpuUsage() { |
||||||
|
return cpuUsage; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCpuUsage(double cpuUsage) { |
||||||
|
this.cpuUsage = cpuUsage; |
||||||
|
} |
||||||
|
|
||||||
|
public double getMemoryUsage() { |
||||||
|
return memoryUsage; |
||||||
|
} |
||||||
|
|
||||||
|
public void setMemoryUsage(double memoryUsage) { |
||||||
|
this.memoryUsage = memoryUsage; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get CPU and memory usage |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static String getResInfoJson(){ |
||||||
|
ResInfo resInfo = new ResInfo(OSUtils.cpuUsage(), OSUtils.memoryUsage()); |
||||||
|
return JSONUtils.toJson(resInfo); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* get CPU and memory usage |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static String getResInfoJson(double cpuUsage , double memoryUsage){ |
||||||
|
ResInfo resInfo = new ResInfo(cpuUsage,memoryUsage); |
||||||
|
return JSONUtils.toJson(resInfo); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* build heartbeat info for zk |
||||||
|
* @param host |
||||||
|
* @param port |
||||||
|
* @param cpuUsage |
||||||
|
* @param memoryUsage |
||||||
|
* @param createTime |
||||||
|
* @param lastHeartbeatTime |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static String buildHeartbeatForZKInfo(String host , int port , |
||||||
|
double cpuUsage , double memoryUsage, |
||||||
|
String createTime,String lastHeartbeatTime){ |
||||||
|
|
||||||
|
return host + Constants.COMMA + port + Constants.COMMA |
||||||
|
+ cpuUsage + Constants.COMMA |
||||||
|
+ memoryUsage + Constants.COMMA |
||||||
|
+ createTime + Constants.COMMA |
||||||
|
+ lastHeartbeatTime; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,321 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.master; |
||||||
|
|
||||||
|
import cn.escheduler.api.quartz.ProcessScheduleJob; |
||||||
|
import cn.escheduler.api.quartz.QuartzExecutors; |
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.IStoppable; |
||||||
|
import cn.escheduler.common.thread.Stopper; |
||||||
|
import cn.escheduler.common.thread.ThreadPoolExecutors; |
||||||
|
import cn.escheduler.common.thread.ThreadUtils; |
||||||
|
import cn.escheduler.common.utils.OSUtils; |
||||||
|
import cn.escheduler.dao.AlertDao; |
||||||
|
import cn.escheduler.dao.ProcessDao; |
||||||
|
import cn.escheduler.dao.ServerDao; |
||||||
|
import cn.escheduler.server.master.runner.MasterSchedulerThread; |
||||||
|
import cn.escheduler.server.zk.ZKMasterClient; |
||||||
|
import org.apache.commons.configuration.Configuration; |
||||||
|
import org.apache.commons.configuration.ConfigurationException; |
||||||
|
import org.apache.commons.configuration.PropertiesConfiguration; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
import org.quartz.SchedulerException; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.boot.CommandLineRunner; |
||||||
|
import org.springframework.boot.SpringApplication; |
||||||
|
import org.springframework.context.annotation.ComponentScan; |
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
import java.util.concurrent.ScheduledExecutorService; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
/** |
||||||
|
* master server |
||||||
|
*/ |
||||||
|
@ComponentScan("cn.escheduler") |
||||||
|
public class MasterServer implements CommandLineRunner, IStoppable { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(MasterServer.class); |
||||||
|
|
||||||
|
/** |
||||||
|
* conf |
||||||
|
*/ |
||||||
|
private static Configuration conf; |
||||||
|
|
||||||
|
/** |
||||||
|
* object lock |
||||||
|
*/ |
||||||
|
private final Object lock = new Object(); |
||||||
|
|
||||||
|
/** |
||||||
|
* whether or not to close the state |
||||||
|
*/ |
||||||
|
private boolean terminated = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* zk master client |
||||||
|
*/ |
||||||
|
private static ZKMasterClient zkMasterClient=null; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* master dao database access |
||||||
|
*/ |
||||||
|
private ServerDao serverDao = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* alert database access |
||||||
|
*/ |
||||||
|
private AlertDao alertDao = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* escheduler database interface
|
||||||
|
*/ |
||||||
|
@Autowired |
||||||
|
private ProcessDao processDao; |
||||||
|
|
||||||
|
/** |
||||||
|
* heartbeat thread pool |
||||||
|
*/ |
||||||
|
private ScheduledExecutorService heartbeatMasterService; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* master exec thread pool |
||||||
|
*/ |
||||||
|
private final ExecutorService masterSchedulerService = ThreadUtils.newDaemonSingleThreadExecutor("Master-Scheduler-Thread"); |
||||||
|
|
||||||
|
/** |
||||||
|
* heartbeat interval, unit second |
||||||
|
*/ |
||||||
|
private int heartBeatInterval; |
||||||
|
|
||||||
|
static { |
||||||
|
try { |
||||||
|
conf = new PropertiesConfiguration(Constants.MASTER_PROPERTIES_PATH); |
||||||
|
}catch (ConfigurationException e){ |
||||||
|
logger.error("load configuration failed : " + e.getMessage(),e); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public MasterServer(){} |
||||||
|
|
||||||
|
public MasterServer(ProcessDao processDao){ |
||||||
|
zkMasterClient = ZKMasterClient.getZKMasterClient(processDao); |
||||||
|
this.serverDao = zkMasterClient.getServerDao(); |
||||||
|
this.alertDao = zkMasterClient.getAlertDao(); |
||||||
|
} |
||||||
|
public void run(ProcessDao processDao){ |
||||||
|
|
||||||
|
// heartbeat interval
|
||||||
|
heartBeatInterval = conf.getInt(Constants.MASTER_HEARTBEAT_INTERVAL, |
||||||
|
Constants.defaultMasterHeartbeatInterval); |
||||||
|
|
||||||
|
heartbeatMasterService = ThreadUtils.newDaemonThreadScheduledExecutor("Master-Main-Thread",Constants.defaulMasterHeartbeatThreadNum); |
||||||
|
|
||||||
|
// heartbeat thread implement
|
||||||
|
Runnable heartBeatThread = heartBeatThread(); |
||||||
|
|
||||||
|
zkMasterClient.setStoppable(this); |
||||||
|
|
||||||
|
// regular heartbeat
|
||||||
|
// delay 5 seconds, send heartbeat every 30 seconds
|
||||||
|
heartbeatMasterService. |
||||||
|
scheduleAtFixedRate(heartBeatThread, 5, heartBeatInterval, TimeUnit.SECONDS); |
||||||
|
|
||||||
|
// master exec thread pool num
|
||||||
|
int masterExecThreadNum = conf.getInt(Constants.MASTER_EXEC_THREADS, |
||||||
|
Constants.defaultMasterExecThreadNum); |
||||||
|
|
||||||
|
// master scheduler thread
|
||||||
|
MasterSchedulerThread masterSchedulerThread = new MasterSchedulerThread( |
||||||
|
zkMasterClient, |
||||||
|
processDao,conf, |
||||||
|
masterExecThreadNum); |
||||||
|
|
||||||
|
// submit master scheduler thread
|
||||||
|
masterSchedulerService.execute(masterSchedulerThread); |
||||||
|
|
||||||
|
// start QuartzExecutors
|
||||||
|
try { |
||||||
|
ProcessScheduleJob.init(processDao); |
||||||
|
QuartzExecutors.getInstance().start(); |
||||||
|
} catch (Exception e) { |
||||||
|
try { |
||||||
|
QuartzExecutors.getInstance().shutdown(); |
||||||
|
} catch (SchedulerException e1) { |
||||||
|
logger.error("QuartzExecutors shutdown failed : " + e1.getMessage(), e1); |
||||||
|
} |
||||||
|
logger.error("start Quartz failed : " + e.getMessage(), e); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* register hooks, which are called before the process exits |
||||||
|
*/ |
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
String host = OSUtils.getHost(); |
||||||
|
// clear master table register info
|
||||||
|
serverDao.deleteMaster(host); |
||||||
|
logger.info("master server stopped"); |
||||||
|
if (zkMasterClient.getActiveMasterNum() <= 1) { |
||||||
|
for (int i = 0; i < Constants.ESCHEDULER_WARN_TIMES_FAILOVER;i++) { |
||||||
|
alertDao.sendServerStopedAlert(1, host, "Master-Server"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
})); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
SpringApplication app = new SpringApplication(MasterServer.class); |
||||||
|
app.setWebEnvironment(false); |
||||||
|
app.run(args); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* blocking implement |
||||||
|
* @throws InterruptedException |
||||||
|
*/ |
||||||
|
public void awaitTermination() throws InterruptedException { |
||||||
|
synchronized (lock) { |
||||||
|
while (!terminated) { |
||||||
|
lock.wait(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* heartbeat thread implement |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public Runnable heartBeatThread(){ |
||||||
|
Runnable heartBeatThread = new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
if(Stopper.isRunning()) { |
||||||
|
// send heartbeat to zk
|
||||||
|
if (StringUtils.isBlank(zkMasterClient.getMasterZNode())) { |
||||||
|
logger.error("master send heartbeat to zk failed"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
zkMasterClient.heartBeatForZk(zkMasterClient.getMasterZNode(), Constants.MASTER_PREFIX); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
return heartBeatThread; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run(String... strings) throws Exception { |
||||||
|
|
||||||
|
MasterServer masterServer = new MasterServer(processDao); |
||||||
|
|
||||||
|
masterServer.run(processDao); |
||||||
|
|
||||||
|
logger.info("master server started"); |
||||||
|
// blocking
|
||||||
|
masterServer.awaitTermination(); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* gracefully stop |
||||||
|
* @param cause why stopping |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public synchronized void stop(String cause) { |
||||||
|
|
||||||
|
try { |
||||||
|
//execute only once
|
||||||
|
if(Stopper.isStoped()){ |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
logger.info("master server is stopping ..., cause : {}", cause); |
||||||
|
|
||||||
|
// set stop signal is true
|
||||||
|
Stopper.stop(); |
||||||
|
|
||||||
|
try { |
||||||
|
//thread sleep 3 seconds for thread quitely stop
|
||||||
|
Thread.sleep(3000L); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.warn("thread sleep exception:" + e.getMessage(), e); |
||||||
|
} |
||||||
|
try { |
||||||
|
heartbeatMasterService.shutdownNow(); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.warn("heartbeat service stopped exception"); |
||||||
|
} |
||||||
|
|
||||||
|
logger.info("heartbeat service stopped"); |
||||||
|
|
||||||
|
//close quartz
|
||||||
|
try{ |
||||||
|
QuartzExecutors.getInstance().shutdown(); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.warn("Quartz service stopped exception:{}",e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
logger.info("Quartz service stopped"); |
||||||
|
|
||||||
|
try { |
||||||
|
ThreadPoolExecutors.getInstance().shutdown(); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.warn("threadpool service stopped exception:{}",e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
logger.info("threadpool service stopped"); |
||||||
|
|
||||||
|
try { |
||||||
|
masterSchedulerService.shutdownNow(); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.warn("master scheduler service stopped exception:{}",e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
logger.info("master scheduler service stopped"); |
||||||
|
|
||||||
|
try { |
||||||
|
zkMasterClient.close(); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.warn("zookeeper service stopped exception:{}",e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
logger.info("zookeeper service stopped"); |
||||||
|
|
||||||
|
synchronized (lock) { |
||||||
|
terminated = true; |
||||||
|
lock.notifyAll(); |
||||||
|
} |
||||||
|
|
||||||
|
} catch (Exception e) { |
||||||
|
logger.error("master server stop exception : " + e.getMessage(), e); |
||||||
|
System.exit(-1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,42 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.master.log; |
||||||
|
|
||||||
|
import ch.qos.logback.classic.Level; |
||||||
|
import ch.qos.logback.classic.spi.ILoggingEvent; |
||||||
|
import ch.qos.logback.core.filter.Filter; |
||||||
|
import ch.qos.logback.core.spi.FilterReply; |
||||||
|
|
||||||
|
/** |
||||||
|
* master log filter |
||||||
|
*/ |
||||||
|
public class MasterLogFilter extends Filter<ILoggingEvent> { |
||||||
|
|
||||||
|
Level level; |
||||||
|
|
||||||
|
@Override |
||||||
|
public FilterReply decide(ILoggingEvent event) { |
||||||
|
if (event.getThreadName().startsWith("Master-")){ |
||||||
|
return FilterReply.ACCEPT; |
||||||
|
} |
||||||
|
return FilterReply.DENY; |
||||||
|
} |
||||||
|
|
||||||
|
public void setLevel(String level) { |
||||||
|
this.level = Level.toLevel(level); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,132 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.master.runner; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.queue.ITaskQueue; |
||||||
|
import cn.escheduler.common.queue.TaskQueueFactory; |
||||||
|
import cn.escheduler.dao.AlertDao; |
||||||
|
import cn.escheduler.dao.ProcessDao; |
||||||
|
import cn.escheduler.dao.model.ProcessInstance; |
||||||
|
import cn.escheduler.dao.model.TaskInstance; |
||||||
|
import cn.escheduler.server.BeanContext; |
||||||
|
import org.apache.commons.configuration.Configuration; |
||||||
|
import org.apache.commons.configuration.ConfigurationException; |
||||||
|
import org.apache.commons.configuration.PropertiesConfiguration; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.concurrent.Callable; |
||||||
|
|
||||||
|
/** |
||||||
|
* master task exec base class
|
||||||
|
*/ |
||||||
|
public class MasterBaseTaskExecThread implements Callable<Boolean> { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(MasterBaseTaskExecThread.class); |
||||||
|
|
||||||
|
/** |
||||||
|
* process dao |
||||||
|
*/ |
||||||
|
protected ProcessDao processDao; |
||||||
|
|
||||||
|
/** |
||||||
|
* alert database access |
||||||
|
*/ |
||||||
|
protected AlertDao alertDao; |
||||||
|
|
||||||
|
/** |
||||||
|
* process instance |
||||||
|
*/ |
||||||
|
protected ProcessInstance processInstance; |
||||||
|
|
||||||
|
/** |
||||||
|
* task instance |
||||||
|
*/ |
||||||
|
protected TaskInstance taskInstance; |
||||||
|
|
||||||
|
/** |
||||||
|
* task queue |
||||||
|
*/ |
||||||
|
protected ITaskQueue taskQueue; |
||||||
|
protected boolean cancel; |
||||||
|
|
||||||
|
/** |
||||||
|
* load configuration file |
||||||
|
*/ |
||||||
|
private static Configuration conf; |
||||||
|
|
||||||
|
static { |
||||||
|
try { |
||||||
|
conf = new PropertiesConfiguration(Constants.MASTER_PROPERTIES_PATH); |
||||||
|
} catch (ConfigurationException e) { |
||||||
|
logger.error(e.getMessage(), e); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public MasterBaseTaskExecThread(TaskInstance taskInstance, ProcessInstance processInstance){ |
||||||
|
this.processDao = BeanContext.getBean(ProcessDao.class); |
||||||
|
this.alertDao = BeanContext.getBean(AlertDao.class); |
||||||
|
this.processInstance = processInstance; |
||||||
|
this.taskQueue = TaskQueueFactory.getTaskQueueInstance(); |
||||||
|
this.cancel = false; |
||||||
|
this.taskInstance = taskInstance; |
||||||
|
} |
||||||
|
|
||||||
|
public TaskInstance getTaskInstance(){ |
||||||
|
return this.taskInstance; |
||||||
|
} |
||||||
|
|
||||||
|
public void kill(){ |
||||||
|
this.cancel = true; |
||||||
|
} |
||||||
|
|
||||||
|
protected TaskInstance submit(){ |
||||||
|
Integer commitRetryTimes = conf.getInt(Constants.MASTER_COMMIT_RETRY_TIMES, |
||||||
|
Constants.defaultMasterCommitRetryTimes); |
||||||
|
Integer commitRetryInterval = conf.getInt(Constants.MASTER_COMMIT_RETRY_INTERVAL, |
||||||
|
Constants.defaultMasterCommitRetryInterval); |
||||||
|
|
||||||
|
int retryTimes = 1; |
||||||
|
|
||||||
|
while (retryTimes <= commitRetryTimes){ |
||||||
|
try { |
||||||
|
TaskInstance task = processDao.submitTask(taskInstance, processInstance); |
||||||
|
if(task != null){ |
||||||
|
return task; |
||||||
|
} |
||||||
|
logger.error("task commit to mysql and queue failed , task has already retry {} times, please check the database", commitRetryTimes); |
||||||
|
Thread.sleep(commitRetryInterval); |
||||||
|
} catch (Exception e) { |
||||||
|
logger.error("task commit to mysql and queue failed : " + e.getMessage(),e); |
||||||
|
} |
||||||
|
retryTimes += 1; |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
protected Boolean submitWaitComplete(){ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Boolean call() throws Exception { |
||||||
|
return submitWaitComplete(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,118 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.master.runner; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.thread.Stopper; |
||||||
|
import cn.escheduler.common.thread.ThreadUtils; |
||||||
|
import cn.escheduler.common.utils.OSUtils; |
||||||
|
import cn.escheduler.dao.ProcessDao; |
||||||
|
import cn.escheduler.dao.model.ProcessInstance; |
||||||
|
import cn.escheduler.server.zk.ZKMasterClient; |
||||||
|
import org.apache.commons.configuration.Configuration; |
||||||
|
import org.apache.curator.framework.imps.CuratorFrameworkState; |
||||||
|
import org.apache.curator.framework.recipes.locks.InterProcessMutex; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
import java.util.concurrent.ThreadPoolExecutor; |
||||||
|
|
||||||
|
/** |
||||||
|
* master scheduler thread |
||||||
|
*/ |
||||||
|
public class MasterSchedulerThread implements Runnable { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(MasterSchedulerThread.class); |
||||||
|
|
||||||
|
private final ExecutorService masterExecService; |
||||||
|
|
||||||
|
/** |
||||||
|
* escheduler database interface
|
||||||
|
*/ |
||||||
|
private final ProcessDao processDao; |
||||||
|
|
||||||
|
private final ZKMasterClient zkMasterClient ; |
||||||
|
|
||||||
|
private int masterExecThreadNum; |
||||||
|
|
||||||
|
private final Configuration conf; |
||||||
|
|
||||||
|
|
||||||
|
public MasterSchedulerThread(ZKMasterClient zkClient, ProcessDao processDao, Configuration conf, int masterExecThreadNum){ |
||||||
|
this.processDao = processDao; |
||||||
|
this.zkMasterClient = zkClient; |
||||||
|
this.conf = conf; |
||||||
|
this.masterExecThreadNum = masterExecThreadNum; |
||||||
|
this.masterExecService = ThreadUtils.newDaemonFixedThreadExecutor("Master-Exec-Thread",masterExecThreadNum); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
while (Stopper.isRunning()){ |
||||||
|
|
||||||
|
// process instance
|
||||||
|
ProcessInstance processInstance = null; |
||||||
|
|
||||||
|
InterProcessMutex mutex = null; |
||||||
|
try { |
||||||
|
|
||||||
|
if(OSUtils.checkResource(conf, true)){ |
||||||
|
if (zkMasterClient.getZkClient().getState() == CuratorFrameworkState.STARTED) { |
||||||
|
|
||||||
|
// create distributed lock with the root node path of the lock space as /escheduler/lock/failover/master
|
||||||
|
String znodeLock = zkMasterClient.getMasterLockPath(); |
||||||
|
|
||||||
|
mutex = new InterProcessMutex(zkMasterClient.getZkClient(), znodeLock); |
||||||
|
mutex.acquire(); |
||||||
|
|
||||||
|
ThreadPoolExecutor poolExecutor = (ThreadPoolExecutor) masterExecService; |
||||||
|
int activeCount = poolExecutor.getActiveCount(); |
||||||
|
// make sure to scan and delete command table in one transaction
|
||||||
|
processInstance = processDao.scanCommand(logger, OSUtils.getHost(), this.masterExecThreadNum - activeCount); |
||||||
|
if (processInstance != null) { |
||||||
|
logger.info("start master exex thread , split DAG ..."); |
||||||
|
masterExecService.execute(new MasterExecThread(processInstance)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// accessing the command table every SLEEP_TIME_MILLIS milliseconds
|
||||||
|
Thread.sleep(Constants.SLEEP_TIME_MILLIS); |
||||||
|
|
||||||
|
}catch (Exception e){ |
||||||
|
logger.error("master scheduler thread exception : " + e.getMessage(),e); |
||||||
|
}finally{ |
||||||
|
if (mutex != null){ |
||||||
|
try { |
||||||
|
mutex.release(); |
||||||
|
} catch (Exception e) { |
||||||
|
if(e.getMessage().equals("instance must be started before calling this method")){ |
||||||
|
logger.warn("lock release"); |
||||||
|
}else{ |
||||||
|
logger.error("lock release failed : " + e.getMessage(),e); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,165 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.master.runner; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.enums.ExecutionStatus; |
||||||
|
import cn.escheduler.common.enums.TaskTimeoutStrategy; |
||||||
|
import cn.escheduler.common.model.TaskNode; |
||||||
|
import cn.escheduler.common.task.TaskTimeoutParameter; |
||||||
|
import cn.escheduler.common.thread.Stopper; |
||||||
|
import cn.escheduler.dao.model.ProcessDefinition; |
||||||
|
import cn.escheduler.dao.model.ProcessInstance; |
||||||
|
import cn.escheduler.dao.model.TaskInstance; |
||||||
|
import com.alibaba.fastjson.JSONObject; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
|
||||||
|
import static cn.escheduler.common.Constants.SCHEDULER_TASKS_KILL; |
||||||
|
|
||||||
|
/** |
||||||
|
* master task exec thread |
||||||
|
*/ |
||||||
|
public class MasterTaskExecThread extends MasterBaseTaskExecThread { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(MasterTaskExecThread.class); |
||||||
|
|
||||||
|
|
||||||
|
public MasterTaskExecThread(TaskInstance taskInstance, ProcessInstance processInstance){ |
||||||
|
super(taskInstance, processInstance); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get task instance |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public TaskInstance getTaskInstance(){ |
||||||
|
return this.taskInstance; |
||||||
|
} |
||||||
|
|
||||||
|
private Boolean alreadyKilled = false; |
||||||
|
|
||||||
|
@Override |
||||||
|
public Boolean submitWaitComplete() { |
||||||
|
Boolean result = false; |
||||||
|
this.taskInstance = submit(); |
||||||
|
if(!this.taskInstance.getState().typeIsFinished()) { |
||||||
|
result = waitTaskQuit(); |
||||||
|
} |
||||||
|
taskInstance.setEndTime(new Date()); |
||||||
|
processDao.updateTaskInstance(taskInstance); |
||||||
|
logger.info("task :{} id:{}, process id:{}, exec thread completed ", |
||||||
|
this.taskInstance.getName(),taskInstance.getId(), processInstance.getId() ); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public Boolean waitTaskQuit(){ |
||||||
|
// query new state
|
||||||
|
taskInstance = processDao.findTaskInstanceById(taskInstance.getId()); |
||||||
|
Boolean result = true; |
||||||
|
// task time out
|
||||||
|
Boolean checkTimeout = false; |
||||||
|
TaskTimeoutParameter taskTimeoutParameter = getTaskTimeoutParameter(); |
||||||
|
if(taskTimeoutParameter.getEnable()){ |
||||||
|
TaskTimeoutStrategy strategy = taskTimeoutParameter.getStrategy(); |
||||||
|
if(strategy == TaskTimeoutStrategy.WARN || strategy == TaskTimeoutStrategy.WARNFAILED){ |
||||||
|
checkTimeout = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
while (Stopper.isRunning()){ |
||||||
|
try { |
||||||
|
if(this.processInstance == null){ |
||||||
|
logger.error("process instance not exists , master task exec thread exit"); |
||||||
|
return result; |
||||||
|
} |
||||||
|
// task instance add queue , waiting worker to kill
|
||||||
|
if(this.cancel || this.processInstance.getState() == ExecutionStatus.READY_STOP){ |
||||||
|
cancelTaskInstance(); |
||||||
|
} |
||||||
|
// task instance finished
|
||||||
|
if (taskInstance.getState().typeIsFinished()){ |
||||||
|
break; |
||||||
|
} |
||||||
|
if(checkTimeout){ |
||||||
|
long remainTime = getRemaintime(taskTimeoutParameter.getInterval()*60); |
||||||
|
if (remainTime < 0) { |
||||||
|
logger.warn("task id: {} execution time out",taskInstance.getId()); |
||||||
|
// process define
|
||||||
|
ProcessDefinition processDefine = processDao.findProcessDefineById(processInstance.getProcessDefinitionId()); |
||||||
|
// send warn mail
|
||||||
|
alertDao.sendTaskTimeoutAlert(processInstance.getWarningGroupId(),processDefine.getReceivers(),processDefine.getReceiversCc(),taskInstance.getId(),taskInstance.getName()); |
||||||
|
checkTimeout = false; |
||||||
|
} |
||||||
|
} |
||||||
|
// updateProcessInstance task instance
|
||||||
|
taskInstance = processDao.findTaskInstanceById(taskInstance.getId()); |
||||||
|
processInstance = processDao.findProcessInstanceById(processInstance.getId()); |
||||||
|
Thread.sleep(Constants.SLEEP_TIME_MILLIS); |
||||||
|
} catch (Exception e) { |
||||||
|
logger.error("exception: "+ e.getMessage(),e); |
||||||
|
logger.error("wait task quit failed, instance id:{}, task id:{}", |
||||||
|
processInstance.getId(), taskInstance.getId()); |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* task instance add queue , waiting worker to kill |
||||||
|
*/ |
||||||
|
private void cancelTaskInstance(){ |
||||||
|
if(alreadyKilled || taskInstance.getHost() == null){ |
||||||
|
return ; |
||||||
|
} |
||||||
|
alreadyKilled = true; |
||||||
|
String queueValue = String.format("%s-%d", |
||||||
|
taskInstance.getHost(), taskInstance.getId()); |
||||||
|
taskQueue.sadd(SCHEDULER_TASKS_KILL, queueValue); |
||||||
|
|
||||||
|
logger.info("master add kill task :{} id:{} to kill queue", |
||||||
|
taskInstance.getName(), taskInstance.getId() ); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get task timeout parameter |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private TaskTimeoutParameter getTaskTimeoutParameter(){ |
||||||
|
String taskJson = taskInstance.getTaskJson(); |
||||||
|
TaskNode taskNode = JSONObject.parseObject(taskJson, TaskNode.class); |
||||||
|
return taskNode.getTaskTimeoutParameter(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* get remain time(s) |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private long getRemaintime(long timeoutSeconds) { |
||||||
|
Date startTime = taskInstance.getStartTime(); |
||||||
|
long usedTime = (System.currentTimeMillis() - startTime.getTime()) / 1000; |
||||||
|
long remainTime = timeoutSeconds - usedTime; |
||||||
|
return remainTime; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,178 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.master.runner; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.enums.ExecutionStatus; |
||||||
|
import cn.escheduler.common.thread.Stopper; |
||||||
|
import cn.escheduler.dao.model.ProcessInstance; |
||||||
|
import cn.escheduler.dao.model.TaskInstance; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
|
||||||
|
/** |
||||||
|
* subflow task exec thread |
||||||
|
*/ |
||||||
|
public class SubProcessTaskExecThread extends MasterBaseTaskExecThread { |
||||||
|
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SubProcessTaskExecThread.class); |
||||||
|
|
||||||
|
|
||||||
|
private ProcessInstance subProcessInstance; |
||||||
|
|
||||||
|
public SubProcessTaskExecThread(TaskInstance taskInstance, ProcessInstance processInstance){ |
||||||
|
super(taskInstance, processInstance); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Boolean submitWaitComplete() { |
||||||
|
|
||||||
|
Boolean result = false; |
||||||
|
try{ |
||||||
|
// submit task instance
|
||||||
|
this.taskInstance = submit(); |
||||||
|
|
||||||
|
if(taskInstance == null){ |
||||||
|
logger.error("sub work flow submit task instance to mysql and queue failed , please check and fix it"); |
||||||
|
return result; |
||||||
|
} |
||||||
|
setTaskInstanceState(); |
||||||
|
waitTaskQuit(); |
||||||
|
subProcessInstance = processDao.findSubProcessInstance(processInstance.getId(), taskInstance.getId()); |
||||||
|
|
||||||
|
// at the end of the subflow , the task state is changed to the subflow state
|
||||||
|
if(subProcessInstance != null){ |
||||||
|
if(subProcessInstance.getState() == ExecutionStatus.STOP){ |
||||||
|
this.taskInstance.setState(ExecutionStatus.KILL); |
||||||
|
}else{ |
||||||
|
this.taskInstance.setState(subProcessInstance.getState()); |
||||||
|
result = true; |
||||||
|
} |
||||||
|
} |
||||||
|
taskInstance.setEndTime(new Date()); |
||||||
|
processDao.updateTaskInstance(taskInstance); |
||||||
|
logger.info("subflow task :{} id:{}, process id:{}, exec thread completed ", |
||||||
|
this.taskInstance.getName(),taskInstance.getId(), processInstance.getId() ); |
||||||
|
result = true; |
||||||
|
|
||||||
|
}catch (Exception e){ |
||||||
|
logger.error("exception: "+ e.getMessage(),e); |
||||||
|
logger.error("wait task quit failed, instance id:{}, task id:{}", |
||||||
|
processInstance.getId(), taskInstance.getId()); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* set task instance state |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private Boolean setTaskInstanceState(){ |
||||||
|
subProcessInstance = processDao.findSubProcessInstance(processInstance.getId(), taskInstance.getId()); |
||||||
|
if(subProcessInstance == null || taskInstance.getState().typeIsFinished()){ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
taskInstance.setState(ExecutionStatus.RUNNING_EXEUTION); |
||||||
|
taskInstance.setStartTime(new Date()); |
||||||
|
processDao.updateTaskInstance(taskInstance); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* updateProcessInstance parent state |
||||||
|
*/ |
||||||
|
private void updateParentProcessState(){ |
||||||
|
ProcessInstance parentProcessInstance = processDao.findProcessInstanceById(this.processInstance.getId()); |
||||||
|
|
||||||
|
if(parentProcessInstance == null){ |
||||||
|
logger.error("parent work flow instance is null , please check it! work flow id {}", processInstance.getId()); |
||||||
|
return; |
||||||
|
} |
||||||
|
this.processInstance.setState(parentProcessInstance.getState()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* wait task quit |
||||||
|
* @throws InterruptedException |
||||||
|
*/ |
||||||
|
private void waitTaskQuit() throws InterruptedException { |
||||||
|
|
||||||
|
logger.info("wait sub work flow: {} complete", this.taskInstance.getName()); |
||||||
|
|
||||||
|
if (taskInstance.getState().typeIsFinished()) { |
||||||
|
logger.info("sub work flow task {} already complete. task state:{}, parent work flow instance state:{}", |
||||||
|
this.taskInstance.getName(), |
||||||
|
this.taskInstance.getState().toString(), |
||||||
|
this.processInstance.getState().toString()); |
||||||
|
return; |
||||||
|
} |
||||||
|
while (Stopper.isRunning()) { |
||||||
|
// waiting for subflow process instance establishment
|
||||||
|
if (subProcessInstance == null) { |
||||||
|
|
||||||
|
Thread.sleep(Constants.SLEEP_TIME_MILLIS); |
||||||
|
|
||||||
|
if(!setTaskInstanceState()){ |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
subProcessInstance = processDao.findProcessInstanceById(subProcessInstance.getId()); |
||||||
|
updateParentProcessState(); |
||||||
|
if (subProcessInstance.getState().typeIsFinished()){ |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if(this.processInstance.getState() == ExecutionStatus.READY_PAUSE){ |
||||||
|
// parent process "ready to pause" , child process "pause"
|
||||||
|
pauseSubProcess(); |
||||||
|
}else if(this.cancel || this.processInstance.getState() == ExecutionStatus.READY_STOP){ |
||||||
|
// parent Process "Ready to Cancel" , subflow "Cancel"
|
||||||
|
stopSubProcess(); |
||||||
|
} |
||||||
|
Thread.sleep(Constants.SLEEP_TIME_MILLIS); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* stop subflow |
||||||
|
*/ |
||||||
|
private void stopSubProcess() { |
||||||
|
if(subProcessInstance.getState() == ExecutionStatus.STOP || |
||||||
|
subProcessInstance.getState() == ExecutionStatus.READY_STOP){ |
||||||
|
return; |
||||||
|
} |
||||||
|
subProcessInstance.setState(ExecutionStatus.READY_STOP); |
||||||
|
processDao.updateProcessInstance(subProcessInstance); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* pause subflow |
||||||
|
*/ |
||||||
|
private void pauseSubProcess() { |
||||||
|
if(subProcessInstance.getState() == ExecutionStatus.PAUSE || |
||||||
|
subProcessInstance.getState() == ExecutionStatus.READY_PAUSE){ |
||||||
|
return; |
||||||
|
} |
||||||
|
subProcessInstance.setState(ExecutionStatus.READY_PAUSE); |
||||||
|
processDao.updateProcessInstance(subProcessInstance); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,122 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.rpc; |
||||||
|
|
||||||
|
import cn.escheduler.rpc.*; |
||||||
|
import io.grpc.ManagedChannel; |
||||||
|
import io.grpc.ManagedChannelBuilder; |
||||||
|
import io.grpc.StatusRuntimeException; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
/** |
||||||
|
* log client |
||||||
|
*/ |
||||||
|
public class LogClient { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(LogClient.class); |
||||||
|
|
||||||
|
private final ManagedChannel channel; |
||||||
|
private final LogViewServiceGrpc.LogViewServiceBlockingStub blockingStub; |
||||||
|
|
||||||
|
/** Construct client connecting to HelloWorld server at {@code host:port}. */ |
||||||
|
public LogClient(String host, int port) { |
||||||
|
this(ManagedChannelBuilder.forAddress(host, port) |
||||||
|
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
|
||||||
|
// needing certificates.
|
||||||
|
.usePlaintext(true)); |
||||||
|
} |
||||||
|
|
||||||
|
/** Construct client for accessing RouteGuide server using the existing channel. */ |
||||||
|
LogClient(ManagedChannelBuilder<?> channelBuilder) { |
||||||
|
/** |
||||||
|
* set max message read size |
||||||
|
*/ |
||||||
|
channelBuilder.maxInboundMessageSize(Integer.MAX_VALUE); |
||||||
|
channel = channelBuilder.build(); |
||||||
|
blockingStub = LogViewServiceGrpc.newBlockingStub(channel); |
||||||
|
} |
||||||
|
|
||||||
|
public void shutdown() throws InterruptedException { |
||||||
|
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* roll view log |
||||||
|
* @param path |
||||||
|
* @param skipLineNum |
||||||
|
* @param limit |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String rollViewLog(String path,int skipLineNum,int limit) { |
||||||
|
logger.info("roll view log , path : {},skipLineNum : {} ,limit :{}", path, skipLineNum, limit); |
||||||
|
LogParameter pathParameter = LogParameter |
||||||
|
.newBuilder() |
||||||
|
.setPath(path) |
||||||
|
.setSkipLineNum(skipLineNum) |
||||||
|
.setLimit(limit) |
||||||
|
.build(); |
||||||
|
RetStrInfo retStrInfo; |
||||||
|
try { |
||||||
|
retStrInfo = blockingStub.rollViewLog(pathParameter); |
||||||
|
return retStrInfo.getMsg(); |
||||||
|
} catch (StatusRuntimeException e) { |
||||||
|
logger.error("roll view log failed : " + e.getMessage(), e); |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* view all log |
||||||
|
* @param path |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String viewLog(String path) { |
||||||
|
logger.info("view log path : {}",path); |
||||||
|
|
||||||
|
PathParameter pathParameter = PathParameter.newBuilder().setPath(path).build(); |
||||||
|
RetStrInfo retStrInfo; |
||||||
|
try { |
||||||
|
retStrInfo = blockingStub.viewLog(pathParameter); |
||||||
|
return retStrInfo.getMsg(); |
||||||
|
} catch (StatusRuntimeException e) { |
||||||
|
logger.error("view log failed : " + e.getMessage(), e); |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get log bytes |
||||||
|
* @param path |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public byte[] getLogBytes(String path) { |
||||||
|
logger.info("get log bytes {}",path); |
||||||
|
|
||||||
|
PathParameter pathParameter = PathParameter.newBuilder().setPath(path).build(); |
||||||
|
RetByteInfo retByteInfo; |
||||||
|
try { |
||||||
|
retByteInfo = blockingStub.getLogBytes(pathParameter); |
||||||
|
return retByteInfo.getData().toByteArray(); |
||||||
|
} catch (StatusRuntimeException e) { |
||||||
|
logger.error("get log bytes failed : " + e.getMessage(), e); |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,204 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.rpc; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.rpc.*; |
||||||
|
import com.google.protobuf.ByteString; |
||||||
|
import io.grpc.Server; |
||||||
|
import io.grpc.ServerBuilder; |
||||||
|
import io.grpc.stub.StreamObserver; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.nio.file.Files; |
||||||
|
import java.nio.file.Paths; |
||||||
|
import java.util.List; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
import java.util.stream.Stream; |
||||||
|
|
||||||
|
/** |
||||||
|
* logger server |
||||||
|
*/ |
||||||
|
public class LoggerServer { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(LoggerServer.class); |
||||||
|
|
||||||
|
/** |
||||||
|
* server |
||||||
|
*/ |
||||||
|
private Server server; |
||||||
|
|
||||||
|
private void start() throws IOException { |
||||||
|
/* The port on which the server should run */ |
||||||
|
int port = Constants.RPC_PORT; |
||||||
|
server = ServerBuilder.forPort(port) |
||||||
|
.addService(new LogViewServiceGrpcImpl()) |
||||||
|
.build() |
||||||
|
.start(); |
||||||
|
logger.info("server started, listening on port : {}" , port); |
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
// Use stderr here since the logger may have been reset by its JVM shutdown hook.
|
||||||
|
logger.info("shutting down gRPC server since JVM is shutting down"); |
||||||
|
LoggerServer.this.stop(); |
||||||
|
logger.info("server shut down"); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void stop() { |
||||||
|
if (server != null) { |
||||||
|
server.shutdown(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* await termination on the main thread since the grpc library uses daemon threads. |
||||||
|
*/ |
||||||
|
private void blockUntilShutdown() throws InterruptedException { |
||||||
|
if (server != null) { |
||||||
|
server.awaitTermination(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* main launches the server from the command line. |
||||||
|
*/ |
||||||
|
public static void main(String[] args) throws IOException, InterruptedException { |
||||||
|
final LoggerServer server = new LoggerServer(); |
||||||
|
server.start(); |
||||||
|
server.blockUntilShutdown(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static class LogViewServiceGrpcImpl extends LogViewServiceGrpc.LogViewServiceImplBase { |
||||||
|
@Override |
||||||
|
public void rollViewLog(LogParameter request, StreamObserver<RetStrInfo> responseObserver) { |
||||||
|
|
||||||
|
logger.info("log parameter path : {} ,skipLine : {}, limit : {}", |
||||||
|
request.getPath(), |
||||||
|
request.getSkipLineNum(), |
||||||
|
request.getLimit()); |
||||||
|
List<String> list = readFile(request.getPath(), request.getSkipLineNum(), request.getLimit()); |
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
for (String line : list){ |
||||||
|
sb.append(line + "\r\n"); |
||||||
|
} |
||||||
|
RetStrInfo retInfoBuild = RetStrInfo.newBuilder().setMsg(sb.toString()).build(); |
||||||
|
responseObserver.onNext(retInfoBuild); |
||||||
|
responseObserver.onCompleted(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void viewLog(PathParameter request, StreamObserver<RetStrInfo> responseObserver) { |
||||||
|
logger.info("task path is : {} " , request.getPath()); |
||||||
|
RetStrInfo retInfoBuild = RetStrInfo.newBuilder().setMsg(readFile(request.getPath())).build(); |
||||||
|
responseObserver.onNext(retInfoBuild); |
||||||
|
responseObserver.onCompleted(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void getLogBytes(PathParameter request, StreamObserver<RetByteInfo> responseObserver) { |
||||||
|
try { |
||||||
|
ByteString bytes = ByteString.copyFrom(getFileBytes(request.getPath())); |
||||||
|
RetByteInfo.Builder builder = RetByteInfo.newBuilder(); |
||||||
|
builder.setData(bytes); |
||||||
|
responseObserver.onNext(builder.build()); |
||||||
|
responseObserver.onCompleted(); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.error("get log bytes failed : " + e.getMessage(),e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get files bytes |
||||||
|
* @param path |
||||||
|
* @return |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
private static byte[] getFileBytes(String path)throws IOException{ |
||||||
|
InputStream in = null; |
||||||
|
ByteArrayOutputStream bos = null; |
||||||
|
try { |
||||||
|
in = new FileInputStream(path); |
||||||
|
bos = new ByteArrayOutputStream(); |
||||||
|
byte[] buffer = new byte[4096]; |
||||||
|
int n = 0; |
||||||
|
while ((n = in.read(buffer)) != -1) { |
||||||
|
bos.write(buffer, 0, n); |
||||||
|
} |
||||||
|
return bos.toByteArray(); |
||||||
|
}catch (IOException e){ |
||||||
|
logger.error("getFileBytes error",e); |
||||||
|
}finally { |
||||||
|
bos.close(); |
||||||
|
in.close(); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* read file content |
||||||
|
* @param path |
||||||
|
* @param skipLine |
||||||
|
* @param limit |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private static List<String> readFile(String path,int skipLine,int limit){ |
||||||
|
try (Stream<String> stream = Files.lines(Paths.get(path))) { |
||||||
|
return stream.skip(skipLine).limit(limit).collect(Collectors.toList()); |
||||||
|
} catch (IOException e) { |
||||||
|
logger.error("read file failed : " + e.getMessage(),e); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* read file content |
||||||
|
* @param path |
||||||
|
* @return |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
private static String readFile(String path){ |
||||||
|
BufferedReader br = null; |
||||||
|
String line = null; |
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
try { |
||||||
|
br = new BufferedReader(new InputStreamReader(new FileInputStream(path))); |
||||||
|
while ((line = br.readLine()) != null){ |
||||||
|
sb.append(line + "\r\n"); |
||||||
|
} |
||||||
|
return sb.toString(); |
||||||
|
}catch (IOException e){ |
||||||
|
logger.error("read file failed : " + e.getMessage(),e); |
||||||
|
}finally { |
||||||
|
try { |
||||||
|
if (br != null){ |
||||||
|
br.close(); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
logger.error(e.getMessage(),e); |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,236 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.utils; |
||||||
|
|
||||||
|
|
||||||
|
import cn.escheduler.common.enums.AlertType; |
||||||
|
import cn.escheduler.common.enums.CommandType; |
||||||
|
import cn.escheduler.common.enums.ShowType; |
||||||
|
import cn.escheduler.common.enums.WarningType; |
||||||
|
import cn.escheduler.common.utils.DateUtils; |
||||||
|
import cn.escheduler.common.utils.JSONUtils; |
||||||
|
import cn.escheduler.dao.AlertDao; |
||||||
|
import cn.escheduler.dao.DaoFactory; |
||||||
|
import cn.escheduler.dao.model.Alert; |
||||||
|
import cn.escheduler.dao.model.ProcessInstance; |
||||||
|
import cn.escheduler.dao.model.TaskInstance; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Date; |
||||||
|
import java.util.LinkedHashMap; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* alert manager |
||||||
|
*/ |
||||||
|
public class AlertManager { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(AlertManager.class); |
||||||
|
|
||||||
|
private AlertDao alertDao = DaoFactory.getDaoInstance(AlertDao.class); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* command type convert chinese |
||||||
|
* @param commandType |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private String getCommandCnName(CommandType commandType) { |
||||||
|
switch (commandType) { |
||||||
|
case RECOVER_TOLERANCE_FAULT_PROCESS: |
||||||
|
return "恢复容错"; |
||||||
|
case RECOVER_SUSPENDED_PROCESS: |
||||||
|
return "恢复暂停流程"; |
||||||
|
case START_CURRENT_TASK_PROCESS: |
||||||
|
return "从当前节点开始执行"; |
||||||
|
case START_FAILURE_TASK_PROCESS: |
||||||
|
return "从失败节点开始执行"; |
||||||
|
case START_PROCESS: |
||||||
|
return "启动工作流"; |
||||||
|
case REPEAT_RUNNING: |
||||||
|
return "重跑"; |
||||||
|
case SCHEDULER: |
||||||
|
return "定时执行"; |
||||||
|
case COMPLEMENT_DATA: |
||||||
|
return "补数"; |
||||||
|
case PAUSE: |
||||||
|
return "暂停工作流"; |
||||||
|
case STOP: |
||||||
|
return "停止工作流"; |
||||||
|
default: |
||||||
|
return "未知的命令类型"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* process instance format |
||||||
|
*/ |
||||||
|
private static final String PROCESS_INSTANCE_FORMAT = |
||||||
|
"\"Id:%d\"," + |
||||||
|
"\"Name:%s\"," + |
||||||
|
"\"Job type: %s\"," + |
||||||
|
"\"State: %s\"," + |
||||||
|
"\"Recovery:%s\"," + |
||||||
|
"\"Run time: %d\"," + |
||||||
|
"\"Start time: %s\"," + |
||||||
|
"\"End time: %s\"," + |
||||||
|
"\"Host: %s\"" ; |
||||||
|
|
||||||
|
/** |
||||||
|
* get process instance content |
||||||
|
* @param processInstance |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String getContentProcessInstance(ProcessInstance processInstance, |
||||||
|
List<TaskInstance> taskInstances){ |
||||||
|
|
||||||
|
String res = ""; |
||||||
|
if(processInstance.getState().typeIsSuccess()){ |
||||||
|
res = String.format(PROCESS_INSTANCE_FORMAT, |
||||||
|
processInstance.getId(), |
||||||
|
processInstance.getName(), |
||||||
|
getCommandCnName(processInstance.getCommandType()), |
||||||
|
processInstance.getState().toString(), |
||||||
|
processInstance.getRecovery().toString(), |
||||||
|
processInstance.getRunTimes(), |
||||||
|
DateUtils.dateToString(processInstance.getStartTime()), |
||||||
|
DateUtils.dateToString(processInstance.getEndTime()), |
||||||
|
processInstance.getHost() |
||||||
|
|
||||||
|
); |
||||||
|
res = "[" + res + "]"; |
||||||
|
}else if(processInstance.getState().typeIsFailure()){ |
||||||
|
|
||||||
|
List<LinkedHashMap> failedTaskList = new ArrayList<>(); |
||||||
|
|
||||||
|
for(TaskInstance task : taskInstances){ |
||||||
|
if(task.getState().typeIsSuccess()){ |
||||||
|
continue; |
||||||
|
} |
||||||
|
LinkedHashMap<String, String> failedTaskMap = new LinkedHashMap(); |
||||||
|
failedTaskMap.put("任务id", String.valueOf(task.getId())); |
||||||
|
failedTaskMap.put("任务名称", task.getName()); |
||||||
|
failedTaskMap.put("任务类型", task.getTaskType()); |
||||||
|
failedTaskMap.put("任务状态", task.getState().toString()); |
||||||
|
failedTaskMap.put("任务开始时间", DateUtils.dateToString(task.getStartTime())); |
||||||
|
failedTaskMap.put("任务结束时间", DateUtils.dateToString(task.getEndTime())); |
||||||
|
failedTaskMap.put("host", task.getHost()); |
||||||
|
failedTaskMap.put("日志路径", task.getLogPath()); |
||||||
|
failedTaskList.add(failedTaskMap); |
||||||
|
} |
||||||
|
res = JSONUtils.toJson(failedTaskList); |
||||||
|
} |
||||||
|
|
||||||
|
return res; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* getting worker fault tolerant content |
||||||
|
* @param processInstance |
||||||
|
* @param toleranceTaskList |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private String getWorkerToleranceContent(ProcessInstance processInstance, List<TaskInstance> toleranceTaskList){ |
||||||
|
|
||||||
|
List<LinkedHashMap<String, String>> toleranceTaskInstanceList = new ArrayList<>(); |
||||||
|
|
||||||
|
for(TaskInstance taskInstance: toleranceTaskList){ |
||||||
|
LinkedHashMap<String, String> toleranceWorkerContentMap = new LinkedHashMap(); |
||||||
|
toleranceWorkerContentMap.put("工作流程名称", processInstance.getName()); |
||||||
|
toleranceWorkerContentMap.put("容错任务名称", taskInstance.getName()); |
||||||
|
toleranceWorkerContentMap.put("容错机器IP", taskInstance.getHost()); |
||||||
|
toleranceWorkerContentMap.put("任务失败次数", String.valueOf(taskInstance.getRetryTimes())); |
||||||
|
toleranceTaskInstanceList.add(toleranceWorkerContentMap); |
||||||
|
} |
||||||
|
return JSONUtils.toJson(toleranceTaskInstanceList); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* send worker alert fault tolerance |
||||||
|
* @param processInstance |
||||||
|
* @param toleranceTaskList |
||||||
|
*/ |
||||||
|
public void sendWarnningWorkerleranceFault(ProcessInstance processInstance, List<TaskInstance> toleranceTaskList){ |
||||||
|
Alert alert = new Alert(); |
||||||
|
alert.setTitle("worker容错报警"); |
||||||
|
alert.setShowType(ShowType.TABLE); |
||||||
|
String content = getWorkerToleranceContent(processInstance, toleranceTaskList); |
||||||
|
alert.setContent(content); |
||||||
|
alert.setAlertType(AlertType.EMAIL); |
||||||
|
alert.setCreateTime(new Date()); |
||||||
|
alert.setAlertGroupId(processInstance.getWarningGroupId()); |
||||||
|
alert.setReceivers(processInstance.getProcessDefinition().getReceivers()); |
||||||
|
alert.setReceiversCc(processInstance.getProcessDefinition().getReceiversCc()); |
||||||
|
|
||||||
|
alertDao.addAlert(alert); |
||||||
|
logger.info("add alert to db , alert : {}", alert.toString()); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* send process instance alert |
||||||
|
* @param processInstance |
||||||
|
*/ |
||||||
|
public void sendWarnningOfProcessInstance(ProcessInstance processInstance, |
||||||
|
List<TaskInstance> taskInstances){ |
||||||
|
|
||||||
|
boolean sendWarnning = false; |
||||||
|
WarningType warningType = processInstance.getWarningType(); |
||||||
|
switch (warningType){ |
||||||
|
case ALL: |
||||||
|
if(processInstance.getState().typeIsFinished()){ |
||||||
|
sendWarnning = true; |
||||||
|
} |
||||||
|
break; |
||||||
|
case SUCCESS: |
||||||
|
if(processInstance.getState().typeIsSuccess()){ |
||||||
|
sendWarnning = true; |
||||||
|
} |
||||||
|
break; |
||||||
|
case FAILURE: |
||||||
|
if(processInstance.getState().typeIsFailure()){ |
||||||
|
sendWarnning = true; |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
} |
||||||
|
if(!sendWarnning){ |
||||||
|
return; |
||||||
|
} |
||||||
|
Alert alert = new Alert(); |
||||||
|
|
||||||
|
|
||||||
|
String cmdName = getCommandCnName(processInstance.getCommandType()); |
||||||
|
String success = processInstance.getState().typeIsSuccess() ? "成功" :"失败"; |
||||||
|
alert.setTitle(cmdName + success); |
||||||
|
ShowType showType = processInstance.getState().typeIsSuccess() ? ShowType.TEXT : ShowType.TABLE; |
||||||
|
alert.setShowType(showType); |
||||||
|
String content = getContentProcessInstance(processInstance, taskInstances); |
||||||
|
alert.setContent(content); |
||||||
|
alert.setAlertType(AlertType.EMAIL); |
||||||
|
alert.setAlertGroupId(processInstance.getWarningGroupId()); |
||||||
|
alert.setCreateTime(new Date()); |
||||||
|
alert.setReceivers(processInstance.getProcessDefinition().getReceivers()); |
||||||
|
alert.setReceiversCc(processInstance.getProcessDefinition().getReceiversCc()); |
||||||
|
|
||||||
|
alertDao.addAlert(alert); |
||||||
|
logger.info("add alert to db , alert: {}", alert.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.utils; |
||||||
|
|
||||||
|
import org.slf4j.Logger; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.regex.Matcher; |
||||||
|
import java.util.regex.Pattern; |
||||||
|
|
||||||
|
/** |
||||||
|
* logger utils |
||||||
|
*/ |
||||||
|
public class LoggerUtils { |
||||||
|
|
||||||
|
/** |
||||||
|
* rules for extracting application ID |
||||||
|
*/ |
||||||
|
private static final Pattern APPLICATION_REGEX = Pattern.compile("\\d+_\\d+"); |
||||||
|
|
||||||
|
/** |
||||||
|
* build job id |
||||||
|
* @param affix |
||||||
|
* @param processDefId |
||||||
|
* @param processInstId |
||||||
|
* @param taskId |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static String buildTaskId(String affix, |
||||||
|
int processDefId, |
||||||
|
int processInstId, |
||||||
|
int taskId){ |
||||||
|
return String.format("%s_%s_%s_%s",affix, |
||||||
|
processDefId, |
||||||
|
processInstId, |
||||||
|
taskId); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* processing log |
||||||
|
* get yarn application id list |
||||||
|
* @param log |
||||||
|
* @param logger |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static List<String> getAppIds(String log, Logger logger) { |
||||||
|
|
||||||
|
List<String> appIds = new ArrayList<String>(); |
||||||
|
|
||||||
|
Matcher matcher = APPLICATION_REGEX.matcher(log); |
||||||
|
|
||||||
|
// analyse logs to get all submit yarn application id
|
||||||
|
while (matcher.find()) { |
||||||
|
String appId = matcher.group(); |
||||||
|
if(!appIds.contains(appId)){ |
||||||
|
logger.info("find app id: {}", appId); |
||||||
|
appIds.add(appId); |
||||||
|
} |
||||||
|
} |
||||||
|
return appIds; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,103 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.utils; |
||||||
|
|
||||||
|
import cn.escheduler.common.enums.CommandType; |
||||||
|
import cn.escheduler.common.process.Property; |
||||||
|
import cn.escheduler.common.utils.ParameterUtils; |
||||||
|
import cn.escheduler.common.utils.placeholder.BusinessTimeUtils; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* param utils |
||||||
|
*/ |
||||||
|
public class ParamUtils { |
||||||
|
|
||||||
|
/** |
||||||
|
* parameter conversion |
||||||
|
* |
||||||
|
* @param globalParams |
||||||
|
* @param localParams |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static Map<String,Property> convert(Map<String,Property> globalParams, |
||||||
|
Map<String,String> globalParamsMap, |
||||||
|
Map<String,Property> localParams, |
||||||
|
CommandType commandType, |
||||||
|
Date scheduleTime){ |
||||||
|
if (globalParams == null |
||||||
|
&& globalParams == null |
||||||
|
&& localParams == null){ |
||||||
|
return null; |
||||||
|
} |
||||||
|
// if it is a complement,
|
||||||
|
// you need to pass in the task instance id to locate the time
|
||||||
|
// of the process instance complement
|
||||||
|
Map<String,String> timeParams = BusinessTimeUtils |
||||||
|
.getBusinessTime(commandType, |
||||||
|
scheduleTime); |
||||||
|
|
||||||
|
if (globalParamsMap != null){ |
||||||
|
timeParams.putAll(globalParamsMap); |
||||||
|
} |
||||||
|
|
||||||
|
if (globalParams != null && localParams != null){ |
||||||
|
globalParams.putAll(localParams); |
||||||
|
}else if (globalParams == null && localParams != null){ |
||||||
|
globalParams = localParams; |
||||||
|
} |
||||||
|
Iterator<Map.Entry<String, Property>> iter = globalParams.entrySet().iterator(); |
||||||
|
while (iter.hasNext()){ |
||||||
|
Map.Entry<String, Property> en = iter.next(); |
||||||
|
Property property = en.getValue(); |
||||||
|
|
||||||
|
if (property.getValue() != null && property.getValue().length() > 0){ |
||||||
|
if (property.getValue().startsWith("$")){ |
||||||
|
/** |
||||||
|
* local parameter refers to global parameter with the same name |
||||||
|
* note: the global parameters of the process instance here are solidified parameters, |
||||||
|
* and there are no variables in them. |
||||||
|
*/ |
||||||
|
String val = property.getValue(); |
||||||
|
val = ParameterUtils.convertParameterPlaceholders(val, timeParams); |
||||||
|
property.setValue(val); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return globalParams; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* format convert |
||||||
|
* @param paramsMap |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static Map<String,String> convert(Map<String,Property> paramsMap){ |
||||||
|
Map<String,String> map = new HashMap<>(); |
||||||
|
Iterator<Map.Entry<String, Property>> iter = paramsMap.entrySet().iterator(); |
||||||
|
while (iter.hasNext()){ |
||||||
|
Map.Entry<String, Property> en = iter.next(); |
||||||
|
map.put(en.getKey(),en.getValue().getValue()); |
||||||
|
} |
||||||
|
return map; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,301 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.utils; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.utils.CommonUtils; |
||||||
|
import cn.escheduler.dao.model.TaskInstance; |
||||||
|
import cn.escheduler.server.rpc.LogClient; |
||||||
|
import org.apache.commons.io.FileUtils; |
||||||
|
import org.apache.commons.lang.StringUtils; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.charset.Charset; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.regex.Matcher; |
||||||
|
import java.util.regex.Pattern; |
||||||
|
|
||||||
|
/** |
||||||
|
* mainly used to get the start command line of a process |
||||||
|
*/ |
||||||
|
public class ProcessUtils { |
||||||
|
/** |
||||||
|
* logger |
||||||
|
*/ |
||||||
|
private final static Logger logger = LoggerFactory.getLogger(ProcessUtils.class); |
||||||
|
|
||||||
|
/** |
||||||
|
* build command line characters |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static String buildCommandStr(List<String> commandList) throws IOException { |
||||||
|
String cmdstr; |
||||||
|
String[] cmd = commandList.toArray(new String[commandList.size()]); |
||||||
|
SecurityManager security = System.getSecurityManager(); |
||||||
|
boolean allowAmbiguousCommands = false; |
||||||
|
if (security == null) { |
||||||
|
allowAmbiguousCommands = true; |
||||||
|
String value = System.getProperty("jdk.lang.Process.allowAmbiguousCommands"); |
||||||
|
if (value != null) { |
||||||
|
allowAmbiguousCommands = !"false".equalsIgnoreCase(value); |
||||||
|
} |
||||||
|
} |
||||||
|
if (allowAmbiguousCommands) { |
||||||
|
|
||||||
|
String executablePath = new File(cmd[0]).getPath(); |
||||||
|
|
||||||
|
if (needsEscaping(VERIFICATION_LEGACY, executablePath)) { |
||||||
|
executablePath = quoteString(executablePath); |
||||||
|
} |
||||||
|
|
||||||
|
cmdstr = createCommandLine( |
||||||
|
VERIFICATION_LEGACY, executablePath, cmd); |
||||||
|
} else { |
||||||
|
String executablePath; |
||||||
|
try { |
||||||
|
executablePath = getExecutablePath(cmd[0]); |
||||||
|
} catch (IllegalArgumentException e) { |
||||||
|
|
||||||
|
StringBuilder join = new StringBuilder(); |
||||||
|
for (String s : cmd) { |
||||||
|
join.append(s).append(' '); |
||||||
|
} |
||||||
|
|
||||||
|
cmd = getTokensFromCommand(join.toString()); |
||||||
|
executablePath = getExecutablePath(cmd[0]); |
||||||
|
|
||||||
|
// Check new executable name once more
|
||||||
|
if (security != null) { |
||||||
|
security.checkExec(executablePath); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
cmdstr = createCommandLine( |
||||||
|
|
||||||
|
isShellFile(executablePath) ? VERIFICATION_CMD_BAT : VERIFICATION_WIN32, quoteString(executablePath), cmd); |
||||||
|
} |
||||||
|
return cmdstr; |
||||||
|
} |
||||||
|
|
||||||
|
private static String getExecutablePath(String path) throws IOException { |
||||||
|
boolean pathIsQuoted = isQuoted(true, path, "Executable name has embedded quote, split the arguments"); |
||||||
|
|
||||||
|
File fileToRun = new File(pathIsQuoted ? path.substring(1, path.length() - 1) : path); |
||||||
|
return fileToRun.getPath(); |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean isShellFile(String executablePath) { |
||||||
|
String upPath = executablePath.toUpperCase(); |
||||||
|
return (upPath.endsWith(".CMD") || upPath.endsWith(".BAT")); |
||||||
|
} |
||||||
|
|
||||||
|
private static String quoteString(String arg) { |
||||||
|
StringBuilder argbuf = new StringBuilder(arg.length() + 2); |
||||||
|
return argbuf.append('"').append(arg).append('"').toString(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private static String[] getTokensFromCommand(String command) { |
||||||
|
ArrayList<String> matchList = new ArrayList<>(8); |
||||||
|
Matcher regexMatcher = LazyPattern.PATTERN.matcher(command); |
||||||
|
while (regexMatcher.find()) { |
||||||
|
matchList.add(regexMatcher.group()); |
||||||
|
} |
||||||
|
return matchList.toArray(new String[matchList.size()]); |
||||||
|
} |
||||||
|
|
||||||
|
private static class LazyPattern { |
||||||
|
// Escape-support version:
|
||||||
|
// "(\")((?:\\\\\\1|.)+?)\\1|([^\\s\"]+)";
|
||||||
|
private static final Pattern PATTERN = Pattern.compile("[^\\s\"]+|\"[^\"]*\""); |
||||||
|
} |
||||||
|
|
||||||
|
private static final int VERIFICATION_CMD_BAT = 0; |
||||||
|
|
||||||
|
private static final int VERIFICATION_WIN32 = 1; |
||||||
|
|
||||||
|
private static final int VERIFICATION_LEGACY = 2; |
||||||
|
|
||||||
|
private static final char[][] ESCAPE_VERIFICATION = {{' ', '\t', '<', '>', '&', '|', '^'}, |
||||||
|
|
||||||
|
{' ', '\t', '<', '>'}, {' ', '\t'}}; |
||||||
|
|
||||||
|
private static String createCommandLine(int verificationType, final String executablePath, final String[] cmd) { |
||||||
|
StringBuilder cmdbuf = new StringBuilder(80); |
||||||
|
|
||||||
|
cmdbuf.append(executablePath); |
||||||
|
|
||||||
|
for (int i = 1; i < cmd.length; ++i) { |
||||||
|
cmdbuf.append(' '); |
||||||
|
String s = cmd[i]; |
||||||
|
if (needsEscaping(verificationType, s)) { |
||||||
|
cmdbuf.append('"').append(s); |
||||||
|
|
||||||
|
if ((verificationType != VERIFICATION_CMD_BAT) && s.endsWith("\\")) { |
||||||
|
cmdbuf.append('\\'); |
||||||
|
} |
||||||
|
cmdbuf.append('"'); |
||||||
|
} else { |
||||||
|
cmdbuf.append(s); |
||||||
|
} |
||||||
|
} |
||||||
|
return cmdbuf.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean isQuoted(boolean noQuotesInside, String arg, String errorMessage) { |
||||||
|
int lastPos = arg.length() - 1; |
||||||
|
if (lastPos >= 1 && arg.charAt(0) == '"' && arg.charAt(lastPos) == '"') { |
||||||
|
// The argument has already been quoted.
|
||||||
|
if (noQuotesInside) { |
||||||
|
if (arg.indexOf('"', 1) != lastPos) { |
||||||
|
// There is ["] inside.
|
||||||
|
throw new IllegalArgumentException(errorMessage); |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
if (noQuotesInside) { |
||||||
|
if (arg.indexOf('"') >= 0) { |
||||||
|
// There is ["] inside.
|
||||||
|
throw new IllegalArgumentException(errorMessage); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean needsEscaping(int verificationType, String arg) { |
||||||
|
|
||||||
|
boolean argIsQuoted = isQuoted((verificationType == VERIFICATION_CMD_BAT), arg, "Argument has embedded quote, use the explicit CMD.EXE call."); |
||||||
|
|
||||||
|
if (!argIsQuoted) { |
||||||
|
char[] testEscape = ESCAPE_VERIFICATION[verificationType]; |
||||||
|
for (int i = 0; i < testEscape.length; ++i) { |
||||||
|
if (arg.indexOf(testEscape[i]) >= 0) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* kill yarn application |
||||||
|
* @param appIds |
||||||
|
* @param logger |
||||||
|
* @param tenantCode |
||||||
|
* @throws IOException |
||||||
|
*/ |
||||||
|
public static void cancelApplication(List<String> appIds, Logger logger, String tenantCode,String workDir) |
||||||
|
throws IOException { |
||||||
|
if (appIds.size() > 0) { |
||||||
|
String appid = appIds.get(appIds.size() - 1); |
||||||
|
String commandFile = String |
||||||
|
.format("%s/%s.kill", workDir, appid); |
||||||
|
String cmd = "yarn application -kill " + appid; |
||||||
|
try { |
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
sb.append("#!/bin/sh\n"); |
||||||
|
sb.append("BASEDIR=$(cd `dirname $0`; pwd)\n"); |
||||||
|
sb.append("cd $BASEDIR\n"); |
||||||
|
if (CommonUtils.getSystemEnvPath() != null) { |
||||||
|
sb.append("source " + CommonUtils.getSystemEnvPath() + "\n"); |
||||||
|
} |
||||||
|
sb.append("\n\n"); |
||||||
|
sb.append(cmd); |
||||||
|
|
||||||
|
File f = new File(commandFile); |
||||||
|
|
||||||
|
if (!f.exists()) { |
||||||
|
FileUtils.writeStringToFile(new File(commandFile), sb.toString(), Charset.forName("UTF-8")); |
||||||
|
} |
||||||
|
|
||||||
|
String runCmd = "sh " + commandFile; |
||||||
|
if (StringUtils.isNotEmpty(tenantCode)) { |
||||||
|
runCmd = "sudo -u " + tenantCode + " " + runCmd; |
||||||
|
} |
||||||
|
|
||||||
|
logger.info("kill cmd:{}", runCmd); |
||||||
|
|
||||||
|
Runtime.getRuntime().exec(runCmd); |
||||||
|
} catch (Exception e) { |
||||||
|
logger.error("kill application failed : " + e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* kill tasks according to different task types |
||||||
|
* @param taskInstance |
||||||
|
*/ |
||||||
|
public static void kill(TaskInstance taskInstance) { |
||||||
|
try { |
||||||
|
int processId = taskInstance.getPid(); |
||||||
|
if(processId == 0 ){ |
||||||
|
logger.error("process kill failed, process id :{}, task id:{}", |
||||||
|
processId, taskInstance.getId()); |
||||||
|
return ; |
||||||
|
} |
||||||
|
|
||||||
|
String cmd = String.format("sudo kill -9 %d", processId); |
||||||
|
|
||||||
|
logger.info("process id:{}, cmd:{}", processId, cmd); |
||||||
|
|
||||||
|
Runtime.getRuntime().exec(cmd); |
||||||
|
|
||||||
|
// find log and kill yarn job
|
||||||
|
killYarnJob(taskInstance); |
||||||
|
|
||||||
|
} catch (Exception e) { |
||||||
|
logger.error("kill failed : " + e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* find logs and kill yarn tasks |
||||||
|
* @param taskInstance |
||||||
|
* @throws IOException |
||||||
|
*/ |
||||||
|
public static void killYarnJob(TaskInstance taskInstance) throws Exception { |
||||||
|
try { |
||||||
|
Thread.sleep(Constants.SLEEP_TIME_MILLIS); |
||||||
|
LogClient logClient = new LogClient(taskInstance.getHost(), Constants.RPC_PORT); |
||||||
|
|
||||||
|
String log = logClient.viewLog(taskInstance.getLogPath()); |
||||||
|
if (StringUtils.isNotEmpty(log)) { |
||||||
|
List<String> appIds = LoggerUtils.getAppIds(log, logger); |
||||||
|
String workerDir = taskInstance.getExecutePath(); |
||||||
|
if (StringUtils.isEmpty(workerDir)) { |
||||||
|
logger.error("task instance work dir is empty"); |
||||||
|
throw new RuntimeException("task instance work dir is empty"); |
||||||
|
} |
||||||
|
if (appIds.size() > 0) { |
||||||
|
cancelApplication(appIds, logger, taskInstance.getProcessInstance().getTenantCode(), taskInstance.getExecutePath()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} catch (Exception e) { |
||||||
|
logger.error("kill yarn job failed : " + e.getMessage(),e); |
||||||
|
throw new RuntimeException("kill yarn job fail"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,117 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.utils; |
||||||
|
|
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.enums.ProgramType; |
||||||
|
import cn.escheduler.common.task.spark.SparkParameters; |
||||||
|
import org.apache.commons.lang.StringUtils; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* spark args utils |
||||||
|
*/ |
||||||
|
public class SparkArgsUtils { |
||||||
|
|
||||||
|
/** |
||||||
|
* build args |
||||||
|
* @param param |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static List<String> buildArgs(SparkParameters param) { |
||||||
|
List<String> args = new ArrayList<>(); |
||||||
|
String deployMode = "cluster"; |
||||||
|
|
||||||
|
args.add(Constants.MASTER); |
||||||
|
if(StringUtils.isNotEmpty(param.getDeployMode())){ |
||||||
|
deployMode = param.getDeployMode(); |
||||||
|
|
||||||
|
} |
||||||
|
if(!"local".equals(deployMode)){ |
||||||
|
args.add("yarn"); |
||||||
|
args.add(Constants.DEPLOY_MODE); |
||||||
|
} |
||||||
|
|
||||||
|
args.add(param.getDeployMode()); |
||||||
|
|
||||||
|
if(param.getProgramType() !=null ){ |
||||||
|
if(param.getProgramType()!=ProgramType.PYTHON){ |
||||||
|
if (StringUtils.isNotEmpty(param.getMainClass())) { |
||||||
|
args.add(Constants.CLASS); |
||||||
|
args.add(param.getMainClass()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
if (param.getDriverCores() != 0) { |
||||||
|
args.add(Constants.DRIVER_CORES); |
||||||
|
args.add(String.format("%d", param.getDriverCores())); |
||||||
|
} |
||||||
|
|
||||||
|
if (StringUtils.isNotEmpty(param.getDriverMemory())) { |
||||||
|
args.add(Constants.DRIVER_MEMORY); |
||||||
|
args.add(param.getDriverMemory()); |
||||||
|
} |
||||||
|
|
||||||
|
if (param.getNumExecutors() != 0) { |
||||||
|
args.add(Constants.NUM_EXECUTORS); |
||||||
|
args.add(String.format("%d", param.getNumExecutors())); |
||||||
|
} |
||||||
|
|
||||||
|
if (param.getExecutorCores() != 0) { |
||||||
|
args.add(Constants.EXECUTOR_CORES); |
||||||
|
args.add(String.format("%d", param.getExecutorCores())); |
||||||
|
} |
||||||
|
|
||||||
|
if (StringUtils.isNotEmpty(param.getExecutorMemory())) { |
||||||
|
args.add(Constants.EXECUTOR_MEMORY); |
||||||
|
args.add(param.getExecutorMemory()); |
||||||
|
} |
||||||
|
|
||||||
|
// --files --conf --libjar ...
|
||||||
|
if (StringUtils.isNotEmpty(param.getOthers())) { |
||||||
|
String others = param.getOthers(); |
||||||
|
if(!others.contains("--queue")){ |
||||||
|
if (StringUtils.isNotEmpty(param.getQueue())) { |
||||||
|
args.add(Constants.SPARK_QUEUE); |
||||||
|
args.add(param.getQueue()); |
||||||
|
} |
||||||
|
} |
||||||
|
args.add(param.getOthers()); |
||||||
|
}else if (StringUtils.isNotEmpty(param.getQueue())) { |
||||||
|
args.add(Constants.SPARK_QUEUE); |
||||||
|
args.add(param.getQueue()); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
if (param.getMainJar() != null) { |
||||||
|
args.add(param.getMainJar().getRes()); |
||||||
|
} |
||||||
|
|
||||||
|
if (StringUtils.isNotEmpty(param.getMainArgs())) { |
||||||
|
args.add(param.getMainArgs()); |
||||||
|
} |
||||||
|
|
||||||
|
return args; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,109 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.utils; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.utils.HadoopUtils; |
||||||
|
import cn.escheduler.dao.model.UdfFunc; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
import org.slf4j.Logger; |
||||||
|
|
||||||
|
import java.text.MessageFormat; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
import static cn.escheduler.common.utils.CollectionUtils.isNotEmpty; |
||||||
|
|
||||||
|
/** |
||||||
|
* udf utils |
||||||
|
*/ |
||||||
|
public class UDFUtils { |
||||||
|
|
||||||
|
/** |
||||||
|
* create function format |
||||||
|
*/ |
||||||
|
private static final String CREATE_FUNCTION_FORMAT = "create temporary function {0} as ''{1}''"; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* create function list |
||||||
|
*/ |
||||||
|
public static List<String> createFuncs(List<UdfFunc> udfFuncs, String tenantCode,Logger logger){ |
||||||
|
// get hive udf jar path
|
||||||
|
String hiveUdfJarPath = HadoopUtils.getHdfsUdfDir(tenantCode); |
||||||
|
logger.info("hive udf jar path : {}" , hiveUdfJarPath); |
||||||
|
|
||||||
|
// is the root directory of udf defined
|
||||||
|
if (StringUtils.isEmpty(hiveUdfJarPath)) { |
||||||
|
logger.error("not define hive udf jar path"); |
||||||
|
throw new RuntimeException("hive udf jar base path not defined "); |
||||||
|
} |
||||||
|
Set<String> resources = getFuncResouces(udfFuncs); |
||||||
|
List<String> funcList = new ArrayList<>(); |
||||||
|
|
||||||
|
// build jar sql
|
||||||
|
buildJarSql(funcList, resources, hiveUdfJarPath); |
||||||
|
|
||||||
|
// build temp function sql
|
||||||
|
buildTempFuncSql(funcList, udfFuncs); |
||||||
|
|
||||||
|
return funcList; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* build jar sql |
||||||
|
*/ |
||||||
|
private static void buildJarSql(List<String> sqls, Set<String> resources, String uploadPath) { |
||||||
|
String defaultFS = HadoopUtils.getInstance().getConfiguration().get(Constants.FS_DEFAULTFS); |
||||||
|
if (!uploadPath.startsWith("hdfs:")) { |
||||||
|
uploadPath = defaultFS + uploadPath; |
||||||
|
} |
||||||
|
|
||||||
|
for (String resource : resources) { |
||||||
|
sqls.add(String.format("add jar %s/%s", uploadPath, resource)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* build temp function sql |
||||||
|
*/ |
||||||
|
private static void buildTempFuncSql(List<String> sqls, List<UdfFunc> udfFuncs) { |
||||||
|
if (isNotEmpty(udfFuncs)) { |
||||||
|
for (UdfFunc udfFunc : udfFuncs) { |
||||||
|
sqls.add(MessageFormat |
||||||
|
.format(CREATE_FUNCTION_FORMAT, udfFunc.getFuncName(), udfFunc.getClassName())); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get the resource names of all functions |
||||||
|
*/ |
||||||
|
private static Set<String> getFuncResouces(List<UdfFunc> udfFuncs) { |
||||||
|
Set<String> resources = new HashSet<>(); |
||||||
|
|
||||||
|
for (UdfFunc udfFunc : udfFuncs) { |
||||||
|
resources.add(udfFunc.getResourceName()); |
||||||
|
} |
||||||
|
|
||||||
|
return resources; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,366 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.IStoppable; |
||||||
|
import cn.escheduler.common.enums.ExecutionStatus; |
||||||
|
import cn.escheduler.common.enums.TaskType; |
||||||
|
import cn.escheduler.common.queue.ITaskQueue; |
||||||
|
import cn.escheduler.common.queue.TaskQueueFactory; |
||||||
|
import cn.escheduler.common.thread.Stopper; |
||||||
|
import cn.escheduler.common.thread.ThreadPoolExecutors; |
||||||
|
import cn.escheduler.common.thread.ThreadUtils; |
||||||
|
import cn.escheduler.common.utils.CollectionUtils; |
||||||
|
import cn.escheduler.common.utils.OSUtils; |
||||||
|
import cn.escheduler.dao.AlertDao; |
||||||
|
import cn.escheduler.dao.DaoFactory; |
||||||
|
import cn.escheduler.dao.ProcessDao; |
||||||
|
import cn.escheduler.dao.ServerDao; |
||||||
|
import cn.escheduler.dao.model.ProcessInstance; |
||||||
|
import cn.escheduler.dao.model.TaskInstance; |
||||||
|
import cn.escheduler.server.utils.ProcessUtils; |
||||||
|
import cn.escheduler.server.worker.runner.FetchTaskThread; |
||||||
|
import cn.escheduler.server.zk.ZKWorkerClient; |
||||||
|
import org.apache.commons.configuration.Configuration; |
||||||
|
import org.apache.commons.configuration.ConfigurationException; |
||||||
|
import org.apache.commons.configuration.PropertiesConfiguration; |
||||||
|
import org.apache.commons.lang.StringUtils; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.Set; |
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
import java.util.concurrent.ScheduledExecutorService; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
/** |
||||||
|
* worker server |
||||||
|
*/ |
||||||
|
public class WorkerServer implements IStoppable { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(WorkerServer.class); |
||||||
|
|
||||||
|
/** |
||||||
|
* conf |
||||||
|
*/ |
||||||
|
private static Configuration conf; |
||||||
|
|
||||||
|
/** |
||||||
|
* object lock |
||||||
|
*/ |
||||||
|
private final Object lock = new Object(); |
||||||
|
|
||||||
|
/** |
||||||
|
* whether or not to close the state |
||||||
|
*/ |
||||||
|
private boolean terminated = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* zk worker client |
||||||
|
*/ |
||||||
|
private static ZKWorkerClient zkWorkerClient = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* worker dao database access |
||||||
|
*/ |
||||||
|
private ServerDao serverDao = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* process database access |
||||||
|
*/ |
||||||
|
private final ProcessDao processDao; |
||||||
|
|
||||||
|
/** |
||||||
|
* alert database access |
||||||
|
*/ |
||||||
|
private final AlertDao alertDao; |
||||||
|
|
||||||
|
/** |
||||||
|
* heartbeat thread pool |
||||||
|
*/ |
||||||
|
private ScheduledExecutorService heartbeatWorerService; |
||||||
|
|
||||||
|
/** |
||||||
|
* heartbeat interval, unit second |
||||||
|
*/ |
||||||
|
private int heartBeatInterval; |
||||||
|
|
||||||
|
/** |
||||||
|
* task queue impl |
||||||
|
*/ |
||||||
|
protected ITaskQueue taskQueue; |
||||||
|
|
||||||
|
/** |
||||||
|
* kill executor service |
||||||
|
*/ |
||||||
|
private ExecutorService killExecutorService; |
||||||
|
|
||||||
|
/** |
||||||
|
* fetch task executor service |
||||||
|
*/ |
||||||
|
private ExecutorService fetchTaskExecutorService; |
||||||
|
|
||||||
|
static { |
||||||
|
try { |
||||||
|
conf = new PropertiesConfiguration(Constants.WORKER_PROPERTIES_PATH); |
||||||
|
}catch (ConfigurationException e){ |
||||||
|
logger.error("load configuration failed : " + e.getMessage(),e); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public WorkerServer(){ |
||||||
|
zkWorkerClient = ZKWorkerClient.getZKWorkerClient(); |
||||||
|
this.serverDao = zkWorkerClient.getServerDao(); |
||||||
|
this.alertDao = DaoFactory.getDaoInstance(AlertDao.class); |
||||||
|
this.processDao = DaoFactory.getDaoInstance(ProcessDao.class); |
||||||
|
taskQueue = TaskQueueFactory.getTaskQueueInstance(); |
||||||
|
|
||||||
|
killExecutorService = ThreadUtils.newDaemonSingleThreadExecutor("Worker-Kill-Thread-Executor"); |
||||||
|
|
||||||
|
fetchTaskExecutorService = ThreadUtils.newDaemonSingleThreadExecutor("Worker-Fetch-Thread-Executor"); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void run(){ |
||||||
|
|
||||||
|
// heartbeat interval
|
||||||
|
heartBeatInterval = conf.getInt(Constants.WORKER_HEARTBEAT_INTERVAL, |
||||||
|
Constants.defaultWorkerHeartbeatInterval); |
||||||
|
|
||||||
|
heartbeatWorerService = ThreadUtils.newDaemonThreadScheduledExecutor("Worker-Heartbeat-Thread-Executor", Constants.defaulWorkerHeartbeatThreadNum); |
||||||
|
|
||||||
|
// heartbeat thread implement
|
||||||
|
Runnable heartBeatThread = heartBeatThread(); |
||||||
|
|
||||||
|
zkWorkerClient.setStoppable(this); |
||||||
|
|
||||||
|
// regular heartbeat
|
||||||
|
// delay 5 seconds, send heartbeat every 30 seconds
|
||||||
|
heartbeatWorerService. |
||||||
|
scheduleAtFixedRate(heartBeatThread, 5, heartBeatInterval, TimeUnit.SECONDS); |
||||||
|
|
||||||
|
// kill process thread implement
|
||||||
|
Runnable killProcessThread = getKillProcessThread(); |
||||||
|
|
||||||
|
// submit kill process thread
|
||||||
|
killExecutorService.execute(killProcessThread); |
||||||
|
|
||||||
|
/** |
||||||
|
* register hooks, which are called before the process exits |
||||||
|
*/ |
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
String host = OSUtils.getHost(); |
||||||
|
// clear worker table register info
|
||||||
|
serverDao.deleteWorker(host); |
||||||
|
logger.info("worker server stopped"); |
||||||
|
if (zkWorkerClient.getActiveMasterNum() <= 1) { |
||||||
|
for (int i = 0; i < Constants.ESCHEDULER_WARN_TIMES_FAILOVER;i++) { |
||||||
|
alertDao.sendServerStopedAlert(1, host, "Worker-Server"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
})); |
||||||
|
|
||||||
|
// get worker number of concurrent tasks
|
||||||
|
int taskNum = conf.getInt(Constants.WORKER_FETCH_TASK_NUM,Constants.defaultWorkerFetchTaskNum); |
||||||
|
|
||||||
|
// new fetch task thread
|
||||||
|
FetchTaskThread fetchTaskThread = new FetchTaskThread(taskNum,zkWorkerClient, processDao,conf, taskQueue); |
||||||
|
|
||||||
|
// submit fetch task thread
|
||||||
|
fetchTaskExecutorService.execute(fetchTaskThread); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public static void main(String[] args)throws Exception{ |
||||||
|
|
||||||
|
// set the name of the current thread
|
||||||
|
Thread.currentThread().setName("Worker-Main-Thread"); |
||||||
|
|
||||||
|
WorkerServer workerServer = new WorkerServer(); |
||||||
|
|
||||||
|
workerServer.run(); |
||||||
|
|
||||||
|
logger.info("worker server started"); |
||||||
|
|
||||||
|
// blocking
|
||||||
|
workerServer.awaitTermination(); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* blocking implement |
||||||
|
* @throws InterruptedException |
||||||
|
*/ |
||||||
|
public void awaitTermination() throws InterruptedException { |
||||||
|
synchronized (lock) { |
||||||
|
while (!terminated) { |
||||||
|
lock.wait(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* heartbeat thread implement |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public Runnable heartBeatThread(){ |
||||||
|
Runnable heartBeatThread = new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
// send heartbeat to zk
|
||||||
|
if (StringUtils.isBlank(zkWorkerClient.getWorkerZNode())){ |
||||||
|
logger.error("worker send heartbeat to zk failed"); |
||||||
|
} |
||||||
|
|
||||||
|
zkWorkerClient.heartBeatForZk(zkWorkerClient.getWorkerZNode() , Constants.WORKER_PREFIX); |
||||||
|
} |
||||||
|
}; |
||||||
|
return heartBeatThread; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* kill process thread implement |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public Runnable getKillProcessThread(){ |
||||||
|
Runnable killProcessThread = new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
Set<String> taskInfoSet = taskQueue.smembers(Constants.SCHEDULER_TASKS_KILL); |
||||||
|
while (Stopper.isRunning()){ |
||||||
|
try { |
||||||
|
Thread.sleep(Constants.SLEEP_TIME_MILLIS); |
||||||
|
} catch (InterruptedException e) { |
||||||
|
logger.error("interrupted exception : " + e.getMessage(),e); |
||||||
|
} |
||||||
|
// if set is null , return
|
||||||
|
if (CollectionUtils.isNotEmpty(taskInfoSet)){ |
||||||
|
for (String taskInfo : taskInfoSet){ |
||||||
|
// task info start with current host
|
||||||
|
if (taskInfo.startsWith(OSUtils.getHost())){ |
||||||
|
String[] taskInfoArr = taskInfo.split("-"); |
||||||
|
if (taskInfoArr.length != 2){ |
||||||
|
continue; |
||||||
|
}else { |
||||||
|
int taskInstId=Integer.parseInt(taskInfoArr[1]); |
||||||
|
|
||||||
|
TaskInstance taskInstance = processDao.findTaskInstanceById(taskInstId); |
||||||
|
|
||||||
|
ProcessInstance instance = processDao.findProcessInstanceDetailById(taskInstance.getProcessInstanceId()); |
||||||
|
|
||||||
|
if(instance != null){ |
||||||
|
taskInstance.setProcessInstance(instance); |
||||||
|
} |
||||||
|
if(taskInstance.getTaskType().equals(TaskType.DEPENDENT.toString())){ |
||||||
|
taskInstance.setState(ExecutionStatus.KILL); |
||||||
|
processDao.saveTaskInstance(taskInstance); |
||||||
|
}else{ |
||||||
|
ProcessUtils.kill(taskInstance); |
||||||
|
} |
||||||
|
taskQueue.srem(Constants.SCHEDULER_TASKS_KILL,taskInfo); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
taskInfoSet = taskQueue.smembers(Constants.SCHEDULER_TASKS_KILL); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
return killProcessThread; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public synchronized void stop(String cause) { |
||||||
|
|
||||||
|
try { |
||||||
|
//execute only once
|
||||||
|
if(Stopper.isStoped()){ |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
logger.info("worker server is stopping ..., cause : {}", cause); |
||||||
|
|
||||||
|
// set stop signal is true
|
||||||
|
Stopper.stop(); |
||||||
|
|
||||||
|
try { |
||||||
|
//thread sleep 3 seconds for thread quitely stop
|
||||||
|
Thread.sleep(3000L); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.warn("thread sleep exception:" + e.getMessage(), e); |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
heartbeatWorerService.shutdownNow(); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.warn("heartbeat service stopped exception"); |
||||||
|
} |
||||||
|
logger.info("heartbeat service stopped"); |
||||||
|
|
||||||
|
try { |
||||||
|
ThreadPoolExecutors.getInstance().shutdown(); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.warn("threadpool service stopped exception:{}",e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
logger.info("threadpool service stopped"); |
||||||
|
|
||||||
|
try { |
||||||
|
killExecutorService.shutdownNow(); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.warn("worker kill executor service stopped exception:{}",e.getMessage()); |
||||||
|
} |
||||||
|
logger.info("worker kill executor service stopped"); |
||||||
|
|
||||||
|
try { |
||||||
|
fetchTaskExecutorService.shutdownNow(); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.warn("worker fetch task service stopped exception:{}",e.getMessage()); |
||||||
|
} |
||||||
|
logger.info("worker fetch task service stopped"); |
||||||
|
|
||||||
|
try{ |
||||||
|
zkWorkerClient.close(); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.warn("zookeeper service stopped exception:{}",e.getMessage()); |
||||||
|
} |
||||||
|
logger.info("zookeeper service stopped"); |
||||||
|
|
||||||
|
//notify
|
||||||
|
synchronized (lock) { |
||||||
|
terminated = true; |
||||||
|
lock.notifyAll(); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
logger.error("worker server stop exception : " + e.getMessage(), e); |
||||||
|
System.exit(-1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,59 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.log; |
||||||
|
|
||||||
|
import ch.qos.logback.classic.spi.ILoggingEvent; |
||||||
|
import ch.qos.logback.core.FileAppender; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
/** |
||||||
|
* task log appender |
||||||
|
*/ |
||||||
|
public class TaskLogAppender extends FileAppender<ILoggingEvent> { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(TaskLogAppender.class); |
||||||
|
|
||||||
|
private String currentlyActiveFile; |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void append(ILoggingEvent event) { |
||||||
|
|
||||||
|
if (currentlyActiveFile == null){ |
||||||
|
currentlyActiveFile = getFile(); |
||||||
|
} |
||||||
|
String activeFile = currentlyActiveFile; |
||||||
|
// thread name: taskThreadName-processDefineId_processInstanceId_taskInstanceId
|
||||||
|
String threadName = event.getThreadName(); |
||||||
|
String[] threadNameArr = threadName.split("-"); |
||||||
|
// logId = processDefineId_processInstanceId_taskInstanceId
|
||||||
|
String logId = threadNameArr[1]; |
||||||
|
// split logId
|
||||||
|
threadNameArr = logId.split("_"); |
||||||
|
String processDefineId = threadNameArr[0]; |
||||||
|
String processInstanceId = threadNameArr[1]; |
||||||
|
String taskInstanceId = threadNameArr[2]; |
||||||
|
|
||||||
|
activeFile = activeFile.replace("{processDefinitionId}",processDefineId); |
||||||
|
activeFile = activeFile.replace("{processInstanceId}",processInstanceId); |
||||||
|
activeFile = activeFile.replace("{taskInstanceId}",taskInstanceId); |
||||||
|
|
||||||
|
setFile(activeFile); |
||||||
|
start(); |
||||||
|
super.subAppend(event); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.log; |
||||||
|
|
||||||
|
import ch.qos.logback.classic.spi.ILoggingEvent; |
||||||
|
import ch.qos.logback.core.filter.Filter; |
||||||
|
import ch.qos.logback.core.spi.FilterReply; |
||||||
|
|
||||||
|
/** |
||||||
|
* task log filter |
||||||
|
*/ |
||||||
|
public class TaskLogFilter extends Filter<ILoggingEvent> { |
||||||
|
|
||||||
|
@Override |
||||||
|
public FilterReply decide(ILoggingEvent event) { |
||||||
|
if (event.getThreadName().startsWith("TaskLogInfo-")){ |
||||||
|
return FilterReply.ACCEPT; |
||||||
|
} |
||||||
|
return FilterReply.DENY; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,345 @@ |
|||||||
|
|
||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.log; |
||||||
|
|
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
import org.slf4j.Marker; |
||||||
|
|
||||||
|
/** |
||||||
|
* custom task logger |
||||||
|
*/ |
||||||
|
public class TaskLogger implements Logger { |
||||||
|
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(TaskLogger.class); |
||||||
|
|
||||||
|
private String taskAppId; |
||||||
|
|
||||||
|
public TaskLogger(String taskAppId) { |
||||||
|
this.taskAppId = taskAppId; |
||||||
|
} |
||||||
|
|
||||||
|
private String addJobId(String msg) { |
||||||
|
return String.format("[taskAppId=%s] %s", taskAppId, msg); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getName() { |
||||||
|
return logger.getName(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isTraceEnabled() { |
||||||
|
return logger.isTraceEnabled(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void trace(String msg) { |
||||||
|
logger.trace(addJobId(msg)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void trace(String format, Object arg) { |
||||||
|
logger.trace(addJobId(format), arg); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void trace(String format, Object arg1, Object arg2) { |
||||||
|
logger.trace(addJobId(format), arg1, arg2); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void trace(String format, Object... arguments) { |
||||||
|
logger.trace(addJobId(format), arguments); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void trace(String msg, Throwable t) { |
||||||
|
logger.trace(addJobId(msg), t); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isTraceEnabled(Marker marker) { |
||||||
|
return logger.isTraceEnabled(marker); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void trace(Marker marker, String msg) { |
||||||
|
logger.trace(marker, addJobId(msg)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void trace(Marker marker, String format, Object arg) { |
||||||
|
logger.trace(marker, addJobId(format), arg); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void trace(Marker marker, String format, Object arg1, Object arg2) { |
||||||
|
logger.trace(marker, addJobId(format), arg1, arg2); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void trace(Marker marker, String format, Object... argArray) { |
||||||
|
logger.trace(marker, addJobId(format), argArray); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void trace(Marker marker, String msg, Throwable t) { |
||||||
|
logger.trace(marker, addJobId(msg), t); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isDebugEnabled() { |
||||||
|
return logger.isDebugEnabled(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void debug(String msg) { |
||||||
|
logger.debug(addJobId(msg)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void debug(String format, Object arg) { |
||||||
|
logger.debug(addJobId(format), arg); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void debug(String format, Object arg1, Object arg2) { |
||||||
|
logger.debug(addJobId(format), arg1, arg2); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void debug(String format, Object... arguments) { |
||||||
|
logger.debug(addJobId(format), arguments); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void debug(String msg, Throwable t) { |
||||||
|
logger.debug(addJobId(msg), t); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isDebugEnabled(Marker marker) { |
||||||
|
return logger.isDebugEnabled(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void debug(Marker marker, String msg) { |
||||||
|
logger.debug(marker, addJobId(msg)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void debug(Marker marker, String format, Object arg) { |
||||||
|
logger.debug(marker, addJobId(format), arg); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void debug(Marker marker, String format, Object arg1, Object arg2) { |
||||||
|
logger.debug(marker, addJobId(format), arg1, arg2); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void debug(Marker marker, String format, Object... arguments) { |
||||||
|
logger.debug(marker, addJobId(format), arguments); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void debug(Marker marker, String msg, Throwable t) { |
||||||
|
logger.debug(marker, addJobId(msg), t); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isInfoEnabled() { |
||||||
|
return logger.isInfoEnabled(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void info(String msg) { |
||||||
|
logger.info(addJobId(msg)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void info(String format, Object arg) { |
||||||
|
logger.info(addJobId(format), arg); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void info(String format, Object arg1, Object arg2) { |
||||||
|
logger.info(addJobId(format), arg1, arg2); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void info(String format, Object... arguments) { |
||||||
|
logger.info(addJobId(format), arguments); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void info(String msg, Throwable t) { |
||||||
|
logger.info(addJobId(msg), t); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isInfoEnabled(Marker marker) { |
||||||
|
return logger.isInfoEnabled(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void info(Marker marker, String msg) { |
||||||
|
logger.info(marker, addJobId(msg)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void info(Marker marker, String format, Object arg) { |
||||||
|
logger.info(marker, addJobId(format), arg); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void info(Marker marker, String format, Object arg1, Object arg2) { |
||||||
|
logger.info(marker, addJobId(format), arg1, arg2); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void info(Marker marker, String format, Object... arguments) { |
||||||
|
logger.info(marker, addJobId(format), arguments); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void info(Marker marker, String msg, Throwable t) { |
||||||
|
logger.info(marker, addJobId(msg), t); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isWarnEnabled() { |
||||||
|
return logger.isWarnEnabled(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void warn(String msg) { |
||||||
|
logger.warn(addJobId(msg)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void warn(String format, Object arg) { |
||||||
|
logger.warn(addJobId(format), arg); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void warn(String format, Object arg1, Object arg2) { |
||||||
|
logger.warn(addJobId(format), arg1, arg2); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void warn(String format, Object... arguments) { |
||||||
|
logger.warn(addJobId(format), arguments); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void warn(String msg, Throwable t) { |
||||||
|
logger.warn(addJobId(msg), t); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isWarnEnabled(Marker marker) { |
||||||
|
return logger.isWarnEnabled(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void warn(Marker marker, String msg) { |
||||||
|
logger.warn(marker, addJobId(msg)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void warn(Marker marker, String format, Object arg) { |
||||||
|
logger.warn(marker, addJobId(format), arg); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void warn(Marker marker, String format, Object arg1, Object arg2) { |
||||||
|
logger.warn(marker, addJobId(format), arg1, arg2); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void warn(Marker marker, String format, Object... arguments) { |
||||||
|
logger.warn(marker, addJobId(format), arguments); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void warn(Marker marker, String msg, Throwable t) { |
||||||
|
logger.warn(marker, addJobId(msg), t); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isErrorEnabled() { |
||||||
|
return logger.isErrorEnabled(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void error(String msg) { |
||||||
|
logger.error(addJobId(msg)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void error(String format, Object arg) { |
||||||
|
logger.error(addJobId(format), arg); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void error(String format, Object arg1, Object arg2) { |
||||||
|
logger.error(addJobId(format), arg1, arg2); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void error(String format, Object... arguments) { |
||||||
|
logger.error(addJobId(format), arguments); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void error(String msg, Throwable t) { |
||||||
|
logger.error(addJobId(msg), t); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isErrorEnabled(Marker marker) { |
||||||
|
return logger.isErrorEnabled(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void error(Marker marker, String msg) { |
||||||
|
logger.error(marker, addJobId(msg)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void error(Marker marker, String format, Object arg) { |
||||||
|
logger.error(marker, addJobId(format), arg); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void error(Marker marker, String format, Object arg1, Object arg2) { |
||||||
|
logger.error(marker, addJobId(format), arg1, arg2); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void error(Marker marker, String format, Object... arguments) { |
||||||
|
logger.error(marker, addJobId(format), arguments); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void error(Marker marker, String msg, Throwable t) { |
||||||
|
logger.error(marker, addJobId(msg), t); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.log; |
||||||
|
|
||||||
|
import ch.qos.logback.classic.Level; |
||||||
|
import ch.qos.logback.classic.spi.ILoggingEvent; |
||||||
|
import ch.qos.logback.core.filter.Filter; |
||||||
|
import ch.qos.logback.core.spi.FilterReply; |
||||||
|
|
||||||
|
/** |
||||||
|
* worker log filter |
||||||
|
*/ |
||||||
|
public class WorkerLogFilter extends Filter<ILoggingEvent> { |
||||||
|
Level level; |
||||||
|
|
||||||
|
@Override |
||||||
|
public FilterReply decide(ILoggingEvent event) { |
||||||
|
if (event.getThreadName().startsWith("Worker-")){ |
||||||
|
return FilterReply.ACCEPT; |
||||||
|
} |
||||||
|
return FilterReply.DENY; |
||||||
|
} |
||||||
|
public void setLevel(String level) { |
||||||
|
this.level = Level.toLevel(level); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,202 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.runner; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.queue.ITaskQueue; |
||||||
|
import cn.escheduler.common.thread.Stopper; |
||||||
|
import cn.escheduler.common.thread.ThreadUtils; |
||||||
|
import cn.escheduler.common.utils.FileUtils; |
||||||
|
import cn.escheduler.common.utils.OSUtils; |
||||||
|
import cn.escheduler.dao.ProcessDao; |
||||||
|
import cn.escheduler.dao.model.ProcessDefinition; |
||||||
|
import cn.escheduler.dao.model.ProcessInstance; |
||||||
|
import cn.escheduler.dao.model.TaskInstance; |
||||||
|
import cn.escheduler.server.zk.ZKWorkerClient; |
||||||
|
import com.cronutils.utils.StringUtils; |
||||||
|
import org.apache.commons.configuration.Configuration; |
||||||
|
import org.apache.curator.framework.recipes.locks.InterProcessMutex; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
import java.util.concurrent.ThreadPoolExecutor; |
||||||
|
|
||||||
|
/** |
||||||
|
* fetch task thread |
||||||
|
*/ |
||||||
|
public class FetchTaskThread implements Runnable{ |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(FetchTaskThread.class); |
||||||
|
/** |
||||||
|
* set worker concurrent tasks |
||||||
|
*/ |
||||||
|
private final int taskNum; |
||||||
|
|
||||||
|
/** |
||||||
|
* zkWorkerClient |
||||||
|
*/ |
||||||
|
private final ZKWorkerClient zkWorkerClient; |
||||||
|
|
||||||
|
/** |
||||||
|
* task queue impl |
||||||
|
*/ |
||||||
|
protected ITaskQueue taskQueue; |
||||||
|
|
||||||
|
/** |
||||||
|
* process database access |
||||||
|
*/ |
||||||
|
private final ProcessDao processDao; |
||||||
|
|
||||||
|
/** |
||||||
|
* worker thread pool executor |
||||||
|
*/ |
||||||
|
private final ExecutorService workerExecService; |
||||||
|
|
||||||
|
/** |
||||||
|
* worker exec nums |
||||||
|
*/ |
||||||
|
private int workerExecNums; |
||||||
|
|
||||||
|
private Configuration conf; |
||||||
|
|
||||||
|
|
||||||
|
public FetchTaskThread(int taskNum, ZKWorkerClient zkWorkerClient, |
||||||
|
ProcessDao processDao, Configuration conf, |
||||||
|
ITaskQueue taskQueue){ |
||||||
|
this.taskNum = taskNum; |
||||||
|
this.zkWorkerClient = zkWorkerClient; |
||||||
|
this.processDao = processDao; |
||||||
|
this.workerExecNums = conf.getInt(Constants.WORKER_EXEC_THREADS, |
||||||
|
Constants.defaultWorkerExecThreadNum); |
||||||
|
// worker thread pool executor
|
||||||
|
this.workerExecService = ThreadUtils.newDaemonFixedThreadExecutor("Worker-Fetch-Task-Thread",workerExecNums); |
||||||
|
this.conf = conf; |
||||||
|
this.taskQueue = taskQueue; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
|
||||||
|
while (Stopper.isRunning()){ |
||||||
|
|
||||||
|
InterProcessMutex mutex = null; |
||||||
|
try { |
||||||
|
if(OSUtils.checkResource(this.conf, false)) { |
||||||
|
|
||||||
|
// creating distributed locks, lock path /escheduler/lock/worker
|
||||||
|
String zNodeLockPath = zkWorkerClient.getWorkerLockPath(); |
||||||
|
mutex = new InterProcessMutex(zkWorkerClient.getZkClient(), zNodeLockPath); |
||||||
|
mutex.acquire(); |
||||||
|
|
||||||
|
ThreadPoolExecutor poolExecutor = (ThreadPoolExecutor) workerExecService; |
||||||
|
|
||||||
|
for (int i = 0; i < taskNum; i++) { |
||||||
|
|
||||||
|
int activeCount = poolExecutor.getActiveCount(); |
||||||
|
if (activeCount >= workerExecNums) { |
||||||
|
logger.info("thread insufficient , activeCount : {} , workerExecNums : {}",activeCount,workerExecNums); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// task instance id str
|
||||||
|
String taskInstIdStr = taskQueue.poll(Constants.SCHEDULER_TASKS_QUEUE); |
||||||
|
|
||||||
|
if (!StringUtils.isEmpty(taskInstIdStr)) { |
||||||
|
Date now = new Date(); |
||||||
|
|
||||||
|
Integer taskId = Integer.parseInt(taskInstIdStr); |
||||||
|
|
||||||
|
// find task instance by task id
|
||||||
|
TaskInstance taskInstance = processDao.findTaskInstanceById(taskId); |
||||||
|
|
||||||
|
logger.info("worker fetch taskId : {} from queue ", taskId); |
||||||
|
|
||||||
|
int retryTimes = 30; |
||||||
|
// mainly to wait for the master insert task to succeed
|
||||||
|
while (taskInstance == null && retryTimes > 0) { |
||||||
|
Thread.sleep(Constants.SLEEP_TIME_MILLIS); |
||||||
|
taskInstance = processDao.findTaskInstanceById(taskId); |
||||||
|
retryTimes--; |
||||||
|
} |
||||||
|
|
||||||
|
if (taskInstance == null) { |
||||||
|
logger.error("task instance is null. task id : {} ", taskId); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// set execute task worker host
|
||||||
|
taskInstance.setHost(OSUtils.getHost()); |
||||||
|
taskInstance.setStartTime(now); |
||||||
|
|
||||||
|
|
||||||
|
// get process instance
|
||||||
|
ProcessInstance processInstance = processDao.findProcessInstanceDetailById(taskInstance.getProcessInstanceId()); |
||||||
|
|
||||||
|
// get process define
|
||||||
|
ProcessDefinition processDefine = processDao.findProcessDefineById(taskInstance.getProcessDefinitionId()); |
||||||
|
|
||||||
|
|
||||||
|
taskInstance.setProcessInstance(processInstance); |
||||||
|
taskInstance.setProcessDefine(processDefine); |
||||||
|
|
||||||
|
|
||||||
|
// get local execute path
|
||||||
|
String execLocalPath = FileUtils.getProcessExecDir(processDefine.getProjectId(), |
||||||
|
processDefine.getId(), |
||||||
|
processInstance.getId(), |
||||||
|
taskInstance.getId()); |
||||||
|
logger.info("task instance local execute path : {} ", execLocalPath); |
||||||
|
|
||||||
|
|
||||||
|
// set task execute path
|
||||||
|
taskInstance.setExecutePath(execLocalPath); |
||||||
|
|
||||||
|
// check and create Linux users
|
||||||
|
FileUtils.createWorkDirAndUserIfAbsent(execLocalPath, |
||||||
|
processDefine.getUserName(), logger); |
||||||
|
|
||||||
|
|
||||||
|
// submit task
|
||||||
|
workerExecService.submit(new TaskScheduleThread(taskInstance, processDao)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Thread.sleep(Constants.SLEEP_TIME_MILLIS); |
||||||
|
|
||||||
|
}catch (Exception e){ |
||||||
|
logger.error("fetch task thread exception : " + e.getMessage(),e); |
||||||
|
} |
||||||
|
finally { |
||||||
|
if (mutex != null){ |
||||||
|
try { |
||||||
|
mutex.release(); |
||||||
|
} catch (Exception e) { |
||||||
|
if(e.getMessage().equals("instance must be started before calling this method")){ |
||||||
|
logger.warn("fetch task lock release"); |
||||||
|
}else{ |
||||||
|
logger.error("fetch task lock release failed : " + e.getMessage(),e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,310 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.runner; |
||||||
|
|
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.enums.ExecutionStatus; |
||||||
|
import cn.escheduler.common.enums.TaskType; |
||||||
|
import cn.escheduler.common.model.TaskNode; |
||||||
|
import cn.escheduler.common.process.Property; |
||||||
|
import cn.escheduler.common.task.AbstractParameters; |
||||||
|
import cn.escheduler.common.task.TaskTimeoutParameter; |
||||||
|
import cn.escheduler.common.utils.CommonUtils; |
||||||
|
import cn.escheduler.common.utils.HadoopUtils; |
||||||
|
import cn.escheduler.common.utils.TaskParametersUtils; |
||||||
|
import cn.escheduler.dao.ProcessDao; |
||||||
|
import cn.escheduler.dao.model.ProcessInstance; |
||||||
|
import cn.escheduler.dao.model.TaskInstance; |
||||||
|
import cn.escheduler.server.utils.LoggerUtils; |
||||||
|
import cn.escheduler.server.worker.log.TaskLogger; |
||||||
|
import cn.escheduler.server.worker.task.AbstractTask; |
||||||
|
import cn.escheduler.server.worker.task.TaskManager; |
||||||
|
import cn.escheduler.server.worker.task.TaskProps; |
||||||
|
import com.alibaba.fastjson.JSONObject; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.*; |
||||||
|
import java.util.concurrent.Callable; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* task scheduler thread |
||||||
|
*/ |
||||||
|
public class TaskScheduleThread implements Callable<Boolean> { |
||||||
|
|
||||||
|
/** |
||||||
|
* logger |
||||||
|
*/ |
||||||
|
private final Logger logger = LoggerFactory.getLogger(TaskScheduleThread.class); |
||||||
|
|
||||||
|
private static final String TASK_PREFIX = "TASK"; |
||||||
|
|
||||||
|
/** |
||||||
|
* task instance |
||||||
|
*/ |
||||||
|
private TaskInstance taskInstance; |
||||||
|
|
||||||
|
/** |
||||||
|
* process database access |
||||||
|
*/ |
||||||
|
private final ProcessDao processDao; |
||||||
|
|
||||||
|
/** |
||||||
|
* execute task info |
||||||
|
*/ |
||||||
|
private AbstractTask task; |
||||||
|
|
||||||
|
public TaskScheduleThread(TaskInstance taskInstance, ProcessDao processDao){ |
||||||
|
this.processDao = processDao; |
||||||
|
this.taskInstance = taskInstance; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Boolean call() throws Exception { |
||||||
|
|
||||||
|
// get task type
|
||||||
|
String taskType = taskInstance.getTaskType(); |
||||||
|
// set task state
|
||||||
|
taskInstance.setState(ExecutionStatus.RUNNING_EXEUTION); |
||||||
|
|
||||||
|
// update task state
|
||||||
|
if(taskType.equals(TaskType.SQL.name()) || taskType.equals(TaskType.PROCEDURE.name())){ |
||||||
|
processDao.changeTaskState(taskInstance.getState(), |
||||||
|
taskInstance.getStartTime(), |
||||||
|
taskInstance.getHost(), |
||||||
|
null, |
||||||
|
System.getProperty("user.dir") + "/logs/" + |
||||||
|
taskInstance.getProcessDefinitionId() +"/" + |
||||||
|
taskInstance.getProcessInstanceId() +"/" + |
||||||
|
taskInstance.getId() + ".log", |
||||||
|
taskInstance.getId()); |
||||||
|
}else{ |
||||||
|
processDao.changeTaskState(taskInstance.getState(), |
||||||
|
taskInstance.getStartTime(), |
||||||
|
taskInstance.getHost(), |
||||||
|
taskInstance.getExecutePath(), |
||||||
|
System.getProperty("user.dir") + "/logs/" + |
||||||
|
taskInstance.getProcessDefinitionId() +"/" + |
||||||
|
taskInstance.getProcessInstanceId() +"/" + |
||||||
|
taskInstance.getId() + ".log", |
||||||
|
taskInstance.getId()); |
||||||
|
} |
||||||
|
|
||||||
|
ExecutionStatus status = ExecutionStatus.SUCCESS; |
||||||
|
|
||||||
|
try { |
||||||
|
|
||||||
|
|
||||||
|
// custom param str
|
||||||
|
String customParamStr = taskInstance.getProcessInstance().getGlobalParams(); |
||||||
|
|
||||||
|
|
||||||
|
Map<String,String> allParamMap = new HashMap<>(); |
||||||
|
|
||||||
|
|
||||||
|
if (customParamStr != null) { |
||||||
|
List<Property> customParamMap = JSONObject.parseArray(customParamStr, Property.class); |
||||||
|
|
||||||
|
Map<String,String> userDefinedParamMap = customParamMap.stream().collect(Collectors.toMap(Property::getProp, Property::getValue)); |
||||||
|
|
||||||
|
allParamMap.putAll(userDefinedParamMap); |
||||||
|
} |
||||||
|
|
||||||
|
logger.info("script path : {}",taskInstance.getExecutePath()); |
||||||
|
|
||||||
|
TaskProps taskProps = new TaskProps(); |
||||||
|
|
||||||
|
taskProps.setTaskDir(taskInstance.getExecutePath()); |
||||||
|
|
||||||
|
String taskJson = taskInstance.getTaskJson(); |
||||||
|
|
||||||
|
|
||||||
|
TaskNode taskNode = JSONObject.parseObject(taskJson, TaskNode.class); |
||||||
|
|
||||||
|
List<String> projectRes = createProjectResFiles(taskNode); |
||||||
|
|
||||||
|
// copy hdfs file to local
|
||||||
|
copyHdfsToLocal(processDao, |
||||||
|
taskInstance.getExecutePath(), |
||||||
|
projectRes, |
||||||
|
logger); |
||||||
|
|
||||||
|
// set task params
|
||||||
|
taskProps.setTaskParams(taskNode.getParams()); |
||||||
|
// set tenant code , execute task linux user
|
||||||
|
taskProps.setTenantCode(taskInstance.getProcessInstance().getTenantCode()); |
||||||
|
|
||||||
|
ProcessInstance processInstance = processDao.findProcessInstanceByTaskId(taskInstance.getId()); |
||||||
|
taskProps.setScheduleTime(processInstance.getScheduleTime()); |
||||||
|
taskProps.setNodeName(taskInstance.getName()); |
||||||
|
taskProps.setTaskInstId(taskInstance.getId()); |
||||||
|
taskProps.setEnvFile(CommonUtils.getSystemEnvPath()); |
||||||
|
// set queue
|
||||||
|
taskProps.setQueue(taskInstance.getProcessInstance().getQueue()); |
||||||
|
taskProps.setTaskStartTime(taskInstance.getStartTime()); |
||||||
|
taskProps.setDefinedParams(allParamMap); |
||||||
|
|
||||||
|
// set task timeout
|
||||||
|
setTaskTimeout(taskProps, taskNode); |
||||||
|
|
||||||
|
taskProps.setDependence(taskInstance.getDependency()); |
||||||
|
|
||||||
|
taskProps.setTaskAppId(String.format("%s_%s_%s", |
||||||
|
taskInstance.getProcessDefine().getId(), |
||||||
|
taskInstance.getProcessInstance().getId(), |
||||||
|
taskInstance.getId())); |
||||||
|
|
||||||
|
// custom logger
|
||||||
|
TaskLogger taskLogger = new TaskLogger(LoggerUtils.buildTaskId(TASK_PREFIX, |
||||||
|
taskInstance.getProcessDefine().getId(), |
||||||
|
taskInstance.getProcessInstance().getId(), |
||||||
|
taskInstance.getId())); |
||||||
|
|
||||||
|
task = TaskManager.newTask(taskInstance.getTaskType(), taskProps, taskLogger); |
||||||
|
|
||||||
|
// job init
|
||||||
|
task.init(); |
||||||
|
|
||||||
|
// job handle
|
||||||
|
task.handle(); |
||||||
|
|
||||||
|
|
||||||
|
logger.info("task : {} exit status code : {}",taskProps.getTaskAppId(),task.getExitStatusCode()); |
||||||
|
|
||||||
|
if (task.getExitStatusCode() == Constants.EXIT_CODE_SUCCESS){ |
||||||
|
status = ExecutionStatus.SUCCESS; |
||||||
|
}else if (task.getExitStatusCode() == Constants.EXIT_CODE_KILL){ |
||||||
|
status = ExecutionStatus.KILL; |
||||||
|
}else { |
||||||
|
status = ExecutionStatus.FAILURE; |
||||||
|
} |
||||||
|
}catch (Exception e){ |
||||||
|
logger.error("task escheduler failure : " + e.getMessage(),e); |
||||||
|
status = ExecutionStatus.FAILURE ; |
||||||
|
logger.error(String.format("task process exception, process id : %s , task : %s", |
||||||
|
taskInstance.getProcessInstanceId(), |
||||||
|
taskInstance.getName()),e); |
||||||
|
kill(); |
||||||
|
} |
||||||
|
// update task instance state
|
||||||
|
processDao.changeTaskState(status, |
||||||
|
new Date(), |
||||||
|
taskInstance.getId()); |
||||||
|
return task.getExitStatusCode() > Constants.EXIT_CODE_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* set task time out |
||||||
|
* @param taskProps |
||||||
|
* @param taskNode |
||||||
|
*/ |
||||||
|
private void setTaskTimeout(TaskProps taskProps, TaskNode taskNode) { |
||||||
|
taskProps.setTaskTimeout(Integer.MAX_VALUE); |
||||||
|
TaskTimeoutParameter taskTimeoutParameter = taskNode.getTaskTimeoutParameter(); |
||||||
|
if (taskTimeoutParameter.getEnable()){ |
||||||
|
taskProps.setTaskTimeoutStrategy(taskTimeoutParameter.getStrategy()); |
||||||
|
switch (taskTimeoutParameter.getStrategy()){ |
||||||
|
case WARN: |
||||||
|
break; |
||||||
|
case FAILED: |
||||||
|
if (Integer.MAX_VALUE > taskTimeoutParameter.getInterval() * 60) { |
||||||
|
taskProps.setTaskTimeout(taskTimeoutParameter.getInterval() * 60); |
||||||
|
} |
||||||
|
break; |
||||||
|
case WARNFAILED: |
||||||
|
if (Integer.MAX_VALUE > taskTimeoutParameter.getInterval() * 60) { |
||||||
|
taskProps.setTaskTimeout(taskTimeoutParameter.getInterval() * 60); |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
logger.error("not support task timeout strategy: {}", taskTimeoutParameter.getStrategy()); |
||||||
|
throw new IllegalArgumentException("not support task timeout strategy"); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* kill task |
||||||
|
*/ |
||||||
|
public void kill(){ |
||||||
|
if (task != null){ |
||||||
|
try { |
||||||
|
task.cancelApplication(true); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.error(e.getMessage(),e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* create project resource files |
||||||
|
*/ |
||||||
|
private List<String> createProjectResFiles(TaskNode taskNode) throws Exception{ |
||||||
|
|
||||||
|
Set<String> projectFiles = new HashSet<>(); |
||||||
|
AbstractParameters baseParam = TaskParametersUtils.getParameters(taskNode.getType(), taskNode.getParams()); |
||||||
|
|
||||||
|
if (baseParam != null) { |
||||||
|
List<String> projectResourceFiles = baseParam.getResourceFilesList(); |
||||||
|
if (projectResourceFiles != null) { |
||||||
|
projectFiles.addAll(projectResourceFiles); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return new ArrayList<>(projectFiles); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* copy hdfs file to local |
||||||
|
* |
||||||
|
* @param processDao |
||||||
|
* @param execLocalPath |
||||||
|
* @param projectRes |
||||||
|
* @param logger |
||||||
|
*/ |
||||||
|
private void copyHdfsToLocal(ProcessDao processDao, String execLocalPath, List<String> projectRes, Logger logger) throws IOException { |
||||||
|
for (String res : projectRes) { |
||||||
|
File resFile = new File(execLocalPath, res); |
||||||
|
if (!resFile.exists()) { |
||||||
|
try { |
||||||
|
/** |
||||||
|
* query the tenant code of the resource according to the name of the resource |
||||||
|
*/ |
||||||
|
String tentnCode = processDao.queryTenantCodeByResName(res); |
||||||
|
String resHdfsPath = HadoopUtils.getHdfsFilename(tentnCode,res); |
||||||
|
|
||||||
|
logger.info("get resource file from hdfs :{}", resHdfsPath); |
||||||
|
HadoopUtils.getInstance().copyHdfsToLocal(resHdfsPath, execLocalPath + File.separator + res, false, true); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.error(e.getMessage(),e); |
||||||
|
throw new RuntimeException(e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
logger.info("file : {} exists ", resFile.getName()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,557 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.task; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.enums.ExecutionStatus; |
||||||
|
import cn.escheduler.common.thread.ThreadUtils; |
||||||
|
import cn.escheduler.common.utils.HadoopUtils; |
||||||
|
import cn.escheduler.dao.ProcessDao; |
||||||
|
import cn.escheduler.dao.model.TaskInstance; |
||||||
|
import cn.escheduler.server.utils.ProcessUtils; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
import org.slf4j.Logger; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.lang.reflect.Field; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.Date; |
||||||
|
import java.util.List; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
import java.util.function.Consumer; |
||||||
|
import java.util.regex.Matcher; |
||||||
|
import java.util.regex.Pattern; |
||||||
|
|
||||||
|
/** |
||||||
|
* abstract command executor |
||||||
|
*/ |
||||||
|
public abstract class AbstractCommandExecutor { |
||||||
|
/** |
||||||
|
* rules for extracting application ID |
||||||
|
*/ |
||||||
|
protected static final Pattern APPLICATION_REGEX = Pattern.compile(Constants.APPLICATION_REGEX); |
||||||
|
|
||||||
|
/** |
||||||
|
* process |
||||||
|
*/ |
||||||
|
private Process process; |
||||||
|
|
||||||
|
/** |
||||||
|
* log handler |
||||||
|
*/ |
||||||
|
protected Consumer<List<String>> logHandler; |
||||||
|
|
||||||
|
/** |
||||||
|
* task dir |
||||||
|
*/ |
||||||
|
protected final String taskDir; |
||||||
|
|
||||||
|
/** |
||||||
|
* task appId |
||||||
|
*/ |
||||||
|
protected final String taskAppId; |
||||||
|
|
||||||
|
/** |
||||||
|
* tenant code , execute task linux user |
||||||
|
*/ |
||||||
|
protected final String tenantCode; |
||||||
|
|
||||||
|
/** |
||||||
|
* env file |
||||||
|
*/ |
||||||
|
protected final String envFile; |
||||||
|
|
||||||
|
/** |
||||||
|
* start time |
||||||
|
*/ |
||||||
|
protected final Date startTime; |
||||||
|
|
||||||
|
/** |
||||||
|
* timeout |
||||||
|
*/ |
||||||
|
protected int timeout; |
||||||
|
|
||||||
|
/** |
||||||
|
* logger |
||||||
|
*/ |
||||||
|
protected Logger logger; |
||||||
|
|
||||||
|
/** |
||||||
|
* log list |
||||||
|
*/ |
||||||
|
protected final List<String> logBuffer; |
||||||
|
|
||||||
|
|
||||||
|
public AbstractCommandExecutor(Consumer<List<String>> logHandler, |
||||||
|
String taskDir, String taskAppId, String tenantCode, String envFile, |
||||||
|
Date startTime, int timeout, Logger logger){ |
||||||
|
this.logHandler = logHandler; |
||||||
|
this.taskDir = taskDir; |
||||||
|
this.taskAppId = taskAppId; |
||||||
|
this.tenantCode = tenantCode; |
||||||
|
this.envFile = envFile; |
||||||
|
this.startTime = startTime; |
||||||
|
this.timeout = timeout; |
||||||
|
this.logger = logger; |
||||||
|
this.logBuffer = Collections.synchronizedList(new ArrayList<>()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* task specific execution logic |
||||||
|
* |
||||||
|
* @param execCommand |
||||||
|
* @param processDao |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public int run(String execCommand, ProcessDao processDao) { |
||||||
|
int exitStatusCode; |
||||||
|
|
||||||
|
try { |
||||||
|
if (StringUtils.isEmpty(execCommand)) { |
||||||
|
exitStatusCode = 0; |
||||||
|
return exitStatusCode; |
||||||
|
} |
||||||
|
|
||||||
|
String commandFilePath = buildCommandFilePath(); |
||||||
|
|
||||||
|
// create command file if not exists
|
||||||
|
createCommandFileIfNotExists(execCommand, commandFilePath); |
||||||
|
|
||||||
|
//build process
|
||||||
|
buildProcess(commandFilePath); |
||||||
|
|
||||||
|
// parse process output
|
||||||
|
parseProcessOutput(process); |
||||||
|
|
||||||
|
// get process id
|
||||||
|
int pid = getProcessId(process); |
||||||
|
|
||||||
|
// task instance id
|
||||||
|
int taskInstId = Integer.parseInt(taskAppId.split("_")[2]); |
||||||
|
|
||||||
|
processDao.updatePidByTaskInstId(taskInstId, pid); |
||||||
|
|
||||||
|
logger.info("process start, process id is: {}", pid); |
||||||
|
|
||||||
|
// if timeout occurs, exit directly
|
||||||
|
long remainTime = getRemaintime(); |
||||||
|
|
||||||
|
// waiting for the run to finish
|
||||||
|
boolean status = process.waitFor(remainTime, TimeUnit.SECONDS); |
||||||
|
|
||||||
|
if (status) { |
||||||
|
exitStatusCode = process.exitValue(); |
||||||
|
logger.info("process has exited, work dir:{}, pid:{} ,exitStatusCode:{}", taskDir, pid,exitStatusCode); |
||||||
|
//update process state to db
|
||||||
|
exitStatusCode = updateState(processDao, exitStatusCode, pid, taskInstId); |
||||||
|
|
||||||
|
} else { |
||||||
|
cancelApplication(); |
||||||
|
exitStatusCode = -1; |
||||||
|
logger.warn("process timeout, work dir:{}, pid:{}", taskDir, pid); |
||||||
|
} |
||||||
|
|
||||||
|
} catch (InterruptedException e) { |
||||||
|
exitStatusCode = -1; |
||||||
|
logger.error(String.format("interrupt exception: {}, task may be cancelled or killed",e.getMessage()), e); |
||||||
|
throw new RuntimeException("interrupt exception. exitCode is : " + exitStatusCode); |
||||||
|
} catch (Exception e) { |
||||||
|
exitStatusCode = -1; |
||||||
|
logger.error(e.getMessage(), e); |
||||||
|
throw new RuntimeException("process error . exitCode is : " + exitStatusCode); |
||||||
|
} |
||||||
|
|
||||||
|
return exitStatusCode; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* build process |
||||||
|
* |
||||||
|
* @param commandFile |
||||||
|
* @throws IOException |
||||||
|
*/ |
||||||
|
private void buildProcess(String commandFile) throws IOException { |
||||||
|
//init process builder
|
||||||
|
ProcessBuilder processBuilder = new ProcessBuilder(); |
||||||
|
// setting up a working directory
|
||||||
|
processBuilder.directory(new File(taskDir)); |
||||||
|
// merge error information to standard output stream
|
||||||
|
processBuilder.redirectErrorStream(true); |
||||||
|
// setting up user to run commands
|
||||||
|
processBuilder.command("sudo", "-u", tenantCode, commandType(), commandFile); |
||||||
|
|
||||||
|
process = processBuilder.start(); |
||||||
|
|
||||||
|
// print command
|
||||||
|
printCommand(processBuilder); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* update process state to db |
||||||
|
* |
||||||
|
* @param processDao |
||||||
|
* @param exitStatusCode |
||||||
|
* @param pid |
||||||
|
* @param taskInstId |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private int updateState(ProcessDao processDao, int exitStatusCode, int pid, int taskInstId) { |
||||||
|
//get yarn state by log
|
||||||
|
if (exitStatusCode != -1) { |
||||||
|
TaskInstance taskInstance = processDao.findTaskInstanceById(taskInstId); |
||||||
|
logger.info("process id is {}", pid); |
||||||
|
|
||||||
|
List<String> appIds = getAppLinks(taskInstance.getLogPath()); |
||||||
|
if (appIds.size() > 0) { |
||||||
|
String appUrl = String.join(Constants.COMMA, appIds); |
||||||
|
logger.info("yarn log url:{}",appUrl); |
||||||
|
processDao.updatePidByTaskInstId(taskInstId, pid, appUrl); |
||||||
|
} |
||||||
|
|
||||||
|
// check if all operations are completed
|
||||||
|
if (!isSuccessOfYarnState(appIds)) { |
||||||
|
exitStatusCode = -1; |
||||||
|
} |
||||||
|
} |
||||||
|
return exitStatusCode; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* cancel python task |
||||||
|
*/ |
||||||
|
public void cancelApplication() throws Exception { |
||||||
|
if (process == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// clear log
|
||||||
|
clear(); |
||||||
|
|
||||||
|
int processId = getProcessId(process); |
||||||
|
|
||||||
|
logger.info("cancel process: {}", processId); |
||||||
|
|
||||||
|
// kill , waiting for completion
|
||||||
|
boolean killed = softKill(processId); |
||||||
|
|
||||||
|
if (!killed) { |
||||||
|
// hard kill
|
||||||
|
hardKill(processId); |
||||||
|
|
||||||
|
// destory
|
||||||
|
process.destroy(); |
||||||
|
|
||||||
|
process = null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* soft kill |
||||||
|
* @param processId |
||||||
|
* @return |
||||||
|
* @throws InterruptedException |
||||||
|
*/ |
||||||
|
private boolean softKill(int processId) { |
||||||
|
|
||||||
|
if (processId != 0 && process.isAlive()) { |
||||||
|
try { |
||||||
|
// sudo -u user command to run command
|
||||||
|
String cmd = String.format("sudo kill %d", processId); |
||||||
|
|
||||||
|
logger.info("soft kill task:{}, process id:{}, cmd:{}", taskAppId, processId, cmd); |
||||||
|
|
||||||
|
Runtime.getRuntime().exec(cmd); |
||||||
|
} catch (IOException e) { |
||||||
|
logger.info("kill attempt failed." + e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return process.isAlive(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* hard kill |
||||||
|
* @param processId |
||||||
|
*/ |
||||||
|
private void hardKill(int processId) { |
||||||
|
if (processId != 0 && process.isAlive()) { |
||||||
|
try { |
||||||
|
String cmd = String.format("sudo kill -9 %d", processId); |
||||||
|
|
||||||
|
logger.info("hard kill task:{}, process id:{}, cmd:{}", taskAppId, processId, cmd); |
||||||
|
|
||||||
|
Runtime.getRuntime().exec(cmd); |
||||||
|
} catch (IOException e) { |
||||||
|
logger.error("kill attempt failed." + e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* print command |
||||||
|
* @param processBuilder |
||||||
|
*/ |
||||||
|
private void printCommand(ProcessBuilder processBuilder) { |
||||||
|
String cmdStr; |
||||||
|
|
||||||
|
try { |
||||||
|
cmdStr = ProcessUtils.buildCommandStr(processBuilder.command()); |
||||||
|
logger.info("task run command:\n{}", cmdStr); |
||||||
|
} catch (IOException e) { |
||||||
|
logger.error(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* clear |
||||||
|
*/ |
||||||
|
private void clear() { |
||||||
|
if (!logBuffer.isEmpty()) { |
||||||
|
// log handle
|
||||||
|
logHandler.accept(logBuffer); |
||||||
|
|
||||||
|
logBuffer.clear(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get the standard output of the process |
||||||
|
*/ |
||||||
|
private void parseProcessOutput(Process process) { |
||||||
|
String threadLoggerInfoName = String.format("TaskLogInfo-%s", taskAppId); |
||||||
|
ThreadUtils.newDaemonSingleThreadExecutor(threadLoggerInfoName).submit(new Runnable(){ |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
BufferedReader inReader = null; |
||||||
|
|
||||||
|
try { |
||||||
|
inReader = new BufferedReader(new InputStreamReader(process.getInputStream())); |
||||||
|
String line; |
||||||
|
|
||||||
|
long lastFlushTime = System.currentTimeMillis(); |
||||||
|
|
||||||
|
while ((line = inReader.readLine()) != null) { |
||||||
|
if(checkShowLog(line)){ |
||||||
|
logBuffer.add(line); |
||||||
|
} |
||||||
|
|
||||||
|
lastFlushTime = flush(lastFlushTime); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
logger.error(e.getMessage(),e); |
||||||
|
} finally { |
||||||
|
clear(); |
||||||
|
close(inReader); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public int getPid() { |
||||||
|
return getProcessId(process); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* check yarn state |
||||||
|
* |
||||||
|
* @param appIds |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public boolean isSuccessOfYarnState(List<String> appIds) { |
||||||
|
|
||||||
|
boolean result = true; |
||||||
|
try { |
||||||
|
for (String appId : appIds) { |
||||||
|
ExecutionStatus applicationStatus = HadoopUtils.getInstance().getApplicationStatus(appId); |
||||||
|
logger.info("appId:{}, final state:{}",appId,applicationStatus.name()); |
||||||
|
if (!applicationStatus.equals(ExecutionStatus.SUCCESS)) { |
||||||
|
result = false; |
||||||
|
} |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
logger.error(String.format("mapreduce applications: %s status failed : " + e.getMessage(), appIds.toString()),e); |
||||||
|
result = false; |
||||||
|
} |
||||||
|
return result; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get app links |
||||||
|
* @param fileName |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private List<String> getAppLinks(String fileName) { |
||||||
|
List<String> logs = convertFile2List(fileName); |
||||||
|
|
||||||
|
List<String> appIds = new ArrayList<String>(); |
||||||
|
/** |
||||||
|
* analysis log,get submited yarn application id |
||||||
|
*/ |
||||||
|
for (String log : logs) { |
||||||
|
|
||||||
|
String appId = findAppId(log); |
||||||
|
if (StringUtils.isNotEmpty(appId) && !appIds.contains(appId)) { |
||||||
|
logger.info("find app id: {}", appId); |
||||||
|
appIds.add(appId); |
||||||
|
} |
||||||
|
} |
||||||
|
return appIds; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* convert file to list |
||||||
|
* @param filename |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private List<String> convertFile2List(String filename) { |
||||||
|
List lineList = new ArrayList<String>(100); |
||||||
|
File file=new File(filename); |
||||||
|
|
||||||
|
if (!file.exists()){ |
||||||
|
return lineList; |
||||||
|
} |
||||||
|
|
||||||
|
BufferedReader br = null; |
||||||
|
try { |
||||||
|
br = new BufferedReader(new InputStreamReader(new FileInputStream(filename), StandardCharsets.UTF_8)); |
||||||
|
String line = null; |
||||||
|
while ((line = br.readLine()) != null) { |
||||||
|
lineList.add(line); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
logger.error(String.format("read file: %s failed : ",filename),e); |
||||||
|
} finally { |
||||||
|
if(br != null){ |
||||||
|
try { |
||||||
|
br.close(); |
||||||
|
} catch (IOException e) { |
||||||
|
logger.error(e.getMessage(),e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
return lineList; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* find app id |
||||||
|
* |
||||||
|
* @return appid |
||||||
|
*/ |
||||||
|
private String findAppId(String line) { |
||||||
|
Matcher matcher = APPLICATION_REGEX.matcher(line); |
||||||
|
|
||||||
|
if (matcher.find() && checkFindApp(line)) { |
||||||
|
return matcher.group(); |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* get remain time(s) |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private long getRemaintime() { |
||||||
|
long usedTime = (System.currentTimeMillis() - startTime.getTime()) / 1000; |
||||||
|
long remainTime = timeout - usedTime; |
||||||
|
|
||||||
|
if (remainTime < 0) { |
||||||
|
throw new RuntimeException("task execution time out"); |
||||||
|
} |
||||||
|
|
||||||
|
return remainTime; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get process id |
||||||
|
* |
||||||
|
* @param process |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private int getProcessId(Process process) { |
||||||
|
int processId = 0; |
||||||
|
|
||||||
|
try { |
||||||
|
Field f = process.getClass().getDeclaredField(Constants.PID); |
||||||
|
f.setAccessible(true); |
||||||
|
|
||||||
|
processId = f.getInt(process); |
||||||
|
} catch (Throwable e) { |
||||||
|
logger.error(e.getMessage(), e); |
||||||
|
} |
||||||
|
|
||||||
|
return processId; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* when log buffer siz or flush time reach condition , then flush |
||||||
|
* |
||||||
|
* @param lastFlushTime last flush time |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private long flush(long lastFlushTime) { |
||||||
|
long now = System.currentTimeMillis(); |
||||||
|
|
||||||
|
/** |
||||||
|
* when log buffer siz or flush time reach condition , then flush |
||||||
|
*/ |
||||||
|
if (logBuffer.size() >= Constants.defaultLogRowsNum || now - lastFlushTime > Constants.defaultLogFlushInterval) { |
||||||
|
lastFlushTime = now; |
||||||
|
/** log handle */ |
||||||
|
logHandler.accept(logBuffer); |
||||||
|
|
||||||
|
logBuffer.clear(); |
||||||
|
} |
||||||
|
return lastFlushTime; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* close buffer reader |
||||||
|
* |
||||||
|
* @param inReader |
||||||
|
*/ |
||||||
|
private void close(BufferedReader inReader) { |
||||||
|
if (inReader != null) { |
||||||
|
try { |
||||||
|
inReader.close(); |
||||||
|
} catch (IOException e) { |
||||||
|
logger.error(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
protected abstract String buildCommandFilePath(); |
||||||
|
protected abstract String commandType(); |
||||||
|
protected abstract boolean checkShowLog(String line); |
||||||
|
protected abstract boolean checkFindApp(String line); |
||||||
|
protected abstract void createCommandFileIfNotExists(String execCommand, String commandFile) throws IOException; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// if(line.contains(taskAppId) || !line.contains("cn.escheduler.server.worker.log.TaskLogger")){
|
||||||
|
// logs.add(line);
|
||||||
|
// }
|
||||||
|
} |
@ -0,0 +1,99 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.task; |
||||||
|
|
||||||
|
import cn.escheduler.common.task.AbstractParameters; |
||||||
|
import org.slf4j.Logger; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* executive task |
||||||
|
*/ |
||||||
|
public abstract class AbstractTask { |
||||||
|
|
||||||
|
/** |
||||||
|
* task props |
||||||
|
**/ |
||||||
|
protected TaskProps taskProps; |
||||||
|
|
||||||
|
/** |
||||||
|
* log record |
||||||
|
*/ |
||||||
|
protected Logger logger; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* cancel |
||||||
|
*/ |
||||||
|
protected volatile boolean cancel = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* exit code |
||||||
|
*/ |
||||||
|
protected volatile int exitStatusCode = -1; |
||||||
|
|
||||||
|
/** |
||||||
|
* @param taskProps |
||||||
|
* @param logger |
||||||
|
*/ |
||||||
|
protected AbstractTask(TaskProps taskProps, Logger logger) { |
||||||
|
this.taskProps = taskProps; |
||||||
|
this.logger = logger; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* init task |
||||||
|
*/ |
||||||
|
public void init() throws Exception { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* task handle |
||||||
|
*/ |
||||||
|
public abstract void handle() throws Exception; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void cancelApplication(boolean status) throws Exception { |
||||||
|
cancel = true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* log process |
||||||
|
*/ |
||||||
|
public void logHandle(List<String> logs) { |
||||||
|
// note that the "new line" is added here to facilitate log parsing
|
||||||
|
logger.info(" -> {}", String.join("\n\t", logs)); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* exit code |
||||||
|
*/ |
||||||
|
public int getExitStatusCode() { |
||||||
|
return exitStatusCode; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* get task parameters |
||||||
|
*/ |
||||||
|
public abstract AbstractParameters getParameters(); |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.task; |
||||||
|
|
||||||
|
import cn.escheduler.dao.DaoFactory; |
||||||
|
import cn.escheduler.dao.ProcessDao; |
||||||
|
import cn.escheduler.dao.model.ProcessInstance; |
||||||
|
import cn.escheduler.dao.model.TaskInstance; |
||||||
|
import cn.escheduler.server.utils.ProcessUtils; |
||||||
|
import org.slf4j.Logger; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* abstract yarn task |
||||||
|
*/ |
||||||
|
public abstract class AbstractYarnTask extends AbstractTask { |
||||||
|
|
||||||
|
/** |
||||||
|
* process instance |
||||||
|
*/ |
||||||
|
protected ProcessInstance processInstance; |
||||||
|
|
||||||
|
/** |
||||||
|
* process task |
||||||
|
*/ |
||||||
|
private ShellCommandExecutor processTask; |
||||||
|
|
||||||
|
/** |
||||||
|
* process database access |
||||||
|
*/ |
||||||
|
protected ProcessDao processDao; |
||||||
|
|
||||||
|
/** |
||||||
|
* @param taskProps |
||||||
|
* @param logger |
||||||
|
* @throws IOException |
||||||
|
*/ |
||||||
|
public AbstractYarnTask(TaskProps taskProps, Logger logger) { |
||||||
|
super(taskProps, logger); |
||||||
|
this.processDao = DaoFactory.getDaoInstance(ProcessDao.class); |
||||||
|
// find process instance by taskId
|
||||||
|
this.processInstance = processDao.findProcessInstanceByTaskId(taskProps.getTaskInstId()); |
||||||
|
this.processTask = new ShellCommandExecutor(this::logHandle, |
||||||
|
taskProps.getTaskDir(), taskProps.getTaskAppId(), |
||||||
|
taskProps.getTenantCode(), taskProps.getEnvFile(), taskProps.getTaskStartTime(), |
||||||
|
taskProps.getTaskTimeout(), logger); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void handle() throws Exception { |
||||||
|
try { |
||||||
|
// construct process
|
||||||
|
exitStatusCode = processTask.run(buildCommand(), processDao); |
||||||
|
} catch (Exception e) { |
||||||
|
logger.error("yarn process failed : " + e.getMessage(), e); |
||||||
|
exitStatusCode = -1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void cancelApplication(boolean status) throws Exception { |
||||||
|
cancel = true; |
||||||
|
// cancel process
|
||||||
|
processTask.cancelApplication(); |
||||||
|
int taskInstId = taskProps.getTaskInstId(); |
||||||
|
TaskInstance taskInstance = processDao.findTaskInstanceById(taskInstId); |
||||||
|
if (status && taskInstance != null){ |
||||||
|
ProcessUtils.killYarnJob(taskInstance); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* create command |
||||||
|
*/ |
||||||
|
protected abstract String buildCommand() throws Exception; |
||||||
|
} |
@ -0,0 +1,112 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.task; |
||||||
|
|
||||||
|
import cn.escheduler.common.utils.FileUtils; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
import org.slf4j.Logger; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
import java.nio.file.Files; |
||||||
|
import java.nio.file.Paths; |
||||||
|
import java.util.Date; |
||||||
|
import java.util.List; |
||||||
|
import java.util.function.Consumer; |
||||||
|
|
||||||
|
/** |
||||||
|
* python command executor |
||||||
|
*/ |
||||||
|
public class PythonCommandExecutor extends AbstractCommandExecutor { |
||||||
|
|
||||||
|
public static final String PYTHON = "python"; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public PythonCommandExecutor(Consumer<List<String>> logHandler, |
||||||
|
String taskDir, String taskAppId, String tenantCode, String envFile, |
||||||
|
Date startTime, int timeout, Logger logger) { |
||||||
|
super(logHandler,taskDir,taskAppId, tenantCode, envFile, startTime, timeout, logger); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* build command file path |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
protected String buildCommandFilePath() { |
||||||
|
return String.format("%s/py_%s.command", taskDir, taskAppId); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* create command file if not exists |
||||||
|
* |
||||||
|
* @param commandFile |
||||||
|
* @throws IOException |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
protected void createCommandFileIfNotExists(String execCommand, String commandFile) throws IOException { |
||||||
|
logger.info("proxy user:{}, work dir:{}", tenantCode, taskDir); |
||||||
|
|
||||||
|
if (!Files.exists(Paths.get(commandFile))) { |
||||||
|
logger.info("generate command file:{}", commandFile); |
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder(200); |
||||||
|
sb.append("#-*- encoding=utf8 -*-\n"); |
||||||
|
sb.append("import os,sys\n"); |
||||||
|
sb.append("BASEDIR = os.path.dirname(os.path.realpath(__file__))\n"); |
||||||
|
sb.append("os.chdir(BASEDIR)\n"); |
||||||
|
|
||||||
|
if (StringUtils.isNotEmpty(envFile)) { |
||||||
|
String[] envArray = envFile.split("\\."); |
||||||
|
if(envArray.length == 2){ |
||||||
|
String path = envArray[0]; |
||||||
|
logger.info("path:"+path); |
||||||
|
int index = path.lastIndexOf("/"); |
||||||
|
sb.append(String.format("sys.path.append('%s')\n",path.substring(0,index))); |
||||||
|
sb.append(String.format("import %s\n",path.substring(index+1))); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sb.append("\n\n"); |
||||||
|
sb.append(String.format("import py_%s_node\n",taskAppId)); |
||||||
|
logger.info(sb.toString()); |
||||||
|
|
||||||
|
// write data to file
|
||||||
|
FileUtils.writeStringToFile(new File(commandFile), sb.toString(), StandardCharsets.UTF_8); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected String commandType() { |
||||||
|
return PYTHON; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected boolean checkShowLog(String line) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected boolean checkFindApp(String line) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,97 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.task; |
||||||
|
|
||||||
|
import org.apache.commons.io.FileUtils; |
||||||
|
import org.slf4j.Logger; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.charset.Charset; |
||||||
|
import java.nio.file.Files; |
||||||
|
import java.nio.file.Paths; |
||||||
|
import java.util.Date; |
||||||
|
import java.util.List; |
||||||
|
import java.util.function.Consumer; |
||||||
|
|
||||||
|
/** |
||||||
|
* command executor |
||||||
|
* |
||||||
|
* 进程,真正在worker服务器上执行的任务 |
||||||
|
*/ |
||||||
|
public class ShellCommandExecutor extends AbstractCommandExecutor { |
||||||
|
|
||||||
|
public static final String SH = "sh"; |
||||||
|
|
||||||
|
|
||||||
|
public ShellCommandExecutor(Consumer<List<String>> logHandler, |
||||||
|
String taskDir, String taskAppId, String tenantCode, String envFile, |
||||||
|
Date startTime, int timeout, Logger logger) { |
||||||
|
super(logHandler,taskDir,taskAppId, tenantCode, envFile, startTime, timeout, logger); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
protected String buildCommandFilePath() { |
||||||
|
// command file
|
||||||
|
return String.format("%s/%s.command", taskDir, taskAppId); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected String commandType() { |
||||||
|
return SH; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected boolean checkShowLog(String line) { |
||||||
|
return line.contains(taskAppId) || !line.contains("cn.escheduler.server.worker.log.TaskLogger"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected boolean checkFindApp(String line) { |
||||||
|
return line.contains(taskAppId); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void createCommandFileIfNotExists(String execCommand, String commandFile) throws IOException { |
||||||
|
logger.info("tenantCode user:{}, task dir:{}", tenantCode, taskAppId); |
||||||
|
|
||||||
|
// create if non existence
|
||||||
|
if (!Files.exists(Paths.get(commandFile))) { |
||||||
|
logger.info("create command file:{}", commandFile); |
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
sb.append("#!/bin/sh\n"); |
||||||
|
sb.append("BASEDIR=$(cd `dirname $0`; pwd)\n"); |
||||||
|
sb.append("cd $BASEDIR\n"); |
||||||
|
|
||||||
|
if (envFile != null) { |
||||||
|
sb.append("source " + envFile + "\n"); |
||||||
|
} |
||||||
|
|
||||||
|
sb.append("\n\n"); |
||||||
|
sb.append(execCommand); |
||||||
|
logger.info("command : {}",sb.toString()); |
||||||
|
|
||||||
|
// write data to file
|
||||||
|
FileUtils.writeStringToFile(new File(commandFile), sb.toString(), |
||||||
|
Charset.forName("UTF-8")); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.task; |
||||||
|
|
||||||
|
|
||||||
|
import cn.escheduler.common.enums.TaskType; |
||||||
|
import cn.escheduler.server.worker.task.dependent.DependentTask; |
||||||
|
import cn.escheduler.server.worker.task.mr.MapReduceTask; |
||||||
|
import cn.escheduler.server.worker.task.processdure.ProcedureTask; |
||||||
|
import cn.escheduler.server.worker.task.python.PythonTask; |
||||||
|
import cn.escheduler.server.worker.task.shell.ShellTask; |
||||||
|
import cn.escheduler.server.worker.task.spark.SparkTask; |
||||||
|
import cn.escheduler.server.worker.task.sql.SqlTask; |
||||||
|
import org.apache.commons.lang3.EnumUtils; |
||||||
|
import org.slf4j.Logger; |
||||||
|
|
||||||
|
/** |
||||||
|
* task manaster |
||||||
|
*/ |
||||||
|
public class TaskManager { |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* create new task |
||||||
|
* @param taskType |
||||||
|
* @param props |
||||||
|
* @param logger |
||||||
|
* @return |
||||||
|
* @throws IllegalArgumentException |
||||||
|
*/ |
||||||
|
public static AbstractTask newTask(String taskType, TaskProps props, Logger logger) |
||||||
|
throws IllegalArgumentException { |
||||||
|
switch (EnumUtils.getEnum(TaskType.class,taskType)) { |
||||||
|
case SHELL: |
||||||
|
return new ShellTask(props, logger); |
||||||
|
case PROCEDURE: |
||||||
|
return new ProcedureTask(props, logger); |
||||||
|
case SQL: |
||||||
|
return new SqlTask(props, logger); |
||||||
|
case MR: |
||||||
|
return new MapReduceTask(props, logger); |
||||||
|
case SPARK: |
||||||
|
return new SparkTask(props, logger); |
||||||
|
case PYTHON: |
||||||
|
return new PythonTask(props, logger); |
||||||
|
case DEPENDENT: |
||||||
|
return new DependentTask(props, logger); |
||||||
|
default: |
||||||
|
logger.error("unsupport task type: {}", taskType); |
||||||
|
throw new IllegalArgumentException("not support task type"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,236 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.task; |
||||||
|
|
||||||
|
import cn.escheduler.common.enums.DataType; |
||||||
|
import cn.escheduler.common.enums.Direct; |
||||||
|
import cn.escheduler.common.enums.TaskTimeoutStrategy; |
||||||
|
import cn.escheduler.common.process.Property; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* task props |
||||||
|
*/ |
||||||
|
public class TaskProps { |
||||||
|
|
||||||
|
/** |
||||||
|
* task node name |
||||||
|
**/ |
||||||
|
private String nodeName; |
||||||
|
|
||||||
|
/** |
||||||
|
* task instance id |
||||||
|
**/ |
||||||
|
private int taskInstId; |
||||||
|
|
||||||
|
/** |
||||||
|
* tenant code , execute task linux user |
||||||
|
**/ |
||||||
|
private String tenantCode; |
||||||
|
|
||||||
|
/** |
||||||
|
* task parameters |
||||||
|
**/ |
||||||
|
private String taskParams; |
||||||
|
|
||||||
|
/** |
||||||
|
* task dir |
||||||
|
**/ |
||||||
|
private String taskDir; |
||||||
|
|
||||||
|
/** |
||||||
|
* queue |
||||||
|
**/ |
||||||
|
private String queue; |
||||||
|
|
||||||
|
/** |
||||||
|
* env file |
||||||
|
**/ |
||||||
|
private String envFile; |
||||||
|
|
||||||
|
/** |
||||||
|
* defined params |
||||||
|
**/ |
||||||
|
private Map<String, String> definedParams; |
||||||
|
|
||||||
|
/** |
||||||
|
* task path |
||||||
|
*/ |
||||||
|
private String taskAppId; |
||||||
|
|
||||||
|
/** |
||||||
|
* task start time |
||||||
|
*/ |
||||||
|
private Date taskStartTime; |
||||||
|
|
||||||
|
/** |
||||||
|
* task timeout |
||||||
|
*/ |
||||||
|
private int taskTimeout; |
||||||
|
|
||||||
|
/** |
||||||
|
* task timeout strategy |
||||||
|
*/ |
||||||
|
private TaskTimeoutStrategy taskTimeoutStrategy; |
||||||
|
/** |
||||||
|
* task dependence |
||||||
|
*/ |
||||||
|
private String dependence; |
||||||
|
|
||||||
|
/** |
||||||
|
* schedule time |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private Date scheduleTime; |
||||||
|
|
||||||
|
|
||||||
|
public String getTenantCode() { |
||||||
|
return tenantCode; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTenantCode(String tenantCode) { |
||||||
|
this.tenantCode = tenantCode; |
||||||
|
} |
||||||
|
|
||||||
|
public String getTaskParams() { |
||||||
|
return taskParams; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTaskParams(String taskParams) { |
||||||
|
this.taskParams = taskParams; |
||||||
|
} |
||||||
|
|
||||||
|
public String getTaskDir() { |
||||||
|
return taskDir; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTaskDir(String taskDir) { |
||||||
|
this.taskDir = taskDir; |
||||||
|
} |
||||||
|
|
||||||
|
public Map<String, String> getDefinedParams() { |
||||||
|
return definedParams; |
||||||
|
} |
||||||
|
|
||||||
|
public void setDefinedParams(Map<String, String> definedParams) { |
||||||
|
this.definedParams = definedParams; |
||||||
|
} |
||||||
|
|
||||||
|
public String getEnvFile() { |
||||||
|
return envFile; |
||||||
|
} |
||||||
|
|
||||||
|
public void setEnvFile(String envFile) { |
||||||
|
this.envFile = envFile; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public String getNodeName() { |
||||||
|
return nodeName; |
||||||
|
} |
||||||
|
|
||||||
|
public void setNodeName(String nodeName) { |
||||||
|
this.nodeName = nodeName; |
||||||
|
} |
||||||
|
|
||||||
|
public int getTaskInstId() { |
||||||
|
return taskInstId; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTaskInstId(int taskInstId) { |
||||||
|
this.taskInstId = taskInstId; |
||||||
|
} |
||||||
|
|
||||||
|
public String getQueue() { |
||||||
|
return queue; |
||||||
|
} |
||||||
|
|
||||||
|
public void setQueue(String queue) { |
||||||
|
this.queue = queue; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public String getTaskAppId() { |
||||||
|
return taskAppId; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTaskAppId(String taskAppId) { |
||||||
|
this.taskAppId = taskAppId; |
||||||
|
} |
||||||
|
|
||||||
|
public Date getTaskStartTime() { |
||||||
|
return taskStartTime; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTaskStartTime(Date taskStartTime) { |
||||||
|
this.taskStartTime = taskStartTime; |
||||||
|
} |
||||||
|
|
||||||
|
public int getTaskTimeout() { |
||||||
|
return taskTimeout; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTaskTimeout(int taskTimeout) { |
||||||
|
this.taskTimeout = taskTimeout; |
||||||
|
} |
||||||
|
|
||||||
|
public TaskTimeoutStrategy getTaskTimeoutStrategy() { |
||||||
|
return taskTimeoutStrategy; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTaskTimeoutStrategy(TaskTimeoutStrategy taskTimeoutStrategy) { |
||||||
|
this.taskTimeoutStrategy = taskTimeoutStrategy; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get parameters map |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public Map<String,Property> getUserDefParamsMap() { |
||||||
|
if (definedParams != null) { |
||||||
|
Map<String,Property> userDefParamsMaps = new HashMap<>(); |
||||||
|
Iterator<Map.Entry<String, String>> iter = definedParams.entrySet().iterator(); |
||||||
|
while (iter.hasNext()){ |
||||||
|
Map.Entry<String, String> en = iter.next(); |
||||||
|
Property property = new Property(en.getKey(), Direct.IN, DataType.VARCHAR , en.getValue()); |
||||||
|
userDefParamsMaps.put(property.getProp(),property); |
||||||
|
} |
||||||
|
return userDefParamsMaps; |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public String getDependence() { |
||||||
|
return dependence; |
||||||
|
} |
||||||
|
|
||||||
|
public void setDependence(String dependence) { |
||||||
|
this.dependence = dependence; |
||||||
|
} |
||||||
|
|
||||||
|
public Date getScheduleTime() { |
||||||
|
return scheduleTime; |
||||||
|
} |
||||||
|
|
||||||
|
public void setScheduleTime(Date scheduleTime) { |
||||||
|
this.scheduleTime = scheduleTime; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,213 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.task.dependent; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.enums.DependResult; |
||||||
|
import cn.escheduler.common.enums.DependentRelation; |
||||||
|
import cn.escheduler.common.enums.ExecutionStatus; |
||||||
|
import cn.escheduler.common.model.DateInterval; |
||||||
|
import cn.escheduler.common.model.DependentItem; |
||||||
|
import cn.escheduler.common.utils.DependentUtils; |
||||||
|
import cn.escheduler.dao.DaoFactory; |
||||||
|
import cn.escheduler.dao.ProcessDao; |
||||||
|
import cn.escheduler.dao.model.ProcessInstance; |
||||||
|
import cn.escheduler.dao.model.TaskInstance; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* dependent item execute |
||||||
|
*/ |
||||||
|
public class DependentExecute { |
||||||
|
/** |
||||||
|
* process dao |
||||||
|
*/ |
||||||
|
private static final ProcessDao processDao = DaoFactory.getDaoInstance(ProcessDao.class); |
||||||
|
|
||||||
|
private List<DependentItem> dependItemList; |
||||||
|
private DependentRelation relation; |
||||||
|
|
||||||
|
private DependResult modelDependResult = DependResult.WAITING; |
||||||
|
private Map<String, DependResult> dependResultMap = new HashMap<>(); |
||||||
|
|
||||||
|
private Logger logger = LoggerFactory.getLogger(DependentExecute.class); |
||||||
|
|
||||||
|
public DependentExecute(List<DependentItem> itemList, DependentRelation relation){ |
||||||
|
this.dependItemList = itemList; |
||||||
|
this.relation = relation; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get dependent item for one dependent item |
||||||
|
* @param dependentItem |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public DependResult getDependentResultForItem(DependentItem dependentItem, Date currentTime){ |
||||||
|
List<DateInterval> dateIntervals = DependentUtils.getDateIntervalList(currentTime, dependentItem.getDateValue()); |
||||||
|
return calculateResultForTasks(dependentItem, dateIntervals ); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* calculate dependent result for one dependent item. |
||||||
|
* @param dependentItem |
||||||
|
* @param dateIntervals |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private DependResult calculateResultForTasks(DependentItem dependentItem, |
||||||
|
List<DateInterval> dateIntervals) { |
||||||
|
DependResult result = DependResult.FAILED; |
||||||
|
for(DateInterval dateInterval : dateIntervals){ |
||||||
|
ProcessInstance processInstance = findLastProcessInterval(dependentItem.getDefinitionId(), |
||||||
|
dateInterval); |
||||||
|
if(processInstance == null){ |
||||||
|
logger.error("cannot find the right process instance: definition id:{}, start:{}, end:{}", |
||||||
|
dependentItem.getDefinitionId(), dateInterval.getStartTime(), dateInterval.getEndTime() ); |
||||||
|
return DependResult.FAILED; |
||||||
|
} |
||||||
|
if(dependentItem.getDepTasks().equals(Constants.DEPENDENT_ALL)){ |
||||||
|
result = getDependResultByState(processInstance.getState()); |
||||||
|
}else{ |
||||||
|
TaskInstance taskInstance = null; |
||||||
|
List<TaskInstance> taskInstanceList = processDao.findValidTaskListByProcessId(processInstance.getId()); |
||||||
|
|
||||||
|
for(TaskInstance task : taskInstanceList){ |
||||||
|
if(task.getName().equals(dependentItem.getDepTasks())){ |
||||||
|
taskInstance = task; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
if(taskInstance == null){ |
||||||
|
// cannot find task in the process instance
|
||||||
|
// maybe because process instance is running or failed.
|
||||||
|
result = getDependResultByState(processInstance.getState()); |
||||||
|
}else{ |
||||||
|
result = getDependResultByState(taskInstance.getState()); |
||||||
|
} |
||||||
|
} |
||||||
|
if(result != DependResult.SUCCESS){ |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* find the last one process instance that : |
||||||
|
* 1. manual run and finish between the interval |
||||||
|
* 2. schedule run and schedule time between the interval |
||||||
|
* @param definitionId |
||||||
|
* @param dateInterval |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private ProcessInstance findLastProcessInterval(int definitionId, DateInterval dateInterval) { |
||||||
|
|
||||||
|
ProcessInstance runningProcess = processDao.findLastRunningProcess(definitionId, dateInterval); |
||||||
|
if(runningProcess != null){ |
||||||
|
return runningProcess; |
||||||
|
} |
||||||
|
|
||||||
|
ProcessInstance lastSchedulerProcess = processDao.findLastSchedulerProcessInterval( |
||||||
|
definitionId, dateInterval |
||||||
|
); |
||||||
|
|
||||||
|
ProcessInstance lastManualProcess = processDao.findLastManualProcessInterval( |
||||||
|
definitionId, dateInterval |
||||||
|
); |
||||||
|
|
||||||
|
if(lastManualProcess ==null){ |
||||||
|
return lastSchedulerProcess; |
||||||
|
} |
||||||
|
if(lastSchedulerProcess == null){ |
||||||
|
return lastManualProcess; |
||||||
|
} |
||||||
|
|
||||||
|
return (lastManualProcess.getEndTime().after(lastSchedulerProcess.getEndTime()))? |
||||||
|
lastManualProcess : lastSchedulerProcess; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get dependent result by task/process instance state |
||||||
|
* @param state |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private DependResult getDependResultByState(ExecutionStatus state) { |
||||||
|
|
||||||
|
if(state.typeIsRunning() || state == ExecutionStatus.SUBMITTED_SUCCESS || state == ExecutionStatus.WAITTING_THREAD){ |
||||||
|
return DependResult.WAITING; |
||||||
|
}else if(state.typeIsSuccess()){ |
||||||
|
return DependResult.SUCCESS; |
||||||
|
}else{ |
||||||
|
return DependResult.FAILED; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* judge depend item finished |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public boolean finish(Date currentTime){ |
||||||
|
if(modelDependResult == DependResult.WAITING){ |
||||||
|
modelDependResult = getModelDependResult(currentTime); |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get model depend result |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public DependResult getModelDependResult(Date currentTime){ |
||||||
|
|
||||||
|
List<DependResult> dependResultList = new ArrayList<>(); |
||||||
|
|
||||||
|
for(DependentItem dependentItem : dependItemList){ |
||||||
|
DependResult dependResult = getDependResultForItem(dependentItem, currentTime); |
||||||
|
if(dependResult != DependResult.WAITING){ |
||||||
|
dependResultMap.put(dependentItem.getKey(), dependResult); |
||||||
|
} |
||||||
|
dependResultList.add(dependResult); |
||||||
|
} |
||||||
|
modelDependResult = DependentUtils.getDependResultForRelation( |
||||||
|
this.relation, dependResultList |
||||||
|
); |
||||||
|
return modelDependResult; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get dependent item result |
||||||
|
* @param item |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public DependResult getDependResultForItem(DependentItem item, Date currentTime){ |
||||||
|
String key = item.getKey(); |
||||||
|
if(dependResultMap.containsKey(key)){ |
||||||
|
return dependResultMap.get(key); |
||||||
|
} |
||||||
|
return getDependentResultForItem(item, currentTime); |
||||||
|
} |
||||||
|
|
||||||
|
public Map<String, DependResult> getDependResultMap(){ |
||||||
|
return dependResultMap; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,163 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.task.dependent; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.enums.DependResult; |
||||||
|
import cn.escheduler.common.enums.ExecutionStatus; |
||||||
|
import cn.escheduler.common.model.DependentTaskModel; |
||||||
|
import cn.escheduler.common.task.AbstractParameters; |
||||||
|
import cn.escheduler.common.task.dependent.DependentParameters; |
||||||
|
import cn.escheduler.common.utils.DependentUtils; |
||||||
|
import cn.escheduler.common.utils.JSONUtils; |
||||||
|
import cn.escheduler.dao.DaoFactory; |
||||||
|
import cn.escheduler.dao.ProcessDao; |
||||||
|
import cn.escheduler.dao.model.TaskInstance; |
||||||
|
import cn.escheduler.server.worker.task.AbstractTask; |
||||||
|
import cn.escheduler.server.worker.task.TaskProps; |
||||||
|
import org.slf4j.Logger; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import static cn.escheduler.common.Constants.DEPENDENT_SPLIT; |
||||||
|
|
||||||
|
public class DependentTask extends AbstractTask { |
||||||
|
|
||||||
|
private List<DependentExecute> dependentTaskList = new ArrayList<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* depend item result map |
||||||
|
* save the result to log file |
||||||
|
*/ |
||||||
|
private Map<String, DependResult> dependResultMap = new HashMap<>(); |
||||||
|
|
||||||
|
private DependentParameters dependentParameters; |
||||||
|
|
||||||
|
private Date dependentDate; |
||||||
|
|
||||||
|
private ProcessDao processDao; |
||||||
|
|
||||||
|
public DependentTask(TaskProps props, Logger logger) { |
||||||
|
super(props, logger); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void init(){ |
||||||
|
logger.info("dependent task initialize"); |
||||||
|
|
||||||
|
this.dependentParameters = JSONUtils.parseObject(this.taskProps.getDependence(), |
||||||
|
DependentParameters.class); |
||||||
|
|
||||||
|
for(DependentTaskModel taskModel : dependentParameters.getDependTaskList()){ |
||||||
|
this.dependentTaskList.add(new DependentExecute( |
||||||
|
taskModel.getDependItemList(), taskModel.getRelation())); |
||||||
|
} |
||||||
|
|
||||||
|
this.processDao = DaoFactory.getDaoInstance(ProcessDao.class); |
||||||
|
|
||||||
|
if(taskProps.getScheduleTime() != null){ |
||||||
|
this.dependentDate = taskProps.getScheduleTime(); |
||||||
|
}else{ |
||||||
|
this.dependentDate = taskProps.getTaskStartTime(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void handle(){ |
||||||
|
// set the name of the current thread
|
||||||
|
String threadLoggerInfoName = String.format("TaskLogInfo-%s", taskProps.getTaskAppId()); |
||||||
|
Thread.currentThread().setName(threadLoggerInfoName); |
||||||
|
|
||||||
|
try{ |
||||||
|
TaskInstance taskInstance = null; |
||||||
|
while(true){ |
||||||
|
taskInstance = processDao.findTaskInstanceById(this.taskProps.getTaskInstId()); |
||||||
|
if(taskInstance.getState() == ExecutionStatus.KILL){ |
||||||
|
this.cancel = true; |
||||||
|
} |
||||||
|
if(this.cancel || allDependentTaskFinish()){ |
||||||
|
break; |
||||||
|
} |
||||||
|
Thread.sleep(Constants.SLEEP_TIME_MILLIS); |
||||||
|
|
||||||
|
} |
||||||
|
if(cancel){ |
||||||
|
exitStatusCode = Constants.EXIT_CODE_KILL; |
||||||
|
}else{ |
||||||
|
DependResult result = getTaskDependResult(); |
||||||
|
exitStatusCode = (result == DependResult.SUCCESS) ? |
||||||
|
Constants.EXIT_CODE_SUCCESS : Constants.EXIT_CODE_FAILURE; |
||||||
|
} |
||||||
|
}catch (Exception e){ |
||||||
|
logger.error("Exception " + e); |
||||||
|
exitStatusCode = -1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get dependent result |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private DependResult getTaskDependResult(){ |
||||||
|
List<DependResult> dependResultList = new ArrayList<>(); |
||||||
|
for(DependentExecute dependentExecute : dependentTaskList){ |
||||||
|
DependResult dependResult = dependentExecute.getModelDependResult(dependentDate); |
||||||
|
dependResultList.add(dependResult); |
||||||
|
} |
||||||
|
DependResult result = DependentUtils.getDependResultForRelation( |
||||||
|
this.dependentParameters.getRelation(), dependResultList |
||||||
|
); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* judge all dependent tasks finish |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private boolean allDependentTaskFinish(){ |
||||||
|
boolean finish = true; |
||||||
|
for(DependentExecute dependentExecute : dependentTaskList){ |
||||||
|
Map<String, DependResult> resultMap = dependentExecute.getDependResultMap(); |
||||||
|
Set<String> keySet = resultMap.keySet(); |
||||||
|
for(String key : keySet){ |
||||||
|
if(!dependResultMap.containsKey(key)){ |
||||||
|
dependResultMap.put(key, resultMap.get(key)); |
||||||
|
//save depend result to log
|
||||||
|
logger.info("dependent item complete {} {},{}", |
||||||
|
DEPENDENT_SPLIT, key, resultMap.get(key).toString()); |
||||||
|
} |
||||||
|
} |
||||||
|
if(!dependentExecute.finish(dependentDate)){ |
||||||
|
finish = false; |
||||||
|
} |
||||||
|
} |
||||||
|
return finish; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void cancelApplication(boolean cancelApplication) throws Exception { |
||||||
|
// cancel process
|
||||||
|
this.cancel = true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public AbstractParameters getParameters() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,143 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.task.mr; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.enums.ProgramType; |
||||||
|
import cn.escheduler.common.process.Property; |
||||||
|
import cn.escheduler.common.task.AbstractParameters; |
||||||
|
import cn.escheduler.common.task.mr.MapreduceParameters; |
||||||
|
import cn.escheduler.common.utils.JSONUtils; |
||||||
|
import cn.escheduler.common.utils.ParameterUtils; |
||||||
|
import cn.escheduler.server.utils.ParamUtils; |
||||||
|
import cn.escheduler.server.worker.task.AbstractYarnTask; |
||||||
|
import cn.escheduler.server.worker.task.TaskProps; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
import org.slf4j.Logger; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* mapreduce task |
||||||
|
*/ |
||||||
|
public class MapReduceTask extends AbstractYarnTask { |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* mapreduce parameters |
||||||
|
*/ |
||||||
|
private MapreduceParameters mapreduceParameters; |
||||||
|
|
||||||
|
/** |
||||||
|
* @param props |
||||||
|
* @param logger |
||||||
|
*/ |
||||||
|
public MapReduceTask(TaskProps props, Logger logger) { |
||||||
|
super(props, logger); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void init() { |
||||||
|
|
||||||
|
logger.info("mapreduce task params {}", taskProps.getTaskParams()); |
||||||
|
|
||||||
|
this.mapreduceParameters = JSONUtils.parseObject(taskProps.getTaskParams(), MapreduceParameters.class); |
||||||
|
|
||||||
|
// check parameters
|
||||||
|
if (!mapreduceParameters.checkParameters()) { |
||||||
|
throw new RuntimeException("mapreduce task params is not valid"); |
||||||
|
} |
||||||
|
|
||||||
|
mapreduceParameters.setQueue(taskProps.getQueue()); |
||||||
|
|
||||||
|
// replace placeholder
|
||||||
|
Map<String, Property> paramsMap = ParamUtils.convert(taskProps.getUserDefParamsMap(), |
||||||
|
taskProps.getDefinedParams(), |
||||||
|
mapreduceParameters.getLocalParametersMap(), |
||||||
|
processInstance.getCmdTypeIfComplement(), |
||||||
|
processInstance.getScheduleTime()); |
||||||
|
if (paramsMap != null){ |
||||||
|
String args = ParameterUtils.convertParameterPlaceholders(mapreduceParameters.getMainArgs(), ParamUtils.convert(paramsMap)); |
||||||
|
mapreduceParameters.setMainArgs(args); |
||||||
|
if(mapreduceParameters.getProgramType() != null && mapreduceParameters.getProgramType() == ProgramType.PYTHON){ |
||||||
|
String others = ParameterUtils.convertParameterPlaceholders(mapreduceParameters.getOthers(), ParamUtils.convert(paramsMap)); |
||||||
|
mapreduceParameters.setOthers(others); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected String buildCommand() throws Exception { |
||||||
|
List<String> parameterList = buildParameters(mapreduceParameters); |
||||||
|
|
||||||
|
String command = ParameterUtils.convertParameterPlaceholders(String.join(" ", parameterList), taskProps.getDefinedParams()); |
||||||
|
logger.info("mapreduce task command: {}", command); |
||||||
|
|
||||||
|
return command; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public AbstractParameters getParameters() { |
||||||
|
return mapreduceParameters; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private List<String> buildParameters(MapreduceParameters mapreduceParameters){ |
||||||
|
|
||||||
|
List<String> result = new ArrayList<>(); |
||||||
|
|
||||||
|
result.add(Constants.HADOOP); |
||||||
|
|
||||||
|
// main jar
|
||||||
|
if(mapreduceParameters.getMainJar()!= null){ |
||||||
|
result.add(Constants.JAR); |
||||||
|
result.add(mapreduceParameters.getMainJar().getRes()); |
||||||
|
} |
||||||
|
|
||||||
|
// main class
|
||||||
|
if(mapreduceParameters.getProgramType() !=null ){ |
||||||
|
if(mapreduceParameters.getProgramType()!= ProgramType.PYTHON){ |
||||||
|
if(StringUtils.isNotEmpty(mapreduceParameters.getMainClass())){ |
||||||
|
result.add(mapreduceParameters.getMainClass()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// others
|
||||||
|
if (StringUtils.isNotEmpty(mapreduceParameters.getOthers())) { |
||||||
|
String others = mapreduceParameters.getOthers(); |
||||||
|
if(!others.contains(Constants.MR_QUEUE)){ |
||||||
|
if (StringUtils.isNotEmpty(mapreduceParameters.getQueue())) { |
||||||
|
result.add(String.format("%s %s=%s", Constants.D, Constants.MR_QUEUE, mapreduceParameters.getQueue())); |
||||||
|
} |
||||||
|
} |
||||||
|
result.add(mapreduceParameters.getOthers()); |
||||||
|
}else if (StringUtils.isNotEmpty(mapreduceParameters.getQueue())) { |
||||||
|
result.add(String.format("%s %s=%s", Constants.D, Constants.MR_QUEUE, mapreduceParameters.getQueue())); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// command args
|
||||||
|
if(StringUtils.isNotEmpty(mapreduceParameters.getMainArgs())){ |
||||||
|
result.add(mapreduceParameters.getMainArgs()); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,328 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.task.processdure; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.enums.DataType; |
||||||
|
import cn.escheduler.common.enums.DbType; |
||||||
|
import cn.escheduler.common.enums.Direct; |
||||||
|
import cn.escheduler.common.enums.TaskTimeoutStrategy; |
||||||
|
import cn.escheduler.common.job.db.BaseDataSource; |
||||||
|
import cn.escheduler.common.job.db.MySQLDataSource; |
||||||
|
import cn.escheduler.common.job.db.PostgreDataSource; |
||||||
|
import cn.escheduler.common.process.Property; |
||||||
|
import cn.escheduler.common.task.AbstractParameters; |
||||||
|
import cn.escheduler.common.task.procedure.ProcedureParameters; |
||||||
|
import cn.escheduler.common.utils.CollectionUtils; |
||||||
|
import cn.escheduler.common.utils.ParameterUtils; |
||||||
|
import cn.escheduler.dao.DaoFactory; |
||||||
|
import cn.escheduler.dao.ProcessDao; |
||||||
|
import cn.escheduler.dao.model.DataSource; |
||||||
|
import cn.escheduler.dao.model.ProcessInstance; |
||||||
|
import cn.escheduler.server.utils.ParamUtils; |
||||||
|
import cn.escheduler.server.worker.task.AbstractTask; |
||||||
|
import cn.escheduler.server.worker.task.TaskProps; |
||||||
|
import com.alibaba.fastjson.JSONObject; |
||||||
|
import com.cronutils.utils.StringUtils; |
||||||
|
import org.slf4j.Logger; |
||||||
|
|
||||||
|
import java.sql.*; |
||||||
|
import java.util.Collection; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* procedure task |
||||||
|
*/ |
||||||
|
public class ProcedureTask extends AbstractTask { |
||||||
|
|
||||||
|
/** |
||||||
|
* procedure parameters |
||||||
|
*/ |
||||||
|
private ProcedureParameters procedureParameters; |
||||||
|
|
||||||
|
/** |
||||||
|
* process database access |
||||||
|
*/ |
||||||
|
private ProcessDao processDao; |
||||||
|
|
||||||
|
public ProcedureTask(TaskProps taskProps, Logger logger) { |
||||||
|
super(taskProps, logger); |
||||||
|
|
||||||
|
logger.info("procedure task params {}", taskProps.getTaskParams()); |
||||||
|
|
||||||
|
this.procedureParameters = JSONObject.parseObject(taskProps.getTaskParams(), ProcedureParameters.class); |
||||||
|
|
||||||
|
// check parameters
|
||||||
|
if (!procedureParameters.checkParameters()) { |
||||||
|
throw new RuntimeException("procedure task params is not valid"); |
||||||
|
} |
||||||
|
|
||||||
|
this.processDao = DaoFactory.getDaoInstance(ProcessDao.class); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void handle() throws Exception { |
||||||
|
// set the name of the current thread
|
||||||
|
String threadLoggerInfoName = String.format("TaskLogInfo-%s", taskProps.getTaskAppId()); |
||||||
|
Thread.currentThread().setName(threadLoggerInfoName); |
||||||
|
|
||||||
|
logger.info("processdure type : {}, datasource : {}, method : {} , localParams : {}", |
||||||
|
procedureParameters.getType(), |
||||||
|
procedureParameters.getDatasource(), |
||||||
|
procedureParameters.getMethod(), |
||||||
|
procedureParameters.getLocalParams()); |
||||||
|
|
||||||
|
// determine whether there is a data source
|
||||||
|
if (procedureParameters.getDatasource() == 0){ |
||||||
|
logger.error("datasource is null"); |
||||||
|
exitStatusCode = 0; |
||||||
|
}else { |
||||||
|
|
||||||
|
DataSource dataSource = processDao.findDataSourceById(procedureParameters.getDatasource()); |
||||||
|
logger.info("datasource name : {} , type : {} , desc : {} , user_id : {} , parameter : {}", |
||||||
|
dataSource.getName(),dataSource.getType(),dataSource.getNote(), |
||||||
|
dataSource.getUserId(),dataSource.getConnectionParams()); |
||||||
|
|
||||||
|
if (dataSource != null){ |
||||||
|
Connection connection = null; |
||||||
|
CallableStatement stmt = null; |
||||||
|
try { |
||||||
|
BaseDataSource baseDataSource = null; |
||||||
|
|
||||||
|
if (DbType.MYSQL.name().equals(dataSource.getType().name())){ |
||||||
|
baseDataSource = JSONObject.parseObject(dataSource.getConnectionParams(),MySQLDataSource.class); |
||||||
|
Class.forName(Constants.JDBC_MYSQL_CLASS_NAME); |
||||||
|
}else if (DbType.POSTGRESQL.name().equals(dataSource.getType().name())){ |
||||||
|
baseDataSource = JSONObject.parseObject(dataSource.getConnectionParams(),PostgreDataSource.class); |
||||||
|
Class.forName(Constants.JDBC_POSTGRESQL_CLASS_NAME); |
||||||
|
} |
||||||
|
|
||||||
|
// get jdbc connection
|
||||||
|
connection = DriverManager.getConnection(baseDataSource.getJdbcUrl(), |
||||||
|
baseDataSource.getUser(), |
||||||
|
baseDataSource.getPassword()); |
||||||
|
|
||||||
|
// get process instance by task instance id
|
||||||
|
ProcessInstance processInstance = processDao.findProcessInstanceByTaskId(taskProps.getTaskInstId()); |
||||||
|
|
||||||
|
// combining local and global parameters
|
||||||
|
Map<String, Property> paramsMap = ParamUtils.convert(taskProps.getUserDefParamsMap(), |
||||||
|
taskProps.getDefinedParams(), |
||||||
|
procedureParameters.getLocalParametersMap(), |
||||||
|
processInstance.getCmdTypeIfComplement(), |
||||||
|
processInstance.getScheduleTime()); |
||||||
|
|
||||||
|
|
||||||
|
Collection<Property> userDefParamsList = null; |
||||||
|
|
||||||
|
if (procedureParameters.getLocalParametersMap() != null){ |
||||||
|
userDefParamsList = procedureParameters.getLocalParametersMap().values(); |
||||||
|
} |
||||||
|
|
||||||
|
String method = ""; |
||||||
|
// no parameters
|
||||||
|
if (CollectionUtils.isEmpty(userDefParamsList)){ |
||||||
|
method = "{call " + procedureParameters.getMethod() + "}"; |
||||||
|
}else { // exists parameters
|
||||||
|
int size = userDefParamsList.size(); |
||||||
|
StringBuilder parameter = new StringBuilder(); |
||||||
|
parameter.append("("); |
||||||
|
for (int i = 0 ;i < size - 1; i++){ |
||||||
|
parameter.append("?,"); |
||||||
|
} |
||||||
|
parameter.append("?)"); |
||||||
|
method = "{call " + procedureParameters.getMethod() + parameter.toString()+ "}"; |
||||||
|
} |
||||||
|
|
||||||
|
logger.info("call method : {}",method); |
||||||
|
// call method
|
||||||
|
stmt = connection.prepareCall(method); |
||||||
|
if(taskProps.getTaskTimeoutStrategy() == TaskTimeoutStrategy.FAILED || taskProps.getTaskTimeoutStrategy() == TaskTimeoutStrategy.WARNFAILED){ |
||||||
|
stmt.setQueryTimeout(taskProps.getTaskTimeout()); |
||||||
|
} |
||||||
|
Map<Integer,Property> outParameterMap = new HashMap<>(); |
||||||
|
if (userDefParamsList != null && userDefParamsList.size() > 0){ |
||||||
|
int index = 1; |
||||||
|
for (Property property : userDefParamsList){ |
||||||
|
logger.info("localParams : prop : {} , dirct : {} , type : {} , value : {}" |
||||||
|
,property.getProp(), |
||||||
|
property.getDirect(), |
||||||
|
property.getType(), |
||||||
|
property.getValue()); |
||||||
|
// set parameters
|
||||||
|
if (property.getDirect().equals(Direct.IN)){ |
||||||
|
ParameterUtils.setInParameter(index,stmt,property.getType(),paramsMap.get(property.getProp()).getValue()); |
||||||
|
}else if (property.getDirect().equals(Direct.OUT)){ |
||||||
|
setOutParameter(index,stmt,property.getType(),paramsMap.get(property.getProp()).getValue()); |
||||||
|
property.setValue(paramsMap.get(property.getProp()).getValue()); |
||||||
|
outParameterMap.put(index,property); |
||||||
|
} |
||||||
|
index++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
stmt.executeUpdate(); |
||||||
|
|
||||||
|
/** |
||||||
|
* print the output parameters to the log |
||||||
|
*/ |
||||||
|
Iterator<Map.Entry<Integer, Property>> iter = outParameterMap.entrySet().iterator(); |
||||||
|
while (iter.hasNext()){ |
||||||
|
Map.Entry<Integer, Property> en = iter.next(); |
||||||
|
|
||||||
|
int index = en.getKey(); |
||||||
|
Property property = en.getValue(); |
||||||
|
String prop = property.getProp(); |
||||||
|
DataType dataType = property.getType(); |
||||||
|
|
||||||
|
if (dataType.equals(DataType.VARCHAR)){ |
||||||
|
String value = stmt.getString(index); |
||||||
|
logger.info("out prameter key : {} , value : {}",prop,value); |
||||||
|
}else if (dataType.equals(DataType.INTEGER)){ |
||||||
|
int value = stmt.getInt(index); |
||||||
|
logger.info("out prameter key : {} , value : {}",prop,value); |
||||||
|
}else if (dataType.equals(DataType.LONG)){ |
||||||
|
long value = stmt.getLong(index); |
||||||
|
logger.info("out prameter key : {} , value : {}",prop,value); |
||||||
|
}else if (dataType.equals(DataType.FLOAT)){ |
||||||
|
float value = stmt.getFloat(index); |
||||||
|
logger.info("out prameter key : {} , value : {}",prop,value); |
||||||
|
}else if (dataType.equals(DataType.DOUBLE)){ |
||||||
|
double value = stmt.getDouble(index); |
||||||
|
logger.info("out prameter key : {} , value : {}",prop,value); |
||||||
|
}else if (dataType.equals(DataType.DATE)){ |
||||||
|
Date value = stmt.getDate(index); |
||||||
|
logger.info("out prameter key : {} , value : {}",prop,value); |
||||||
|
}else if (dataType.equals(DataType.TIME)){ |
||||||
|
Time value = stmt.getTime(index); |
||||||
|
logger.info("out prameter key : {} , value : {}",prop,value); |
||||||
|
}else if (dataType.equals(DataType.TIMESTAMP)){ |
||||||
|
Timestamp value = stmt.getTimestamp(index); |
||||||
|
logger.info("out prameter key : {} , value : {}",prop,value); |
||||||
|
}else if (dataType.equals(DataType.BOOLEAN)){ |
||||||
|
boolean value = stmt.getBoolean(index); |
||||||
|
logger.info("out prameter key : {} , value : {}",prop,value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
exitStatusCode = 0; |
||||||
|
}catch (Exception e){ |
||||||
|
logger.error(e.getMessage(),e); |
||||||
|
exitStatusCode = -1; |
||||||
|
throw new RuntimeException("process interrupted. exit status code is : " + exitStatusCode); |
||||||
|
} |
||||||
|
finally { |
||||||
|
if (stmt != null) { |
||||||
|
try { |
||||||
|
stmt.close(); |
||||||
|
} catch (SQLException e) { |
||||||
|
exitStatusCode = -1; |
||||||
|
logger.error(e.getMessage(),e); |
||||||
|
} |
||||||
|
} |
||||||
|
if (connection != null) { |
||||||
|
try { |
||||||
|
connection.close(); |
||||||
|
} catch (SQLException e) { |
||||||
|
exitStatusCode = -1; |
||||||
|
logger.error(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public AbstractParameters getParameters() { |
||||||
|
return procedureParameters; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* set out parameter |
||||||
|
* @param index |
||||||
|
* @param stmt |
||||||
|
* @param dataType |
||||||
|
* @param value |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
private void setOutParameter(int index,CallableStatement stmt,DataType dataType,String value)throws Exception{ |
||||||
|
if (dataType.equals(DataType.VARCHAR)){ |
||||||
|
if (StringUtils.isEmpty(value)){ |
||||||
|
stmt.registerOutParameter(index, Types.VARCHAR); |
||||||
|
}else { |
||||||
|
stmt.registerOutParameter(index, Types.VARCHAR, value); |
||||||
|
} |
||||||
|
|
||||||
|
}else if (dataType.equals(DataType.INTEGER)){ |
||||||
|
if (StringUtils.isEmpty(value)){ |
||||||
|
stmt.registerOutParameter(index, Types.INTEGER); |
||||||
|
}else { |
||||||
|
stmt.registerOutParameter(index, Types.INTEGER, value); |
||||||
|
} |
||||||
|
|
||||||
|
}else if (dataType.equals(DataType.LONG)){ |
||||||
|
if (StringUtils.isEmpty(value)){ |
||||||
|
stmt.registerOutParameter(index,Types.INTEGER); |
||||||
|
}else { |
||||||
|
stmt.registerOutParameter(index,Types.INTEGER ,value); |
||||||
|
} |
||||||
|
}else if (dataType.equals(DataType.FLOAT)){ |
||||||
|
if (StringUtils.isEmpty(value)){ |
||||||
|
stmt.registerOutParameter(index, Types.FLOAT); |
||||||
|
}else { |
||||||
|
stmt.registerOutParameter(index, Types.FLOAT,value); |
||||||
|
} |
||||||
|
}else if (dataType.equals(DataType.DOUBLE)){ |
||||||
|
if (StringUtils.isEmpty(value)){ |
||||||
|
stmt.registerOutParameter(index, Types.DOUBLE); |
||||||
|
}else { |
||||||
|
stmt.registerOutParameter(index, Types.DOUBLE , value); |
||||||
|
} |
||||||
|
|
||||||
|
}else if (dataType.equals(DataType.DATE)){ |
||||||
|
if (StringUtils.isEmpty(value)){ |
||||||
|
stmt.registerOutParameter(index, Types.DATE); |
||||||
|
}else { |
||||||
|
stmt.registerOutParameter(index, Types.DATE , value); |
||||||
|
} |
||||||
|
|
||||||
|
}else if (dataType.equals(DataType.TIME)){ |
||||||
|
if (StringUtils.isEmpty(value)){ |
||||||
|
stmt.registerOutParameter(index, Types.TIME); |
||||||
|
}else { |
||||||
|
stmt.registerOutParameter(index, Types.TIME , value); |
||||||
|
} |
||||||
|
|
||||||
|
}else if (dataType.equals(DataType.TIMESTAMP)){ |
||||||
|
if (StringUtils.isEmpty(value)){ |
||||||
|
stmt.registerOutParameter(index, Types.TIMESTAMP); |
||||||
|
}else { |
||||||
|
stmt.registerOutParameter(index, Types.TIMESTAMP , value); |
||||||
|
} |
||||||
|
|
||||||
|
}else if (dataType.equals(DataType.BOOLEAN)){ |
||||||
|
if (StringUtils.isEmpty(value)){ |
||||||
|
stmt.registerOutParameter(index, Types.BOOLEAN); |
||||||
|
}else { |
||||||
|
stmt.registerOutParameter(index, Types.BOOLEAN , value); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,165 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.task.python; |
||||||
|
|
||||||
|
|
||||||
|
import cn.escheduler.common.process.Property; |
||||||
|
import cn.escheduler.common.task.AbstractParameters; |
||||||
|
import cn.escheduler.common.task.python.PythonParameters; |
||||||
|
import cn.escheduler.common.utils.CommonUtils; |
||||||
|
import cn.escheduler.common.utils.JSONUtils; |
||||||
|
import cn.escheduler.common.utils.ParameterUtils; |
||||||
|
import cn.escheduler.dao.DaoFactory; |
||||||
|
import cn.escheduler.dao.ProcessDao; |
||||||
|
import cn.escheduler.dao.model.ProcessInstance; |
||||||
|
import cn.escheduler.server.utils.ParamUtils; |
||||||
|
import cn.escheduler.server.worker.task.AbstractTask; |
||||||
|
import cn.escheduler.server.worker.task.PythonCommandExecutor; |
||||||
|
import cn.escheduler.server.worker.task.TaskProps; |
||||||
|
import org.slf4j.Logger; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.nio.file.Files; |
||||||
|
import java.nio.file.Path; |
||||||
|
import java.nio.file.StandardOpenOption; |
||||||
|
import java.nio.file.attribute.FileAttribute; |
||||||
|
import java.nio.file.attribute.PosixFilePermission; |
||||||
|
import java.nio.file.attribute.PosixFilePermissions; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
/** |
||||||
|
* python task |
||||||
|
*/ |
||||||
|
public class PythonTask extends AbstractTask { |
||||||
|
|
||||||
|
/** |
||||||
|
* python parameters |
||||||
|
*/ |
||||||
|
private PythonParameters pythonParameters; |
||||||
|
|
||||||
|
/** |
||||||
|
* task dir |
||||||
|
*/ |
||||||
|
private String taskDir; |
||||||
|
|
||||||
|
private PythonCommandExecutor pythonProcessTask; |
||||||
|
|
||||||
|
/** |
||||||
|
* process database access |
||||||
|
*/ |
||||||
|
private ProcessDao processDao; |
||||||
|
|
||||||
|
|
||||||
|
public PythonTask(TaskProps taskProps, Logger logger) { |
||||||
|
super(taskProps, logger); |
||||||
|
|
||||||
|
this.taskDir = taskProps.getTaskDir(); |
||||||
|
|
||||||
|
this.pythonProcessTask = new PythonCommandExecutor(this::logHandle, |
||||||
|
taskProps.getTaskDir(), taskProps.getTaskAppId(), |
||||||
|
taskProps.getTenantCode(), CommonUtils.getPythonSystemEnvPath(), taskProps.getTaskStartTime(), |
||||||
|
taskProps.getTaskTimeout(), logger); |
||||||
|
this.processDao = DaoFactory.getDaoInstance(ProcessDao.class); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void init() { |
||||||
|
logger.info("python task params {}", taskProps.getTaskParams()); |
||||||
|
|
||||||
|
pythonParameters = JSONUtils.parseObject(taskProps.getTaskParams(), PythonParameters.class); |
||||||
|
|
||||||
|
if (!pythonParameters.checkParameters()) { |
||||||
|
throw new RuntimeException("python task params is not valid"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void handle() throws Exception { |
||||||
|
try { |
||||||
|
// construct process
|
||||||
|
exitStatusCode = pythonProcessTask.run(buildCommand(), processDao); |
||||||
|
} catch (Exception e) { |
||||||
|
logger.error("python process exception", e); |
||||||
|
exitStatusCode = -1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void cancelApplication(boolean cancelApplication) throws Exception { |
||||||
|
// cancel process
|
||||||
|
pythonProcessTask.cancelApplication(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* build command |
||||||
|
* @return |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
private String buildCommand() throws Exception { |
||||||
|
// generate scripts
|
||||||
|
String fileName = String.format("%s/py_%s_node.py", taskDir, taskProps.getTaskAppId()); |
||||||
|
Path path = new File(fileName).toPath(); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (Files.exists(path)) { |
||||||
|
return fileName; |
||||||
|
} |
||||||
|
|
||||||
|
String rawScript = pythonParameters.getRawScript().replaceAll("\\r\\n", "\n"); |
||||||
|
|
||||||
|
|
||||||
|
// find process instance by task id
|
||||||
|
ProcessInstance processInstance = processDao.findProcessInstanceByTaskId(taskProps.getTaskInstId()); |
||||||
|
|
||||||
|
/** |
||||||
|
* combining local and global parameters |
||||||
|
*/ |
||||||
|
Map<String, Property> paramsMap = ParamUtils.convert(taskProps.getUserDefParamsMap(), |
||||||
|
taskProps.getDefinedParams(), |
||||||
|
pythonParameters.getLocalParametersMap(), |
||||||
|
processInstance.getCmdTypeIfComplement(), |
||||||
|
processInstance.getScheduleTime()); |
||||||
|
if (paramsMap != null){ |
||||||
|
rawScript = ParameterUtils.convertParameterPlaceholders(rawScript, ParamUtils.convert(paramsMap)); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
pythonParameters.setRawScript(rawScript); |
||||||
|
|
||||||
|
logger.info("raw script : {}", pythonParameters.getRawScript()); |
||||||
|
logger.info("task dir : {}", taskDir); |
||||||
|
|
||||||
|
Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxr-xr-x"); |
||||||
|
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms); |
||||||
|
|
||||||
|
Files.createFile(path, attr); |
||||||
|
|
||||||
|
Files.write(path, pythonParameters.getRawScript().getBytes(), StandardOpenOption.APPEND); |
||||||
|
|
||||||
|
return fileName; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public AbstractParameters getParameters() { |
||||||
|
return pythonParameters; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,159 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.task.shell; |
||||||
|
|
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.process.Property; |
||||||
|
import cn.escheduler.common.task.AbstractParameters; |
||||||
|
import cn.escheduler.common.task.shell.ShellParameters; |
||||||
|
import cn.escheduler.common.utils.JSONUtils; |
||||||
|
import cn.escheduler.common.utils.ParameterUtils; |
||||||
|
import cn.escheduler.dao.DaoFactory; |
||||||
|
import cn.escheduler.dao.ProcessDao; |
||||||
|
import cn.escheduler.dao.model.ProcessInstance; |
||||||
|
import cn.escheduler.server.utils.ParamUtils; |
||||||
|
import cn.escheduler.server.worker.task.AbstractTask; |
||||||
|
import cn.escheduler.server.worker.task.ShellCommandExecutor; |
||||||
|
import cn.escheduler.server.worker.task.TaskProps; |
||||||
|
import org.slf4j.Logger; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.nio.file.Files; |
||||||
|
import java.nio.file.Path; |
||||||
|
import java.nio.file.StandardOpenOption; |
||||||
|
import java.nio.file.attribute.FileAttribute; |
||||||
|
import java.nio.file.attribute.PosixFilePermission; |
||||||
|
import java.nio.file.attribute.PosixFilePermissions; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
/** |
||||||
|
* shell task |
||||||
|
*/ |
||||||
|
public class ShellTask extends AbstractTask { |
||||||
|
|
||||||
|
private ShellParameters shellParameters; |
||||||
|
|
||||||
|
/** |
||||||
|
* task dir |
||||||
|
*/ |
||||||
|
private String taskDir; |
||||||
|
|
||||||
|
private ShellCommandExecutor processTask; |
||||||
|
|
||||||
|
/** |
||||||
|
* process database access |
||||||
|
*/ |
||||||
|
private ProcessDao processDao; |
||||||
|
|
||||||
|
|
||||||
|
public ShellTask(TaskProps props, Logger logger) { |
||||||
|
super(props, logger); |
||||||
|
|
||||||
|
this.taskDir = props.getTaskDir(); |
||||||
|
|
||||||
|
this.processTask = new ShellCommandExecutor(this::logHandle, |
||||||
|
props.getTaskDir(), props.getTaskAppId(), |
||||||
|
props.getTenantCode(), props.getEnvFile(), props.getTaskStartTime(), |
||||||
|
props.getTaskTimeout(), logger); |
||||||
|
this.processDao = DaoFactory.getDaoInstance(ProcessDao.class); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void init() { |
||||||
|
logger.info("shell task params {}", taskProps.getTaskParams()); |
||||||
|
|
||||||
|
shellParameters = JSONUtils.parseObject(taskProps.getTaskParams(), ShellParameters.class); |
||||||
|
|
||||||
|
if (!shellParameters.checkParameters()) { |
||||||
|
throw new RuntimeException("shell task params is not valid"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void handle() throws Exception { |
||||||
|
try { |
||||||
|
// construct process
|
||||||
|
exitStatusCode = processTask.run(buildCommand(), processDao); |
||||||
|
} catch (Exception e) { |
||||||
|
logger.error(e.getMessage(), e); |
||||||
|
exitStatusCode = -1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void cancelApplication(boolean cancelApplication) throws Exception { |
||||||
|
// cancel process
|
||||||
|
processTask.cancelApplication(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* create command |
||||||
|
* @return |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
private String buildCommand() throws Exception { |
||||||
|
// generate scripts
|
||||||
|
String fileName = String.format("%s/%s_node.sh", taskDir, taskProps.getTaskAppId()); |
||||||
|
Path path = new File(fileName).toPath(); |
||||||
|
|
||||||
|
if (Files.exists(path)) { |
||||||
|
return fileName; |
||||||
|
} |
||||||
|
|
||||||
|
String script = shellParameters.getRawScript().replaceAll("\\r\\n", "\n"); |
||||||
|
|
||||||
|
// find process instance by task id
|
||||||
|
ProcessInstance processInstance = processDao.findProcessInstanceByTaskId(taskProps.getTaskInstId()); |
||||||
|
|
||||||
|
/** |
||||||
|
* combining local and global parameters |
||||||
|
*/ |
||||||
|
Map<String, Property> paramsMap = ParamUtils.convert(taskProps.getUserDefParamsMap(), |
||||||
|
taskProps.getDefinedParams(), |
||||||
|
shellParameters.getLocalParametersMap(), |
||||||
|
processInstance.getCmdTypeIfComplement(), |
||||||
|
processInstance.getScheduleTime()); |
||||||
|
if (paramsMap != null){ |
||||||
|
script = ParameterUtils.convertParameterPlaceholders(script, ParamUtils.convert(paramsMap)); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
shellParameters.setRawScript(script); |
||||||
|
|
||||||
|
logger.info("raw script : {}", shellParameters.getRawScript()); |
||||||
|
logger.info("task dir : {}", taskDir); |
||||||
|
|
||||||
|
Set<PosixFilePermission> perms = PosixFilePermissions.fromString(Constants.RWXR_XR_X); |
||||||
|
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms); |
||||||
|
|
||||||
|
Files.createFile(path, attr); |
||||||
|
|
||||||
|
Files.write(path, shellParameters.getRawScript().getBytes(), StandardOpenOption.APPEND); |
||||||
|
|
||||||
|
return fileName; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public AbstractParameters getParameters() { |
||||||
|
return shellParameters; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,112 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.task.spark; |
||||||
|
|
||||||
|
import cn.escheduler.common.process.Property; |
||||||
|
import cn.escheduler.common.task.AbstractParameters; |
||||||
|
import cn.escheduler.common.task.spark.SparkParameters; |
||||||
|
import cn.escheduler.common.utils.JSONUtils; |
||||||
|
import cn.escheduler.common.utils.ParameterUtils; |
||||||
|
import cn.escheduler.dao.model.ProcessInstance; |
||||||
|
import cn.escheduler.server.utils.ParamUtils; |
||||||
|
import cn.escheduler.server.utils.SparkArgsUtils; |
||||||
|
import cn.escheduler.server.worker.task.AbstractYarnTask; |
||||||
|
import cn.escheduler.server.worker.task.TaskProps; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
import org.slf4j.Logger; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* spark task |
||||||
|
*/ |
||||||
|
public class SparkTask extends AbstractYarnTask { |
||||||
|
|
||||||
|
/** |
||||||
|
* spark command |
||||||
|
*/ |
||||||
|
private static final String SPARK_COMMAND = "spark-submit"; |
||||||
|
|
||||||
|
/** |
||||||
|
* spark parameters |
||||||
|
*/ |
||||||
|
private SparkParameters sparkParameters; |
||||||
|
|
||||||
|
public SparkTask(TaskProps props, Logger logger) { |
||||||
|
super(props, logger); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void init() { |
||||||
|
|
||||||
|
logger.info("spark task params {}", taskProps.getTaskParams()); |
||||||
|
|
||||||
|
sparkParameters = JSONUtils.parseObject(taskProps.getTaskParams(), SparkParameters.class); |
||||||
|
|
||||||
|
if (!sparkParameters.checkParameters()) { |
||||||
|
throw new RuntimeException("spark task params is not valid"); |
||||||
|
} |
||||||
|
sparkParameters.setQueue(taskProps.getQueue()); |
||||||
|
|
||||||
|
if (StringUtils.isNotEmpty(sparkParameters.getMainArgs())) { |
||||||
|
String args = sparkParameters.getMainArgs(); |
||||||
|
// get process instance by task instance id
|
||||||
|
ProcessInstance processInstance = processDao.findProcessInstanceByTaskId(taskProps.getTaskInstId()); |
||||||
|
|
||||||
|
/** |
||||||
|
* combining local and global parameters |
||||||
|
*/ |
||||||
|
Map<String, Property> paramsMap = ParamUtils.convert(taskProps.getUserDefParamsMap(), |
||||||
|
taskProps.getDefinedParams(), |
||||||
|
sparkParameters.getLocalParametersMap(), |
||||||
|
processInstance.getCmdTypeIfComplement(), |
||||||
|
processInstance.getScheduleTime()); |
||||||
|
if (paramsMap != null ){ |
||||||
|
args = ParameterUtils.convertParameterPlaceholders(args, ParamUtils.convert(paramsMap)); |
||||||
|
} |
||||||
|
sparkParameters.setMainArgs(args); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* create command |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
protected String buildCommand() { |
||||||
|
List<String> args = new ArrayList<>(); |
||||||
|
|
||||||
|
args.add(SPARK_COMMAND); |
||||||
|
|
||||||
|
// other parameters
|
||||||
|
args.addAll(SparkArgsUtils.buildArgs(sparkParameters)); |
||||||
|
|
||||||
|
String command = ParameterUtils |
||||||
|
.convertParameterPlaceholders(String.join(" ", args), taskProps.getDefinedParams()); |
||||||
|
|
||||||
|
logger.info("spark task command : {}", command); |
||||||
|
|
||||||
|
return command; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public AbstractParameters getParameters() { |
||||||
|
return sparkParameters; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,378 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.task.sql; |
||||||
|
|
||||||
|
import cn.escheduler.alert.utils.MailUtils; |
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.enums.DbType; |
||||||
|
import cn.escheduler.common.enums.ShowType; |
||||||
|
import cn.escheduler.common.enums.TaskTimeoutStrategy; |
||||||
|
import cn.escheduler.common.enums.UdfType; |
||||||
|
import cn.escheduler.common.job.db.*; |
||||||
|
import cn.escheduler.common.process.Property; |
||||||
|
import cn.escheduler.common.task.AbstractParameters; |
||||||
|
import cn.escheduler.common.task.sql.SqlParameters; |
||||||
|
import cn.escheduler.common.task.sql.SqlType; |
||||||
|
import cn.escheduler.common.utils.CollectionUtils; |
||||||
|
import cn.escheduler.common.utils.ParameterUtils; |
||||||
|
import cn.escheduler.dao.AlertDao; |
||||||
|
import cn.escheduler.dao.DaoFactory; |
||||||
|
import cn.escheduler.dao.ProcessDao; |
||||||
|
import cn.escheduler.dao.model.*; |
||||||
|
import cn.escheduler.server.utils.ParamUtils; |
||||||
|
import cn.escheduler.server.utils.UDFUtils; |
||||||
|
import cn.escheduler.server.worker.task.AbstractTask; |
||||||
|
import cn.escheduler.server.worker.task.TaskProps; |
||||||
|
import com.alibaba.fastjson.JSONArray; |
||||||
|
import com.alibaba.fastjson.JSONObject; |
||||||
|
import com.alibaba.fastjson.serializer.SerializerFeature; |
||||||
|
import org.apache.commons.lang.StringUtils; |
||||||
|
import org.apache.commons.lang3.EnumUtils; |
||||||
|
import org.slf4j.Logger; |
||||||
|
|
||||||
|
import java.sql.*; |
||||||
|
import java.util.*; |
||||||
|
import java.util.regex.Matcher; |
||||||
|
import java.util.regex.Pattern; |
||||||
|
|
||||||
|
/** |
||||||
|
* sql task |
||||||
|
*/ |
||||||
|
public class SqlTask extends AbstractTask { |
||||||
|
|
||||||
|
/** |
||||||
|
* sql parameters |
||||||
|
*/ |
||||||
|
private SqlParameters sqlParameters; |
||||||
|
|
||||||
|
/** |
||||||
|
* process database access |
||||||
|
*/ |
||||||
|
private ProcessDao processDao; |
||||||
|
|
||||||
|
/** |
||||||
|
* alert dao |
||||||
|
*/ |
||||||
|
private AlertDao alertDao; |
||||||
|
|
||||||
|
|
||||||
|
public SqlTask(TaskProps props, Logger logger) { |
||||||
|
super(props, logger); |
||||||
|
|
||||||
|
logger.info("sql task params {}", taskProps.getTaskParams()); |
||||||
|
this.sqlParameters = JSONObject.parseObject(props.getTaskParams(), SqlParameters.class); |
||||||
|
|
||||||
|
if (!sqlParameters.checkParameters()) { |
||||||
|
throw new RuntimeException("sql task params is not valid"); |
||||||
|
} |
||||||
|
this.processDao = DaoFactory.getDaoInstance(ProcessDao.class); |
||||||
|
this.alertDao = DaoFactory.getDaoInstance(AlertDao.class); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void handle() throws Exception { |
||||||
|
// set the name of the current thread
|
||||||
|
String threadLoggerInfoName = String.format("TaskLogInfo-%s", taskProps.getTaskAppId()); |
||||||
|
Thread.currentThread().setName(threadLoggerInfoName); |
||||||
|
logger.info(sqlParameters.toString()); |
||||||
|
logger.info("sql type : {}, datasource : {}, sql : {} , localParams : {},udfs : {},showType : {},connParams : {}", |
||||||
|
sqlParameters.getType(), sqlParameters.getDatasource(), sqlParameters.getSql(), |
||||||
|
sqlParameters.getLocalParams(), sqlParameters.getUdfs(), sqlParameters.getShowType(), sqlParameters.getConnParams()); |
||||||
|
|
||||||
|
// determine whether there is a data source
|
||||||
|
if (sqlParameters.getDatasource() == 0){ |
||||||
|
logger.error("datasource is null"); |
||||||
|
exitStatusCode = -1; |
||||||
|
}else { |
||||||
|
List<String> createFuncs = null; |
||||||
|
DataSource dataSource = processDao.findDataSourceById(sqlParameters.getDatasource()); |
||||||
|
logger.info("datasource name : {} , type : {} , desc : {} , user_id : {} , parameter : {}", |
||||||
|
dataSource.getName(),dataSource.getType(),dataSource.getNote(), |
||||||
|
dataSource.getUserId(),dataSource.getConnectionParams()); |
||||||
|
|
||||||
|
if (dataSource != null){ |
||||||
|
Connection con = null; |
||||||
|
try { |
||||||
|
BaseDataSource baseDataSource = null; |
||||||
|
if (DbType.MYSQL.name().equals(dataSource.getType().name())){ |
||||||
|
baseDataSource = JSONObject.parseObject(dataSource.getConnectionParams(),MySQLDataSource.class); |
||||||
|
Class.forName(Constants.JDBC_MYSQL_CLASS_NAME); |
||||||
|
}else if (DbType.POSTGRESQL.name().equals(dataSource.getType().name())){ |
||||||
|
baseDataSource = JSONObject.parseObject(dataSource.getConnectionParams(),PostgreDataSource.class); |
||||||
|
Class.forName(Constants.JDBC_POSTGRESQL_CLASS_NAME); |
||||||
|
}else if (DbType.HIVE.name().equals(dataSource.getType().name())){ |
||||||
|
baseDataSource = JSONObject.parseObject(dataSource.getConnectionParams(),HiveDataSource.class); |
||||||
|
Class.forName(Constants.JDBC_HIVE_CLASS_NAME); |
||||||
|
}else if (DbType.SPARK.name().equals(dataSource.getType().name())){ |
||||||
|
baseDataSource = JSONObject.parseObject(dataSource.getConnectionParams(),SparkDataSource.class); |
||||||
|
Class.forName(Constants.JDBC_SPARK_CLASS_NAME); |
||||||
|
} |
||||||
|
|
||||||
|
Map<Integer,Property> sqlParamMap = new HashMap<Integer,Property>(); |
||||||
|
StringBuilder sqlBuilder = new StringBuilder(); |
||||||
|
|
||||||
|
// ready to execute SQL and parameter entity Map
|
||||||
|
setSqlAndSqlParamsMap(sqlBuilder,sqlParamMap); |
||||||
|
|
||||||
|
if(EnumUtils.isValidEnum(UdfType.class, sqlParameters.getType()) && StringUtils.isNotEmpty(sqlParameters.getUdfs())){ |
||||||
|
List<UdfFunc> udfFuncList = processDao.queryUdfFunListByids(sqlParameters.getUdfs()); |
||||||
|
createFuncs = UDFUtils.createFuncs(udfFuncList, taskProps.getTenantCode(), logger); |
||||||
|
} |
||||||
|
|
||||||
|
// execute sql task
|
||||||
|
con = executeFuncAndSql(baseDataSource,sqlBuilder.toString(),sqlParamMap,createFuncs); |
||||||
|
|
||||||
|
} finally { |
||||||
|
if (con != null) { |
||||||
|
try { |
||||||
|
con.close(); |
||||||
|
} catch (SQLException e) { |
||||||
|
throw e; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* ready to execute SQL and parameter entity Map |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private void setSqlAndSqlParamsMap(StringBuilder sqlBuilder,Map<Integer,Property> sqlParamsMap) { |
||||||
|
|
||||||
|
String sql = sqlParameters.getSql(); |
||||||
|
|
||||||
|
// find process instance by task id
|
||||||
|
ProcessInstance processInstance = processDao.findProcessInstanceByTaskId(taskProps.getTaskInstId()); |
||||||
|
|
||||||
|
Map<String, Property> paramsMap = ParamUtils.convert(taskProps.getUserDefParamsMap(), |
||||||
|
taskProps.getDefinedParams(), |
||||||
|
sqlParameters.getLocalParametersMap(), |
||||||
|
processInstance.getCmdTypeIfComplement(), |
||||||
|
processInstance.getScheduleTime()); |
||||||
|
|
||||||
|
// spell SQL according to the final user-defined variable
|
||||||
|
if(paramsMap == null){ |
||||||
|
sqlBuilder.append(sql); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// special characters need to be escaped, ${} needs to be escaped
|
||||||
|
String rgex = "'?\\$\\{(.*?)\\}'?"; |
||||||
|
setSqlParamsMap(sql,rgex,sqlParamsMap,paramsMap); |
||||||
|
|
||||||
|
// replace the ${} of the SQL statement with the Placeholder
|
||||||
|
String formatSql = sql.replaceAll(rgex,"?"); |
||||||
|
sqlBuilder.append(formatSql); |
||||||
|
|
||||||
|
// print repalce sql
|
||||||
|
printReplacedSql(sql,formatSql,rgex,sqlParamsMap); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public AbstractParameters getParameters() { |
||||||
|
return this.sqlParameters; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* execute sql |
||||||
|
* @param baseDataSource |
||||||
|
* @param sql |
||||||
|
* @param params |
||||||
|
*/ |
||||||
|
public Connection executeFuncAndSql(BaseDataSource baseDataSource, String sql, Map<Integer,Property> params, List<String> createFuncs){ |
||||||
|
Connection connection = null; |
||||||
|
try { |
||||||
|
|
||||||
|
if (DbType.HIVE.name().equals(sqlParameters.getType())) { |
||||||
|
Properties paramProp = new Properties(); |
||||||
|
paramProp.setProperty("user", baseDataSource.getUser()); |
||||||
|
paramProp.setProperty("password", baseDataSource.getPassword()); |
||||||
|
Map<String, String> connParamMap = CollectionUtils.stringToMap(sqlParameters.getConnParams(), Constants.SEMICOLON,"hiveconf:"); |
||||||
|
if(connParamMap != null){ |
||||||
|
paramProp.putAll(connParamMap); |
||||||
|
} |
||||||
|
|
||||||
|
connection = DriverManager.getConnection(baseDataSource.getJdbcUrl(),paramProp); |
||||||
|
}else{ |
||||||
|
connection = DriverManager.getConnection(baseDataSource.getJdbcUrl(), |
||||||
|
baseDataSource.getUser(), baseDataSource.getPassword()); |
||||||
|
} |
||||||
|
|
||||||
|
Statement funcStmt = connection.createStatement(); |
||||||
|
// create temp function
|
||||||
|
if (createFuncs != null) { |
||||||
|
for (String createFunc : createFuncs) { |
||||||
|
logger.info("hive create function sql: {}", createFunc); |
||||||
|
funcStmt.execute(createFunc); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
PreparedStatement stmt = connection.prepareStatement(sql); |
||||||
|
if(taskProps.getTaskTimeoutStrategy() == TaskTimeoutStrategy.FAILED || taskProps.getTaskTimeoutStrategy() == TaskTimeoutStrategy.WARNFAILED){ |
||||||
|
stmt.setQueryTimeout(taskProps.getTaskTimeout()); |
||||||
|
} |
||||||
|
if(params != null){ |
||||||
|
for(Integer key : params.keySet()){ |
||||||
|
Property prop = params.get(key); |
||||||
|
ParameterUtils.setInParameter(key,stmt,prop.getType(),prop.getValue()); |
||||||
|
} |
||||||
|
} |
||||||
|
// decide whether to executeQuery or executeUpdate based on sqlType
|
||||||
|
if(sqlParameters.getSqlType() == SqlType.QUERY.ordinal()){ |
||||||
|
// query statements need to be convert to JsonArray and inserted into Alert to send
|
||||||
|
JSONArray array=new JSONArray(); |
||||||
|
ResultSet resultSet = stmt.executeQuery(); |
||||||
|
ResultSetMetaData md=resultSet.getMetaData(); |
||||||
|
int num=md.getColumnCount(); |
||||||
|
|
||||||
|
while(resultSet.next()){ |
||||||
|
JSONObject mapOfColValues=new JSONObject(true); |
||||||
|
for(int i=1;i<=num;i++){ |
||||||
|
mapOfColValues.put(md.getColumnName(i), resultSet.getObject(i)); |
||||||
|
} |
||||||
|
array.add(mapOfColValues); |
||||||
|
} |
||||||
|
|
||||||
|
logger.info("execute sql : {}",JSONObject.toJSONString(array, SerializerFeature.WriteMapNullValue)); |
||||||
|
|
||||||
|
// send as an attachment
|
||||||
|
if(StringUtils.isEmpty(sqlParameters.getShowType())){ |
||||||
|
logger.info("showType is empty,don't need send email"); |
||||||
|
}else{ |
||||||
|
if(array.size() > 0 ){ |
||||||
|
sendAttachment(taskProps.getNodeName() + " query resultsets ",JSONObject.toJSONString(array, SerializerFeature.WriteMapNullValue)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
exitStatusCode = 0; |
||||||
|
|
||||||
|
}else if(sqlParameters.getSqlType() == SqlType.NON_QUERY.ordinal()){ |
||||||
|
// non query statement
|
||||||
|
int result = stmt.executeUpdate(); |
||||||
|
exitStatusCode = 0; |
||||||
|
} |
||||||
|
|
||||||
|
} catch (Exception e) { |
||||||
|
logger.error(e.getMessage(),e); |
||||||
|
} |
||||||
|
return connection; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* send mail as an attachment |
||||||
|
* @param title |
||||||
|
* @param content |
||||||
|
*/ |
||||||
|
public void sendAttachment(String title,String content){ |
||||||
|
|
||||||
|
// process instance
|
||||||
|
ProcessInstance instance = processDao.findProcessInstanceByTaskId(taskProps.getTaskInstId()); |
||||||
|
|
||||||
|
// process define
|
||||||
|
ProcessDefinition processDefine = processDao.findProcessDefineById(instance.getProcessDefinitionId()); |
||||||
|
|
||||||
|
List<User> users = alertDao.queryUserByAlertGroupId(instance.getWarningGroupId()); |
||||||
|
|
||||||
|
// receiving group list
|
||||||
|
List<String> receviersList = new ArrayList<String>(); |
||||||
|
for(User user:users){ |
||||||
|
receviersList.add(user.getEmail()); |
||||||
|
} |
||||||
|
// custom receiver
|
||||||
|
String receivers = processDefine.getReceivers(); |
||||||
|
if (StringUtils.isNotEmpty(receivers)){ |
||||||
|
String[] splits = receivers.split(Constants.COMMA); |
||||||
|
for (String receiver : splits){ |
||||||
|
receviersList.add(receiver); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// copy list
|
||||||
|
List<String> receviersCcList = new ArrayList<String>(); |
||||||
|
|
||||||
|
|
||||||
|
// Custom Copier
|
||||||
|
String receiversCc = processDefine.getReceiversCc(); |
||||||
|
|
||||||
|
if (StringUtils.isNotEmpty(receiversCc)){ |
||||||
|
String[] splits = receiversCc.split(Constants.COMMA); |
||||||
|
for (String receiverCc : splits){ |
||||||
|
receviersCcList.add(receiverCc); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
String showTypeName = sqlParameters.getShowType().replace(Constants.COMMA,"").trim(); |
||||||
|
if(EnumUtils.isValidEnum(ShowType.class,showTypeName)){ |
||||||
|
MailUtils.sendMails(receviersList,receviersCcList,title, content, ShowType.valueOf(showTypeName)); |
||||||
|
}else{ |
||||||
|
logger.error("showType: {} is not valid " ,showTypeName); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* regular expressions match the contents between two specified strings |
||||||
|
* @param content |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public void setSqlParamsMap(String content, String rgex, Map<Integer,Property> sqlParamsMap, Map<String,Property> paramsPropsMap){ |
||||||
|
Pattern pattern = Pattern.compile(rgex); |
||||||
|
Matcher m = pattern.matcher(content); |
||||||
|
int index = 1; |
||||||
|
while (m.find()) { |
||||||
|
|
||||||
|
String paramName = m.group(1); |
||||||
|
Property prop = paramsPropsMap.get(paramName); |
||||||
|
|
||||||
|
sqlParamsMap.put(index,prop); |
||||||
|
index ++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* print replace sql |
||||||
|
* @param content |
||||||
|
* @param formatSql |
||||||
|
* @param rgex |
||||||
|
* @param sqlParamsMap |
||||||
|
*/ |
||||||
|
public void printReplacedSql(String content, String formatSql,String rgex, Map<Integer,Property> sqlParamsMap){ |
||||||
|
//parameter print style
|
||||||
|
logger.info("after replace sql , preparing : {}" , formatSql); |
||||||
|
StringBuffer logPrint = new StringBuffer("replaced sql , parameters:"); |
||||||
|
for(int i=1;i<=sqlParamsMap.size();i++){ |
||||||
|
logPrint.append(sqlParamsMap.get(i).getValue()+"("+sqlParamsMap.get(i).getType()+")"); |
||||||
|
} |
||||||
|
logger.info(logPrint.toString()); |
||||||
|
|
||||||
|
//direct print style
|
||||||
|
Pattern pattern = Pattern.compile(rgex); |
||||||
|
Matcher m = pattern.matcher(content); |
||||||
|
int index = 1; |
||||||
|
StringBuffer sb = new StringBuffer("replaced sql , direct:"); |
||||||
|
while (m.find()) { |
||||||
|
|
||||||
|
m.appendReplacement(sb, sqlParamsMap.get(index).getValue()); |
||||||
|
|
||||||
|
index ++; |
||||||
|
} |
||||||
|
m.appendTail(sb); |
||||||
|
logger.info(sb.toString()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,465 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.zk; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.enums.ExecutionStatus; |
||||||
|
import cn.escheduler.common.utils.CollectionUtils; |
||||||
|
import cn.escheduler.common.utils.DateUtils; |
||||||
|
import cn.escheduler.common.utils.OSUtils; |
||||||
|
import cn.escheduler.common.zk.AbstractZKClient; |
||||||
|
import cn.escheduler.dao.AlertDao; |
||||||
|
import cn.escheduler.dao.DaoFactory; |
||||||
|
import cn.escheduler.dao.ProcessDao; |
||||||
|
import cn.escheduler.dao.ServerDao; |
||||||
|
import cn.escheduler.dao.model.ProcessInstance; |
||||||
|
import cn.escheduler.dao.model.TaskInstance; |
||||||
|
import cn.escheduler.server.ResInfo; |
||||||
|
import cn.escheduler.server.utils.ProcessUtils; |
||||||
|
import org.apache.curator.framework.CuratorFramework; |
||||||
|
import org.apache.curator.framework.recipes.cache.PathChildrenCache; |
||||||
|
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; |
||||||
|
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; |
||||||
|
import org.apache.curator.framework.recipes.locks.InterProcessMutex; |
||||||
|
import org.apache.curator.utils.ThreadUtils; |
||||||
|
import org.apache.zookeeper.CreateMode; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
import java.util.List; |
||||||
|
import java.util.concurrent.ThreadFactory; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* zookeeper master client |
||||||
|
* |
||||||
|
* single instance |
||||||
|
*/ |
||||||
|
public class ZKMasterClient extends AbstractZKClient { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ZKMasterClient.class); |
||||||
|
|
||||||
|
private static final ThreadFactory defaultThreadFactory = ThreadUtils.newGenericThreadFactory("Master-Main-Thread"); |
||||||
|
|
||||||
|
/** |
||||||
|
* master znode |
||||||
|
*/ |
||||||
|
private String masterZNode = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* master database access |
||||||
|
*/ |
||||||
|
private ServerDao serverDao = null; |
||||||
|
/** |
||||||
|
* alert database access |
||||||
|
*/ |
||||||
|
private AlertDao alertDao = null; |
||||||
|
/** |
||||||
|
* flow database access |
||||||
|
*/ |
||||||
|
private ProcessDao processDao; |
||||||
|
|
||||||
|
|
||||||
|
private Date createTime = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* zkMasterClient |
||||||
|
*/ |
||||||
|
private static ZKMasterClient zkMasterClient = null; |
||||||
|
|
||||||
|
|
||||||
|
private ZKMasterClient(ProcessDao processDao){ |
||||||
|
this.processDao = processDao; |
||||||
|
init(); |
||||||
|
} |
||||||
|
|
||||||
|
private ZKMasterClient(){} |
||||||
|
|
||||||
|
/** |
||||||
|
* get zkMasterClient |
||||||
|
* @param processDao |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static synchronized ZKMasterClient getZKMasterClient(ProcessDao processDao){ |
||||||
|
if(zkMasterClient == null){ |
||||||
|
zkMasterClient = new ZKMasterClient(processDao); |
||||||
|
} |
||||||
|
zkMasterClient.processDao = processDao; |
||||||
|
|
||||||
|
return zkMasterClient; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* init |
||||||
|
*/ |
||||||
|
public void init(){ |
||||||
|
// init dao
|
||||||
|
this.initDao(); |
||||||
|
|
||||||
|
// init system znode
|
||||||
|
this.initSystemZNode(); |
||||||
|
|
||||||
|
// monitor master
|
||||||
|
this.listenerMaster(); |
||||||
|
|
||||||
|
// monitor worker
|
||||||
|
this.listenerWorker(); |
||||||
|
|
||||||
|
// register master
|
||||||
|
this.registMaster(); |
||||||
|
|
||||||
|
// check if fault tolerance is required
|
||||||
|
if (getActiveMasterNum() == 1) { |
||||||
|
processDao.selfFaultTolerant(ExecutionStatus.RUNNING_EXEUTION.ordinal()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* init dao |
||||||
|
*/ |
||||||
|
public void initDao(){ |
||||||
|
this.serverDao = DaoFactory.getDaoInstance(ServerDao.class); |
||||||
|
this.alertDao = DaoFactory.getDaoInstance(AlertDao.class); |
||||||
|
this.processDao = DaoFactory.getDaoInstance(ProcessDao.class); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get maste dao |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public ServerDao getServerDao(){ |
||||||
|
return serverDao; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get alert dao |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public AlertDao getAlertDao() { |
||||||
|
return alertDao; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* register master znode |
||||||
|
*/ |
||||||
|
public void registMaster(){ |
||||||
|
|
||||||
|
// get current date
|
||||||
|
Date now = new Date(); |
||||||
|
createTime = now ; |
||||||
|
try { |
||||||
|
|
||||||
|
// encapsulation master znnode
|
||||||
|
masterZNode = masterZNodeParentPath + "/" + OSUtils.getHost() + "_"; |
||||||
|
List<String> masterZNodeList = zkClient.getChildren().forPath(masterZNodeParentPath); |
||||||
|
|
||||||
|
if (CollectionUtils.isNotEmpty(masterZNodeList)){ |
||||||
|
boolean flag = false; |
||||||
|
for (String masterZNode : masterZNodeList){ |
||||||
|
if (masterZNode.startsWith(OSUtils.getHost())){ |
||||||
|
flag = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (flag){ |
||||||
|
logger.error("register failure , master already started on host : {}" , OSUtils.getHost()); |
||||||
|
// exit system
|
||||||
|
System.exit(-1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// specify the format of stored data in ZK nodes
|
||||||
|
String heartbeatZKInfo = getOsInfo(now); |
||||||
|
// create temporary sequence nodes for master znode
|
||||||
|
masterZNode = zkClient.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(masterZNode, heartbeatZKInfo.getBytes()); |
||||||
|
|
||||||
|
logger.info("register master node {} success" , masterZNode); |
||||||
|
|
||||||
|
// handle dead server
|
||||||
|
handleDeadServer(masterZNode, Constants.MASTER_PREFIX, Constants.DELETE_ZK_OP); |
||||||
|
|
||||||
|
// delete master server from database
|
||||||
|
serverDao.deleteMaster(OSUtils.getHost()); |
||||||
|
|
||||||
|
// register master znode
|
||||||
|
serverDao.registerMaster(OSUtils.getHost(), |
||||||
|
OSUtils.getProcessID(), |
||||||
|
masterZNode, |
||||||
|
ResInfo.getResInfoJson(), |
||||||
|
createTime, |
||||||
|
createTime); |
||||||
|
|
||||||
|
} catch (Exception e) { |
||||||
|
logger.error("register master failure : " + e.getMessage(),e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* monitor master |
||||||
|
*/ |
||||||
|
public void listenerMaster(){ |
||||||
|
PathChildrenCache masterPc = new PathChildrenCache(zkClient, masterZNodeParentPath, true ,defaultThreadFactory); |
||||||
|
|
||||||
|
try { |
||||||
|
Date now = new Date(); |
||||||
|
createTime = now ; |
||||||
|
masterPc.start(); |
||||||
|
masterPc.getListenable().addListener(new PathChildrenCacheListener() { |
||||||
|
@Override |
||||||
|
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { |
||||||
|
switch (event.getType()) { |
||||||
|
case CHILD_ADDED: |
||||||
|
logger.info("master node added : {}",event.getData().getPath()); |
||||||
|
break; |
||||||
|
case CHILD_REMOVED: |
||||||
|
String path = event.getData().getPath(); |
||||||
|
logger.info("master node deleted : {}",event.getData().getPath()); |
||||||
|
|
||||||
|
InterProcessMutex mutexLock = null; |
||||||
|
try { |
||||||
|
// handle dead server, add to zk dead server pth
|
||||||
|
handleDeadServer(path, Constants.MASTER_PREFIX, Constants.ADD_ZK_OP); |
||||||
|
|
||||||
|
if(masterZNode.equals(path)){ |
||||||
|
logger.error("master server({}) of myself dead , stopping...", path); |
||||||
|
stoppable.stop(String.format("master server(%s) of myself dead , stopping...", path)); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// create a distributed lock, and the root node path of the lock space is /escheduler/lock/failover/master
|
||||||
|
String znodeLock = zkMasterClient.getMasterFailoverLockPath(); |
||||||
|
mutexLock = new InterProcessMutex(zkMasterClient.getZkClient(), znodeLock); |
||||||
|
mutexLock.acquire(); |
||||||
|
|
||||||
|
String masterHost = getHostByEventDataPath(path); |
||||||
|
for (int i = 0; i < Constants.ESCHEDULER_WARN_TIMES_FAILOVER;i++) { |
||||||
|
alertDao.sendServerStopedAlert(1, masterHost, "Master-Server"); |
||||||
|
} |
||||||
|
|
||||||
|
logger.info("start master failover ..."); |
||||||
|
|
||||||
|
List<ProcessInstance> needFailoverProcessInstanceList = processDao.queryNeddFailoverProcessInstances(masterHost); |
||||||
|
|
||||||
|
//updateProcessInstance host is null and insert into command
|
||||||
|
for(ProcessInstance processInstance : needFailoverProcessInstanceList){ |
||||||
|
processDao.processNeedFailoverProcessInstances(processInstance); |
||||||
|
} |
||||||
|
|
||||||
|
logger.info("master failover end"); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.error("master failover failed : " + e.getMessage(),e); |
||||||
|
}finally { |
||||||
|
if (mutexLock != null){ |
||||||
|
try { |
||||||
|
mutexLock.release(); |
||||||
|
} catch (Exception e) { |
||||||
|
logger.error("lock relase failed : " + e.getMessage(),e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
case CHILD_UPDATED: |
||||||
|
if (event.getData().getPath().contains(OSUtils.getHost())){ |
||||||
|
byte[] bytes = zkClient.getData().forPath(event.getData().getPath()); |
||||||
|
String resInfoStr = new String(bytes); |
||||||
|
String[] splits = resInfoStr.split(Constants.COMMA); |
||||||
|
if (splits.length != Constants.HEARTBEAT_FOR_ZOOKEEPER_INFO_LENGTH) { |
||||||
|
return; |
||||||
|
} |
||||||
|
// updateProcessInstance Master information in database according to host
|
||||||
|
serverDao.updateMaster(OSUtils.getHost(), |
||||||
|
OSUtils.getProcessID(), |
||||||
|
ResInfo.getResInfoJson(Double.parseDouble(splits[2]), |
||||||
|
Double.parseDouble(splits[3])), |
||||||
|
DateUtils.stringToDate(splits[5])); |
||||||
|
|
||||||
|
logger.debug("master zk node updated : {}",event.getData().getPath()); |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.error("monitor master failed : " + e.getMessage(),e); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* monitor worker |
||||||
|
*/ |
||||||
|
public void listenerWorker(){ |
||||||
|
|
||||||
|
PathChildrenCache workerPc = new PathChildrenCache(zkClient,workerZNodeParentPath,true ,defaultThreadFactory); |
||||||
|
try { |
||||||
|
Date now = new Date(); |
||||||
|
createTime = now ; |
||||||
|
workerPc.start(); |
||||||
|
workerPc.getListenable().addListener(new PathChildrenCacheListener() { |
||||||
|
@Override |
||||||
|
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) { |
||||||
|
switch (event.getType()) { |
||||||
|
case CHILD_ADDED: |
||||||
|
logger.info("node added : {}" ,event.getData().getPath()); |
||||||
|
break; |
||||||
|
case CHILD_REMOVED: |
||||||
|
String path = event.getData().getPath(); |
||||||
|
|
||||||
|
logger.info("node deleted : {}",event.getData().getPath()); |
||||||
|
|
||||||
|
InterProcessMutex mutex = null; |
||||||
|
try { |
||||||
|
|
||||||
|
// handle dead server
|
||||||
|
handleDeadServer(path, Constants.WORKER_PREFIX, Constants.ADD_ZK_OP); |
||||||
|
|
||||||
|
// create a distributed lock, and the root node path of the lock space is /escheduler/lock/failover/worker
|
||||||
|
String znodeLock = zkMasterClient.getWorkerFailoverLockPath(); |
||||||
|
mutex = new InterProcessMutex(zkMasterClient.getZkClient(), znodeLock); |
||||||
|
mutex.acquire(); |
||||||
|
|
||||||
|
String workerHost = getHostByEventDataPath(path); |
||||||
|
for (int i = 0; i < Constants.ESCHEDULER_WARN_TIMES_FAILOVER;i++) { |
||||||
|
alertDao.sendServerStopedAlert(1, workerHost, "Worker-Server"); |
||||||
|
} |
||||||
|
|
||||||
|
logger.info("start worker failover ..."); |
||||||
|
|
||||||
|
|
||||||
|
List<TaskInstance> needFailoverTaskInstanceList = processDao.queryNeedFailoverTaskInstances(workerHost); |
||||||
|
for(TaskInstance taskInstance : needFailoverTaskInstanceList){ |
||||||
|
ProcessInstance instance = processDao.findProcessInstanceDetailById(taskInstance.getProcessInstanceId()); |
||||||
|
if(instance!=null){ |
||||||
|
taskInstance.setProcessInstance(instance); |
||||||
|
} |
||||||
|
// only kill yarn job if exists , the local thread has exited
|
||||||
|
ProcessUtils.killYarnJob(taskInstance); |
||||||
|
} |
||||||
|
|
||||||
|
//updateProcessInstance state value is NEED_FAULT_TOLERANCE
|
||||||
|
processDao.updateNeedFailoverTaskInstances(workerHost); |
||||||
|
|
||||||
|
logger.info("worker failover end"); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.error("worker failover failed : " + e.getMessage(),e); |
||||||
|
} |
||||||
|
finally { |
||||||
|
if (mutex != null){ |
||||||
|
try { |
||||||
|
mutex.release(); |
||||||
|
} catch (Exception e) { |
||||||
|
logger.error("lock relase failed : " + e.getMessage(),e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.error("listener worker failed : " + e.getMessage(),e); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* get os info |
||||||
|
* @param now |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private String getOsInfo(Date now) { |
||||||
|
return ResInfo.buildHeartbeatForZKInfo(OSUtils.getHost(), |
||||||
|
OSUtils.getProcessID(), |
||||||
|
OSUtils.cpuUsage(), |
||||||
|
OSUtils.memoryUsage(), |
||||||
|
DateUtils.dateToString(now), |
||||||
|
DateUtils.dateToString(now)); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* get master znode |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String getMasterZNode() { |
||||||
|
return masterZNode; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* get master lock path |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String getMasterLockPath(){ |
||||||
|
return conf.getString(Constants.ZOOKEEPER_ESCHEDULER_LOCK_MASTERS); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get master failover lock path |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String getMasterFailoverLockPath(){ |
||||||
|
return conf.getString(Constants.ZOOKEEPER_ESCHEDULER_LOCK_FAILOVER_MASTERS); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get worker failover lock path |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String getWorkerFailoverLockPath(){ |
||||||
|
return conf.getString(Constants.ZOOKEEPER_ESCHEDULER_LOCK_FAILOVER_WORKERS); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get zkclient |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public CuratorFramework getZkClient() { |
||||||
|
return zkClient; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* get host ip |
||||||
|
* @param path |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private String getHostByEventDataPath(String path) { |
||||||
|
int startIndex = path.lastIndexOf("/")+1; |
||||||
|
int endIndex = path.lastIndexOf("_"); |
||||||
|
|
||||||
|
if(startIndex >= endIndex){ |
||||||
|
logger.error("parse ip error"); |
||||||
|
} |
||||||
|
return path.substring(startIndex, endIndex); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,286 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.zk; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.utils.CollectionUtils; |
||||||
|
import cn.escheduler.common.utils.DateUtils; |
||||||
|
import cn.escheduler.common.utils.OSUtils; |
||||||
|
import cn.escheduler.common.zk.AbstractZKClient; |
||||||
|
import cn.escheduler.dao.DaoFactory; |
||||||
|
import cn.escheduler.dao.ServerDao; |
||||||
|
import cn.escheduler.server.ResInfo; |
||||||
|
import org.apache.curator.framework.CuratorFramework; |
||||||
|
import org.apache.curator.framework.recipes.cache.PathChildrenCache; |
||||||
|
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; |
||||||
|
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; |
||||||
|
import org.apache.curator.utils.ThreadUtils; |
||||||
|
import org.apache.zookeeper.CreateMode; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
import java.util.List; |
||||||
|
import java.util.concurrent.ThreadFactory; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* zookeeper worker client |
||||||
|
* single instance |
||||||
|
*/ |
||||||
|
public class ZKWorkerClient extends AbstractZKClient { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ZKWorkerClient.class); |
||||||
|
|
||||||
|
|
||||||
|
private static final ThreadFactory defaultThreadFactory = ThreadUtils.newGenericThreadFactory("Worker-Main-Thread"); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* worker znode |
||||||
|
*/ |
||||||
|
private String workerZNode = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* worker database access |
||||||
|
*/ |
||||||
|
private ServerDao serverDao = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* create time |
||||||
|
*/ |
||||||
|
private Date createTime = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* zkWorkerClient |
||||||
|
*/ |
||||||
|
private static ZKWorkerClient zkWorkerClient = null; |
||||||
|
|
||||||
|
private ZKWorkerClient(){ |
||||||
|
init(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* init |
||||||
|
*/ |
||||||
|
private void init(){ |
||||||
|
// init worker dao
|
||||||
|
serverDao = DaoFactory.getDaoInstance(ServerDao.class); |
||||||
|
|
||||||
|
// init system znode
|
||||||
|
this.initSystemZNode(); |
||||||
|
|
||||||
|
// monitor worker
|
||||||
|
this.listenerWorker(); |
||||||
|
|
||||||
|
// register worker
|
||||||
|
this.registWorker(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* get zkWorkerClient |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static synchronized ZKWorkerClient getZKWorkerClient(){ |
||||||
|
if(zkWorkerClient == null){ |
||||||
|
zkWorkerClient = new ZKWorkerClient(); |
||||||
|
} |
||||||
|
|
||||||
|
return zkWorkerClient; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get worker dao |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public ServerDao getServerDao(){ |
||||||
|
return serverDao; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public String initWorkZNode() throws Exception { |
||||||
|
|
||||||
|
Date now = new Date(); |
||||||
|
String heartbeatZKInfo = getOsInfo(now); |
||||||
|
|
||||||
|
|
||||||
|
workerZNode = workerZNodeParentPath + "/" + OSUtils.getHost() + "_"; |
||||||
|
workerZNode = zkClient.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(workerZNode, |
||||||
|
heartbeatZKInfo.getBytes()); |
||||||
|
logger.info("register worker node {} success", workerZNode); |
||||||
|
return workerZNode; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* register worker |
||||||
|
*/ |
||||||
|
private void registWorker(){ |
||||||
|
|
||||||
|
// get current date
|
||||||
|
Date now = new Date(); |
||||||
|
createTime = now ; |
||||||
|
try { |
||||||
|
|
||||||
|
// encapsulation worker znnode
|
||||||
|
workerZNode = workerZNodeParentPath + "/" + OSUtils.getHost() + "_"; |
||||||
|
List<String> workerZNodeList = zkClient.getChildren().forPath(workerZNodeParentPath); |
||||||
|
|
||||||
|
|
||||||
|
if (CollectionUtils.isNotEmpty(workerZNodeList)){ |
||||||
|
boolean flag = false; |
||||||
|
for (String workerZNode : workerZNodeList){ |
||||||
|
if (workerZNode.startsWith(OSUtils.getHost())){ |
||||||
|
flag = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (flag){ |
||||||
|
logger.info("register failure , worker already started on : {}, please wait for a moment and try again" , OSUtils.getHost()); |
||||||
|
// exit system
|
||||||
|
System.exit(-1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// String heartbeatZKInfo = getOsInfo(now);
|
||||||
|
// workerZNode = zkClient.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(workerZNode,
|
||||||
|
// heartbeatZKInfo.getBytes());
|
||||||
|
|
||||||
|
initWorkZNode(); |
||||||
|
// handle dead server
|
||||||
|
handleDeadServer(workerZNode, Constants.WORKER_PREFIX, Constants.DELETE_ZK_OP); |
||||||
|
|
||||||
|
// delete worker server from database
|
||||||
|
serverDao.deleteWorker(OSUtils.getHost()); |
||||||
|
|
||||||
|
// register worker znode
|
||||||
|
serverDao.registerWorker(OSUtils.getHost(), |
||||||
|
OSUtils.getProcessID(), |
||||||
|
workerZNode, |
||||||
|
ResInfo.getResInfoJson(), |
||||||
|
createTime, |
||||||
|
createTime); |
||||||
|
} catch (Exception e) { |
||||||
|
logger.error("register worker failure : " + e.getMessage(),e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* monitor worker |
||||||
|
*/ |
||||||
|
private void listenerWorker(){ |
||||||
|
PathChildrenCache workerPc = new PathChildrenCache(zkClient, workerZNodeParentPath, true, defaultThreadFactory); |
||||||
|
try { |
||||||
|
|
||||||
|
Date now = new Date(); |
||||||
|
createTime = now ; |
||||||
|
workerPc.start(); |
||||||
|
workerPc.getListenable().addListener(new PathChildrenCacheListener() { |
||||||
|
@Override |
||||||
|
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { |
||||||
|
switch (event.getType()) { |
||||||
|
case CHILD_ADDED: |
||||||
|
logger.info("node added : {}" ,event.getData().getPath()); |
||||||
|
break; |
||||||
|
case CHILD_REMOVED: |
||||||
|
String path = event.getData().getPath(); |
||||||
|
|
||||||
|
// handle dead server, add to zk dead server path
|
||||||
|
handleDeadServer(path, Constants.WORKER_PREFIX, Constants.ADD_ZK_OP); |
||||||
|
|
||||||
|
//find myself dead
|
||||||
|
if(workerZNode.equals(path)){ |
||||||
|
|
||||||
|
logger.warn(" worker server({}) of myself dead , stopping...", path); |
||||||
|
stoppable.stop(String.format("worker server(%s) of myself dead , stopping",path)); |
||||||
|
} |
||||||
|
logger.info("node deleted : {}", event.getData().getPath()); |
||||||
|
break; |
||||||
|
case CHILD_UPDATED: |
||||||
|
if (event.getData().getPath().contains(OSUtils.getHost())){ |
||||||
|
byte[] bytes = zkClient.getData().forPath(event.getData().getPath()); |
||||||
|
String resInfoStr = new String(bytes); |
||||||
|
String[] splits = resInfoStr.split(Constants.COMMA); |
||||||
|
if (splits.length != Constants.HEARTBEAT_FOR_ZOOKEEPER_INFO_LENGTH) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// updateProcessInstance master info in database according to host
|
||||||
|
serverDao.updateWorker(OSUtils.getHost(), |
||||||
|
OSUtils.getProcessID(), |
||||||
|
ResInfo.getResInfoJson(Double.parseDouble(splits[2]) |
||||||
|
,Double.parseDouble(splits[3])), |
||||||
|
DateUtils.stringToDate(splits[5])); |
||||||
|
logger.debug("node updated : {}",event.getData().getPath()); |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
}catch (Exception e){ |
||||||
|
logger.error("monitor worker failed : " + e.getMessage(),e); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get os info |
||||||
|
* @param now |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private String getOsInfo(Date now) { |
||||||
|
return ResInfo.buildHeartbeatForZKInfo(OSUtils.getHost(), |
||||||
|
OSUtils.getProcessID(), |
||||||
|
OSUtils.cpuUsage(), |
||||||
|
OSUtils.memoryUsage(), |
||||||
|
DateUtils.dateToString(now), |
||||||
|
DateUtils.dateToString(now)); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* get worker znode |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String getWorkerZNode() { |
||||||
|
return workerZNode; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* get zkclient |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public CuratorFramework getZkClient() { |
||||||
|
return zkClient; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* get worker lock path |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String getWorkerLockPath(){ |
||||||
|
return conf.getString(Constants.ZOOKEEPER_ESCHEDULER_LOCK_WORKERS); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
logging.config=classpath:master_logback.xml |
@ -0,0 +1,21 @@ |
|||||||
|
# master execute thread num |
||||||
|
master.exec.threads=100 |
||||||
|
|
||||||
|
# master execute task number in parallel |
||||||
|
master.exec.task.number=20 |
||||||
|
|
||||||
|
# master heartbeat interval |
||||||
|
master.heartbeat.interval=10 |
||||||
|
|
||||||
|
# master commit task retry times |
||||||
|
master.task.commit.retryTimes=5 |
||||||
|
|
||||||
|
# master commit task interval |
||||||
|
master.task.commit.interval=100 |
||||||
|
|
||||||
|
|
||||||
|
# only less than cpu avg load, master server can work. default value : the number of cpu cores * 2 |
||||||
|
master.max.cpuload.avg=10 |
||||||
|
|
||||||
|
# only larger than reserved memory, master server can work. default value : physical memory * 1/10, unit is G. |
||||||
|
master.reserved.memory=1 |
@ -0,0 +1,34 @@ |
|||||||
|
<!-- Logback configuration. See http://logback.qos.ch/manual/index.html --> |
||||||
|
<configuration scan="true" scanPeriod="120 seconds"> <!--debug="true" --> |
||||||
|
<property name="log.base" value="logs" /> |
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> |
||||||
|
<encoder> |
||||||
|
<pattern> |
||||||
|
[%level] %date{yyyy-MM-dd HH:mm:ss.SSS} %logger{96}:[%line] - %msg%n |
||||||
|
</pattern> |
||||||
|
<charset>UTF-8</charset> |
||||||
|
</encoder> |
||||||
|
</appender> |
||||||
|
|
||||||
|
<appender name="MASTERLOGFILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
||||||
|
<file>${log.base}/escheduler-master.log</file> |
||||||
|
<filter class="cn.escheduler.server.master.log.MasterLogFilter"> |
||||||
|
<level>INFO</level> |
||||||
|
</filter> |
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> |
||||||
|
<fileNamePattern>${log.base}/escheduler-master.%d{yyyy-MM-dd_HH}.%i.log</fileNamePattern> |
||||||
|
<maxHistory>168</maxHistory> |
||||||
|
<maxFileSize>200MB</maxFileSize> |
||||||
|
</rollingPolicy> |
||||||
|
<encoder> |
||||||
|
<pattern> |
||||||
|
[%level] %date{yyyy-MM-dd HH:mm:ss.SSS} %logger{96}:[%line] - %msg%n |
||||||
|
</pattern> |
||||||
|
<charset>UTF-8</charset> |
||||||
|
</encoder> |
||||||
|
</appender> |
||||||
|
|
||||||
|
<root level="INFO"> |
||||||
|
<appender-ref ref="MASTERLOGFILE"/> |
||||||
|
</root> |
||||||
|
</configuration> |
@ -0,0 +1,15 @@ |
|||||||
|
# worker execute thread num |
||||||
|
worker.exec.threads=100 |
||||||
|
|
||||||
|
# worker heartbeat interval |
||||||
|
worker.heartbeat.interval=10 |
||||||
|
|
||||||
|
# submit the number of tasks at a time |
||||||
|
worker.fetch.task.num = 10 |
||||||
|
|
||||||
|
|
||||||
|
# only less than cpu avg load, worker server can work. default value : the number of cpu cores * 2 |
||||||
|
worker.max.cpuload.avg=10 |
||||||
|
|
||||||
|
# only larger than reserved memory, worker server can work. default value : physical memory * 1/6, unit is G. |
||||||
|
worker.reserved.memory=1 |
@ -0,0 +1,109 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.master; |
||||||
|
|
||||||
|
import cn.escheduler.common.enums.ExecutionStatus; |
||||||
|
import cn.escheduler.dao.datasource.ConnectionFactory; |
||||||
|
import cn.escheduler.dao.mapper.ProcessDefinitionMapper; |
||||||
|
import cn.escheduler.dao.mapper.ProcessInstanceMapper; |
||||||
|
import cn.escheduler.dao.mapper.TaskInstanceMapper; |
||||||
|
import cn.escheduler.dao.model.ProcessDefinition; |
||||||
|
import cn.escheduler.dao.model.ProcessInstance; |
||||||
|
import cn.escheduler.dao.model.TaskInstance; |
||||||
|
import cn.escheduler.server.utils.AlertManager; |
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.Test; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* alert manager test |
||||||
|
*/ |
||||||
|
public class AlertManagerTest { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(AlertManagerTest.class); |
||||||
|
|
||||||
|
ProcessDefinitionMapper processDefinitionMapper; |
||||||
|
ProcessInstanceMapper processInstanceMapper; |
||||||
|
TaskInstanceMapper taskInstanceMapper; |
||||||
|
|
||||||
|
AlertManager alertManager; |
||||||
|
|
||||||
|
@Before |
||||||
|
public void before(){ |
||||||
|
processDefinitionMapper = ConnectionFactory.getSqlSession().getMapper(ProcessDefinitionMapper.class); |
||||||
|
processInstanceMapper = ConnectionFactory.getSqlSession().getMapper(ProcessInstanceMapper.class); |
||||||
|
taskInstanceMapper = ConnectionFactory.getSqlSession().getMapper(TaskInstanceMapper.class); |
||||||
|
alertManager = new AlertManager(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* send worker alert fault tolerance |
||||||
|
*/ |
||||||
|
@Test |
||||||
|
public void sendWarnningWorkerleranceFaultTest(){ |
||||||
|
// process instance
|
||||||
|
ProcessInstance processInstance = processInstanceMapper.queryDetailById(13028); |
||||||
|
|
||||||
|
// set process definition
|
||||||
|
ProcessDefinition processDefinition = processDefinitionMapper.queryByDefineId(47); |
||||||
|
processInstance.setProcessDefinition(processDefinition); |
||||||
|
|
||||||
|
|
||||||
|
// fault task instance
|
||||||
|
TaskInstance toleranceTask1 = taskInstanceMapper.queryById(5038); |
||||||
|
TaskInstance toleranceTask2 = taskInstanceMapper.queryById(5039); |
||||||
|
|
||||||
|
List<TaskInstance> toleranceTaskList = new ArrayList<>(2); |
||||||
|
toleranceTaskList.add(toleranceTask1); |
||||||
|
toleranceTaskList.add(toleranceTask2); |
||||||
|
|
||||||
|
alertManager.sendWarnningWorkerleranceFault(processInstance, toleranceTaskList); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* send worker alert fault tolerance |
||||||
|
*/ |
||||||
|
@Test |
||||||
|
public void sendWarnningOfProcessInstanceTest(){ |
||||||
|
// process instance
|
||||||
|
ProcessInstance processInstance = processInstanceMapper.queryDetailById(13028); |
||||||
|
|
||||||
|
// set process definition
|
||||||
|
ProcessDefinition processDefinition = processDefinitionMapper.queryByDefineId(47); |
||||||
|
processInstance.setProcessDefinition(processDefinition); |
||||||
|
|
||||||
|
|
||||||
|
// fault task instance
|
||||||
|
TaskInstance toleranceTask1 = taskInstanceMapper.queryById(5038); |
||||||
|
toleranceTask1.setState(ExecutionStatus.FAILURE); |
||||||
|
TaskInstance toleranceTask2 = taskInstanceMapper.queryById(5039); |
||||||
|
toleranceTask2.setState(ExecutionStatus.FAILURE); |
||||||
|
|
||||||
|
List<TaskInstance> toleranceTaskList = new ArrayList<>(2); |
||||||
|
toleranceTaskList.add(toleranceTask1); |
||||||
|
toleranceTaskList.add(toleranceTask2); |
||||||
|
|
||||||
|
alertManager.sendWarnningOfProcessInstance(processInstance, toleranceTaskList); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,107 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.master; |
||||||
|
|
||||||
|
import cn.escheduler.common.enums.CommandType; |
||||||
|
import cn.escheduler.common.enums.FailureStrategy; |
||||||
|
import cn.escheduler.common.enums.WarningType; |
||||||
|
import cn.escheduler.dao.datasource.ConnectionFactory; |
||||||
|
import cn.escheduler.dao.mapper.CommandMapper; |
||||||
|
import cn.escheduler.dao.model.Command; |
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.Test; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
/** |
||||||
|
* master test |
||||||
|
*/ |
||||||
|
public class MasterCommandTest { |
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(MasterCommandTest.class); |
||||||
|
|
||||||
|
private CommandMapper commandMapper; |
||||||
|
|
||||||
|
@Before |
||||||
|
public void before(){ |
||||||
|
commandMapper = ConnectionFactory.getSqlSession().getMapper(CommandMapper.class); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Test |
||||||
|
public void StartFromFailedCommand(){ |
||||||
|
Command cmd = new Command(); |
||||||
|
cmd.setCommandType(CommandType.START_FAILURE_TASK_PROCESS); |
||||||
|
cmd.setCommandParam("{\"ProcessInstanceId\":325}"); |
||||||
|
cmd.setProcessDefinitionId(63); |
||||||
|
|
||||||
|
commandMapper.insert(cmd); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void RecoverSuspendCommand(){ |
||||||
|
|
||||||
|
Command cmd = new Command(); |
||||||
|
cmd.setProcessDefinitionId(44); |
||||||
|
cmd.setCommandParam("{\"ProcessInstanceId\":290}"); |
||||||
|
cmd.setCommandType(CommandType.RECOVER_SUSPENDED_PROCESS); |
||||||
|
|
||||||
|
commandMapper.insert(cmd); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Test |
||||||
|
public void startNewProcessCommand(){ |
||||||
|
Command cmd = new Command(); |
||||||
|
cmd.setCommandType(CommandType.START_PROCESS); |
||||||
|
cmd.setProcessDefinitionId(167); |
||||||
|
cmd.setFailureStrategy(FailureStrategy.CONTINUE); |
||||||
|
cmd.setWarningType(WarningType.NONE); |
||||||
|
cmd.setWarningGroupId(4); |
||||||
|
cmd.setExecutorId(19); |
||||||
|
|
||||||
|
commandMapper.insert(cmd); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void ToleranceCommand(){ |
||||||
|
Command cmd = new Command(); |
||||||
|
cmd.setCommandType(CommandType.RECOVER_TOLERANCE_FAULT_PROCESS); |
||||||
|
cmd.setCommandParam("{\"ProcessInstanceId\":816}"); |
||||||
|
cmd.setProcessDefinitionId(15); |
||||||
|
|
||||||
|
commandMapper.insert(cmd); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void insertCommand(){ |
||||||
|
Command cmd = new Command(); |
||||||
|
cmd.setCommandType(CommandType.START_PROCESS); |
||||||
|
cmd.setFailureStrategy(FailureStrategy.CONTINUE); |
||||||
|
cmd.setWarningType(WarningType.ALL); |
||||||
|
cmd.setProcessDefinitionId(72); |
||||||
|
cmd.setExecutorId(10); |
||||||
|
commandMapper.insert(cmd); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,102 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.master; |
||||||
|
|
||||||
|
import cn.escheduler.common.enums.CommandType; |
||||||
|
import cn.escheduler.common.enums.DataType; |
||||||
|
import cn.escheduler.common.enums.Direct; |
||||||
|
import cn.escheduler.common.process.Property; |
||||||
|
import cn.escheduler.common.utils.ParameterUtils; |
||||||
|
import cn.escheduler.common.utils.placeholder.BusinessTimeUtils; |
||||||
|
import cn.escheduler.server.utils.ParamUtils; |
||||||
|
import com.alibaba.fastjson.JSON; |
||||||
|
import org.junit.Test; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.Calendar; |
||||||
|
import java.util.Date; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* user define param |
||||||
|
*/ |
||||||
|
public class ParamsTest { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ParamsTest.class); |
||||||
|
|
||||||
|
|
||||||
|
@Test |
||||||
|
public void systemParamsTest()throws Exception{ |
||||||
|
String command = "${system.biz.date}"; |
||||||
|
|
||||||
|
// start process
|
||||||
|
Map<String,String> timeParams = BusinessTimeUtils |
||||||
|
.getBusinessTime(CommandType.START_PROCESS, |
||||||
|
new Date()); |
||||||
|
|
||||||
|
command = ParameterUtils.convertParameterPlaceholders(command, timeParams); |
||||||
|
|
||||||
|
logger.info("start process : {}",command); |
||||||
|
|
||||||
|
|
||||||
|
Calendar calendar = Calendar.getInstance(); |
||||||
|
calendar.setTime(new Date()); |
||||||
|
calendar.add(Calendar.DAY_OF_MONTH, -5); |
||||||
|
|
||||||
|
|
||||||
|
command = "${system.biz.date}"; |
||||||
|
// complement data
|
||||||
|
timeParams = BusinessTimeUtils |
||||||
|
.getBusinessTime(CommandType.COMPLEMENT_DATA, |
||||||
|
calendar.getTime()); |
||||||
|
command = ParameterUtils.convertParameterPlaceholders(command, timeParams); |
||||||
|
logger.info("complement data : {}",command); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void convertTest()throws Exception{ |
||||||
|
Map<String,Property> globalParams = new HashMap<>(); |
||||||
|
Property property = new Property(); |
||||||
|
property.setProp("global_param"); |
||||||
|
property.setDirect(Direct.IN); |
||||||
|
property.setType(DataType.VARCHAR); |
||||||
|
property.setValue("${system.biz.date}"); |
||||||
|
globalParams.put("global_param",property); |
||||||
|
|
||||||
|
Map<String,String> globalParamsMap = new HashMap<>(); |
||||||
|
globalParamsMap.put("global_param","${system.biz.date}"); |
||||||
|
|
||||||
|
|
||||||
|
Map<String,Property> localParams = new HashMap<>(); |
||||||
|
Property localProperty = new Property(); |
||||||
|
localProperty.setProp("local_param"); |
||||||
|
localProperty.setDirect(Direct.IN); |
||||||
|
localProperty.setType(DataType.VARCHAR); |
||||||
|
localProperty.setValue("${global_param}"); |
||||||
|
localParams.put("local_param", localProperty); |
||||||
|
|
||||||
|
Map<String, Property> paramsMap = ParamUtils.convert(globalParams, globalParamsMap, |
||||||
|
localParams, CommandType.START_PROCESS, new Date()); |
||||||
|
logger.info(JSON.toJSONString(paramsMap)); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,103 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.shell; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.enums.ExecutionStatus; |
||||||
|
import cn.escheduler.common.model.TaskNode; |
||||||
|
import cn.escheduler.dao.DaoFactory; |
||||||
|
import cn.escheduler.dao.ProcessDao; |
||||||
|
import cn.escheduler.dao.model.TaskInstance; |
||||||
|
import cn.escheduler.server.utils.LoggerUtils; |
||||||
|
import cn.escheduler.server.worker.log.TaskLogger; |
||||||
|
import cn.escheduler.server.worker.task.AbstractTask; |
||||||
|
import cn.escheduler.server.worker.task.TaskManager; |
||||||
|
import cn.escheduler.server.worker.task.TaskProps; |
||||||
|
import com.alibaba.fastjson.JSONObject; |
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.Test; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
|
||||||
|
/** |
||||||
|
* python shell command executor test |
||||||
|
*/ |
||||||
|
public class ShellCommandExecutorTest { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ShellCommandExecutorTest.class); |
||||||
|
private static final String TASK_PREFIX = "TASK"; |
||||||
|
|
||||||
|
private ProcessDao processDao = null; |
||||||
|
|
||||||
|
@Before |
||||||
|
public void before(){ |
||||||
|
processDao = DaoFactory.getDaoInstance(ProcessDao.class); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void test() throws Exception { |
||||||
|
|
||||||
|
TaskProps taskProps = new TaskProps(); |
||||||
|
// processDefineId_processInstanceId_taskInstanceId
|
||||||
|
taskProps.setTaskDir("/opt/soft/program/tmp/escheduler/exec/flow/5/36/2864/7657"); |
||||||
|
taskProps.setTaskAppId("36_2864_7657"); |
||||||
|
// set tenant -> task execute linux user
|
||||||
|
taskProps.setTenantCode("hdfs"); |
||||||
|
taskProps.setTaskStartTime(new Date()); |
||||||
|
taskProps.setTaskTimeout(360000); |
||||||
|
taskProps.setTaskInstId(7657); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TaskInstance taskInstance = processDao.findTaskInstanceById(7657); |
||||||
|
|
||||||
|
String taskJson = taskInstance.getTaskJson(); |
||||||
|
TaskNode taskNode = JSONObject.parseObject(taskJson, TaskNode.class); |
||||||
|
taskProps.setTaskParams(taskNode.getParams()); |
||||||
|
|
||||||
|
|
||||||
|
// custom logger
|
||||||
|
TaskLogger taskLogger = new TaskLogger(LoggerUtils.buildTaskId(TASK_PREFIX, |
||||||
|
taskInstance.getProcessDefinitionId(), |
||||||
|
taskInstance.getProcessInstanceId(), |
||||||
|
taskInstance.getId())); |
||||||
|
|
||||||
|
|
||||||
|
AbstractTask task = TaskManager.newTask(taskInstance.getTaskType(), taskProps, taskLogger); |
||||||
|
|
||||||
|
logger.info("task info : {}", task); |
||||||
|
|
||||||
|
// job init
|
||||||
|
task.init(); |
||||||
|
|
||||||
|
// job handle
|
||||||
|
task.handle(); |
||||||
|
ExecutionStatus status = ExecutionStatus.SUCCESS; |
||||||
|
|
||||||
|
if (task.getExitStatusCode() == Constants.EXIT_CODE_SUCCESS){ |
||||||
|
status = ExecutionStatus.SUCCESS; |
||||||
|
}else if (task.getExitStatusCode() == Constants.EXIT_CODE_KILL){ |
||||||
|
status = ExecutionStatus.KILL; |
||||||
|
}else { |
||||||
|
status = ExecutionStatus.FAILURE; |
||||||
|
} |
||||||
|
|
||||||
|
logger.info(status.toString()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,104 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.sql; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.common.enums.ExecutionStatus; |
||||||
|
import cn.escheduler.common.model.TaskNode; |
||||||
|
import cn.escheduler.dao.DaoFactory; |
||||||
|
import cn.escheduler.dao.ProcessDao; |
||||||
|
import cn.escheduler.dao.model.TaskInstance; |
||||||
|
import cn.escheduler.server.utils.LoggerUtils; |
||||||
|
import cn.escheduler.server.worker.log.TaskLogger; |
||||||
|
import cn.escheduler.server.worker.task.AbstractTask; |
||||||
|
import cn.escheduler.server.worker.task.TaskManager; |
||||||
|
import cn.escheduler.server.worker.task.TaskProps; |
||||||
|
import com.alibaba.fastjson.JSONObject; |
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.Test; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
|
||||||
|
/** |
||||||
|
* python shell command executor test |
||||||
|
*/ |
||||||
|
public class SqlExecutorTest { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SqlExecutorTest.class); |
||||||
|
private static final String TASK_PREFIX = "TASK"; |
||||||
|
|
||||||
|
private ProcessDao processDao = null; |
||||||
|
|
||||||
|
@Before |
||||||
|
public void before(){ |
||||||
|
processDao = DaoFactory.getDaoInstance(ProcessDao.class); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void test() throws Exception { |
||||||
|
|
||||||
|
TaskProps taskProps = new TaskProps(); |
||||||
|
taskProps.setTaskDir(""); |
||||||
|
// processDefineId_processInstanceId_taskInstanceId
|
||||||
|
taskProps.setTaskAppId("51_11282_263978"); |
||||||
|
// set tenant -> task execute linux user
|
||||||
|
taskProps.setTenantCode("hdfs"); |
||||||
|
taskProps.setTaskStartTime(new Date()); |
||||||
|
taskProps.setTaskTimeout(360000); |
||||||
|
taskProps.setTaskInstId(263978); |
||||||
|
taskProps.setNodeName("mysql sql test"); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TaskInstance taskInstance = processDao.findTaskInstanceById(263978); |
||||||
|
|
||||||
|
String taskJson = taskInstance.getTaskJson(); |
||||||
|
TaskNode taskNode = JSONObject.parseObject(taskJson, TaskNode.class); |
||||||
|
taskProps.setTaskParams(taskNode.getParams()); |
||||||
|
|
||||||
|
|
||||||
|
// custom logger
|
||||||
|
TaskLogger taskLogger = new TaskLogger(LoggerUtils.buildTaskId(TASK_PREFIX, |
||||||
|
taskInstance.getProcessDefinitionId(), |
||||||
|
taskInstance.getProcessInstanceId(), |
||||||
|
taskInstance.getId())); |
||||||
|
|
||||||
|
|
||||||
|
AbstractTask task = TaskManager.newTask(taskInstance.getTaskType(), taskProps, taskLogger); |
||||||
|
|
||||||
|
logger.info("task info : {}", task); |
||||||
|
|
||||||
|
// job init
|
||||||
|
task.init(); |
||||||
|
|
||||||
|
// job handle
|
||||||
|
task.handle(); |
||||||
|
ExecutionStatus status = ExecutionStatus.SUCCESS; |
||||||
|
|
||||||
|
if (task.getExitStatusCode() == Constants.EXIT_CODE_SUCCESS){ |
||||||
|
status = ExecutionStatus.SUCCESS; |
||||||
|
}else if (task.getExitStatusCode() == Constants.EXIT_CODE_KILL){ |
||||||
|
status = ExecutionStatus.KILL; |
||||||
|
}else { |
||||||
|
status = ExecutionStatus.FAILURE; |
||||||
|
} |
||||||
|
|
||||||
|
logger.info(status.toString()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,65 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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. |
||||||
|
*/ |
||||||
|
package cn.escheduler.server.worker.task.dependent; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import cn.escheduler.server.worker.task.TaskProps; |
||||||
|
import org.junit.Assert; |
||||||
|
import org.junit.Test; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
public class DependentTaskTest { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(DependentTaskTest.class); |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testDependInit(){ |
||||||
|
|
||||||
|
|
||||||
|
TaskProps taskProps = new TaskProps(); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
String dependString = "{\n" + |
||||||
|
"\"dependTaskList\":[\n" + |
||||||
|
" {\n" + |
||||||
|
" \"dependItemList\":[\n" + |
||||||
|
" {\n" + |
||||||
|
" \"definitionId\": 101,\n" + |
||||||
|
" \"depTasks\": \"ALL\",\n" + |
||||||
|
" \"cycle\": \"day\",\n" + |
||||||
|
" \"dateValue\": \"last1Day\"\n" + |
||||||
|
" }\n" + |
||||||
|
" ],\n" + |
||||||
|
" \"relation\": \"AND\"\n" + |
||||||
|
" }\n" + |
||||||
|
" ],\n" + |
||||||
|
"\"relation\":\"OR\"\n" + |
||||||
|
"}"; |
||||||
|
|
||||||
|
taskProps.setTaskInstId(252612); |
||||||
|
taskProps.setDependence(dependString); |
||||||
|
DependentTask dependentTask = new DependentTask(taskProps, logger); |
||||||
|
dependentTask.init(); |
||||||
|
dependentTask.handle(); |
||||||
|
Assert.assertEquals(dependentTask.getExitStatusCode(), Constants.EXIT_CODE_FAILURE ); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package cn.escheduler.server.zk; |
||||||
|
|
||||||
|
import cn.escheduler.common.Constants; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import static org.junit.Assert.*; |
||||||
|
|
||||||
|
public class ZKWorkerClientTest { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void getZKWorkerClient() throws Exception { |
||||||
|
|
||||||
|
|
||||||
|
ZKWorkerClient zkWorkerClient = ZKWorkerClient.getZKWorkerClient(); |
||||||
|
zkWorkerClient.removeDeadServerByHost("127.0.0.1", Constants.WORKER_PREFIX); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue