mirror of
https://github.com/PowerJob/PowerJob.git
synced 2025-07-17 00:00:04 +08:00
init project
This commit is contained in:
parent
fe53dfd7e3
commit
f74a87772a
49
.gitignore
vendored
49
.gitignore
vendored
@ -1,23 +1,36 @@
|
|||||||
# Compiled class file
|
HELP.md
|
||||||
*.class
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**
|
||||||
|
!**/src/test/**
|
||||||
|
|
||||||
# Log file
|
### STS ###
|
||||||
*.log
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
# BlueJ files
|
### IntelliJ IDEA ###
|
||||||
*.ctxt
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
# Mobile Tools for Java (J2ME)
|
### NetBeans ###
|
||||||
.mtj.tmp/
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
|
||||||
# Package Files #
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### others ###
|
||||||
*.jar
|
*.jar
|
||||||
*.war
|
*.log
|
||||||
*.nar
|
*/.DS_Store
|
||||||
*.ear
|
|
||||||
*.zip
|
|
||||||
*.tar.gz
|
|
||||||
*.rar
|
|
||||||
|
|
||||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
|
||||||
hs_err_pid*
|
|
||||||
|
2
.idea/.gitignore
generated
vendored
Normal file
2
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/workspace.xml
|
1
.idea/.name
generated
Normal file
1
.idea/.name
generated
Normal file
@ -0,0 +1 @@
|
|||||||
|
oh-my-scheduler
|
19
.idea/misc.xml
generated
Normal file
19
.idea/misc.xml
generated
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="MavenProjectsManager">
|
||||||
|
<option name="originalFiles">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$/pom.xml" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="ignoredFiles">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$/oh-my-scheduler-sdk/pom.xml" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8.0_231" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
17
oh-my-scheduler-common/pom.xml
Normal file
17
oh-my-scheduler-common/pom.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<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">
|
||||||
|
<parent>
|
||||||
|
<artifactId>oh-my-scheduler</artifactId>
|
||||||
|
<groupId>com.github.kfcfans</groupId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>oh-my-scheduler-common</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.github.kfcfans.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务执行类型
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
public enum ExecuteType {
|
||||||
|
STANDALONE,
|
||||||
|
BROADCAST,
|
||||||
|
MAP_REDUCE
|
||||||
|
}
|
17
oh-my-scheduler-server/pom.xml
Normal file
17
oh-my-scheduler-server/pom.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<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">
|
||||||
|
<parent>
|
||||||
|
<artifactId>oh-my-scheduler</artifactId>
|
||||||
|
<groupId>com.github.kfcfans</groupId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>oh-my-scheduler-server</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
89
oh-my-scheduler-worker/pom.xml
Normal file
89
oh-my-scheduler-worker/pom.xml
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<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">
|
||||||
|
<parent>
|
||||||
|
<artifactId>oh-my-scheduler</artifactId>
|
||||||
|
<groupId>com.github.kfcfans</groupId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>oh-my-scheduler-worker</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<spring.version>5.2.4.RELEASE</spring.version>
|
||||||
|
<akka.version>2.6.4</akka.version>
|
||||||
|
<slf4j.version>1.7.30</slf4j.version>
|
||||||
|
<oms.common.version>1.0.0-SNAPSHOT</oms.common.version>
|
||||||
|
<h2.db.version>1.4.200</h2.db.version>
|
||||||
|
<hikaricp.version>3.4.2</hikaricp.version>
|
||||||
|
<guava.version>28.2-jre</guava.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- Spring 编译期依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-context</artifactId>
|
||||||
|
<version>${spring.version}</version>
|
||||||
|
<!-- <scope>provided</scope>-->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- akka remote -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.typesafe.akka</groupId>
|
||||||
|
<artifactId>akka-remote_2.13</artifactId>
|
||||||
|
<version>${akka.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- slf4j -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>1.7.30</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- oms-common -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.kfcfans</groupId>
|
||||||
|
<artifactId>oh-my-scheduler-common</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- h2 database -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
<version>${h2.db.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- HikariCP -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.zaxxer</groupId>
|
||||||
|
<artifactId>HikariCP</artifactId>
|
||||||
|
<version>${hikaricp.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- guava -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>${guava.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 开发阶段输出日志 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<version>1.2.3</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.github.kfcfans.oms.worker;
|
||||||
|
|
||||||
|
import com.github.kfcfans.oms.worker.common.utils.SpringUtils;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端启动类
|
||||||
|
*
|
||||||
|
* @author KFCFans
|
||||||
|
* @since 2020/3/16
|
||||||
|
*/
|
||||||
|
public class OhMyWorker implements ApplicationContextAware, InitializingBean {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||||
|
SpringUtils.inject(applicationContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.actors;
|
||||||
|
|
||||||
|
import akka.actor.AbstractActor;
|
||||||
|
import com.github.kfcfans.oms.worker.pojo.request.ServerScheduleJobReq;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理来自服务器的请求
|
||||||
|
* 请求链:server -> taskTracker -> worker
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class ServerRequestActor extends AbstractActor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Receive createReceive() {
|
||||||
|
return receiveBuilder()
|
||||||
|
.match(ServerScheduleJobReq.class, this::onReceiveServerScheduleJobReq)
|
||||||
|
.matchAny(obj -> log.warn("[ServerRequestActor] receive unknown request: {}.", obj))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onReceiveServerScheduleJobReq(ServerScheduleJobReq req) {
|
||||||
|
// 接受到任务,创建 TaskTracker
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.actors;
|
||||||
|
|
||||||
|
import akka.actor.AbstractActor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理来自 TaskTracker 的请求
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
public class TaskTrackerRequestActor extends AbstractActor {
|
||||||
|
@Override
|
||||||
|
public Receive createReceive() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.common;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Worker 配置文件
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/16
|
||||||
|
*/
|
||||||
|
public class OhMyConfig {
|
||||||
|
/**
|
||||||
|
* 应用名称
|
||||||
|
*/
|
||||||
|
private String appName;
|
||||||
|
/**
|
||||||
|
* 调度服务器地址,ip:port (多值使用 , 分隔)
|
||||||
|
*/
|
||||||
|
private String serverAddress;
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.common.constants;
|
||||||
|
|
||||||
|
import com.google.common.base.Splitter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* splitter & joiner
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
public class CommonSJ {
|
||||||
|
public static final Splitter commaSplitter = Splitter.on(",");
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.common.constants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* task 常熟
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
public class TaskConstant {
|
||||||
|
|
||||||
|
public static final String ROOT_TASK_NAME = "OMS_ROOT_TASK";
|
||||||
|
|
||||||
|
public static final String ROOT_TASK_ID = "0";
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.common.constants;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务状态,task_info 表中 status 字段的枚举值
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum TaskStatus {
|
||||||
|
|
||||||
|
/* ******************* TaskTracker 专用 ******************* */
|
||||||
|
WAITING_DISPATCH(1, "等待调度器调度"),
|
||||||
|
DISPATCH_SUCCESS(2, "调度成功"),
|
||||||
|
DISPATCH_FAILED(3, "调度失败"),
|
||||||
|
WORKER_PROCESS_SUCCESS(4, "worker执行成功"),
|
||||||
|
WORKER_PROCESS_FAILED(5, "worker执行失败");
|
||||||
|
|
||||||
|
|
||||||
|
/* ******************* Worker 专用 ******************* */
|
||||||
|
|
||||||
|
private int value;
|
||||||
|
private String des;
|
||||||
|
}
|
@ -0,0 +1,460 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.common.utils;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.*;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP and Port Helper for RPC
|
||||||
|
*
|
||||||
|
* @author tjq borrowed from dubbo
|
||||||
|
* @since 2020/3/16
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public class NetUtils {
|
||||||
|
|
||||||
|
private static final String ANYHOST_VALUE = "0.0.0.0";
|
||||||
|
private static final String LOCALHOST_KEY = "localhost";
|
||||||
|
private static final String LOCALHOST_VALUE = "127.0.0.1";
|
||||||
|
|
||||||
|
// returned port range is [30000, 39999]
|
||||||
|
private static final int RND_PORT_START = 30000;
|
||||||
|
private static final int RND_PORT_RANGE = 10000;
|
||||||
|
|
||||||
|
// valid port range is (0, 65535]
|
||||||
|
private static final int MIN_PORT = 0;
|
||||||
|
private static final int MAX_PORT = 65535;
|
||||||
|
|
||||||
|
private static final Pattern ADDRESS_PATTERN = Pattern.compile("^\\d{1,3}(\\.\\d{1,3}){3}\\:\\d{1,5}$");
|
||||||
|
private static final Pattern LOCAL_IP_PATTERN = Pattern.compile("127(\\.\\d{1,3}){3}$");
|
||||||
|
private static final Pattern IP_PATTERN = Pattern.compile("\\d{1,3}(\\.\\d{1,3}){3,5}$");
|
||||||
|
|
||||||
|
private static volatile InetAddress LOCAL_ADDRESS = null;
|
||||||
|
|
||||||
|
private static final String SPLIT_IPV4_CHARECTER = "\\.";
|
||||||
|
private static final String SPLIT_IPV6_CHARECTER = ":";
|
||||||
|
|
||||||
|
public static int getRandomPort() {
|
||||||
|
return RND_PORT_START + ThreadLocalRandom.current().nextInt(RND_PORT_RANGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getAvailablePort() {
|
||||||
|
try (ServerSocket ss = new ServerSocket()) {
|
||||||
|
ss.bind(null);
|
||||||
|
return ss.getLocalPort();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return getRandomPort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getAvailablePort(int port) {
|
||||||
|
if (port <= 0) {
|
||||||
|
return getAvailablePort();
|
||||||
|
}
|
||||||
|
for (int i = port; i < MAX_PORT; i++) {
|
||||||
|
try (ServerSocket ss = new ServerSocket(i)) {
|
||||||
|
return i;
|
||||||
|
} catch (IOException e) {
|
||||||
|
// continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isInvalidPort(int port) {
|
||||||
|
return port <= MIN_PORT || port > MAX_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isValidAddress(String address) {
|
||||||
|
return ADDRESS_PATTERN.matcher(address).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isLocalHost(String host) {
|
||||||
|
return host != null
|
||||||
|
&& (LOCAL_IP_PATTERN.matcher(host).matches()
|
||||||
|
|| host.equalsIgnoreCase(LOCALHOST_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isAnyHost(String host) {
|
||||||
|
return ANYHOST_VALUE.equals(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isInvalidLocalHost(String host) {
|
||||||
|
return host == null
|
||||||
|
|| host.length() == 0
|
||||||
|
|| host.equalsIgnoreCase(LOCALHOST_KEY)
|
||||||
|
|| host.equals(ANYHOST_VALUE)
|
||||||
|
|| (LOCAL_IP_PATTERN.matcher(host).matches());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isValidLocalHost(String host) {
|
||||||
|
return !isInvalidLocalHost(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InetSocketAddress getLocalSocketAddress(String host, int port) {
|
||||||
|
return isInvalidLocalHost(host) ?
|
||||||
|
new InetSocketAddress(port) : new InetSocketAddress(host, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isValidV4Address(InetAddress address) {
|
||||||
|
if (address == null || address.isLoopbackAddress()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String name = address.getHostAddress();
|
||||||
|
boolean result = (name != null
|
||||||
|
&& IP_PATTERN.matcher(name).matches()
|
||||||
|
&& !ANYHOST_VALUE.equals(name)
|
||||||
|
&& !LOCALHOST_VALUE.equals(name));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an ipv6 address
|
||||||
|
*
|
||||||
|
* @return true if it is reachable
|
||||||
|
*/
|
||||||
|
static boolean isPreferIPV6Address() {
|
||||||
|
boolean preferIpv6 = Boolean.getBoolean("java.net.preferIPv6Addresses");
|
||||||
|
if (!preferIpv6) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* normalize the ipv6 Address, convert scope name to scope id.
|
||||||
|
* e.g.
|
||||||
|
* convert
|
||||||
|
* fe80:0:0:0:894:aeec:f37d:23e1%en0
|
||||||
|
* to
|
||||||
|
* fe80:0:0:0:894:aeec:f37d:23e1%5
|
||||||
|
* <p>
|
||||||
|
* The %5 after ipv6 address is called scope id.
|
||||||
|
* see java doc of {@link Inet6Address} for more details.
|
||||||
|
*
|
||||||
|
* @param address the input address
|
||||||
|
* @return the normalized address, with scope id converted to int
|
||||||
|
*/
|
||||||
|
static InetAddress normalizeV6Address(Inet6Address address) {
|
||||||
|
String addr = address.getHostAddress();
|
||||||
|
int i = addr.lastIndexOf('%');
|
||||||
|
if (i > 0) {
|
||||||
|
try {
|
||||||
|
return InetAddress.getByName(addr.substring(0, i) + '%' + address.getScopeId());
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
// ignore
|
||||||
|
log.debug("Unknown IPV6 address: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本机 IP 地址
|
||||||
|
*/
|
||||||
|
public static String getLocalHost() {
|
||||||
|
InetAddress address = getLocalAddress();
|
||||||
|
return address == null ? LOCALHOST_VALUE : address.getHostAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find first valid IP from local network card
|
||||||
|
*
|
||||||
|
* @return first valid local IP
|
||||||
|
*/
|
||||||
|
public static InetAddress getLocalAddress() {
|
||||||
|
if (LOCAL_ADDRESS != null) {
|
||||||
|
return LOCAL_ADDRESS;
|
||||||
|
}
|
||||||
|
InetAddress localAddress = getLocalAddress0();
|
||||||
|
LOCAL_ADDRESS = localAddress;
|
||||||
|
return localAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Optional<InetAddress> toValidAddress(InetAddress address) {
|
||||||
|
if (address instanceof Inet6Address) {
|
||||||
|
Inet6Address v6Address = (Inet6Address) address;
|
||||||
|
if (isPreferIPV6Address()) {
|
||||||
|
return Optional.ofNullable(normalizeV6Address(v6Address));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isValidV4Address(address)) {
|
||||||
|
return Optional.of(address);
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static InetAddress getLocalAddress0() {
|
||||||
|
InetAddress localAddress = null;
|
||||||
|
try {
|
||||||
|
localAddress = InetAddress.getLocalHost();
|
||||||
|
Optional<InetAddress> addressOp = toValidAddress(localAddress);
|
||||||
|
if (addressOp.isPresent()) {
|
||||||
|
return addressOp.get();
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
log.warn("[Triple]", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
|
||||||
|
if (null == interfaces) {
|
||||||
|
return localAddress;
|
||||||
|
}
|
||||||
|
while (interfaces.hasMoreElements()) {
|
||||||
|
try {
|
||||||
|
NetworkInterface network = interfaces.nextElement();
|
||||||
|
if (network.isLoopback() || network.isVirtual() || !network.isUp()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Enumeration<InetAddress> addresses = network.getInetAddresses();
|
||||||
|
while (addresses.hasMoreElements()) {
|
||||||
|
try {
|
||||||
|
Optional<InetAddress> addressOp = toValidAddress(addresses.nextElement());
|
||||||
|
if (addressOp.isPresent()) {
|
||||||
|
try {
|
||||||
|
if(addressOp.get().isReachable(100)){
|
||||||
|
return addressOp.get();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
log.warn("[Triple]", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
log.warn("[Triple]", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
log.warn("[Triple]", e);
|
||||||
|
}
|
||||||
|
return localAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getHostName(String address) {
|
||||||
|
try {
|
||||||
|
int i = address.indexOf(':');
|
||||||
|
if (i > -1) {
|
||||||
|
address = address.substring(0, i);
|
||||||
|
}
|
||||||
|
InetAddress inetAddress = InetAddress.getByName(address);
|
||||||
|
if (inetAddress != null) {
|
||||||
|
return inetAddress.getHostName();
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param hostName
|
||||||
|
* @return ip address or hostName if UnknownHostException
|
||||||
|
*/
|
||||||
|
public static String getIpByHost(String hostName) {
|
||||||
|
try {
|
||||||
|
return InetAddress.getByName(hostName).getHostAddress();
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
return hostName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toAddressString(InetSocketAddress address) {
|
||||||
|
return address.getAddress().getHostAddress() + ":" + address.getPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InetSocketAddress toAddress(String address) {
|
||||||
|
int i = address.indexOf(':');
|
||||||
|
String host;
|
||||||
|
int port;
|
||||||
|
if (i > -1) {
|
||||||
|
host = address.substring(0, i);
|
||||||
|
port = Integer.parseInt(address.substring(i + 1));
|
||||||
|
} else {
|
||||||
|
host = address;
|
||||||
|
port = 0;
|
||||||
|
}
|
||||||
|
return new InetSocketAddress(host, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toURL(String protocol, String host, int port, String path) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(protocol).append("://");
|
||||||
|
sb.append(host).append(':').append(port);
|
||||||
|
if (path.charAt(0) != '/') {
|
||||||
|
sb.append('/');
|
||||||
|
}
|
||||||
|
sb.append(path);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void joinMulticastGroup(MulticastSocket multicastSocket, InetAddress multicastAddress) throws IOException {
|
||||||
|
setInterface(multicastSocket, multicastAddress instanceof Inet6Address);
|
||||||
|
multicastSocket.setLoopbackMode(false);
|
||||||
|
multicastSocket.joinGroup(multicastAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setInterface(MulticastSocket multicastSocket, boolean preferIpv6) throws IOException {
|
||||||
|
boolean interfaceSet = false;
|
||||||
|
Enumeration interfaces = NetworkInterface.getNetworkInterfaces();
|
||||||
|
while (interfaces.hasMoreElements()) {
|
||||||
|
NetworkInterface i = (NetworkInterface) interfaces.nextElement();
|
||||||
|
Enumeration addresses = i.getInetAddresses();
|
||||||
|
while (addresses.hasMoreElements()) {
|
||||||
|
InetAddress address = (InetAddress) addresses.nextElement();
|
||||||
|
if (preferIpv6 && address instanceof Inet6Address) {
|
||||||
|
try {
|
||||||
|
if(address.isReachable(100)){
|
||||||
|
multicastSocket.setInterface(address);
|
||||||
|
interfaceSet = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
} else if (!preferIpv6 && address instanceof Inet4Address) {
|
||||||
|
try {
|
||||||
|
if(address.isReachable(100)){
|
||||||
|
multicastSocket.setInterface(address);
|
||||||
|
interfaceSet = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (interfaceSet) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param pattern
|
||||||
|
* @param host
|
||||||
|
* @param port
|
||||||
|
* @return
|
||||||
|
* @throws UnknownHostException
|
||||||
|
*/
|
||||||
|
public static boolean matchIpRange(String pattern, String host, int port) throws UnknownHostException {
|
||||||
|
if (pattern == null || host == null) {
|
||||||
|
throw new IllegalArgumentException("Illegal Argument pattern or hostName. Pattern:" + pattern + ", Host:" + host);
|
||||||
|
}
|
||||||
|
pattern = pattern.trim();
|
||||||
|
if ("*.*.*.*".equals(pattern) || "*".equals(pattern)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
InetAddress inetAddress = InetAddress.getByName(host);
|
||||||
|
boolean isIpv4 = isValidV4Address(inetAddress) ? true : false;
|
||||||
|
String[] hostAndPort = getPatternHostAndPort(pattern, isIpv4);
|
||||||
|
if (hostAndPort[1] != null && !hostAndPort[1].equals(String.valueOf(port))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pattern = hostAndPort[0];
|
||||||
|
|
||||||
|
String splitCharacter = SPLIT_IPV4_CHARECTER;
|
||||||
|
if (!isIpv4) {
|
||||||
|
splitCharacter = SPLIT_IPV6_CHARECTER;
|
||||||
|
}
|
||||||
|
String[] mask = pattern.split(splitCharacter);
|
||||||
|
//check format of pattern
|
||||||
|
checkHostPattern(pattern, mask, isIpv4);
|
||||||
|
|
||||||
|
host = inetAddress.getHostAddress();
|
||||||
|
|
||||||
|
String[] ipAddress = host.split(splitCharacter);
|
||||||
|
if (pattern.equals(host)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// short name condition
|
||||||
|
if (!ipPatternContainExpression(pattern)) {
|
||||||
|
InetAddress patternAddress = InetAddress.getByName(pattern);
|
||||||
|
if (patternAddress.getHostAddress().equals(host)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < mask.length; i++) {
|
||||||
|
if ("*".equals(mask[i]) || mask[i].equals(ipAddress[i])) {
|
||||||
|
continue;
|
||||||
|
} else if (mask[i].contains("-")) {
|
||||||
|
String[] rangeNumStrs = mask[i].split("-");
|
||||||
|
if (rangeNumStrs.length != 2) {
|
||||||
|
throw new IllegalArgumentException("There is wrong format of ip Address: " + mask[i]);
|
||||||
|
}
|
||||||
|
Integer min = getNumOfIpSegment(rangeNumStrs[0], isIpv4);
|
||||||
|
Integer max = getNumOfIpSegment(rangeNumStrs[1], isIpv4);
|
||||||
|
Integer ip = getNumOfIpSegment(ipAddress[i], isIpv4);
|
||||||
|
if (ip < min || ip > max) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if ("0".equals(ipAddress[i]) && ("0".equals(mask[i]) || "00".equals(mask[i]) || "000".equals(mask[i]) || "0000".equals(mask[i]))) {
|
||||||
|
continue;
|
||||||
|
} else if (!mask[i].equals(ipAddress[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean ipPatternContainExpression(String pattern) {
|
||||||
|
return pattern.contains("*") || pattern.contains("-");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkHostPattern(String pattern, String[] mask, boolean isIpv4) {
|
||||||
|
if (!isIpv4) {
|
||||||
|
if (mask.length != 8 && ipPatternContainExpression(pattern)) {
|
||||||
|
throw new IllegalArgumentException("If you config ip expression that contains '*' or '-', please fill qulified ip pattern like 234e:0:4567:0:0:0:3d:*. ");
|
||||||
|
}
|
||||||
|
if (mask.length != 8 && !pattern.contains("::")) {
|
||||||
|
throw new IllegalArgumentException("The host is ipv6, but the pattern is not ipv6 pattern : " + pattern);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mask.length != 4) {
|
||||||
|
throw new IllegalArgumentException("The host is ipv4, but the pattern is not ipv4 pattern : " + pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] getPatternHostAndPort(String pattern, boolean isIpv4) {
|
||||||
|
String[] result = new String[2];
|
||||||
|
if (pattern.startsWith("[") && pattern.contains("]:")) {
|
||||||
|
int end = pattern.indexOf("]:");
|
||||||
|
result[0] = pattern.substring(1, end);
|
||||||
|
result[1] = pattern.substring(end + 2);
|
||||||
|
return result;
|
||||||
|
} else if (pattern.startsWith("[") && pattern.endsWith("]")) {
|
||||||
|
result[0] = pattern.substring(1, pattern.length() - 1);
|
||||||
|
result[1] = null;
|
||||||
|
return result;
|
||||||
|
} else if (isIpv4 && pattern.contains(":")) {
|
||||||
|
int end = pattern.indexOf(":");
|
||||||
|
result[0] = pattern.substring(0, end);
|
||||||
|
result[1] = pattern.substring(end + 1);
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
result[0] = pattern;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Integer getNumOfIpSegment(String ipSegment, boolean isIpv4) {
|
||||||
|
if (isIpv4) {
|
||||||
|
return Integer.parseInt(ipSegment);
|
||||||
|
}
|
||||||
|
return Integer.parseInt(ipSegment, 16);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.common.utils;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring ApplicationContext 工具类
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/16
|
||||||
|
*/
|
||||||
|
public class SpringUtils {
|
||||||
|
|
||||||
|
private static boolean supportSpringBean = false;
|
||||||
|
private static ApplicationContext context;
|
||||||
|
|
||||||
|
public static void inject(ApplicationContext ctx) {
|
||||||
|
context = ctx;
|
||||||
|
supportSpringBean = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean supportSpringBean() {
|
||||||
|
return supportSpringBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getBean(Class<T> clz) {
|
||||||
|
return context.getBean(clz);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T getBean(String className) {
|
||||||
|
return (T) context.getBean(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.persistence;
|
||||||
|
|
||||||
|
import com.zaxxer.hikari.HikariConfig;
|
||||||
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库连接管理
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
public class ConnectionFactory {
|
||||||
|
|
||||||
|
private static DataSource dataSource;
|
||||||
|
|
||||||
|
public static Connection getConnection() throws SQLException {
|
||||||
|
return getDataSource().getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DataSource getDataSource() {
|
||||||
|
if (dataSource != null) {
|
||||||
|
return dataSource;
|
||||||
|
}
|
||||||
|
synchronized (ConnectionFactory.class) {
|
||||||
|
if (dataSource == null) {
|
||||||
|
HikariConfig config = new HikariConfig();
|
||||||
|
config.setDriverClassName("org.h2.Driver");
|
||||||
|
config.setJdbcUrl("jdbc:h2:file:~/.h2/oms/oms_worker_db");
|
||||||
|
config.setAutoCommit(true);
|
||||||
|
// 池中最小空闲连接数量
|
||||||
|
config.setMinimumIdle(2);
|
||||||
|
// 池中最大连接数量
|
||||||
|
config.setMaximumPoolSize(16);
|
||||||
|
dataSource = new HikariDataSource(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dataSource;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.persistence;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简单查询直接类,只支持 select * from task_info where xxx = xxx and xxx = xxx 的查询
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class SimpleTaskQuery {
|
||||||
|
|
||||||
|
private static final String PREFIX_SQL = "select * from task_info where ";
|
||||||
|
private static final String LINK = " and ";
|
||||||
|
|
||||||
|
private String taskId;
|
||||||
|
private String jobId;
|
||||||
|
private String instanceId;
|
||||||
|
private String taskName;
|
||||||
|
private String address;
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
private Integer limit;
|
||||||
|
|
||||||
|
public String getQuerySQL() {
|
||||||
|
StringBuilder sb = new StringBuilder(PREFIX_SQL);
|
||||||
|
if (!StringUtils.isEmpty(taskId)) {
|
||||||
|
sb.append("task_id = '").append(taskId).append("'").append(LINK);
|
||||||
|
}
|
||||||
|
if (!StringUtils.isEmpty(jobId)) {
|
||||||
|
sb.append("job_id = '").append(jobId).append("'").append(LINK);
|
||||||
|
}
|
||||||
|
if (!StringUtils.isEmpty(instanceId)) {
|
||||||
|
sb.append("instance_id = '").append(instanceId).append("'").append(LINK);
|
||||||
|
}
|
||||||
|
if (!StringUtils.isEmpty(address)) {
|
||||||
|
sb.append("address = '").append(address).append("'").append(LINK);
|
||||||
|
}
|
||||||
|
if (!StringUtils.isEmpty(taskName)) {
|
||||||
|
sb.append("task_name = '").append(taskName).append("'").append(LINK);
|
||||||
|
}
|
||||||
|
if (status != null) {
|
||||||
|
sb.append("status = ").append(status).append(LINK);
|
||||||
|
}
|
||||||
|
|
||||||
|
String substring = sb.substring(0, sb.length() - LINK.length());
|
||||||
|
if (limit != null) {
|
||||||
|
substring = substring + " limit " + limit;
|
||||||
|
}
|
||||||
|
return substring;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.persistence;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务持久化接口
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
public interface TaskDAO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化任务表
|
||||||
|
*/
|
||||||
|
boolean initTable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插入任务数据
|
||||||
|
*/
|
||||||
|
boolean save(TaskDO task);
|
||||||
|
boolean batchSave(Collection<TaskDO> tasks);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新任务数据,必须有主键 instanceId + taskId
|
||||||
|
*/
|
||||||
|
boolean update(TaskDO task);
|
||||||
|
|
||||||
|
TaskDO selectByKey(String instanceId, String taskId);
|
||||||
|
|
||||||
|
List<TaskDO> simpleQuery(SimpleTaskQuery query);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,170 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.persistence;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.sql.*;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务持久化实现层,表名:task_info
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class TaskDAOImpl implements TaskDAO {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean initTable() {
|
||||||
|
|
||||||
|
String delTableSQL = "drop table if exists task_info";
|
||||||
|
String createTableSQL = "create table task_info (task_id varchar(20), instance_id varchar(20), job_id varchar(20), task_name varchar(20), task_content text, address varchar(20), status int(11), result text, created_time bigint(20), last_modified_time bigint(20), unique key pkey (instance_id, task_id))";
|
||||||
|
|
||||||
|
try (Connection conn = ConnectionFactory.getConnection(); Statement stat = conn.createStatement()) {
|
||||||
|
stat.execute(delTableSQL);
|
||||||
|
stat.execute(createTableSQL);
|
||||||
|
}catch (Exception e) {
|
||||||
|
log.error("[TaskDAO] initTable failed.", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean save(TaskDO task) {
|
||||||
|
String insertSQL = "insert into task_info(task_id, instance_id, job_id, task_name, task_content, address, status, result, created_time, last_modified_time) values (?,?,?,?,?,?,?,?,?,?)";
|
||||||
|
try (Connection conn = ConnectionFactory.getConnection(); PreparedStatement ps = conn.prepareStatement(insertSQL)) {
|
||||||
|
fillInsertPreparedStatement(task, ps);
|
||||||
|
return ps.execute();
|
||||||
|
}catch (Exception e) {
|
||||||
|
log.error("[TaskDAO] insert failed.", e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean batchSave(Collection<TaskDO> tasks) {
|
||||||
|
String insertSQL = "insert into task_info(task_id, instance_id, job_id, task_name, task_content, address, status, result, created_time, last_modified_time) values (?,?,?,?,?,?,?,?,?,?)";
|
||||||
|
try (Connection conn = ConnectionFactory.getConnection(); PreparedStatement ps = conn.prepareStatement(insertSQL)) {
|
||||||
|
|
||||||
|
for (TaskDO task : tasks) {
|
||||||
|
|
||||||
|
fillInsertPreparedStatement(task, ps);
|
||||||
|
ps.addBatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
ps.executeBatch();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}catch (Exception e) {
|
||||||
|
log.error("[TaskDAO] insert failed.", e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean update(TaskDO task) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TaskDO selectByKey(String instanceId, String taskId) {
|
||||||
|
String selectSQL = "select * from task_info where instance_id = ? and task_id = ?";
|
||||||
|
ResultSet rs = null;
|
||||||
|
try (Connection conn = ConnectionFactory.getConnection(); PreparedStatement ps = conn.prepareStatement(selectSQL)) {
|
||||||
|
ps.setString(1, instanceId);
|
||||||
|
ps.setString(2, taskId);
|
||||||
|
rs = ps.executeQuery();
|
||||||
|
if (rs.next()) {
|
||||||
|
return convert(rs);
|
||||||
|
}
|
||||||
|
}catch (Exception e) {
|
||||||
|
log.error("[TaskDAO] selectByKey failed(instanceId = {}, taskId = {}).", instanceId, taskId, e);
|
||||||
|
}finally {
|
||||||
|
if (rs != null) {
|
||||||
|
try {
|
||||||
|
rs.close();
|
||||||
|
}catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<TaskDO> simpleQuery(SimpleTaskQuery query) {
|
||||||
|
|
||||||
|
ResultSet rs = null;
|
||||||
|
String sql = query.getQuerySQL();
|
||||||
|
List<TaskDO> result = Lists.newLinkedList();
|
||||||
|
try (Connection conn = ConnectionFactory.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
|
rs = ps.executeQuery();
|
||||||
|
while (rs.next()) {
|
||||||
|
result.add(convert(rs));
|
||||||
|
}
|
||||||
|
}catch (Exception e) {
|
||||||
|
log.error("[TaskDAO] simpleQuery failed(sql = {}).", sql, e);
|
||||||
|
}finally {
|
||||||
|
if (rs != null) {
|
||||||
|
try {
|
||||||
|
rs.close();
|
||||||
|
}catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TaskDO convert(ResultSet rs) throws SQLException {
|
||||||
|
TaskDO task = new TaskDO();
|
||||||
|
task.setTaskId(rs.getString("task_id"));
|
||||||
|
task.setInstanceId(rs.getString("instance_id"));
|
||||||
|
task.setJobId(rs.getString("job_id"));
|
||||||
|
task.setTaskName(rs.getString("task_name"));
|
||||||
|
task.setTaskContent(rs.getString("task_content"));
|
||||||
|
task.setAddress(rs.getString("address"));
|
||||||
|
task.setStatus(rs.getInt("status"));
|
||||||
|
task.setResult(rs.getString("result"));
|
||||||
|
task.setCreatedTime(rs.getLong("created_time"));
|
||||||
|
task.setLastModifiedTime(rs.getLong("last_modified_time"));
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fillInsertPreparedStatement(TaskDO task, PreparedStatement ps) throws SQLException {
|
||||||
|
ps.setString(1, task.getTaskId());
|
||||||
|
ps.setString(2, task.getInstanceId());
|
||||||
|
ps.setString(3, task.getJobId());
|
||||||
|
ps.setString(4, task.getTaskName());
|
||||||
|
ps.setString(5, task.getTaskContent());
|
||||||
|
ps.setString(6, task.getAddress());
|
||||||
|
ps.setInt(7, task.getStatus());
|
||||||
|
ps.setString(8, task.getResult());
|
||||||
|
ps.setLong(9, task.getCreatedTime());
|
||||||
|
ps.setLong(10, task.getLastModifiedTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
TaskDAOImpl taskDAO = new TaskDAOImpl();
|
||||||
|
taskDAO.initTable();
|
||||||
|
|
||||||
|
TaskDO taskDO = new TaskDO();
|
||||||
|
taskDO.setJobId("11");
|
||||||
|
taskDO.setInstanceId("22");
|
||||||
|
taskDO.setTaskId("2.1");
|
||||||
|
taskDO.setTaskName("zzz");
|
||||||
|
taskDO.setTaskContent("hhhh");
|
||||||
|
|
||||||
|
taskDAO.save(taskDO);
|
||||||
|
|
||||||
|
SimpleTaskQuery query = new SimpleTaskQuery();
|
||||||
|
query.setInstanceId("22");
|
||||||
|
query.setTaskId("2.1");
|
||||||
|
System.out.println(taskDAO.simpleQuery(query));
|
||||||
|
|
||||||
|
Thread.sleep(100000);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.persistence;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TaskDO(为了简化 DAO 层,一张表实现两种功能)
|
||||||
|
* 对于 TaskTracker,task_info 存储了当前 JobInstance 所有的子任务信息
|
||||||
|
* 对于普通的 Worker,task_info 存储了当前无法处理的任务信息
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class TaskDO {
|
||||||
|
|
||||||
|
// 层次命名法,可以表示 Map 后的父子关系,如 0.1.2 代表 rootTask map 的第一个 task map 的第二个 task
|
||||||
|
private String taskId;
|
||||||
|
|
||||||
|
private String jobId;
|
||||||
|
private String instanceId;
|
||||||
|
// 任务名称
|
||||||
|
private String taskName;
|
||||||
|
// 任务参数
|
||||||
|
private String taskContent;
|
||||||
|
// 对于JobTracker为workerAddress,对于普通Worker为jobTrackerAddress
|
||||||
|
private String address;
|
||||||
|
// 任务状态,0~10代表 JobTracker 使用,11~20代表普通Worker使用
|
||||||
|
private int status;
|
||||||
|
// 执行结果
|
||||||
|
private String result;
|
||||||
|
// 创建时间
|
||||||
|
private long createdTime;
|
||||||
|
// 最后修改时间
|
||||||
|
private long lastModifiedTime;
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.persistence;
|
||||||
|
|
||||||
|
|
||||||
|
import com.github.kfcfans.oms.worker.common.constants.TaskStatus;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务持久化服务
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
public class TaskPersistenceService {
|
||||||
|
|
||||||
|
private TaskDAO taskDAO = new TaskDAOImpl();
|
||||||
|
private static final int MAX_BATCH_SIZE = 50;
|
||||||
|
|
||||||
|
public boolean save(TaskDO task) {
|
||||||
|
return taskDAO.save(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean batchSave(List<TaskDO> tasks) {
|
||||||
|
if (CollectionUtils.isEmpty(tasks)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tasks.size() <= MAX_BATCH_SIZE) {
|
||||||
|
return taskDAO.batchSave(tasks);
|
||||||
|
}
|
||||||
|
List<List<TaskDO>> partition = Lists.partition(tasks, MAX_BATCH_SIZE);
|
||||||
|
for (List<TaskDO> p : partition) {
|
||||||
|
boolean b = taskDAO.batchSave(p);
|
||||||
|
if (!b) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 TaskTracker 准备派发给 Worker 执行的 task
|
||||||
|
*/
|
||||||
|
public List<TaskDO> getNeedDispatchTask(String instanceId) {
|
||||||
|
SimpleTaskQuery query = new SimpleTaskQuery();
|
||||||
|
query.setInstanceId(instanceId);
|
||||||
|
query.setStatus(TaskStatus.WAITING_DISPATCH.getValue());
|
||||||
|
query.setLimit(100);
|
||||||
|
return taskDAO.simpleQuery(query);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.pojo.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 被调度执行的任务实例详情
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/16
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class JobInstanceInfo {
|
||||||
|
|
||||||
|
private String jobId;
|
||||||
|
private String instanceId;
|
||||||
|
// 任务执行类型,单机、广播、MR
|
||||||
|
private String executeType;
|
||||||
|
// 处理器类型(JavaBean、Jar、脚本等)
|
||||||
|
private String processorType;
|
||||||
|
// 处理器信息
|
||||||
|
private String processorInfo;
|
||||||
|
// 任务执行时间限制,单位毫秒
|
||||||
|
private long timeLimit;
|
||||||
|
// 可用处理器地址,可能多值,逗号分隔
|
||||||
|
private String workerAddress;
|
||||||
|
|
||||||
|
/* *********************** Map/MapReduce 任务专用 *********************** */
|
||||||
|
|
||||||
|
// 每台机器的处理线程数上限
|
||||||
|
private int threadConcurrency;
|
||||||
|
// 子任务重试次数(任务本身的重试机制由server控制)
|
||||||
|
private int taskRetryNum;
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.pojo.request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务端调度任务请求(一次任务处理的入口)
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
public class ServerScheduleJobReq {
|
||||||
|
|
||||||
|
// 调度的服务器地址,默认通讯目标
|
||||||
|
private String serverAddress;
|
||||||
|
|
||||||
|
/* *********************** 任务相关属性 *********************** */
|
||||||
|
|
||||||
|
private String jobId;
|
||||||
|
private String instanceId;
|
||||||
|
// 任务执行类型,单机、广播、MR
|
||||||
|
private String executeType;
|
||||||
|
// 处理器类型(JavaBean、Jar、脚本等)
|
||||||
|
private String processorType;
|
||||||
|
// 处理器信息
|
||||||
|
private String processorInfo;
|
||||||
|
// 任务执行时间限制,单位毫秒
|
||||||
|
private long timeLimit;
|
||||||
|
// 可用处理器地址,可能多值,逗号分隔
|
||||||
|
private String workerAddress;
|
||||||
|
|
||||||
|
/* *********************** Map/MapReduce 任务专用 *********************** */
|
||||||
|
|
||||||
|
// 每台机器的处理线程数上限
|
||||||
|
private int threadConcurrency;
|
||||||
|
// 子任务重试次数(任务本身的重试机制由server控制)
|
||||||
|
private int taskRetryNum;
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.pojo.request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WorkerMapTaskRequest
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
public class WorkerMapTaskRequest {
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.tracker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 广播任务使用的 TaskTracker
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
public class BroadcastTaskTracker extends TaskTracker {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispatch() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.tracker;
|
||||||
|
|
||||||
|
import com.github.kfcfans.oms.worker.pojo.request.WorkerMapTaskRequest;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MapReduce 任务使用的 TaskTracker
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
public class MapReduceTaskTracker extends StandaloneTaskTracker {
|
||||||
|
|
||||||
|
public void newTask(WorkerMapTaskRequest mapRequest) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.tracker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单机任务使用的 TaskTracker
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
public class StandaloneTaskTracker extends TaskTracker {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispatch() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
package com.github.kfcfans.oms.worker.tracker;
|
||||||
|
|
||||||
|
import akka.actor.ActorRef;
|
||||||
|
import com.github.kfcfans.common.ExecuteType;
|
||||||
|
import com.github.kfcfans.oms.worker.common.constants.CommonSJ;
|
||||||
|
import com.github.kfcfans.oms.worker.common.constants.TaskConstant;
|
||||||
|
import com.github.kfcfans.oms.worker.common.utils.NetUtils;
|
||||||
|
import com.github.kfcfans.oms.worker.persistence.TaskDO;
|
||||||
|
import com.github.kfcfans.oms.worker.persistence.TaskPersistenceService;
|
||||||
|
import com.github.kfcfans.oms.worker.pojo.model.JobInstanceInfo;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 负责管理 JobInstance 的运行,主要包括任务的派发(MR可能存在大量的任务)和状态的更新
|
||||||
|
*
|
||||||
|
* @author tjq
|
||||||
|
* @since 2020/3/17
|
||||||
|
*/
|
||||||
|
public abstract class TaskTracker {
|
||||||
|
|
||||||
|
// 任务实例信息
|
||||||
|
protected JobInstanceInfo jobInstanceInfo;
|
||||||
|
protected ActorRef actor;
|
||||||
|
|
||||||
|
protected TaskPersistenceService taskPersistenceService;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分发任务
|
||||||
|
*/
|
||||||
|
public abstract void dispatch();
|
||||||
|
|
||||||
|
public void updateTaskStatus() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean finished() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 持久化根任务,只有完成持久化才能视为任务开始running(先持久化,再报告server)
|
||||||
|
*/
|
||||||
|
private void persistenceTask() {
|
||||||
|
|
||||||
|
ExecuteType executeType = ExecuteType.valueOf(jobInstanceInfo.getExecuteType());
|
||||||
|
boolean persistenceResult;
|
||||||
|
|
||||||
|
// 单机、MR模型下,根任务模型本机直接执行(JobTracker一般为负载最小的机器,且MR的根任务通常伴随着 map 操作,本机执行可以有效减少网络I/O开销)
|
||||||
|
if (executeType != ExecuteType.BROADCAST) {
|
||||||
|
TaskDO rootTask = new TaskDO();
|
||||||
|
rootTask.setStatus(1);
|
||||||
|
rootTask.setJobId(jobInstanceInfo.getJobId());
|
||||||
|
rootTask.setInstanceId(jobInstanceInfo.getInstanceId());
|
||||||
|
rootTask.setTaskId(TaskConstant.ROOT_TASK_ID);
|
||||||
|
rootTask.setAddress(NetUtils.getLocalHost());
|
||||||
|
rootTask.setTaskName(TaskConstant.ROOT_TASK_NAME);
|
||||||
|
rootTask.setCreatedTime(System.currentTimeMillis());
|
||||||
|
rootTask.setCreatedTime(System.currentTimeMillis());
|
||||||
|
|
||||||
|
persistenceResult = taskPersistenceService.save(rootTask);
|
||||||
|
}else {
|
||||||
|
List<TaskDO> taskList = Lists.newLinkedList();
|
||||||
|
List<String> addrList = CommonSJ.commaSplitter.splitToList(jobInstanceInfo.getWorkerAddress());
|
||||||
|
for (int i = 0; i < addrList.size(); i++) {
|
||||||
|
TaskDO task = new TaskDO();
|
||||||
|
task.setStatus(1);
|
||||||
|
task.setJobId(jobInstanceInfo.getJobId());
|
||||||
|
task.setInstanceId(jobInstanceInfo.getInstanceId());
|
||||||
|
task.setTaskId(String.valueOf(i));
|
||||||
|
task.setAddress(addrList.get(i));
|
||||||
|
task.setTaskName(TaskConstant.ROOT_TASK_NAME);
|
||||||
|
task.setCreatedTime(System.currentTimeMillis());
|
||||||
|
task.setCreatedTime(System.currentTimeMillis());
|
||||||
|
|
||||||
|
taskList.add(task);
|
||||||
|
}
|
||||||
|
persistenceResult = taskPersistenceService.batchSave(taskList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!persistenceResult) {
|
||||||
|
throw new RuntimeException("create root task failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动任务分发器
|
||||||
|
*/
|
||||||
|
private void initDispatcher() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
2
oh-my-scheduler.iml
Normal file
2
oh-my-scheduler.iml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4" />
|
36
pom.xml
Normal file
36
pom.xml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<groupId>com.github.kfcfans</groupId>
|
||||||
|
<artifactId>oh-my-scheduler</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<modules>
|
||||||
|
<module>oh-my-scheduler-worker</module>
|
||||||
|
<module>oh-my-scheduler-server</module>
|
||||||
|
<module>oh-my-scheduler-common</module>
|
||||||
|
</modules>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>1.8</java.version>
|
||||||
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
<lombok.version>1.18.12</lombok.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- lombok -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
Loading…
x
Reference in New Issue
Block a user