diff --git a/README_zhCN.md b/README_zhCN.md index aaf56372..3fe70d47 100644 --- a/README_zhCN.md +++ b/README_zhCN.md @@ -1,5 +1,9 @@ # [English](./README.md) | 简体中文 +

+祝大家兔年吉祥,新的一年身体健康,万事如意,阖家欢乐,幸福安康! +

+

PowerJob

@@ -34,8 +38,7 @@ PowerJob(原OhMyScheduler)是全新一代分布式调度与计算框架, PowerJob 的设计目标为企业级的分布式任务调度平台,即成为公司内部的**任务调度中间件**。整个公司统一部署调度中心 powerjob-server,旗下所有业务线应用只需要依赖 `powerjob-worker` 即可接入调度中心获取任务调度与分布式计算能力。 ### 在线试用 -* 试用地址:[try.powerjob.tech](http://try.powerjob.tech/#/welcome?appName=powerjob-agent-test&password=123) -* [建议先阅读使用教程了解 PowerJob 的概念和基本用法](https://www.yuque.com/powerjob/guidence/trial) +* [点击查看试用说明和教程](https://www.yuque.com/powerjob/guidence/trial) ### 同类产品对比 | | QuartZ | xxl-job | SchedulerX 2.0 | PowerJob | diff --git a/docker-compose.yml b/docker-compose.yml index d12faee8..cdec2914 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,14 +5,12 @@ version: '3' services: powerjob-mysql: - build: - context: ./others environment: MYSQL_ROOT_HOST: "%" MYSQL_ROOT_PASSWORD: No1Bug2Please3! restart: always container_name: powerjob-mysql - image: powerjob/powerjob-mysql:4.1.1 + image: powerjob/powerjob-mysql:latest ports: - "3307:3306" volumes: @@ -21,7 +19,7 @@ services: powerjob-server: container_name: powerjob-server - image: tjqq/powerjob-server:latest + image: powerjob/powerjob-server:latest restart: always depends_on: - powerjob-mysql @@ -36,7 +34,7 @@ services: powerjob-worker-samples: container_name: powerjob-worker-samples - image: tjqq/powerjob-worker-samples:latest + image: powerjob/powerjob-worker-samples:latest restart: always depends_on: - powerjob-mysql diff --git a/others/dev/build_test_env.sh b/others/dev/build_test_env.sh new file mode 100755 index 00000000..b14f4bb1 --- /dev/null +++ b/others/dev/build_test_env.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# 构建 PowerJob 测试环境 + +echo "================== 关闭全部服务 ==================" +docker-compose down +echo "================== 构建 jar ==================" +cd `dirname $0`/../.. || exit +# mvn clean package -Pdev -DskipTests -U -e -pl powerjob-server,powerjob-worker-agent -am +# -U:强制检查snapshot库 -pl:指定需要构建的模块,多模块逗号分割 -am:同时构建依赖模块,一般与pl连用 -Pxxx:指定使用的配置文件 +mvn clean package -Pdev -DskipTests +echo "================== 拷贝 jar ==================" +/bin/cp -rf powerjob-server/powerjob-server-starter/target/*.jar powerjob-server/docker/powerjob-server.jar +/bin/cp -rf powerjob-worker-agent/target/*.jar powerjob-worker-agent/powerjob-agent.jar +ls -l powerjob-server/docker/powerjob-server.jar +ls -l powerjob-worker-agent/powerjob-agent.jar + +cd others/dev +docker-compose build +docker-compose up \ No newline at end of file diff --git a/others/dev/docker-compose.yml b/others/dev/docker-compose.yml new file mode 100644 index 00000000..2a6d55e3 --- /dev/null +++ b/others/dev/docker-compose.yml @@ -0,0 +1,75 @@ +# 构建 PowerJob 测试环境 + +version: '3' +services: + powerjob-mysql: + build: + context: ../ + environment: + MYSQL_ROOT_HOST: "%" + MYSQL_ROOT_PASSWORD: No1Bug2Please3! + restart: always + container_name: powerjob-mysql + image: powerjob/powerjob-mysql:test_env + ports: + - "3309:3306" + volumes: + - ~/powerjob-data/powerjob-mysql:/var/lib/mysql + command: --lower_case_table_names=1 + + powerjob-server: + build: + context: ../../powerjob-server/docker + container_name: powerjob-server + image: powerjob/powerjob-server:test_env + restart: always + depends_on: + - powerjob-mysql + environment: + PARAMS: "--spring.profiles.active=product --oms.mongodb.enable=false --spring.datasource.core.jdbc-url=jdbc:mysql://powerjob-mysql:3306/powerjob-daily?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai" + JVMOPTIONS: "-Xmx1024m" + ports: + - "7700:7700" + - "10086:10086" + - "10010:10010" + volumes: + - ~/powerjob-data/powerjob-server:/root/powerjob/server/ + + powerjob-worker-agent: + build: + context: ../../powerjob-worker-agent + container_name: powerjob-worker-agent + image: powerjob/powerjob-worker-agent:test_env + restart: always + depends_on: + - powerjob-mysql + - powerjob-server + ports: + - "5002:5005" + - "10002:10000" + - "27777:27777" + volumes: + - ~/powerjob-data/powerjob-worker-agent:/root + entrypoint: + - "sh" + - "-c" + - "./wait-for-it.sh powerjob-server:7700 --strict -- java -Xmx768m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=10000 -Dcom.sun.management.jmxremote.rmi.port=10000 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar /powerjob-agent.jar --app powerjob-worker-samples --server powerjob-server:7700" + + powerjob-worker-agent2: + container_name: powerjob-worker-agent2 + image: powerjob/powerjob-worker-agent:test_env + restart: always + depends_on: + - powerjob-mysql + - powerjob-server + ports: + - "5003:5005" + - "10003:10000" + - "27778:27777" + volumes: + - ~/powerjob-data/powerjob-worker-agent2:/root + entrypoint: + - "sh" + - "-c" + - "./wait-for-it.sh powerjob-server:7700 --strict -- java -Xmx768m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=10000 -Dcom.sun.management.jmxremote.rmi.port=10000 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar /powerjob-agent.jar --app powerjob-worker-samples --server powerjob-server:7700" + diff --git a/others/script/build_docker.sh b/others/dev/publish_docker.sh similarity index 74% rename from others/script/build_docker.sh rename to others/dev/publish_docker.sh index 4149668e..ee295e3d 100755 --- a/others/script/build_docker.sh +++ b/others/dev/publish_docker.sh @@ -33,11 +33,19 @@ read -r -p "是否重新构建镜像(y/n):" rebuild if [ "$rebuild" = "y" ] || [ "$rebuild" = "Y" ]; then echo "================== 删除旧镜像 ==================" docker rmi -f tjqq/powerjob-server:$version + docker rmi -f powerjob/powerjob-server:$version docker rmi -f tjqq/powerjob-agent:$version + docker rmi -f powerjob/powerjob-agent:$version + docker rmi -f powerjob/powerjob-mysql:$version + docker rmi -f powerjob/powerjob-worker-samples:$version echo "================== 构建 powerjob-server 镜像 ==================" docker build -t tjqq/powerjob-server:$version powerjob-server/docker/. || exit echo "================== 构建 powerjob-agent 镜像 ==================" docker build -t tjqq/powerjob-agent:$version powerjob-worker-agent/. || exit + echo "================== 构建 powerjob-mysql 镜像 ==================" + docker build -t powerjob/powerjob-mysql:$version others/. || exit + echo "================== 构建 powerjob-worker-samples 镜像 ==================" + docker build -t powerjob/powerjob-worker-samples:$version powerjob-worker-samples/. || exit read -r -p "是否正式发布该镜像(y/n):" needrelease if [ "$needrelease" = "y" ] || [ "$needrelease" = "Y" ]; then @@ -47,6 +55,25 @@ if [ "$rebuild" = "y" ] || [ "$rebuild" = "Y" ]; then docker push tjqq/powerjob-server:$version echo "================== 正在推送 agent 镜像到中央仓库 ==================" docker push tjqq/powerjob-agent:$version + echo "================== 正在推送 powerjob-mysql 镜像到中央仓库 ==================" + docker push powerjob/powerjob-mysql:$version + echo "================== 正在推送 samples 镜像到中央仓库 ==================" + docker push powerjob/powerjob-worker-samples:$version + echo "================== 双写推送 ==================" + docker tag tjqq/powerjob-server:$version powerjob/powerjob-server:$version + docker push powerjob/powerjob-server:$version + docker tag tjqq/powerjob-agent:$version powerjob/powerjob-agent:$version + docker push powerjob/powerjob-agent:$version + echo "================== 更新 LATEST 版本 ==================" + docker tag powerjob/powerjob-server:$version powerjob/powerjob-server:latest + docker push powerjob/powerjob-server:latest + docker tag powerjob/powerjob-agent:$version powerjob/powerjob-agent:latest + docker push powerjob/powerjob-agent:latest + docker tag powerjob/powerjob-mysql:$version powerjob/powerjob-mysql:latest + docker push powerjob/powerjob-mysql:latest + docker tag powerjob/powerjob-worker-samples:$version powerjob/powerjob-worker-samples:latest + docker push powerjob/powerjob-worker-samples:latest + echo "================== Docker 推送完毕 ==================" fi fi fi diff --git a/pom.xml b/pom.xml index 8514171c..8381f0a0 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ tech.powerjob powerjob - 3.0.0 + 4.0.0 pom powerjob http://www.powerjob.tech @@ -44,6 +44,7 @@ powerjob-worker-spring-boot-starter powerjob-worker-samples powerjob-official-processors + powerjob-remote diff --git a/powerjob-client/pom.xml b/powerjob-client/pom.xml index 94a23dc2..ab5656df 100644 --- a/powerjob-client/pom.xml +++ b/powerjob-client/pom.xml @@ -5,18 +5,18 @@ powerjob tech.powerjob - 3.0.0 + 4.0.0 4.0.0 powerjob-client - 4.2.1 + 4.3.0 jar 5.9.1 1.2.83 - 4.2.1 + 4.3.0 3.2.4 diff --git a/powerjob-common/pom.xml b/powerjob-common/pom.xml index 06b86c42..a0966360 100644 --- a/powerjob-common/pom.xml +++ b/powerjob-common/pom.xml @@ -5,12 +5,12 @@ powerjob tech.powerjob - 3.0.0 + 4.0.0 4.0.0 powerjob-common - 4.2.1 + 4.3.0 jar @@ -19,7 +19,6 @@ 2.11.0 31.1-jre 3.14.9 - 2.6.12 5.3.0 2.14.0-rc1 5.9.0 @@ -54,18 +53,6 @@ ${okhttp.version} - - - com.typesafe.akka - akka-remote_2.13 - ${akka.version} - - - com.typesafe.akka - akka-slf4j_2.13 - ${akka.version} - - commons-io diff --git a/powerjob-common/src/main/java/tech/powerjob/common/OmsConstant.java b/powerjob-common/src/main/java/tech/powerjob/common/OmsConstant.java index 129ac429..f589e869 100644 --- a/powerjob-common/src/main/java/tech/powerjob/common/OmsConstant.java +++ b/powerjob-common/src/main/java/tech/powerjob/common/OmsConstant.java @@ -8,6 +8,11 @@ package tech.powerjob.common; */ public class OmsConstant { + /** + * package name + */ + public static final String PACKAGE = "tech.powerjob"; + public static final int SERVER_DEFAULT_AKKA_PORT = 10086; public static final int SERVER_DEFAULT_HTTP_PORT = 10010; @@ -17,6 +22,10 @@ public class OmsConstant { public static final String NONE = "N/A"; public static final String COMMA = ","; + + public static final String AND = "&"; + + public static final String EQUAL = "="; public static final String LINE_SEPARATOR = "\r\n"; public static final String HTTP_HEADER_CONTENT_TYPE = "Content-Type"; diff --git a/powerjob-common/src/main/java/tech/powerjob/common/PowerJobDKey.java b/powerjob-common/src/main/java/tech/powerjob/common/PowerJobDKey.java index 5e1cf0da..aa27ea48 100644 --- a/powerjob-common/src/main/java/tech/powerjob/common/PowerJobDKey.java +++ b/powerjob-common/src/main/java/tech/powerjob/common/PowerJobDKey.java @@ -23,6 +23,17 @@ public class PowerJobDKey { */ public static final String IGNORED_NETWORK_INTERFACE_REGEX = "powerjob.network.interface.ignored"; + /** + * Enables compression during data transfer, such as gzip under the HTTP protocol. default value is 'false' + * Note that enabling compression reduces network usage, but increases CPU consumption + */ + public static final String TRANSPORTER_USE_COMPRESSING = "powerjob.transporter.compression.enabled"; + + /** + * keep-alive connection timeout(in seconds), value <= 0 means disable keepalive. default value is 75 + */ + public static final String TRANSPORTER_KEEP_ALIVE_TIMEOUT = "powerjob.transporter.keepalive.timeout"; + public static final String WORKER_STATUS_CHECK_PERIOD = "powerjob.worker.status-check.normal.period"; /** @@ -30,6 +41,8 @@ public class PowerJobDKey { * It's VERY dangerous */ public static final String WORKER_ALLOWED_FORCE_STOP_THREAD = "powerjob.worker.allowed-force-stop-thread"; + + public static final String WORKER_WORK_SPACE = "powerjob.worker.workspace"; /** * ms */ diff --git a/powerjob-common/src/main/java/tech/powerjob/common/PowerSerializable.java b/powerjob-common/src/main/java/tech/powerjob/common/PowerSerializable.java index 48a59ab5..e8dd990f 100644 --- a/powerjob-common/src/main/java/tech/powerjob/common/PowerSerializable.java +++ b/powerjob-common/src/main/java/tech/powerjob/common/PowerSerializable.java @@ -9,12 +9,4 @@ import java.io.Serializable; * @since 2020/4/16 */ public interface PowerSerializable extends Serializable { - - /** - * request path for http or other protocol, like '/worker/stopInstance' - * @return null for non-http request object or no-null path for http request needed object - */ - default String path() { - return null; - } } diff --git a/powerjob-common/src/main/java/tech/powerjob/common/ProtocolConstant.java b/powerjob-common/src/main/java/tech/powerjob/common/ProtocolConstant.java deleted file mode 100644 index 9136ce35..00000000 --- a/powerjob-common/src/main/java/tech/powerjob/common/ProtocolConstant.java +++ /dev/null @@ -1,18 +0,0 @@ -package tech.powerjob.common; - -/** - * HttpProtocolConstant - * - * @author tjq - * @since 2021/2/8 - */ -public class ProtocolConstant { - - public static final String SERVER_PATH_HEARTBEAT = "/server/heartbeat"; - public static final String SERVER_PATH_STATUS_REPORT = "/server/statusReport"; - public static final String SERVER_PATH_LOG_REPORT = "/server/logReport"; - - public static final String WORKER_PATH_DISPATCH_JOB = "/worker/runJob"; - public static final String WORKER_PATH_STOP_INSTANCE = "/worker/stopInstance"; - public static final String WORKER_PATH_QUERY_INSTANCE_INFO = "/worker/queryInstanceInfo"; -} diff --git a/powerjob-common/src/main/java/tech/powerjob/common/RemoteConstant.java b/powerjob-common/src/main/java/tech/powerjob/common/RemoteConstant.java index dbc6c8a4..0ee242df 100644 --- a/powerjob-common/src/main/java/tech/powerjob/common/RemoteConstant.java +++ b/powerjob-common/src/main/java/tech/powerjob/common/RemoteConstant.java @@ -12,25 +12,93 @@ public class RemoteConstant { /* ************************ AKKA WORKER ************************ */ public static final int DEFAULT_WORKER_PORT = 27777; - public static final String WORKER_ACTOR_SYSTEM_NAME = "oms"; - - public static final String TASK_TRACKER_ACTOR_NAME = "task_tracker"; - public static final String PROCESSOR_TRACKER_ACTOR_NAME = "processor_tracker"; - public static final String WORKER_ACTOR_NAME = "worker"; - public static final String TROUBLESHOOTING_ACTOR_NAME = "troubleshooting"; - - public static final String WORKER_AKKA_CONFIG_NAME = "oms-worker.akka.conf"; - - - /* ************************ AKKA SERVER ************************ */ - public static final String SERVER_ACTOR_SYSTEM_NAME = "oms-server"; - - public static final String SERVER_ACTOR_NAME = "server_actor"; - public static final String SERVER_FRIEND_ACTOR_NAME = "friend_actor"; - public static final String SERVER_AKKA_CONFIG_NAME = "oms-server.akka.conf"; - /* ************************ OTHERS ************************ */ public static final String EMPTY_ADDRESS = "N/A"; public static final long DEFAULT_TIMEOUT_MS = 5000; + + /* ************************ SERVER-self_side (s4s == server for server side) ************************ */ + public static final String S4S_PATH = "friend"; + + /** + * server 集群间的心跳处理 + */ + public static final String S4S_HANDLER_PING = "ping"; + /** + * 处理其他 server 的执行请求 + */ + public static final String S4S_HANDLER_PROCESS = "process"; + + /* ************************ SERVER-worker_side(s4w == server for worker side) ************************ */ + public static final String S4W_PATH = "server"; + /** + * server 处理在线日志 + */ + public static final String S4W_HANDLER_REPORT_LOG = "reportLog"; + /** + * server 处理 worker 心跳 + */ + public static final String S4W_HANDLER_WORKER_HEARTBEAT = "workerHeartbeat"; + + /** + * server 处理 TaskTracker 上报的任务实例状态 + */ + public static final String S4W_HANDLER_REPORT_INSTANCE_STATUS = "reportInstanceStatus"; + + /** + * server 查询任务的可执行集群 + */ + public static final String S4W_HANDLER_QUERY_JOB_CLUSTER = "queryJobCluster"; + + /** + * server 处理 worker 请求部署容器命令 + */ + public static final String S4W_HANDLER_WORKER_NEED_DEPLOY_CONTAINER = "queryContainer"; + + /* ************************ Worker-TaskTracker ************************ */ + public static final String WTT_PATH = "taskTracker"; + + /** + * server 任务执行命令 + */ + public static final String WTT_HANDLER_RUN_JOB = "runJob"; + /** + * server 停止任务实例命令 + */ + public static final String WTT_HANDLER_STOP_INSTANCE = "stopInstance"; + + /** + * sever 查询任务状态 + */ + public static final String WTT_HANDLER_QUERY_INSTANCE_STATUS = "queryInstanceStatus"; + + /** + * PT 上报任务状态,包含执行结果 + */ + public static final String WTT_HANDLER_REPORT_TASK_STATUS = "reportTaskStatus"; + /** + * PT 上报自身状态 + */ + public static final String WTT_HANDLER_REPORT_PROCESSOR_TRACKER_STATUS = "reportProcessorTrackerStatus"; + + /** + * Map 任务 + */ + public static final String WTT_HANDLER_MAP_TASK = "mapTask"; + + /* ************************ Worker-ProcessorTracker ************************ */ + public static final String WPT_PATH = "processorTracker"; + + public static final String WPT_HANDLER_START_TASK = "startTask"; + + public static final String WPT_HANDLER_STOP_INSTANCE = "stopInstance"; + + /* ************************ Worker-NORMAL ************************ */ + + public static final String WORKER_PATH = "worker"; + + public static final String WORKER_HANDLER_DEPLOY_CONTAINER = "deployContainer"; + + public static final String WORKER_HANDLER_DESTROY_CONTAINER = "destroyContainer"; + } diff --git a/powerjob-common/src/main/java/tech/powerjob/common/request/ServerDiscoveryRequest.java b/powerjob-common/src/main/java/tech/powerjob/common/request/ServerDiscoveryRequest.java new file mode 100644 index 00000000..34a7d6bd --- /dev/null +++ b/powerjob-common/src/main/java/tech/powerjob/common/request/ServerDiscoveryRequest.java @@ -0,0 +1,59 @@ +package tech.powerjob.common.request; + +import lombok.Setter; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.StringUtils; +import tech.powerjob.common.enums.Protocol; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * 服务发现请求 + * + * @author tjq + * @since 2023/1/21 + */ +@Setter +@Accessors(chain = true) +public class ServerDiscoveryRequest implements Serializable { + + private Long appId; + + private String protocol; + + private String currentServer; + + private String clientVersion; + + public Map toMap() { + Map ret = new HashMap<>(); + ret.put("appId", appId); + ret.put("protocol", protocol); + if (StringUtils.isNotEmpty(currentServer)) { + ret.put("currentServer", currentServer); + } + if (StringUtils.isNotEmpty(clientVersion)) { + ret.put("clientVersion", clientVersion); + } + return ret; + } + + public Long getAppId() { + return appId; + } + + public String getProtocol() { + return Optional.ofNullable(protocol).orElse(Protocol.AKKA.name()); + } + + public String getCurrentServer() { + return currentServer; + } + + public String getClientVersion() { + return clientVersion; + } +} diff --git a/powerjob-common/src/main/java/tech/powerjob/common/request/ServerQueryInstanceStatusReq.java b/powerjob-common/src/main/java/tech/powerjob/common/request/ServerQueryInstanceStatusReq.java index 6cf1a985..a37dfbf2 100644 --- a/powerjob-common/src/main/java/tech/powerjob/common/request/ServerQueryInstanceStatusReq.java +++ b/powerjob-common/src/main/java/tech/powerjob/common/request/ServerQueryInstanceStatusReq.java @@ -1,10 +1,9 @@ package tech.powerjob.common.request; -import tech.powerjob.common.PowerSerializable; -import tech.powerjob.common.ProtocolConstant; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import tech.powerjob.common.PowerSerializable; /** * 服务器查询实例运行状态,需要返回详细的运行数据 @@ -18,8 +17,4 @@ import lombok.NoArgsConstructor; public class ServerQueryInstanceStatusReq implements PowerSerializable { private Long instanceId; - @Override - public String path() { - return ProtocolConstant.WORKER_PATH_QUERY_INSTANCE_INFO; - } } diff --git a/powerjob-common/src/main/java/tech/powerjob/common/request/ServerScheduleJobReq.java b/powerjob-common/src/main/java/tech/powerjob/common/request/ServerScheduleJobReq.java index 364aba55..ac8df503 100644 --- a/powerjob-common/src/main/java/tech/powerjob/common/request/ServerScheduleJobReq.java +++ b/powerjob-common/src/main/java/tech/powerjob/common/request/ServerScheduleJobReq.java @@ -1,8 +1,7 @@ package tech.powerjob.common.request; -import tech.powerjob.common.PowerSerializable; -import tech.powerjob.common.ProtocolConstant; import lombok.Data; +import tech.powerjob.common.PowerSerializable; import java.util.List; @@ -98,9 +97,4 @@ public class ServerScheduleJobReq implements PowerSerializable { * 日志配置 */ private String logConfig; - - @Override - public String path() { - return ProtocolConstant.WORKER_PATH_DISPATCH_JOB; - } } diff --git a/powerjob-common/src/main/java/tech/powerjob/common/request/ServerStopInstanceReq.java b/powerjob-common/src/main/java/tech/powerjob/common/request/ServerStopInstanceReq.java index 000eb993..765e6f07 100644 --- a/powerjob-common/src/main/java/tech/powerjob/common/request/ServerStopInstanceReq.java +++ b/powerjob-common/src/main/java/tech/powerjob/common/request/ServerStopInstanceReq.java @@ -1,10 +1,9 @@ package tech.powerjob.common.request; -import tech.powerjob.common.PowerSerializable; -import tech.powerjob.common.ProtocolConstant; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import tech.powerjob.common.PowerSerializable; /** @@ -18,9 +17,4 @@ import lombok.NoArgsConstructor; @AllArgsConstructor public class ServerStopInstanceReq implements PowerSerializable { private Long instanceId; - - @Override - public String path() { - return ProtocolConstant.WORKER_PATH_STOP_INSTANCE; - } } diff --git a/powerjob-common/src/main/java/tech/powerjob/common/serialize/JsonUtils.java b/powerjob-common/src/main/java/tech/powerjob/common/serialize/JsonUtils.java index 6809fa4c..77ca831c 100644 --- a/powerjob-common/src/main/java/tech/powerjob/common/serialize/JsonUtils.java +++ b/powerjob-common/src/main/java/tech/powerjob/common/serialize/JsonUtils.java @@ -3,10 +3,11 @@ package tech.powerjob.common.serialize; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; import lombok.extern.slf4j.Slf4j; -import tech.powerjob.common.exception.PowerJobException; import org.apache.commons.lang3.exception.ExceptionUtils; +import tech.powerjob.common.exception.PowerJobException; import java.io.IOException; @@ -19,13 +20,11 @@ import java.io.IOException; @Slf4j public class JsonUtils { - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - static { - OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); - // - OBJECT_MAPPER.configure(JsonParser.Feature.IGNORE_UNDEFINED, true); - } + private static final JsonMapper JSON_MAPPER = JsonMapper.builder() + .configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true) + .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) + .configure(JsonParser.Feature.IGNORE_UNDEFINED, true) + .build(); private JsonUtils(){ @@ -33,15 +32,16 @@ public class JsonUtils { public static String toJSONString(Object obj) { try { - return OBJECT_MAPPER.writeValueAsString(obj); - }catch (Exception ignore) { + return JSON_MAPPER.writeValueAsString(obj); + }catch (Exception e) { + log.error("[PowerJob] toJSONString failed", e); } return null; } public static String toJSONStringUnsafe(Object obj) { try { - return OBJECT_MAPPER.writeValueAsString(obj); + return JSON_MAPPER.writeValueAsString(obj); }catch (Exception e) { throw new PowerJobException(e); } @@ -49,31 +49,32 @@ public class JsonUtils { public static byte[] toBytes(Object obj) { try { - return OBJECT_MAPPER.writeValueAsBytes(obj); - }catch (Exception ignore) { + return JSON_MAPPER.writeValueAsBytes(obj); + }catch (Exception e) { + log.error("[PowerJob] serialize failed", e); } return null; } public static T parseObject(String json, Class clz) throws JsonProcessingException { - return OBJECT_MAPPER.readValue(json, clz); + return JSON_MAPPER.readValue(json, clz); } public static T parseObject(byte[] b, Class clz) throws IOException { - return OBJECT_MAPPER.readValue(b, clz); + return JSON_MAPPER.readValue(b, clz); } public static T parseObject(byte[] b, TypeReference typeReference) throws IOException { - return OBJECT_MAPPER.readValue(b, typeReference); + return JSON_MAPPER.readValue(b, typeReference); } public static T parseObject(String json, TypeReference typeReference) throws IOException { - return OBJECT_MAPPER.readValue(json, typeReference); + return JSON_MAPPER.readValue(json, typeReference); } public static T parseObjectIgnoreException(String json, Class clz) { try { - return OBJECT_MAPPER.readValue(json, clz); + return JSON_MAPPER.readValue(json, clz); }catch (Exception e) { log.error("unable to parse json string to object,current string:{}",json,e); return null; @@ -83,7 +84,7 @@ public class JsonUtils { public static T parseObjectUnsafe(String json, Class clz) { try { - return OBJECT_MAPPER.readValue(json, clz); + return JSON_MAPPER.readValue(json, clz); }catch (Exception e) { ExceptionUtils.rethrow(e); } diff --git a/powerjob-common/src/main/java/tech/powerjob/common/utils/CollectionUtils.java b/powerjob-common/src/main/java/tech/powerjob/common/utils/CollectionUtils.java new file mode 100644 index 00000000..2ac86ef4 --- /dev/null +++ b/powerjob-common/src/main/java/tech/powerjob/common/utils/CollectionUtils.java @@ -0,0 +1,33 @@ +package tech.powerjob.common.utils; + +import java.util.Collection; +import java.util.Map; + +/** + * CollectionUtils + * + * @author tjq + * @since 2023/1/20 + */ +public class CollectionUtils { + + /** + * Return {@code true} if the supplied Collection is {@code null} or empty. + * Otherwise, return {@code false}. + * @param collection the Collection to check + * @return whether the given Collection is empty + */ + public static boolean isEmpty(Collection collection) { + return (collection == null || collection.isEmpty()); + } + + /** + * Return {@code true} if the supplied Map is {@code null} or empty. + * Otherwise, return {@code false}. + * @param map the Map to check + * @return whether the given Map is empty + */ + public static boolean isEmpty(Map map) { + return (map == null || map.isEmpty()); + } +} diff --git a/powerjob-common/src/main/java/tech/powerjob/common/utils/JavaUtils.java b/powerjob-common/src/main/java/tech/powerjob/common/utils/JavaUtils.java index 4b0b0c24..efff1973 100644 --- a/powerjob-common/src/main/java/tech/powerjob/common/utils/JavaUtils.java +++ b/powerjob-common/src/main/java/tech/powerjob/common/utils/JavaUtils.java @@ -42,14 +42,20 @@ public class JavaUtils { if (connection instanceof JarURLConnection) { return getImplementationVersion(((JarURLConnection) connection).getJarFile()); } - try (JarFile jarFile = new JarFile(new File(codeSourceLocation.toURI()))) { + final File file = new File(codeSourceLocation.toURI()); + // idea 场景,查找版本失败 + if (!file.exists() || file.isDirectory()) { + return "UNKNOWN"; + } + try (JarFile jarFile = new JarFile(file)) { return getImplementationVersion(jarFile); } } catch (Throwable t) { log.warn("[JavaUtils] determinePackageVersion for clz[{}] failed, msg: {}", clz.getSimpleName(), t.toString()); + // windows 下无权限访问会一直报错一直重试,需要在此兼容 + return "UNKNOWN"; } - return null; } private static String getImplementationVersion(JarFile jarFile) throws IOException { return jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION); diff --git a/powerjob-official-processors/pom.xml b/powerjob-official-processors/pom.xml index 41229d45..f84021bb 100644 --- a/powerjob-official-processors/pom.xml +++ b/powerjob-official-processors/pom.xml @@ -5,12 +5,12 @@ powerjob tech.powerjob - 3.0.0 + 4.0.0 4.0.0 powerjob-official-processors - 1.2.2 + 1.3.0 jar @@ -20,10 +20,11 @@ 5.9.1 1.2.9 - 4.2.1 + 4.3.0 5.2.9.RELEASE 2.1.214 8.0.28 + 5.3.23 1.2.83 @@ -75,6 +76,13 @@ provided + + + org.springframework + spring-context + ${spring.version} + provided + org.springframework diff --git a/powerjob-official-processors/src/main/java/tech/powerjob/official/processors/impl/script/AbstractScriptProcessor.java b/powerjob-official-processors/src/main/java/tech/powerjob/official/processors/impl/script/AbstractScriptProcessor.java index 935df2ca..768160e2 100644 --- a/powerjob-official-processors/src/main/java/tech/powerjob/official/processors/impl/script/AbstractScriptProcessor.java +++ b/powerjob-official-processors/src/main/java/tech/powerjob/official/processors/impl/script/AbstractScriptProcessor.java @@ -1,5 +1,6 @@ package tech.powerjob.official.processors.impl.script; +import tech.powerjob.worker.common.utils.PowerFileUtils; import tech.powerjob.worker.core.processor.ProcessResult; import tech.powerjob.worker.core.processor.TaskContext; import tech.powerjob.worker.log.OmsLogger; @@ -34,7 +35,7 @@ public abstract class AbstractScriptProcessor extends CommonBasicProcessor { protected static final String SH_SHELL = "/bin/sh"; protected static final String CMD_SHELL = "cmd.exe"; - private static final String WORKER_DIR = System.getProperty("user.home") + "/powerjob/worker/official_script_processor/"; + private static final String WORKER_DIR = PowerFileUtils.workspace() + "/official_script_processor/"; @Override protected ProcessResult process0(TaskContext context) throws Exception { diff --git a/powerjob-official-processors/src/main/java/tech/powerjob/official/processors/impl/sql/SpringDatasourceSqlProcessor.java b/powerjob-official-processors/src/main/java/tech/powerjob/official/processors/impl/sql/SpringDatasourceSqlProcessor.java index 120feb54..b8cf0b10 100644 --- a/powerjob-official-processors/src/main/java/tech/powerjob/official/processors/impl/sql/SpringDatasourceSqlProcessor.java +++ b/powerjob-official-processors/src/main/java/tech/powerjob/official/processors/impl/sql/SpringDatasourceSqlProcessor.java @@ -3,13 +3,13 @@ package tech.powerjob.official.processors.impl.sql; import tech.powerjob.worker.core.processor.TaskContext; import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; -import org.springframework.util.Assert; import org.apache.commons.lang3.StringUtils; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; import java.util.Map; +import java.util.Objects; /** * 简单 Spring SQL 处理器,目前只能用 Spring Bean 的方式加载 @@ -75,8 +75,8 @@ public class SpringDatasourceSqlProcessor extends AbstractSqlProcessor { * @param dataSource 数据源 */ public void registerDataSource(String dataSourceName, DataSource dataSource) { - Assert.notNull(dataSourceName, "DataSource name must not be null"); - Assert.notNull(dataSource, "DataSource must not be null"); + Objects.requireNonNull(dataSourceName, "DataSource name must not be null"); + Objects.requireNonNull(dataSource, "DataSource must not be null"); dataSourceMap.put(dataSourceName, dataSource); log.info("register data source({})' successfully.", dataSourceName); } diff --git a/powerjob-remote/pom.xml b/powerjob-remote/pom.xml new file mode 100644 index 00000000..5dd8343f --- /dev/null +++ b/powerjob-remote/pom.xml @@ -0,0 +1,47 @@ + + + + powerjob + tech.powerjob + 4.0.0 + + 4.0.0 + pom + + powerjob-remote-framework + powerjob-remote-benchmark + powerjob-remote-impl-http + powerjob-remote-impl-akka + + + powerjob-remote + + + 8 + 8 + UTF-8 + + 5.9.0 + 1.2.9 + + + + + + org.junit.jupiter + junit-jupiter-api + ${junit.version} + test + + + + ch.qos.logback + logback-classic + ${logback.version} + test + + + + \ No newline at end of file diff --git a/powerjob-remote/powerjob-remote-benchmark/pom.xml b/powerjob-remote/powerjob-remote-benchmark/pom.xml new file mode 100644 index 00000000..827c31cb --- /dev/null +++ b/powerjob-remote/powerjob-remote-benchmark/pom.xml @@ -0,0 +1,106 @@ + + + + powerjob-remote + tech.powerjob + 4.0.0 + + 4.0.0 + + powerjob-remote-benchmark + + + 8 + 8 + UTF-8 + + 3.10.1 + 3.2.2 + + 1.2.9 + 2.7.4 + 4.3.0 + 4.3.0 + + 3.9.0 + 4.2.9 + + + + + + + io.gatling.highcharts + gatling-charts-highcharts + ${gatling.version} + test + + + + ch.qos.logback + logback-classic + ${logback.version} + + + + org.springframework.boot + spring-boot-starter-web + ${springboot.version} + + + + tech.powerjob + powerjob-remote-impl-http + ${powerjob-remote-impl-http.version} + + + + tech.powerjob + powerjob-remote-impl-akka + ${powerjob-remote-impl-akka.version} + + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${springboot.version} + + tech.powerjob.remote.benchmark.BenchmarkApplication + + + + + repackage + + + + + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + maven-jar-plugin + ${maven-jar-plugin.version} + + + io.gatling + gatling-maven-plugin + ${gatling-maven-plugin.version} + + + + + + + + \ No newline at end of file diff --git a/powerjob-remote/powerjob-remote-benchmark/src/main/java/tech/powerjob/remote/benchmark/BenchmarkApplication.java b/powerjob-remote/powerjob-remote-benchmark/src/main/java/tech/powerjob/remote/benchmark/BenchmarkApplication.java new file mode 100644 index 00000000..64dcb25e --- /dev/null +++ b/powerjob-remote/powerjob-remote-benchmark/src/main/java/tech/powerjob/remote/benchmark/BenchmarkApplication.java @@ -0,0 +1,19 @@ +package tech.powerjob.remote.benchmark; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 测试工程 + * 用于 remote 协议压测 + * + * @author tjq + * @since 2023/1/7 + */ +@SpringBootApplication +public class BenchmarkApplication { + + public static void main(String[] args) { + SpringApplication.run(BenchmarkApplication.class, args); + } +} diff --git a/powerjob-remote/powerjob-remote-benchmark/src/main/java/tech/powerjob/remote/benchmark/EngineService.java b/powerjob-remote/powerjob-remote-benchmark/src/main/java/tech/powerjob/remote/benchmark/EngineService.java new file mode 100644 index 00000000..41079878 --- /dev/null +++ b/powerjob-remote/powerjob-remote-benchmark/src/main/java/tech/powerjob/remote/benchmark/EngineService.java @@ -0,0 +1,68 @@ +package tech.powerjob.remote.benchmark; + +import com.google.common.collect.Lists; +import lombok.Getter; +import org.springframework.stereotype.Service; +import tech.powerjob.common.enums.Protocol; +import tech.powerjob.remote.framework.BenchmarkActor; +import tech.powerjob.remote.framework.base.Address; +import tech.powerjob.remote.framework.base.ServerType; +import tech.powerjob.remote.framework.engine.EngineConfig; +import tech.powerjob.remote.framework.engine.impl.PowerJobRemoteEngine; +import tech.powerjob.remote.framework.transporter.Transporter; + +import javax.annotation.PostConstruct; + +/** + * EngineService + * + * @author tjq + * @since 2023/1/7 + */ +@Service +public class EngineService { + + public static final String HOST = "127.0.0.1"; + + public static final int SERVER_AKKA_PORT = 10001; + public static final int SERVER_HTTP_PORT = 10002; + + public static final int CLIENT_AKKA_PORT = 20001; + public static final int CLIENT_HTTP_PORT = 20002; + + @Getter + private Transporter akkaTransporter; + @Getter + private Transporter httpTransporter; + + @PostConstruct + public void init() { + // http server + new PowerJobRemoteEngine().start(new EngineConfig() + .setServerType(ServerType.SERVER) + .setActorList(Lists.newArrayList(new BenchmarkActor())) + .setType(Protocol.HTTP.name()) + .setBindAddress(new Address().setHost(HOST).setPort(SERVER_HTTP_PORT))); + + // akka server + new PowerJobRemoteEngine().start(new EngineConfig() + .setServerType(ServerType.SERVER) + .setActorList(Lists.newArrayList(new BenchmarkActor())) + .setType(Protocol.AKKA.name()) + .setBindAddress(new Address().setHost(HOST).setPort(SERVER_AKKA_PORT))); + + // http client + httpTransporter = new PowerJobRemoteEngine().start(new EngineConfig() + .setServerType(ServerType.WORKER) + .setActorList(Lists.newArrayList(new BenchmarkActor())) + .setType(Protocol.HTTP.name()) + .setBindAddress(new Address().setHost(HOST).setPort(CLIENT_HTTP_PORT))).getTransporter(); + + // akka client + akkaTransporter = new PowerJobRemoteEngine().start(new EngineConfig() + .setServerType(ServerType.WORKER) + .setActorList(Lists.newArrayList(new BenchmarkActor())) + .setType(Protocol.AKKA.name()) + .setBindAddress(new Address().setHost(HOST).setPort(CLIENT_AKKA_PORT))).getTransporter(); + } +} diff --git a/powerjob-remote/powerjob-remote-benchmark/src/main/java/tech/powerjob/remote/benchmark/PressureTestController.java b/powerjob-remote/powerjob-remote-benchmark/src/main/java/tech/powerjob/remote/benchmark/PressureTestController.java new file mode 100644 index 00000000..c2f9fcc4 --- /dev/null +++ b/powerjob-remote/powerjob-remote-benchmark/src/main/java/tech/powerjob/remote/benchmark/PressureTestController.java @@ -0,0 +1,82 @@ +package tech.powerjob.remote.benchmark; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import tech.powerjob.common.enums.Protocol; +import tech.powerjob.remote.framework.BenchmarkActor; +import tech.powerjob.remote.framework.base.Address; +import tech.powerjob.remote.framework.base.HandlerLocation; +import tech.powerjob.remote.framework.base.URL; + +import javax.annotation.Resource; + +import java.util.concurrent.CompletionStage; + +import static tech.powerjob.remote.benchmark.EngineService.*; + +/** + * 压测测试入口 + * + * @author tjq + * @since 2023/1/7 + */ +@Slf4j +@RestController +@RequestMapping("/pressure") +public class PressureTestController { + + private static final HandlerLocation HL = new HandlerLocation().setRootPath("benchmark").setMethodPath("standard"); + + @Resource + private EngineService engineService; + + @GetMapping("/tell") + public void httpTell(String protocol, Integer blockMs, Integer responseSize, String content) { + Address address = new Address().setHost(HOST); + URL url = new URL().setLocation(HL).setAddress(address); + final BenchmarkActor.BenchmarkRequest request = new BenchmarkActor.BenchmarkRequest().setContent(content).setBlockingMills(blockMs).setResponseSize(responseSize); + try { + if (Protocol.HTTP.name().equalsIgnoreCase(protocol)) { + address.setPort(SERVER_HTTP_PORT); + engineService.getHttpTransporter().tell(url, request); + } else { + address.setPort(SERVER_AKKA_PORT); + engineService.getAkkaTransporter().tell(url, request); + } + } catch (Exception e) { + log.error("[HttpTell] process failed!", e); + ExceptionUtils.rethrow(e); + } + } + + + @GetMapping("/ask") + public void httpAsk(String protocol, Integer blockMs, Integer responseSize, String content, Boolean debug) { + Address address = new Address().setHost(HOST); + URL url = new URL().setLocation(HL).setAddress(address); + final BenchmarkActor.BenchmarkRequest request = new BenchmarkActor.BenchmarkRequest().setContent(content).setBlockingMills(blockMs).setResponseSize(responseSize); + try { + CompletionStage responseOpt = null; + + if (Protocol.HTTP.name().equalsIgnoreCase(protocol)) { + address.setPort(SERVER_HTTP_PORT); + responseOpt = engineService.getHttpTransporter().ask(url, request, BenchmarkActor.BenchmarkResponse.class); + } else { + address.setPort(SERVER_AKKA_PORT); + responseOpt = engineService.getAkkaTransporter().ask(url, request, BenchmarkActor.BenchmarkResponse.class); + } + final BenchmarkActor.BenchmarkResponse response = responseOpt.toCompletableFuture().get(); + if (BooleanUtils.isTrue(debug)) { + log.info("[httpAsk] response: {}", response); + } + } catch (Exception e) { + log.error("[httpAsk] process failed", e); + ExceptionUtils.rethrow(e); + } + } + +} diff --git a/powerjob-remote/powerjob-remote-benchmark/src/test/java/Engine.java b/powerjob-remote/powerjob-remote-benchmark/src/test/java/Engine.java new file mode 100644 index 00000000..fa70e9eb --- /dev/null +++ b/powerjob-remote/powerjob-remote-benchmark/src/test/java/Engine.java @@ -0,0 +1,20 @@ +import io.gatling.app.Gatling; +import io.gatling.core.config.GatlingPropertiesBuilder; + +/** + * 压测启动入口 + * + * @author tjq + * @since 2023/1/8 + */ +public class Engine { + + public static void main(String[] args) { + GatlingPropertiesBuilder props = new GatlingPropertiesBuilder() + .resourcesDirectory(IDEPathHelper.mavenResourcesDirectory.toString()) + .resultsDirectory(IDEPathHelper.resultsDirectory.toString()) + .binariesDirectory(IDEPathHelper.mavenBinariesDirectory.toString()); + + Gatling.fromMap(props.build()); + } +} diff --git a/powerjob-remote/powerjob-remote-benchmark/src/test/java/IDEPathHelper.java b/powerjob-remote/powerjob-remote-benchmark/src/test/java/IDEPathHelper.java new file mode 100644 index 00000000..5315cad1 --- /dev/null +++ b/powerjob-remote/powerjob-remote-benchmark/src/test/java/IDEPathHelper.java @@ -0,0 +1,33 @@ +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static java.util.Objects.requireNonNull; + +/** + * @author gatling-maven-plugin-demo-java + */ +public class IDEPathHelper { + + static final Path mavenSourcesDirectory; + static final Path mavenResourcesDirectory; + static final Path mavenBinariesDirectory; + static final Path resultsDirectory; + static final Path recorderConfigFile; + + static { + try { + Path projectRootDir = Paths.get(requireNonNull(IDEPathHelper.class.getResource("gatling.conf"), "Couldn't locate gatling.conf").toURI()).getParent().getParent().getParent(); + Path mavenTargetDirectory = projectRootDir.resolve("target"); + Path mavenSrcTestDirectory = projectRootDir.resolve("src").resolve("test"); + + mavenSourcesDirectory = mavenSrcTestDirectory.resolve("java"); + mavenResourcesDirectory = mavenSrcTestDirectory.resolve("resources"); + mavenBinariesDirectory = mavenTargetDirectory.resolve("test-classes"); + resultsDirectory = mavenTargetDirectory.resolve("gatling"); + recorderConfigFile = mavenResourcesDirectory.resolve("recorder.conf"); + } catch (URISyntaxException e) { + throw new ExceptionInInitializerError(e); + } + } +} diff --git a/powerjob-remote/powerjob-remote-benchmark/src/test/java/tech/powerjob/remote/benchmark/Constant.java b/powerjob-remote/powerjob-remote-benchmark/src/test/java/tech/powerjob/remote/benchmark/Constant.java new file mode 100644 index 00000000..71820edb --- /dev/null +++ b/powerjob-remote/powerjob-remote-benchmark/src/test/java/tech/powerjob/remote/benchmark/Constant.java @@ -0,0 +1,14 @@ +package tech.powerjob.remote.benchmark; + +/** + * Constant + * 压测时需要修改的常量 + * + * @author tjq + * @since 2023/1/8 + */ +public class Constant { + + public static final String SERVER_HOST = "127.0.0.1"; + +} diff --git a/powerjob-remote/powerjob-remote-benchmark/src/test/java/tech/powerjob/remote/benchmark/HttpSimulation.java b/powerjob-remote/powerjob-remote-benchmark/src/test/java/tech/powerjob/remote/benchmark/HttpSimulation.java new file mode 100644 index 00000000..00b1a8b4 --- /dev/null +++ b/powerjob-remote/powerjob-remote-benchmark/src/test/java/tech/powerjob/remote/benchmark/HttpSimulation.java @@ -0,0 +1,62 @@ +package tech.powerjob.remote.benchmark; + +import static io.gatling.javaapi.core.CoreDsl.*; +import static io.gatling.javaapi.http.HttpDsl.*; + +import io.gatling.javaapi.core.*; +import io.gatling.javaapi.http.*; +/** + * 以 HTTP 为入口压测 + * + * @author tjq + * @since 2023/1/8 + */ +public class HttpSimulation extends Simulation { + + String baseUrl = String.format("http://%s:8080", Constant.SERVER_HOST); + HttpProtocolBuilder httpProtocol = http // 4 + .baseUrl(baseUrl) // 5 + .acceptHeader("application/json") // 6 + .doNotTrackHeader("1") + .acceptLanguageHeader("en-US,en;q=0.5") + .acceptEncodingHeader("gzip, deflate") + .userAgentHeader("Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0"); + + ScenarioBuilder warmup = scenario("WarmupSimulation") + .exec(http("PowerJob-Warmup-HTTP") + .get("/pressure/ask?protocol=HTTP&debug=false&responseSize=1024")) + .exec(http("PowerJob-Warmup-AKKA") + .get("/pressure/ask?protocol=AKKA&debug=false&responseSize=1024")) + ; + + ScenarioBuilder httpAsk = scenario("HttpSimulation") // 7 + .exec(http("PowerJob-Remote-Http") // 请求名称,用于压测报表展示 + .get("/pressure/ask?protocol=HTTP&debug=false&responseSize=1024")) // 9 + ; + + ScenarioBuilder akkaAsk = scenario("AkkaSimulation") // 7 + .exec(http("PowerJob-Remote-AKKA") // 请求名称,用于压测报表展示 + .get("/pressure/ask?protocol=AKKA&debug=false&responseSize=1024")) // 9 + ; + + + /* + atOnceUsers(10) 一次模拟的用户数量(10) + nothingFor(4 seconds) 在指定的时间段(4 seconds)内什么都不干 + constantUsersPerSec(10) during(20 seconds) 以固定的速度模拟用户,指定每秒模拟的用户数(10),指定模拟测试时间长度(20 seconds) + rampUsersPerSec(10) to (20) during(20 seconds) 在指定的时间(20 seconds)内,使每秒模拟的用户从数量1(10)逐渐增加到数量2(20),速度匀速 + heavisideUsers(100) over(10 seconds) 在指定的时间(10 seconds)内使用类似单位阶跃函数的方法逐渐增加模拟并发的用户,直到总数达到指定的数量(100).简单说就是每秒并发用户数递增 + */ + + { + setUp( // 11 + warmup.injectOpen(constantUsersPerSec(50).during(10)) + .andThen( + httpAsk.injectOpen(incrementUsersPerSec(100).times(10).eachLevelLasting(10)) + ) + .andThen( + akkaAsk.injectOpen(incrementUsersPerSec(100).times(10).eachLevelLasting(10)) + ) + ).protocols(httpProtocol); // 13 + } +} diff --git a/powerjob-remote/powerjob-remote-benchmark/src/test/resources/gatling.conf b/powerjob-remote/powerjob-remote-benchmark/src/test/resources/gatling.conf new file mode 100644 index 00000000..c6db7626 --- /dev/null +++ b/powerjob-remote/powerjob-remote-benchmark/src/test/resources/gatling.conf @@ -0,0 +1,127 @@ +######################### +# Gatling Configuration # +######################### + +# This file contains all the settings configurable for Gatling with their default values + +gatling { + core { + #outputDirectoryBaseName = "" # The prefix for each simulation result folder (then suffixed by the report generation timestamp) + #runDescription = "" # The description for this simulation run, displayed in each report + #encoding = "utf-8" # Encoding to use throughout Gatling for file and string manipulation + #simulationClass = "" # The FQCN of the simulation to run (when used in conjunction with noReports, the simulation for which assertions will be validated) + #elFileBodiesCacheMaxCapacity = 200 # Cache size for request body EL templates, set to 0 to disable + #rawFileBodiesCacheMaxCapacity = 200 # Cache size for request body Raw templates, set to 0 to disable + #rawFileBodiesInMemoryMaxSize = 1000 # Below this limit, raw file bodies will be cached in memory + #pebbleFileBodiesCacheMaxCapacity = 200 # Cache size for request body Peeble templates, set to 0 to disable + #feederAdaptiveLoadModeThreshold = 100 # File size threshold (in MB). Below load eagerly in memory, above use batch mode with default buffer size + #shutdownTimeout = 10000 # Milliseconds to wait for the actor system to shutdown + extract { + regex { + #cacheMaxCapacity = 200 # Cache size for the compiled regexes, set to 0 to disable caching + } + xpath { + #cacheMaxCapacity = 200 # Cache size for the compiled XPath queries, set to 0 to disable caching + } + jsonPath { + #cacheMaxCapacity = 200 # Cache size for the compiled jsonPath queries, set to 0 to disable caching + } + css { + #cacheMaxCapacity = 200 # Cache size for the compiled CSS selectors queries, set to 0 to disable caching + } + } + directory { + #simulations = user-files/simulations # Directory where simulation classes are located (for bundle packaging only) + #resources = user-files/resources # Directory where resources, such as feeder files and request bodies are located (for bundle packaging only) + #reportsOnly = "" # If set, name of report folder to look for in order to generate its report + #binaries = "" # If set, name of the folder where compiles classes are located: Defaults to GATLING_HOME/target. + #results = results # Name of the folder where all reports folder are located + } + } + socket { + #connectTimeout = 10000 # Timeout in millis for establishing a TCP socket + #tcpNoDelay = true + #soKeepAlive = false # if TCP keepalive configured at OS level should be used + #soReuseAddress = false + } + netty { + #useNativeTransport = true # if Netty native transport should be used instead of Java NIO + #allocator = "pooled" # switch to unpooled for unpooled ByteBufAllocator + #maxThreadLocalCharBufferSize = 200000 # Netty's default is 16k + } + ssl { + #useOpenSsl = true # if OpenSSL should be used instead of JSSE (only the latter can be debugged with -Djava.net.debug=ssl) + #useOpenSslFinalizers = false # if OpenSSL contexts should be freed with Finalizer or if using RefCounted is fine + #handshakeTimeout = 10000 # TLS handshake timeout in millis + #useInsecureTrustManager = true # Use an insecure TrustManager that trusts all server certificates + #enabledProtocols = [] # Array of enabled protocols for HTTPS, if empty use Netty's defaults + #enabledCipherSuites = [] # Array of enabled cipher suites for HTTPS, if empty enable all available ciphers + #sessionCacheSize = 0 # SSLSession cache size, set to 0 to use JDK's default + #sessionTimeout = 0 # SSLSession timeout in seconds, set to 0 to use JDK's default (24h) + #enableSni = true # When set to true, enable Server Name indication (SNI) + keyStore { + #type = "" # Type of SSLContext's KeyManagers store + #file = "" # Location of SSLContext's KeyManagers store + #password = "" # Password for SSLContext's KeyManagers store + #algorithm = "" # Algorithm used SSLContext's KeyManagers store + } + trustStore { + #type = "" # Type of SSLContext's TrustManagers store + #file = "" # Location of SSLContext's TrustManagers store + #password = "" # Password for SSLContext's TrustManagers store + #algorithm = "" # Algorithm used by SSLContext's TrustManagers store + } + } + charting { + #noReports = false # When set to true, don't generate HTML reports + #maxPlotPerSeries = 1000 # Number of points per graph in Gatling reports + #useGroupDurationMetric = false # Switch group timings from cumulated response time to group duration. + indicators { + #lowerBound = 800 # Lower bound for the requests' response time to track in the reports and the console summary + #higherBound = 1200 # Higher bound for the requests' response time to track in the reports and the console summary + #percentile1 = 50 # Value for the 1st percentile to track in the reports, the console summary and Graphite + #percentile2 = 75 # Value for the 2nd percentile to track in the reports, the console summary and Graphite + #percentile3 = 95 # Value for the 3rd percentile to track in the reports, the console summary and Graphite + #percentile4 = 99 # Value for the 4th percentile to track in the reports, the console summary and Graphite + } + } + http { + #fetchedCssCacheMaxCapacity = 200 # Cache size for CSS parsed content, set to 0 to disable + #fetchedHtmlCacheMaxCapacity = 200 # Cache size for HTML parsed content, set to 0 to disable + #perUserCacheMaxCapacity = 200 # Per virtual user cache size, set to 0 to disable + #warmUpUrl = "https://gatling.io" # The URL to use to warm-up the HTTP stack (blank means disabled) + #enableGA = true # Very light Google Analytics (Gatling and Java version), please support + #pooledConnectionIdleTimeout = 60000 # Timeout in millis for a connection to stay idle in the pool + #requestTimeout = 60000 # Timeout in millis for performing an HTTP request + #enableHostnameVerification = false # When set to true, enable hostname verification: SSLEngine.setHttpsEndpointIdentificationAlgorithm("HTTPS") + dns { + #queryTimeout = 5000 # Timeout in millis of each DNS query in millis + #maxQueriesPerResolve = 6 # Maximum allowed number of DNS queries for a given name resolution + } + } + jms { + #replyTimeoutScanPeriod = 1000 # scan period for timedout reply messages + } + data { + #writers = [console, file] # The list of DataWriters to which Gatling write simulation data (currently supported : console, file, graphite) + console { + #light = false # When set to true, displays a light version without detailed request stats + #writePeriod = 5 # Write interval, in seconds + } + file { + #bufferSize = 8192 # FileDataWriter's internal data buffer size, in bytes + } + leak { + #noActivityTimeout = 30 # Period, in seconds, for which Gatling may have no activity before considering a leak may be happening + } + graphite { + #light = false # only send the all* stats + #host = "localhost" # The host where the Carbon server is located + #port = 2003 # The port to which the Carbon server listens to (2003 is default for plaintext, 2004 is default for pickle) + #protocol = "tcp" # The protocol used to send data to Carbon (currently supported : "tcp", "udp") + #rootPathPrefix = "gatling" # The common prefix of all metrics sent to Graphite + #bufferSize = 8192 # Internal data buffer size, in bytes + #writePeriod = 1 # Write period, in seconds + } + } +} diff --git a/powerjob-remote/powerjob-remote-framework/pom.xml b/powerjob-remote/powerjob-remote-framework/pom.xml new file mode 100644 index 00000000..29e6ca81 --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/pom.xml @@ -0,0 +1,41 @@ + + + + powerjob-remote + tech.powerjob + 4.0.0 + + 4.0.0 + + 4.3.0 + powerjob-remote-framework + + + 8 + 8 + UTF-8 + + 4.3.0 + 0.10.2 + + + + + + + tech.powerjob + powerjob-common + ${powerjob-common.version} + + + + org.reflections + reflections + ${reflections.version} + + + + + \ No newline at end of file diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/BenchmarkActor.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/BenchmarkActor.java new file mode 100644 index 00000000..325aa6e2 --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/BenchmarkActor.java @@ -0,0 +1,94 @@ +package tech.powerjob.remote.framework; + +import lombok.Data; +import lombok.experimental.Accessors; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.RandomStringUtils; +import tech.powerjob.common.PowerSerializable; +import tech.powerjob.common.utils.CommonUtils; +import tech.powerjob.remote.framework.actor.Actor; +import tech.powerjob.remote.framework.actor.Handler; + +import java.util.Optional; + +/** + * 基准测试 + * + * @author tjq + * @since 2023/1/1 + */ +@Slf4j +@Actor(path = "benchmark") +public class BenchmarkActor { + + @Handler(path = "standard") + public BenchmarkResponse standardRequest(BenchmarkRequest request) { + long startTs = System.currentTimeMillis(); + log.info("[BenchmarkActor] [standardRequest] receive request: {}", request); + BenchmarkResponse response = new BenchmarkResponse() + .setSuccess(true) + .setContent(request.getContent()) + .setProcessThread(Thread.currentThread().getName()) + .setServerReceiveTs(System.currentTimeMillis()); + if (request.getResponseSize() != null && request.getResponseSize() > 0) { + response.setExtra(RandomStringUtils.randomPrint(request.getResponseSize())); + } + executeSleep(request); + response.setServerCost(System.currentTimeMillis() - startTs); + return response; + } + + @Handler(path = "emptyReturn") + public void emptyReturn(BenchmarkRequest request) { + log.info("[BenchmarkActor] [emptyReturn] receive request: {}", request); + executeSleep(request); + } + + @Handler(path = "stringReturn") + public String stringReturn(BenchmarkRequest request) { + log.info("[BenchmarkActor] [stringReturn] receive request: {}", request); + executeSleep(request); + return RandomStringUtils.randomPrint(Optional.ofNullable(request.getResponseSize()).orElse(100)); + } + + private static void executeSleep(BenchmarkRequest request) { + if (request.getBlockingMills() != null && request.getBlockingMills() > 0) { + CommonUtils.easySleep(request.getBlockingMills()); + } + } + + + @Data + @Accessors(chain = true) + public static class BenchmarkRequest implements PowerSerializable { + /** + * 请求内容 + */ + private String content; + /** + * 期望的响应大小,可空 + */ + private Integer responseSize; + /** + * 阻塞时间,模拟 IO 耗时 + */ + private Integer blockingMills; + } + + @Data + @Accessors(chain = true) + public static class BenchmarkResponse implements PowerSerializable { + private boolean success; + /** + * 原路返回原来的 content + */ + private String content; + + private String processThread; + private long serverReceiveTs; + + private long serverCost; + + private String extra; + } +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/actor/Actor.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/actor/Actor.java new file mode 100644 index 00000000..419b3353 --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/actor/Actor.java @@ -0,0 +1,21 @@ +package tech.powerjob.remote.framework.actor; + +import java.lang.annotation.*; + +/** + * 行为处理器 + * + * @author tjq + * @since 2022/12/31 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface Actor { + + /** + * root path + * @return root path + */ + String path(); +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/actor/ActorInfo.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/actor/ActorInfo.java new file mode 100644 index 00000000..d139dc73 --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/actor/ActorInfo.java @@ -0,0 +1,27 @@ +package tech.powerjob.remote.framework.actor; + + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * ActorInfo + * + * @author tjq + * @since 2022/12/31 + */ +@Getter +@Setter +@Accessors(chain = true) +public class ActorInfo { + + private Object actor; + + private Actor anno; + + private List handlerInfos; + +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/actor/Handler.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/actor/Handler.java new file mode 100644 index 00000000..e5d1972d --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/actor/Handler.java @@ -0,0 +1,27 @@ +package tech.powerjob.remote.framework.actor; + +import java.lang.annotation.*; + +/** + * Handler + * + * @author tjq + * @since 2022/12/31 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface Handler { + + /** + * handler path + * @return handler path + */ + String path(); + + /** + * 处理类型 + * @return 阻塞 or 非阻塞 + */ + ProcessType processType() default ProcessType.BLOCKING; +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/actor/HandlerInfo.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/actor/HandlerInfo.java new file mode 100644 index 00000000..dd502d9e --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/actor/HandlerInfo.java @@ -0,0 +1,34 @@ +package tech.powerjob.remote.framework.actor; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; +import tech.powerjob.remote.framework.base.HandlerLocation; + +import java.io.Serializable; +import java.lang.reflect.Method; + +/** + * HandlerInfo + * + * @author tjq + * @since 2022/12/31 + */ +@Getter +@Setter +@ToString +@Accessors(chain = true) +public class HandlerInfo { + + private HandlerLocation location; + /** + * handler 对应的方法 + */ + private Method method; + + /** + * Handler 注解携带的信息 + */ + private Handler anno; +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/actor/ProcessType.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/actor/ProcessType.java new file mode 100644 index 00000000..ef3d7623 --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/actor/ProcessType.java @@ -0,0 +1,20 @@ +package tech.powerjob.remote.framework.actor; + +/** + * 处理器类型 + * + * @author tjq + * @since 2023/1/1 + */ +public enum ProcessType { + + /** + * 阻塞式 + */ + BLOCKING, + /** + * 非阻塞式 + */ + NO_BLOCKING + +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/base/Address.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/base/Address.java new file mode 100644 index 00000000..5541f111 --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/base/Address.java @@ -0,0 +1,37 @@ +package tech.powerjob.remote.framework.base; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 地址 + * + * @author tjq + * @since 2022/12/31 + */ +@Getter +@Setter +@Accessors(chain = true) +public class Address implements Serializable { + private String host; + private int port; + + public String toFullAddress() { + return String.format("%s:%d", host, port); + } + + public static Address fromIpv4(String ipv4) { + String[] split = ipv4.split(":"); + return new Address() + .setHost(split[0]) + .setPort(Integer.parseInt(split[1])); + } + + @Override + public String toString() { + return toFullAddress(); + } +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/base/HandlerLocation.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/base/HandlerLocation.java new file mode 100644 index 00000000..6f9cc5ba --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/base/HandlerLocation.java @@ -0,0 +1,33 @@ +package tech.powerjob.remote.framework.base; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * handler location + * + * @author tjq + * @since 2022/12/31 + */ +@Getter +@Setter +@ToString +@Accessors(chain = true) +public class HandlerLocation implements Serializable { + /** + * 根路径 + */ + private String rootPath; + /** + * 方法路径 + */ + private String methodPath; + + public String toPath() { + return String.format("/%s/%s", rootPath, methodPath); + } +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/base/RemotingException.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/base/RemotingException.java new file mode 100644 index 00000000..fd3185db --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/base/RemotingException.java @@ -0,0 +1,16 @@ +package tech.powerjob.remote.framework.base; + +import java.io.IOException; + +/** + * RemotingException + * + * @author tjq + * @since 2022/12/31 + */ +public class RemotingException extends RuntimeException { + + public RemotingException(String message) { + super(message); + } +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/base/ServerType.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/base/ServerType.java new file mode 100644 index 00000000..2b57212f --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/base/ServerType.java @@ -0,0 +1,12 @@ +package tech.powerjob.remote.framework.base; + +/** + * 服务器类型类型 + * + * @author tjq + * @since 2022/12/31 + */ +public enum ServerType { + SERVER, + WORKER +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/base/URL.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/base/URL.java new file mode 100644 index 00000000..c1909b99 --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/base/URL.java @@ -0,0 +1,32 @@ +package tech.powerjob.remote.framework.base; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * URL + * + * @author tjq + * @since 2022/12/31 + */ +@Data +@Accessors(chain = true) +public class URL implements Serializable { + + /** + * 调用的集群类型(用于兼容 AKKA 等除了IP还需要指定 system 访问的情况) + */ + private ServerType serverType; + + /** + * remote address + */ + private Address address; + + /** + * location + */ + private HandlerLocation location; +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/cs/CSInitializer.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/cs/CSInitializer.java new file mode 100644 index 00000000..a5e140d9 --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/cs/CSInitializer.java @@ -0,0 +1,43 @@ +package tech.powerjob.remote.framework.cs; + +import tech.powerjob.remote.framework.actor.ActorInfo; +import tech.powerjob.remote.framework.transporter.Transporter; + +import java.io.Closeable; +import java.io.IOException; +import java.util.List; + +/** + * client & server initializer + * + * @author MuBao + * @since 2022/12/31 + */ +public interface CSInitializer { + + /** + * 类型名称,比如 akka, netty4,httpJson + * @return 名称 + */ + String type(); + + /** + * initialize the framework + * @param config config + */ + void init(CSInitializerConfig config); + + /** + * build a Transporter by based network framework + * @return Transporter + */ + Transporter buildTransporter(); + + /** + * bind Actor, publish handler's service + * @param actorInfos actor infos + */ + void bindHandlers(List actorInfos); + + void close() throws IOException; +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/cs/CSInitializerConfig.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/cs/CSInitializerConfig.java new file mode 100644 index 00000000..a64051fd --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/cs/CSInitializerConfig.java @@ -0,0 +1,25 @@ +package tech.powerjob.remote.framework.cs; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import tech.powerjob.remote.framework.base.Address; +import tech.powerjob.remote.framework.base.ServerType; + +import java.io.Serializable; + +/** + * CSInitializerConfig + * + * @author tjq + * @since 2022/12/31 + */ +@Getter +@Setter +@Accessors(chain = true) +public class CSInitializerConfig implements Serializable { + + private Address bindAddress; + + private ServerType serverType; +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/EngineConfig.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/EngineConfig.java new file mode 100644 index 00000000..a9b82961 --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/EngineConfig.java @@ -0,0 +1,37 @@ +package tech.powerjob.remote.framework.engine; + +import lombok.Data; +import lombok.experimental.Accessors; +import tech.powerjob.remote.framework.base.Address; +import tech.powerjob.remote.framework.base.ServerType; + +import java.io.Serializable; +import java.util.List; + +/** + * EngineConfig + * + * @author tjq + * @since 2022/12/31 + */ +@Data +@Accessors(chain = true) +public class EngineConfig implements Serializable { + + /** + * 服务类型 + */ + private ServerType serverType; + /** + * 需要启动的引擎类型 + */ + private String type; + /** + * 绑定的本地地址 + */ + private Address bindAddress; + /** + * actor实例,交由使用侧自己实例化以便自行注入各种 bean + */ + private List actorList; +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/EngineOutput.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/EngineOutput.java new file mode 100644 index 00000000..b6d427d8 --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/EngineOutput.java @@ -0,0 +1,18 @@ +package tech.powerjob.remote.framework.engine; + +import lombok.Getter; +import lombok.Setter; +import tech.powerjob.remote.framework.transporter.Transporter; + + +/** + * 引擎输出 + * + * @author tjq + * @since 2022/12/31 + */ +@Getter +@Setter +public class EngineOutput { + private Transporter transporter; +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/RemoteEngine.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/RemoteEngine.java new file mode 100644 index 00000000..e86d2722 --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/RemoteEngine.java @@ -0,0 +1,16 @@ +package tech.powerjob.remote.framework.engine; + +import java.io.IOException; + +/** + * RemoteEngine + * + * @author tjq + * @since 2022/12/31 + */ +public interface RemoteEngine { + + EngineOutput start(EngineConfig engineConfig); + + void close() throws IOException; +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/impl/ActorFactory.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/impl/ActorFactory.java new file mode 100644 index 00000000..cfd9108c --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/impl/ActorFactory.java @@ -0,0 +1,89 @@ +package tech.powerjob.remote.framework.engine.impl; + +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.exception.ExceptionUtils; +import tech.powerjob.remote.framework.actor.Actor; +import tech.powerjob.remote.framework.actor.ActorInfo; +import tech.powerjob.remote.framework.actor.Handler; +import tech.powerjob.remote.framework.actor.HandlerInfo; +import tech.powerjob.remote.framework.base.HandlerLocation; + +import java.lang.reflect.Method; +import java.util.List; + +/** + * load all Actor + * + * @author tjq + * @since 2022/12/31 + */ +@Slf4j +class ActorFactory { + + static List load(List actorList) { + + List actorInfos = Lists.newArrayList(); + + actorList.forEach(actor -> { + final Class clz = actor.getClass(); + try { + final Actor anno = clz.getAnnotation(Actor.class); + + ActorInfo actorInfo = new ActorInfo().setActor(actor).setAnno(anno); + actorInfo.setHandlerInfos(loadHandlerInfos4Actor(actorInfo)); + + actorInfos.add(actorInfo); + } catch (Throwable t) { + log.error("[ActorFactory] process Actor[{}] failed!", clz); + ExceptionUtils.rethrow(t); + } + }); + + return actorInfos; + } + + private static List loadHandlerInfos4Actor(ActorInfo actorInfo) { + List ret = Lists.newArrayList(); + + Actor anno = actorInfo.getAnno(); + String rootPath = anno.path(); + Object actor = actorInfo.getActor(); + + findHandlerMethod(rootPath, actor.getClass(), ret); + return ret; + } + + private static void findHandlerMethod(String rootPath, Class clz, List result) { + Method[] declaredMethods = clz.getDeclaredMethods(); + for (Method handlerMethod: declaredMethods) { + Handler handlerMethodAnnotation = handlerMethod.getAnnotation(Handler.class); + if (handlerMethodAnnotation == null) { + continue; + } + + HandlerLocation handlerLocation = new HandlerLocation() + .setRootPath(suitPath(rootPath)) + .setMethodPath(suitPath(handlerMethodAnnotation.path())); + + HandlerInfo handlerInfo = new HandlerInfo() + .setAnno(handlerMethodAnnotation) + .setMethod(handlerMethod) + .setLocation(handlerLocation); + result.add(handlerInfo); + } + + // 递归处理父类 + final Class superclass = clz.getSuperclass(); + if (superclass != null) { + findHandlerMethod(rootPath, superclass, result); + } + } + + static String suitPath(String path) { + if (path.startsWith("/")) { + return path.replaceFirst("/", ""); + } + return path; + } +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/impl/CSInitializerFactory.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/impl/CSInitializerFactory.java new file mode 100644 index 00000000..8b02f3d4 --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/impl/CSInitializerFactory.java @@ -0,0 +1,44 @@ +package tech.powerjob.remote.framework.engine.impl; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.reflections.Reflections; +import tech.powerjob.common.OmsConstant; +import tech.powerjob.common.exception.PowerJobException; +import tech.powerjob.remote.framework.cs.CSInitializer; + +import java.util.Set; + +/** + * build CSInitializer + * + * @author tjq + * @since 2022/12/31 + */ +@Slf4j +class CSInitializerFactory { + + static CSInitializer build(String targetType) { + + Reflections reflections = new Reflections(OmsConstant.PACKAGE); + Set> cSInitializerClzSet = reflections.getSubTypesOf(CSInitializer.class); + + log.info("[CSInitializerFactory] scan subTypeOf CSInitializer: {}", cSInitializerClzSet); + + for (Class clz : cSInitializerClzSet) { + try { + CSInitializer csInitializer = clz.getDeclaredConstructor().newInstance(); + String type = csInitializer.type(); + log.info("[CSInitializerFactory] new instance for CSInitializer[{}] successfully, type={}, object: {}", clz, type, csInitializer); + if (targetType.equalsIgnoreCase(type)) { + return csInitializer; + } + } catch (Exception e) { + log.error("[CSInitializerFactory] new instance for CSInitializer[{}] failed, maybe you should provide a non-parameter constructor", clz); + ExceptionUtils.rethrow(e); + } + } + + throw new PowerJobException(String.format("can't load CSInitializer[%s], ensure your package name start with 'tech.powerjob' and import the dependencies!", targetType)); + } +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/impl/PowerJobRemoteEngine.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/impl/PowerJobRemoteEngine.java new file mode 100644 index 00000000..2f9cbc6b --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/engine/impl/PowerJobRemoteEngine.java @@ -0,0 +1,66 @@ +package tech.powerjob.remote.framework.engine.impl; + +import com.google.common.base.Stopwatch; +import lombok.extern.slf4j.Slf4j; +import tech.powerjob.remote.framework.actor.ActorInfo; +import tech.powerjob.remote.framework.cs.CSInitializer; +import tech.powerjob.remote.framework.cs.CSInitializerConfig; +import tech.powerjob.remote.framework.engine.EngineConfig; +import tech.powerjob.remote.framework.engine.EngineOutput; +import tech.powerjob.remote.framework.engine.RemoteEngine; +import tech.powerjob.remote.framework.transporter.Transporter; + +import java.io.IOException; +import java.util.List; + +/** + * 初始化 PowerJob 整个网络层 + * + * @author tjq + * @since 2022/12/31 + */ +@Slf4j +public class PowerJobRemoteEngine implements RemoteEngine { + + private CSInitializer csInitializer; + + @Override + public EngineOutput start(EngineConfig engineConfig) { + + final String engineType = engineConfig.getType(); + EngineOutput engineOutput = new EngineOutput(); + log.info("[PowerJobRemoteEngine] [{}] start remote engine with config: {}", engineType, engineConfig); + + List actorInfos = ActorFactory.load(engineConfig.getActorList()); + csInitializer = CSInitializerFactory.build(engineType); + + String type = csInitializer.type(); + + Stopwatch sw = Stopwatch.createStarted(); + log.info("[PowerJobRemoteEngine] [{}] try to startup CSInitializer[type={}]", engineType, type); + + csInitializer.init(new CSInitializerConfig() + .setBindAddress(engineConfig.getBindAddress()) + .setServerType(engineConfig.getServerType()) + ); + + // 构建通讯器 + Transporter transporter = csInitializer.buildTransporter(); + engineOutput.setTransporter(transporter); + + log.info("[PowerJobRemoteEngine] [{}] start to bind Handler", engineType); + actorInfos.forEach(actor -> actor.getHandlerInfos().forEach(handlerInfo -> log.info("[PowerJobRemoteEngine] [{}] PATH={}, handler={}", engineType, handlerInfo.getLocation().toPath(), handlerInfo.getMethod()))); + + // 绑定 handler + csInitializer.bindHandlers(actorInfos); + + log.info("[PowerJobRemoteEngine] [{}] startup successfully, cost: {}", engineType, sw); + + return engineOutput; + } + + @Override + public void close() throws IOException { + csInitializer.close(); + } +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/package-info.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/package-info.java new file mode 100644 index 00000000..54f6fb30 --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/package-info.java @@ -0,0 +1,7 @@ +/** + * PowerJob 网络框架层 + * + * @author tjq + * @since 2022/12/31 + */ +package tech.powerjob.remote.framework; \ No newline at end of file diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/transporter/Protocol.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/transporter/Protocol.java new file mode 100644 index 00000000..d014771f --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/transporter/Protocol.java @@ -0,0 +1,16 @@ +package tech.powerjob.remote.framework.transporter; + +/** + * 通讯协议 + * + * @author tjq + * @since 2022/12/31 + */ +public interface Protocol { + + /** + * 通讯协议名称 + * @return 协议名称 + */ + String name(); +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/transporter/Transporter.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/transporter/Transporter.java new file mode 100644 index 00000000..f66cb1af --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/transporter/Transporter.java @@ -0,0 +1,40 @@ +package tech.powerjob.remote.framework.transporter; + +import tech.powerjob.common.PowerSerializable; +import tech.powerjob.remote.framework.base.RemotingException; +import tech.powerjob.remote.framework.base.URL; + +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutorService; + +/** + * 通讯器,封装与远程服务端交互逻辑 + * + * @author tjq + * @since 2022/12/31 + */ +public interface Transporter { + + /** + * Protocol + * @return return protocol + */ + Protocol getProtocol(); + + /** + *send message + * @param url url + * @param request request + */ + void tell(URL url, PowerSerializable request); + + /** + * ask by request + * @param url url + * @param request request + * @param clz response type + * @return CompletionStage + * @throws RemotingException remote exception + */ + CompletionStage ask(URL url, PowerSerializable request, Class clz) throws RemotingException; +} diff --git a/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/utils/RemoteUtils.java b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/utils/RemoteUtils.java new file mode 100644 index 00000000..d7fa159a --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/main/java/tech/powerjob/remote/framework/utils/RemoteUtils.java @@ -0,0 +1,35 @@ +package tech.powerjob.remote.framework.utils; + +import org.apache.commons.lang3.ArrayUtils; +import tech.powerjob.common.PowerSerializable; + +import java.util.Optional; + +/** + * RemoteUtils + * + * @author tjq + * @since 2023/1/1 + */ +public class RemoteUtils { + + public static Optional> findPowerSerialize(Class[] parameterTypes) { + + if (ArrayUtils.isEmpty(parameterTypes)) { + return Optional.empty(); + } + + for (Class clz : parameterTypes) { + final Class[] interfaces = clz.getInterfaces(); + if (ArrayUtils.isEmpty(interfaces)) { + continue; + } + + if (PowerSerializable.class.isAssignableFrom(clz)) { + return Optional.of(clz); + } + } + return Optional.empty(); + } + +} diff --git a/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/base/AddressTest.java b/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/base/AddressTest.java new file mode 100644 index 00000000..f209de67 --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/base/AddressTest.java @@ -0,0 +1,24 @@ +package tech.powerjob.remote.framework.base; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * test address + * + * @author tjq + * @since 2023/1/20 + */ +@Slf4j +class AddressTest { + + @Test + void testAddress() { + String ip = "192.168.1.1:10085"; + final Address address = Address.fromIpv4(ip); + log.info("[AddressTest] parse address: {}", address); + assert ip.equals(address.toFullAddress()); + } +} \ No newline at end of file diff --git a/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/engine/RemoteEngineTest.java b/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/engine/RemoteEngineTest.java new file mode 100644 index 00000000..eaf8c79d --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/engine/RemoteEngineTest.java @@ -0,0 +1,28 @@ +package tech.powerjob.remote.framework.engine; + +import com.google.common.collect.Sets; +import org.junit.jupiter.api.Test; +import tech.powerjob.remote.framework.base.Address; +import tech.powerjob.remote.framework.engine.impl.PowerJobRemoteEngine; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * RemoteEngineTest + * + * @author tjq + * @since 2022/12/31 + */ +class RemoteEngineTest { + + @Test + void start() { + + RemoteEngine remoteEngine = new PowerJobRemoteEngine(); + + EngineConfig engineConfig = new EngineConfig(); + engineConfig.setType("TEST"); + engineConfig.setBindAddress(new Address().setHost("127.0.0.1").setPort(10086)); + remoteEngine.start(engineConfig); + } +} \ No newline at end of file diff --git a/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/engine/impl/ActorFactoryTest.java b/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/engine/impl/ActorFactoryTest.java new file mode 100644 index 00000000..7e80d2ae --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/engine/impl/ActorFactoryTest.java @@ -0,0 +1,35 @@ +package tech.powerjob.remote.framework.engine.impl; + +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import tech.powerjob.remote.framework.actor.ActorInfo; +import tech.powerjob.remote.framework.test.TestActor; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * HandlerFactoryTest + * + * @author tjq + * @since 2022/12/31 + */ +@Slf4j +class ActorFactoryTest { + + @Test + void load() { + ActorFactory.load(Lists.newArrayList(new TestActor())); + } + + @Test + void testSuitPath() { + final String testPath1 = ActorFactory.suitPath("/test"); + final String testPath2 = ActorFactory.suitPath("test"); + log.info("[ActorFactoryTest] testPath1: {}, testPath2: {}", testPath1, testPath2); + assert testPath1.equals(testPath2); + } + +} \ No newline at end of file diff --git a/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/engine/impl/CSInitializerFactoryTest.java b/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/engine/impl/CSInitializerFactoryTest.java new file mode 100644 index 00000000..657a5434 --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/engine/impl/CSInitializerFactoryTest.java @@ -0,0 +1,29 @@ +package tech.powerjob.remote.framework.engine.impl; + +import com.google.common.collect.Sets; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import tech.powerjob.common.exception.PowerJobException; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * CSInitializerFactoryTest + * + * @author tjq + * @since 2022/12/31 + */ +class CSInitializerFactoryTest { + + @Test + void testBuildNormal() { + CSInitializerFactory.build("TEST"); + } + + @Test + void testNotFind() { + Assertions.assertThrows(PowerJobException.class, () -> { + CSInitializerFactory.build("omicron"); + }); + } +} \ No newline at end of file diff --git a/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/test/TestActor.java b/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/test/TestActor.java new file mode 100644 index 00000000..a1d3351a --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/test/TestActor.java @@ -0,0 +1,42 @@ +package tech.powerjob.remote.framework.test; + +import lombok.extern.slf4j.Slf4j; +import tech.powerjob.remote.framework.actor.Actor; +import tech.powerjob.remote.framework.actor.Handler; + +import java.util.Map; + +/** + * TestActor + * + * @author tjq + * @since 2022/12/31 + */ +@Slf4j +@Actor(path = "/test") +public class TestActor { + + public static void simpleStaticMethod() { + } + + public void simpleMethod() { + } + + @Handler(path = "/method1") + public String handlerMethod1() { + log.info("[TestActor] handlerMethod1"); + return "1"; + } + + @Handler(path = "/method2") + public String handlerMethod2(String name) { + log.info("[TestActor] handlerMethod2 req: {}", name); + return name; + } + + @Handler(path = "/returnEmpty") + public void handlerEmpty(Map req) { + log.info("[TestActor] handlerEmpty req: {}", req); + } + +} diff --git a/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/test/TestCSInitializer.java b/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/test/TestCSInitializer.java new file mode 100644 index 00000000..53ce61c3 --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/test/TestCSInitializer.java @@ -0,0 +1,46 @@ +package tech.powerjob.remote.framework.test; + +import lombok.extern.slf4j.Slf4j; +import tech.powerjob.remote.framework.actor.ActorInfo; +import tech.powerjob.remote.framework.actor.HandlerInfo; +import tech.powerjob.remote.framework.cs.CSInitializer; +import tech.powerjob.remote.framework.cs.CSInitializerConfig; +import tech.powerjob.remote.framework.transporter.Transporter; + +import java.io.IOException; +import java.util.List; + +/** + * TestCSInitializer + * + * @author tjq + * @since 2022/12/31 + */ +@Slf4j +public class TestCSInitializer implements CSInitializer { + @Override + public String type() { + return "TEST"; + } + + @Override + public void init(CSInitializerConfig config) { + log.info("TestCSInitializer#init"); + } + + @Override + public Transporter buildTransporter() { + log.info("TestCSInitializer#buildTransporter"); + return null; + } + + @Override + public void bindHandlers(List actorInfos) { + log.info("TestCSInitializer#actorInfos"); + } + + @Override + public void close() throws IOException { + log.info("TestCSInitializer#close"); + } +} diff --git a/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/utils/RemoteUtilsTest.java b/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/utils/RemoteUtilsTest.java new file mode 100644 index 00000000..01b5c9c4 --- /dev/null +++ b/powerjob-remote/powerjob-remote-framework/src/test/java/tech/powerjob/remote/framework/utils/RemoteUtilsTest.java @@ -0,0 +1,35 @@ +package tech.powerjob.remote.framework.utils; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import tech.powerjob.common.model.AlarmConfig; +import tech.powerjob.common.request.ServerScheduleJobReq; + +import java.util.Optional; + + +/** + * RemoteUtilsTest + * + * @author tjq + * @since 2023/1/1 + */ +@Slf4j +class RemoteUtilsTest { + + @Test + void findPowerSerialize() { + + Class[] contains = {AlarmConfig.class, ServerScheduleJobReq.class}; + Class[] notContains = {AlarmConfig.class}; + + final Optional> notContainsResult = RemoteUtils.findPowerSerialize(notContains); + log.info("[RemoteUtilsTest] notContainsResult: {}", notContainsResult); + final Optional> containsResult = RemoteUtils.findPowerSerialize(contains); + log.info("[RemoteUtilsTest] containsResult: {}", containsResult); + + assert !notContainsResult.isPresent(); + assert containsResult.isPresent(); + + } +} \ No newline at end of file diff --git a/powerjob-remote/powerjob-remote-impl-akka/pom.xml b/powerjob-remote/powerjob-remote-impl-akka/pom.xml new file mode 100644 index 00000000..fdbd6e3d --- /dev/null +++ b/powerjob-remote/powerjob-remote-impl-akka/pom.xml @@ -0,0 +1,45 @@ + + + + powerjob-remote + tech.powerjob + 4.0.0 + + 4.0.0 + + powerjob-remote-impl-akka + 4.3.0 + + + 8 + 8 + UTF-8 + + 4.3.0 + + 2.6.20 + + + + + tech.powerjob + powerjob-remote-framework + ${powerjob-remote-framework.version} + + + + + com.typesafe.akka + akka-remote_2.13 + ${akka.version} + + + com.typesafe.akka + akka-slf4j_2.13 + ${akka.version} + + + + \ No newline at end of file diff --git a/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaCSInitializer.java b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaCSInitializer.java new file mode 100644 index 00000000..cad05a8f --- /dev/null +++ b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaCSInitializer.java @@ -0,0 +1,94 @@ +package tech.powerjob.remote.akka; + +import akka.actor.ActorRef; +import akka.actor.ActorSystem; +import akka.actor.DeadLetter; +import akka.actor.Props; +import akka.routing.RoundRobinPool; +import com.google.common.collect.Maps; +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import lombok.extern.slf4j.Slf4j; +import tech.powerjob.common.serialize.JsonUtils; +import tech.powerjob.remote.framework.actor.ActorInfo; +import tech.powerjob.remote.framework.base.Address; +import tech.powerjob.remote.framework.cs.CSInitializer; +import tech.powerjob.remote.framework.cs.CSInitializerConfig; +import tech.powerjob.remote.framework.transporter.Transporter; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * AkkaCSInitializer + * + * @author tjq + * @since 2022/12/31 + */ +@Slf4j +public class AkkaCSInitializer implements CSInitializer { + + private ActorSystem actorSystem; + private CSInitializerConfig config; + + @Override + public String type() { + return tech.powerjob.common.enums.Protocol.AKKA.name(); + } + + @Override + public void init(CSInitializerConfig config) { + + this.config = config; + + Address bindAddress = config.getBindAddress(); + log.info("[PowerJob-AKKA] bindAddress: {}", bindAddress); + + // 初始化 ActorSystem(macOS上 new ServerSocket 检测端口占用的方法并不生效,可能是AKKA是Scala写的缘故?没办法...只能靠异常重试了) + Map overrideConfig = Maps.newHashMap(); + overrideConfig.put("akka.remote.artery.canonical.hostname", bindAddress.getHost()); + overrideConfig.put("akka.remote.artery.canonical.port", bindAddress.getPort()); + + Config akkaBasicConfig = ConfigFactory.load(AkkaConstant.AKKA_CONFIG); + Config akkaFinalConfig = ConfigFactory.parseMap(overrideConfig).withFallback(akkaBasicConfig); + + log.info("[PowerJob-AKKA] try to start AKKA System."); + + // 启动时绑定当前的 actorSystemName + String actorSystemName = AkkaConstant.fetchActorSystemName(config.getServerType()); + this.actorSystem = ActorSystem.create(actorSystemName, akkaFinalConfig); + + // 处理系统中产生的异常情况 + ActorRef troubleshootingActor = actorSystem.actorOf(Props.create(AkkaTroubleshootingActor.class), "troubleshooting"); + actorSystem.eventStream().subscribe(troubleshootingActor, DeadLetter.class); + + log.info("[PowerJob-AKKA] initialize actorSystem[{}] successfully!", actorSystem.name()); + } + + @Override + public Transporter buildTransporter() { + return new AkkaTransporter(actorSystem); + } + + @Override + public void bindHandlers(List actorInfos) { + int cores = Runtime.getRuntime().availableProcessors(); + actorInfos.forEach(actorInfo -> { + String rootPath = actorInfo.getAnno().path(); + AkkaMappingService.ActorConfig actorConfig = AkkaMappingService.parseActorName(rootPath); + + log.info("[PowerJob-AKKA] start to process actor[path={},config={}]", rootPath, JsonUtils.toJSONString(actorConfig)); + + actorSystem.actorOf(AkkaProxyActor.props(actorInfo) + .withDispatcher("akka.".concat(actorConfig.getDispatcherName())) + .withRouter(new RoundRobinPool(cores)), actorConfig.getActorName()); + + }); + } + + @Override + public void close() throws IOException { + actorSystem.terminate(); + } +} diff --git a/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaConstant.java b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaConstant.java new file mode 100644 index 00000000..d106260a --- /dev/null +++ b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaConstant.java @@ -0,0 +1,29 @@ +package tech.powerjob.remote.akka; + +import tech.powerjob.remote.framework.base.ServerType; + +/** + * AkkaConstant + * + * @author tjq + * @since 2022/12/31 + */ +public class AkkaConstant { + + public static final String AKKA_CONFIG = "powerjob.akka.conf"; + + public static final String WORKER_ACTOR_SYSTEM_NAME = "oms"; + public static final String SERVER_ACTOR_SYSTEM_NAME = "oms-server"; + + /** + * 获取 actorSystem 名称 + * @param serverType 当前服务器类型,powerjob-server 为 server,powerjob-worker 为 worker + * @return actorSystemName + */ + public static String fetchActorSystemName(ServerType serverType) { + + + return serverType == ServerType.SERVER ? SERVER_ACTOR_SYSTEM_NAME : WORKER_ACTOR_SYSTEM_NAME; + } + +} diff --git a/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaMappingService.java b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaMappingService.java new file mode 100644 index 00000000..afe1f3af --- /dev/null +++ b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaMappingService.java @@ -0,0 +1,61 @@ +package tech.powerjob.remote.akka; + +import com.google.common.collect.Maps; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import tech.powerjob.common.RemoteConstant; + +import java.util.Map; + +/** + * 构建 Actor Mapping + * + * @author tjq + * @since 2023/1/7 + */ +public class AkkaMappingService { + + /** + * Actor's RootPath -> Akka Actor Name + */ + private static final Map RP_2_ACTOR_CFG = Maps.newHashMap(); + + static { + addMappingRule(RemoteConstant.S4W_PATH, "server_actor", "w-r-c-d"); + addMappingRule(RemoteConstant.S4S_PATH, "friend_actor", "friend-request-actor-dispatcher"); + + addMappingRule(RemoteConstant.WTT_PATH, "task_tracker", "task-tracker-dispatcher"); + addMappingRule(RemoteConstant.WPT_PATH, "processor_tracker", "processor-tracker-dispatcher"); + } + + private static final String DEFAULT_DISPATCH_NAME = "common-dispatcher"; + + /** + * 根据 actor 的 rootPath 获取 Akka Actor Name,不存在改写则使用当前路径 + * @param actorRootPath actorRootPath + * @return actorName + */ + public static ActorConfig parseActorName(String actorRootPath) { + return RP_2_ACTOR_CFG.getOrDefault(actorRootPath, + new ActorConfig() + .setActorName(actorRootPath) + .setDispatcherName(DEFAULT_DISPATCH_NAME) + ); + } + + @Getter + @Setter + @Accessors(chain = true) + public static class ActorConfig { + private String actorName; + private String dispatcherName; + } + + private static void addMappingRule(String newActorPath, String oldActorName, String dispatchName) { + ActorConfig actorConfig = new ActorConfig() + .setActorName(oldActorName) + .setDispatcherName(dispatchName == null ? DEFAULT_DISPATCH_NAME : dispatchName); + RP_2_ACTOR_CFG.put(newActorPath, actorConfig); + } +} diff --git a/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaProtocol.java b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaProtocol.java new file mode 100644 index 00000000..ca1a7a66 --- /dev/null +++ b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaProtocol.java @@ -0,0 +1,16 @@ +package tech.powerjob.remote.akka; + +import tech.powerjob.remote.framework.transporter.Protocol; + +/** + * AkkaProtocol + * + * @author tjq + * @since 2022/12/31 + */ +public class AkkaProtocol implements Protocol { + @Override + public String name() { + return tech.powerjob.common.enums.Protocol.AKKA.name(); + } +} diff --git a/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaProxyActor.java b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaProxyActor.java new file mode 100644 index 00000000..af4f9441 --- /dev/null +++ b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaProxyActor.java @@ -0,0 +1,70 @@ +package tech.powerjob.remote.akka; + +import akka.actor.AbstractActor; +import akka.actor.Props; +import akka.japi.pf.ReceiveBuilder; +import lombok.extern.slf4j.Slf4j; +import tech.powerjob.common.exception.PowerJobException; +import tech.powerjob.remote.framework.actor.ActorInfo; +import tech.powerjob.remote.framework.actor.HandlerInfo; +import tech.powerjob.remote.framework.base.HandlerLocation; +import tech.powerjob.remote.framework.utils.RemoteUtils; + +import java.lang.reflect.Method; +import java.util.Optional; + +/** + * 代理用的 actor + * + * @author tjq + * @since 2023/1/6 + */ +@Slf4j +public class AkkaProxyActor extends AbstractActor { + + private final Receive receive; + private final ActorInfo actorInfo; + + public static Props props(ActorInfo actorInfo) { + return Props.create(AkkaProxyActor.class, () -> new AkkaProxyActor(actorInfo)); + } + + public AkkaProxyActor(ActorInfo actorInfo) { + this.actorInfo = actorInfo; + final ReceiveBuilder receiveBuilder = receiveBuilder(); + actorInfo.getHandlerInfos().forEach(handlerInfo -> { + final HandlerLocation location = handlerInfo.getLocation(); + final Method handlerMethod = handlerInfo.getMethod(); + final Optional> powerSerializeClz = RemoteUtils.findPowerSerialize(handlerMethod.getParameterTypes()); + if (!powerSerializeClz.isPresent()) { + throw new PowerJobException("build proxy for handler failed due to handler args is not PowerSerialize: " + location); + } + final Class bindClz = powerSerializeClz.get(); + receiveBuilder.match(bindClz, req -> onReceiveProcessorReportTaskStatusReq(req, handlerInfo)); + }); + this.receive = receiveBuilder.build(); + } + + @Override + public Receive createReceive() { + return receive; + } + + private void onReceiveProcessorReportTaskStatusReq(T req, HandlerInfo handlerInfo) { + + try { + final Object ret = handlerInfo.getMethod().invoke(actorInfo.getActor(), req); + if (ret == null) { + return; + } + if (ret instanceof Optional) { + if (!((Optional) ret).isPresent()) { + return; + } + } + getSender().tell(ret, getSelf()); + } catch (Exception e) { + log.error("[PowerJob-AKKA] process failed!", e); + } + } +} diff --git a/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaTransporter.java b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaTransporter.java new file mode 100644 index 00000000..0151fd14 --- /dev/null +++ b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaTransporter.java @@ -0,0 +1,69 @@ +package tech.powerjob.remote.akka; + +import akka.actor.ActorSelection; +import akka.actor.ActorSystem; +import akka.pattern.Patterns; +import tech.powerjob.common.PowerSerializable; +import tech.powerjob.common.RemoteConstant; +import tech.powerjob.common.utils.CommonUtils; +import tech.powerjob.remote.framework.base.HandlerLocation; +import tech.powerjob.remote.framework.base.RemotingException; +import tech.powerjob.remote.framework.base.URL; +import tech.powerjob.remote.framework.transporter.Protocol; +import tech.powerjob.remote.framework.transporter.Transporter; + +import java.time.Duration; +import java.util.concurrent.CompletionStage; + +/** + * AkkaTransporter + * + * @author tjq + * @since 2022/12/31 + */ +public class AkkaTransporter implements Transporter { + + private final ActorSystem actorSystem; + + + /** + * akka://@:/ + */ + private static final String AKKA_NODE_PATH = "akka://%s@%s/user/%s"; + + public AkkaTransporter(ActorSystem actorSystem) { + this.actorSystem = actorSystem; + } + + @Override + public Protocol getProtocol() { + return new AkkaProtocol(); + } + + @Override + public void tell(URL url, PowerSerializable request) { + ActorSelection actorSelection = fetchActorSelection(url); + actorSelection.tell(request, null); + } + + @Override + @SuppressWarnings("unchecked") + public CompletionStage ask(URL url, PowerSerializable request, Class clz) throws RemotingException { + ActorSelection actorSelection = fetchActorSelection(url); + return (CompletionStage) Patterns.ask(actorSelection, request, Duration.ofMillis(RemoteConstant.DEFAULT_TIMEOUT_MS)); + } + + private ActorSelection fetchActorSelection(URL url) { + + HandlerLocation location = url.getLocation(); + String targetActorSystemName = AkkaConstant.fetchActorSystemName(url.getServerType()); + + String targetActorName = AkkaMappingService.parseActorName(location.getRootPath()).getActorName(); + + CommonUtils.requireNonNull(targetActorName, "can't find actor by URL: " + location); + + String address = url.getAddress().toFullAddress(); + + return actorSystem.actorSelection(String.format(AKKA_NODE_PATH, targetActorSystemName, address, targetActorName)); + } +} diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/actors/TroubleshootingActor.java b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaTroubleshootingActor.java similarity index 60% rename from powerjob-worker/src/main/java/tech/powerjob/worker/actors/TroubleshootingActor.java rename to powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaTroubleshootingActor.java index 1996f7cd..80153648 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/actors/TroubleshootingActor.java +++ b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/AkkaTroubleshootingActor.java @@ -1,17 +1,17 @@ -package tech.powerjob.worker.actors; +package tech.powerjob.remote.akka; import akka.actor.AbstractActor; import akka.actor.DeadLetter; import lombok.extern.slf4j.Slf4j; /** - * 处理系统异常的 Actor + * TroubleshootingActor * - * @author 朱八 - * @since 2020/7/16 + * @author tjq + * @since 2022/12/31 */ @Slf4j -public class TroubleshootingActor extends AbstractActor { +public class AkkaTroubleshootingActor extends AbstractActor { @Override public Receive createReceive() { return receiveBuilder() @@ -20,6 +20,6 @@ public class TroubleshootingActor extends AbstractActor { } public void onReceiveDeadLetter(DeadLetter dl) { - log.warn("[TroubleshootingActor] receive DeadLetter: {}", dl); + log.warn("[PowerJob-AKKA] receive DeadLetter: {}", dl); } } diff --git a/powerjob-common/src/main/java/tech/powerjob/common/serialize/PowerAkkaSerializer.java b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/PowerAkkaSerializer.java similarity index 87% rename from powerjob-common/src/main/java/tech/powerjob/common/serialize/PowerAkkaSerializer.java rename to powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/PowerAkkaSerializer.java index 2f05dd6f..ba58b841 100644 --- a/powerjob-common/src/main/java/tech/powerjob/common/serialize/PowerAkkaSerializer.java +++ b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/PowerAkkaSerializer.java @@ -1,6 +1,7 @@ -package tech.powerjob.common.serialize; +package tech.powerjob.remote.akka; import akka.serialization.JSerializer; +import tech.powerjob.common.serialize.SerializerUtils; /** * Using custom serializers for akka-remote @@ -31,3 +32,4 @@ public class PowerAkkaSerializer extends JSerializer { return false; } } + diff --git a/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/package-info.java b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/package-info.java new file mode 100644 index 00000000..b5ce4d7e --- /dev/null +++ b/powerjob-remote/powerjob-remote-impl-akka/src/main/java/tech/powerjob/remote/akka/package-info.java @@ -0,0 +1,8 @@ +/** + * 由于 AKKA 后续转向收费运营模式,PowerJob 计划移除 akka 支持,因此不再维护该 module。 + * 如果存在任何使用上的问题,请切换到其他通讯协议(建议使用 HTTP) + * + * @author PowerJob发言人 + * @since 2022/12/31 + */ +package tech.powerjob.remote.akka; \ No newline at end of file diff --git a/powerjob-remote/powerjob-remote-impl-akka/src/main/resources/powerjob.akka.conf b/powerjob-remote/powerjob-remote-impl-akka/src/main/resources/powerjob.akka.conf new file mode 100644 index 00000000..8bd7ab69 --- /dev/null +++ b/powerjob-remote/powerjob-remote-impl-akka/src/main/resources/powerjob.akka.conf @@ -0,0 +1,133 @@ +akka { + + loggers = ["akka.event.slf4j.Slf4jLogger"] + loglevel = "WARNING" + + actor { + # cluster is better(recommend by official document), but I prefer remote + provider = remote + allow-java-serialization = off + + serializers { + power-serializer = "tech.powerjob.remote.akka.PowerAkkaSerializer" + } + + serialization-bindings { + "tech.powerjob.common.PowerSerializable" = power-serializer + } + } + remote { + artery { + transport = tcp # See Selecting a transport below + # over write by code + canonical.hostname = "127.0.0.1" + canonical.port = 25520 + } + } + + # dispatcher + task-tracker-dispatcher { + # Dispatcher is the name of the event-based dispatcher + type = Dispatcher + # What kind of ExecutionService to use + executor = "fork-join-executor" + # Configuration for the fork join pool + fork-join-executor { + # Min number of threads to cap factor-based parallelism number to + parallelism-min = 2 + # Parallelism (threads) ... ceil(available processors * factor) + parallelism-factor = 4.0 + # Max number of threads to cap factor-based parallelism number to + parallelism-max = 64 + } + # Throughput defines the maximum number of messages to be + # processed per actor before the thread jumps to the next actor. + # Set to 1 for as fair as possible. + throughput = 10 + } + + processor-tracker-dispatcher { + type = Dispatcher + executor = "fork-join-executor" + fork-join-executor { + parallelism-min = 2 + parallelism-factor = 2.0 + parallelism-max = 64 + } + throughput = 10 + } + + worker-common-dispatcher { + type = Dispatcher + executor = "fork-join-executor" + fork-join-executor { + parallelism-min = 2 + parallelism-factor = 2.0 + parallelism-max = 8 + } + throughput = 10 + } + + ##################### server config ##################### + # worker-request-core-dispatcher + w-r-c-d { + # Dispatcher is the name of the event-based dispatcher + type = Dispatcher + # What kind of ExecutionService to use + executor = "fork-join-executor" + # Configuration for the fork join pool + fork-join-executor { + # Min number of threads to cap factor-based parallelism number to + parallelism-min = 2 + # Parallelism (threads) ... ceil(available processors * factor) + parallelism-factor = 4.0 + # Max number of threads to cap factor-based parallelism number to + parallelism-max = 128 + } + # Throughput defines the maximum number of messages to be + # processed per actor before the thread jumps to the next actor. + # Set to 1 for as fair as possible. + throughput = 10 + } + + friend-request-actor-dispatcher { + # Dispatcher is the name of the event-based dispatcher + type = Dispatcher + # What kind of ExecutionService to use + executor = "fork-join-executor" + # Configuration for the fork join pool + fork-join-executor { + # Min number of threads to cap factor-based parallelism number to + parallelism-min = 2 + # Parallelism (threads) ... ceil(available processors * factor) + parallelism-factor = 4.0 + # Max number of threads to cap factor-based parallelism number to + parallelism-max = 128 + } + # Throughput defines the maximum number of messages to be + # processed per actor before the thread jumps to the next actor. + # Set to 1 for as fair as possible. + throughput = 5 + } + + ##################### default config ##################### + common-dispatcher { + # Dispatcher is the name of the event-based dispatcher + type = Dispatcher + # What kind of ExecutionService to use + executor = "fork-join-executor" + # Configuration for the fork join pool + fork-join-executor { + # Min number of threads to cap factor-based parallelism number to + parallelism-min = 2 + # Parallelism (threads) ... ceil(available processors * factor) + parallelism-factor = 4.0 + # Max number of threads to cap factor-based parallelism number to + parallelism-max = 64 + } + # Throughput defines the maximum number of messages to be + # processed per actor before the thread jumps to the next actor. + # Set to 1 for as fair as possible. + throughput = 10 + } +} \ No newline at end of file diff --git a/powerjob-remote/powerjob-remote-impl-http/pom.xml b/powerjob-remote/powerjob-remote-impl-http/pom.xml new file mode 100644 index 00000000..a7348987 --- /dev/null +++ b/powerjob-remote/powerjob-remote-impl-http/pom.xml @@ -0,0 +1,49 @@ + + + + powerjob-remote + tech.powerjob + 4.0.0 + + 4.0.0 + + powerjob-remote-impl-http + 4.3.0 + + + 8 + 8 + UTF-8 + + 4.3.7 + 4.3.0 + + + + + + tech.powerjob + powerjob-remote-framework + ${powerjob-remote-framework.version} + + + + + io.vertx + vertx-core + ${vertx.version} + + + + io.vertx + vertx-web + ${vertx.version} + + + + + + + \ No newline at end of file diff --git a/powerjob-remote/powerjob-remote-impl-http/src/main/java/tech/powerjob/remote/http/HttpProtocol.java b/powerjob-remote/powerjob-remote-impl-http/src/main/java/tech/powerjob/remote/http/HttpProtocol.java new file mode 100644 index 00000000..2e874652 --- /dev/null +++ b/powerjob-remote/powerjob-remote-impl-http/src/main/java/tech/powerjob/remote/http/HttpProtocol.java @@ -0,0 +1,17 @@ +package tech.powerjob.remote.http; + +import tech.powerjob.remote.framework.transporter.Protocol; + +/** + * HttpProtocol + * + * @author tjq + * @since 2022/12/31 + */ +public class HttpProtocol implements Protocol { + + @Override + public String name() { + return tech.powerjob.common.enums.Protocol.HTTP.name(); + } +} diff --git a/powerjob-remote/powerjob-remote-impl-http/src/main/java/tech/powerjob/remote/http/HttpVertxCSInitializer.java b/powerjob-remote/powerjob-remote-impl-http/src/main/java/tech/powerjob/remote/http/HttpVertxCSInitializer.java new file mode 100644 index 00000000..9d37a6e8 --- /dev/null +++ b/powerjob-remote/powerjob-remote-impl-http/src/main/java/tech/powerjob/remote/http/HttpVertxCSInitializer.java @@ -0,0 +1,145 @@ +package tech.powerjob.remote.http; + +import io.netty.handler.codec.http.HttpResponseStatus; +import io.vertx.core.Handler; +import io.vertx.core.Vertx; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpServer; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RequestBody; +import io.vertx.ext.web.Route; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.BodyHandler; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import tech.powerjob.common.exception.PowerJobException; +import tech.powerjob.remote.framework.actor.ActorInfo; +import tech.powerjob.remote.framework.actor.HandlerInfo; +import tech.powerjob.remote.framework.actor.ProcessType; +import tech.powerjob.remote.framework.cs.CSInitializer; +import tech.powerjob.remote.framework.cs.CSInitializerConfig; +import tech.powerjob.remote.framework.transporter.Transporter; +import tech.powerjob.remote.framework.utils.RemoteUtils; +import tech.powerjob.remote.http.vertx.VertxInitializer; +import tech.powerjob.remote.http.vertx.VertxTransporter; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +/** + * HttpCSInitializer + * 在纠结了1晚上后,最终决定选用 vertx 作为 http 底层,而不是直接使用 netty,理由如下: + * - netty 实现容易,但性能调优方面需要时间成本和实践经验,而 vertx 作为 netty 的"嫡系"框架,对 netty 的封装理论上炉火纯青,性能不成问题 + * - vertx 唯一的缺点是其作为相对上层的框架,可能存在较为严重的包冲突问题,尤其是对于那些本身跑在 vertx-framework 上的用户 + * - 不过该问题可以通过更换协议解决,预计后续提供一个基于 netty 和自定义协议的实现 + * + * @author tjq + * @since 2022/12/31 + */ +@Slf4j +public class HttpVertxCSInitializer implements CSInitializer { + + private Vertx vertx; + private HttpServer httpServer; + private HttpClient httpClient; + + private CSInitializerConfig config; + + @Override + public String type() { + return tech.powerjob.common.enums.Protocol.HTTP.name(); + } + + @Override + public void init(CSInitializerConfig config) { + this.config = config; + vertx = VertxInitializer.buildVertx(); + httpServer = VertxInitializer.buildHttpServer(vertx); + httpClient = VertxInitializer.buildHttpClient(vertx); + } + + @Override + public Transporter buildTransporter() { + return new VertxTransporter(httpClient); + } + + @Override + @SneakyThrows + public void bindHandlers(List actorInfos) { + Router router = Router.router(vertx); + // 处理请求响应 + router.route().handler(BodyHandler.create()); + actorInfos.forEach(actorInfo -> { + Optional.ofNullable(actorInfo.getHandlerInfos()).orElse(Collections.emptyList()).forEach(handlerInfo -> { + String handlerHttpPath = handlerInfo.getLocation().toPath(); + ProcessType processType = handlerInfo.getAnno().processType(); + + Handler routingContextHandler = buildRequestHandler(actorInfo, handlerInfo); + Route route = router.post(handlerHttpPath); + if (processType == ProcessType.BLOCKING) { + route.blockingHandler(routingContextHandler, false); + } else { + route.handler(routingContextHandler); + } + }); + }); + + // 启动 vertx http server + final int port = config.getBindAddress().getPort(); + final String host = config.getBindAddress().getHost(); + + httpServer.requestHandler(router) + .exceptionHandler(e -> log.error("[PowerJob] unknown exception in Actor communication!", e)) + .listen(port, host) + .toCompletionStage() + .toCompletableFuture() + .get(1, TimeUnit.MINUTES); + + log.info("[PowerJobRemoteEngine] startup vertx HttpServer successfully!"); + } + + private Handler buildRequestHandler(ActorInfo actorInfo, HandlerInfo handlerInfo) { + Method method = handlerInfo.getMethod(); + Optional> powerSerializeClz = RemoteUtils.findPowerSerialize(method.getParameterTypes()); + + // 内部框架,严格模式,绑定失败直接报错 + if (!powerSerializeClz.isPresent()) { + throw new PowerJobException("can't find any 'PowerSerialize' object in handler args: " + handlerInfo.getLocation()); + } + + return ctx -> { + final RequestBody body = ctx.body(); + final Object convertResult = body.asPojo(powerSerializeClz.get()); + try { + Object response = method.invoke(actorInfo.getActor(), convertResult); + if (response != null) { + if (response instanceof String) { + ctx.end((String) response); + } else { + ctx.json(JsonObject.mapFrom(response)); + } + return; + } + + ctx.end(); + } catch (Throwable t) { + // 注意这里是框架实际运行时,日志输出用标准 PowerJob 格式 + log.error("[PowerJob] invoke Handler[{}] failed!", handlerInfo.getLocation(), t); + ctx.fail(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), t); + } + }; + } + + + @Override + public void close() throws IOException { + httpClient.close(); + httpServer.close(); + vertx.close(); + } +} diff --git a/powerjob-remote/powerjob-remote-impl-http/src/main/java/tech/powerjob/remote/http/vertx/VertxInitializer.java b/powerjob-remote/powerjob-remote-impl-http/src/main/java/tech/powerjob/remote/http/vertx/VertxInitializer.java new file mode 100644 index 00000000..d74c0591 --- /dev/null +++ b/powerjob-remote/powerjob-remote-impl-http/src/main/java/tech/powerjob/remote/http/vertx/VertxInitializer.java @@ -0,0 +1,87 @@ +package tech.powerjob.remote.http.vertx; + +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpClientOptions; +import io.vertx.core.http.HttpServer; +import io.vertx.core.http.HttpServerOptions; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import tech.powerjob.common.OmsConstant; +import tech.powerjob.common.PowerJobDKey; + +/** + * VertxInitializer + * PowerJob 只是将 vertx 作为 toolkit 使用 + * + * @author tjq + * @since 2023/1/1 + */ +@Slf4j +public class VertxInitializer { + + /** + * 默认开启长连接,且 75S 超时 + */ + private static final int DEFAULT_KEEP_ALIVE_TIMEOUT = 75; + + private static final int CONNECTION_TIMEOUT_MS = 3000; + + private static final int SERVER_IDLE_TIMEOUT_S = 300; + + public static Vertx buildVertx() { + final int cpuCores = Runtime.getRuntime().availableProcessors(); + VertxOptions options = new VertxOptions() + .setWorkerPoolSize(Math.max(16, 2 * cpuCores)) + .setInternalBlockingPoolSize(Math.max(32, 4 * cpuCores)); + log.info("[PowerJob-Vertx] use vertx options: {}", options); + return Vertx.vertx(options); + } + + public static HttpServer buildHttpServer(Vertx vertx) { + HttpServerOptions httpServerOptions = new HttpServerOptions() + .setIdleTimeout(SERVER_IDLE_TIMEOUT_S); + tryEnableCompression(httpServerOptions); + log.info("[PowerJob-Vertx] use HttpServerOptions: {}", httpServerOptions.toJson()); + return vertx.createHttpServer(httpServerOptions); + } + private static void tryEnableCompression(HttpServerOptions httpServerOptions) { + // 非核心组件,不直接依赖类(无 import),加载报错可忽略 + try { + httpServerOptions + .addCompressor(io.netty.handler.codec.compression.StandardCompressionOptions.gzip()) + .setCompressionSupported(true); + log.warn("[PowerJob-Vertx] enable server side compression successfully!"); + } catch (Exception e) { + log.warn("[PowerJob-Vertx] enable server side compression failed!", e); + } + } + + public static HttpClient buildHttpClient(Vertx vertx) { + + HttpClientOptions httpClientOptions = new HttpClientOptions() + .setMetricsName(OmsConstant.PACKAGE) + .setConnectTimeout(CONNECTION_TIMEOUT_MS) + .setMaxPoolSize(Math.max(8, Runtime.getRuntime().availableProcessors()) * 2); + + // 长连接 + String keepaliveTimeout = System.getProperty(PowerJobDKey.TRANSPORTER_KEEP_ALIVE_TIMEOUT, String.valueOf(DEFAULT_KEEP_ALIVE_TIMEOUT)); + int keepaliveTimeoutInt = Integer.parseInt(keepaliveTimeout); + if (keepaliveTimeoutInt > 0) { + httpClientOptions.setKeepAlive(true).setKeepAliveTimeout(keepaliveTimeoutInt); + } else { + httpClientOptions.setKeepAlive(false); + } + + // 压缩判定 + String enableCompressing = System.getProperty(PowerJobDKey.TRANSPORTER_USE_COMPRESSING); + if (StringUtils.isNotEmpty(enableCompressing)) { + httpClientOptions.setTryUseCompression(StringUtils.equalsIgnoreCase(enableCompressing, Boolean.TRUE.toString())); + } + + log.info("[PowerJob-Vertx] use HttpClientOptions: {}", httpClientOptions.toJson()); + return vertx.createHttpClient(httpClientOptions); + } + +} diff --git a/powerjob-remote/powerjob-remote-impl-http/src/main/java/tech/powerjob/remote/http/vertx/VertxTransporter.java b/powerjob-remote/powerjob-remote-impl-http/src/main/java/tech/powerjob/remote/http/vertx/VertxTransporter.java new file mode 100644 index 00000000..e4b545fc --- /dev/null +++ b/powerjob-remote/powerjob-remote-impl-http/src/main/java/tech/powerjob/remote/http/vertx/VertxTransporter.java @@ -0,0 +1,85 @@ +package tech.powerjob.remote.http.vertx; + +import io.netty.handler.codec.http.HttpResponseStatus; +import io.vertx.core.Future; +import io.vertx.core.http.*; +import io.vertx.core.json.JsonObject; +import tech.powerjob.common.PowerSerializable; +import tech.powerjob.remote.framework.base.RemotingException; +import tech.powerjob.remote.framework.base.URL; +import tech.powerjob.remote.framework.transporter.Protocol; +import tech.powerjob.remote.framework.transporter.Transporter; +import tech.powerjob.remote.http.HttpProtocol; + +import java.util.concurrent.CompletionStage; + +/** + * VertxTransporter + * + * @author tjq + * @since 2023/1/1 + */ +public class VertxTransporter implements Transporter { + + private final HttpClient httpClient; + + private static final Protocol PROTOCOL = new HttpProtocol(); + + public VertxTransporter(HttpClient httpClient) { + this.httpClient = httpClient; + } + + @Override + public Protocol getProtocol() { + return PROTOCOL; + } + + @Override + public void tell(URL url, PowerSerializable request) { + post(url, request, null); + } + + @Override + public CompletionStage ask(URL url, PowerSerializable request, Class clz) throws RemotingException { + return post(url, request, clz); + } + + @SuppressWarnings("unchecked") + private CompletionStage post(URL url, PowerSerializable request, Class clz) { + final String host = url.getAddress().getHost(); + final int port = url.getAddress().getPort(); + final String path = url.getLocation().toPath(); + RequestOptions requestOptions = new RequestOptions() + .setMethod(HttpMethod.POST) + .setHost(host) + .setPort(port) + .setURI(path); + // 获取远程服务器的HTTP连接 + Future httpClientRequestFuture = httpClient.request(requestOptions); + // 转换 -> 发送请求获取响应 + Future responseFuture = httpClientRequestFuture.compose(httpClientRequest -> httpClientRequest.send(JsonObject.mapFrom(request).toBuffer())); + return responseFuture.compose(httpClientResponse -> { + // throw exception + final int statusCode = httpClientResponse.statusCode(); + if (statusCode != HttpResponseStatus.OK.code()) { + // CompletableFuture.get() 时会传递抛出该异常 + throw new RemotingException(String.format("request [host:%s,port:%s,url:%s] failed, status: %d, msg: %s", + host, port, path, statusCode, httpClientResponse.statusMessage() + )); + } + + return httpClientResponse.body().compose(x -> { + + if (clz == null) { + return Future.succeededFuture(null); + } + + if (clz.equals(String.class)) { + return Future.succeededFuture((T) x.toString()); + } + + return Future.succeededFuture(x.toJsonObject().mapTo(clz)); + }); + }).toCompletionStage(); + } +} diff --git a/powerjob-remote/powerjob-remote-impl-http/src/test/java/tech/powerjob/remote/http/HttpVertxCSInitializerTest.java b/powerjob-remote/powerjob-remote-impl-http/src/test/java/tech/powerjob/remote/http/HttpVertxCSInitializerTest.java new file mode 100644 index 00000000..a009293b --- /dev/null +++ b/powerjob-remote/powerjob-remote-impl-http/src/test/java/tech/powerjob/remote/http/HttpVertxCSInitializerTest.java @@ -0,0 +1,77 @@ +package tech.powerjob.remote.http; + +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import tech.powerjob.common.enums.Protocol; +import tech.powerjob.common.utils.CommonUtils; +import tech.powerjob.remote.framework.BenchmarkActor; +import tech.powerjob.remote.framework.base.Address; +import tech.powerjob.remote.framework.base.HandlerLocation; +import tech.powerjob.remote.framework.base.URL; +import tech.powerjob.remote.framework.engine.EngineConfig; +import tech.powerjob.remote.framework.engine.EngineOutput; +import tech.powerjob.remote.framework.engine.RemoteEngine; +import tech.powerjob.remote.framework.engine.impl.PowerJobRemoteEngine; +import tech.powerjob.remote.framework.transporter.Transporter; + +import java.util.concurrent.CompletionStage; +import java.util.concurrent.TimeUnit; + +/** + * HttpVertxCSInitializerTest + * + * @author tjq + * @since 2023/1/2 + */ +@Slf4j +class HttpVertxCSInitializerTest { + + @Test + void testHttpVertxCSInitializerTest() throws Exception { + + final Address address = new Address().setPort(7890).setHost("127.0.0.1"); + + EngineConfig engineConfig = new EngineConfig() + .setType(Protocol.HTTP.name()) + .setBindAddress(address) + .setActorList(Lists.newArrayList(new BenchmarkActor())); + + RemoteEngine engine = new PowerJobRemoteEngine(); + EngineOutput engineOutput = engine.start(engineConfig); + log.info("[HttpVertxCSInitializerTest] engine start up successfully!"); + Transporter transporter = engineOutput.getTransporter(); + + BenchmarkActor.BenchmarkRequest request = new BenchmarkActor.BenchmarkRequest() + .setContent("request from test") + .setBlockingMills(100) + .setResponseSize(10240); + + log.info("[HttpVertxCSInitializerTest] test empty request!"); + URL emptyURL = new URL() + .setAddress(address) + .setLocation(new HandlerLocation().setMethodPath("emptyReturn").setRootPath("benchmark")); + transporter.tell(emptyURL, request); + + log.info("[HttpVertxCSInitializerTest] test string request!"); + URL stringURL = new URL() + .setAddress(address) + .setLocation(new HandlerLocation().setMethodPath("stringReturn").setRootPath("benchmark")); + final String strResponse = transporter.ask(stringURL, request, String.class).toCompletableFuture().get(); + log.info("[HttpVertxCSInitializerTest] strResponse: {}", strResponse); + + log.info("[HttpVertxCSInitializerTest] test normal request!"); + URL url = new URL() + .setAddress(address) + .setLocation(new HandlerLocation().setMethodPath("standard").setRootPath("benchmark")); + + final CompletionStage benchmarkResponseCompletionStage = transporter.ask(url, request, BenchmarkActor.BenchmarkResponse.class); + final BenchmarkActor.BenchmarkResponse response = benchmarkResponseCompletionStage.toCompletableFuture().get(10, TimeUnit.SECONDS); + log.info("[HttpVertxCSInitializerTest] response: {}", response); + + + + + CommonUtils.easySleep(10000); + } +} \ No newline at end of file diff --git a/powerjob-server/pom.xml b/powerjob-server/pom.xml index 37f64bbc..e1eb723a 100644 --- a/powerjob-server/pom.xml +++ b/powerjob-server/pom.xml @@ -5,12 +5,12 @@ powerjob tech.powerjob - 3.0.0 + 4.0.0 4.0.0 powerjob-server - 4.2.1 + 4.3.0 pom @@ -28,7 +28,7 @@ 2.9.2 2.7.4 - 4.2.1 + 8.0.30 19.7.0.0 @@ -43,13 +43,16 @@ 3.8.0 1.2.83 1.0.1 - 4.0.2 true 3.0.10 9.1.6 + + 4.3.0 + 4.3.0 + 4.3.0 @@ -99,11 +102,21 @@ - + tech.powerjob powerjob-common - ${powerjob.common.version} + ${powerjob-common.version} + + + tech.powerjob + powerjob-remote-impl-http + ${powerjob-remote-impl-http.version} + + + tech.powerjob + powerjob-remote-impl-akka + ${powerjob-remote-impl-akka.version} @@ -245,17 +258,6 @@ - - io.vertx - vertx-web - ${vertx-web.version} - - - io.vertx - vertx-web-client - ${vertx-web.version} - - com.cronutils cron-utils @@ -267,6 +269,12 @@ io.springfox springfox-swagger2 ${swagger.version} + + + guava + com.google.guava + + diff --git a/powerjob-server/powerjob-server-common/pom.xml b/powerjob-server/powerjob-server-common/pom.xml index 227e65b1..c45da96b 100644 --- a/powerjob-server/powerjob-server-common/pom.xml +++ b/powerjob-server/powerjob-server-common/pom.xml @@ -5,7 +5,7 @@ powerjob-server tech.powerjob - 4.2.1 + 4.3.0 ../pom.xml 4.0.0 diff --git a/powerjob-server/powerjob-server-common/src/main/java/tech/powerjob/server/common/module/ServerInfo.java b/powerjob-server/powerjob-server-common/src/main/java/tech/powerjob/server/common/module/ServerInfo.java index d2c004fa..c264b258 100644 --- a/powerjob-server/powerjob-server-common/src/main/java/tech/powerjob/server/common/module/ServerInfo.java +++ b/powerjob-server/powerjob-server-common/src/main/java/tech/powerjob/server/common/module/ServerInfo.java @@ -15,5 +15,7 @@ public class ServerInfo { private String ip; + private long bornTime; + private String version = "UNKNOWN"; } diff --git a/powerjob-server/powerjob-server-core/pom.xml b/powerjob-server/powerjob-server-core/pom.xml index 2a3e13ea..8b3d02b3 100644 --- a/powerjob-server/powerjob-server-core/pom.xml +++ b/powerjob-server/powerjob-server-core/pom.xml @@ -5,7 +5,7 @@ powerjob-server tech.powerjob - 4.2.1 + 4.3.0 ../pom.xml 4.0.0 diff --git a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/DispatchService.java b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/DispatchService.java index 4a80cf87..b0821b62 100644 --- a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/DispatchService.java +++ b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/DispatchService.java @@ -3,14 +3,18 @@ package tech.powerjob.server.core; import com.google.common.collect.Lists; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; import tech.powerjob.common.RemoteConstant; import tech.powerjob.common.SystemInstanceResult; -import tech.powerjob.common.enums.*; +import tech.powerjob.common.enums.ExecuteType; +import tech.powerjob.common.enums.InstanceStatus; +import tech.powerjob.common.enums.ProcessorType; +import tech.powerjob.common.enums.TimeExpressionType; import tech.powerjob.common.request.ServerScheduleJobReq; +import tech.powerjob.remote.framework.base.URL; import tech.powerjob.server.common.Holder; import tech.powerjob.server.common.module.WorkerInfo; import tech.powerjob.server.core.instance.InstanceManager; @@ -19,7 +23,8 @@ import tech.powerjob.server.core.lock.UseCacheLock; import tech.powerjob.server.persistence.remote.model.InstanceInfoDO; import tech.powerjob.server.persistence.remote.model.JobInfoDO; import tech.powerjob.server.persistence.remote.repository.InstanceInfoRepository; -import tech.powerjob.server.remote.transport.TransportService; +import tech.powerjob.server.remote.transporter.TransportService; +import tech.powerjob.server.remote.transporter.impl.ServerURLFactory; import tech.powerjob.server.remote.worker.WorkerClusterQueryService; import java.util.ArrayList; @@ -165,7 +170,8 @@ public class DispatchService { WorkerInfo taskTracker = suitableWorkers.get(0); String taskTrackerAddress = taskTracker.getAddress(); - transportService.tell(Protocol.of(taskTracker.getProtocol()), taskTrackerAddress, req); + URL workerUrl = ServerURLFactory.dispatchJob2Worker(taskTrackerAddress); + transportService.tell(taskTracker.getProtocol(), workerUrl, req); log.info("[Dispatcher-{}|{}] send schedule request to TaskTracker[protocol:{},address:{}] successfully: {}.", jobId, instanceId, taskTracker.getProtocol(), taskTrackerAddress, req); // 修改状态 diff --git a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/container/ContainerService.java b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/container/ContainerService.java index 56dc22ee..35de0310 100644 --- a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/container/ContainerService.java +++ b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/container/ContainerService.java @@ -1,25 +1,5 @@ package tech.powerjob.server.core.container; -import tech.powerjob.common.OmsConstant; -import tech.powerjob.common.enums.Protocol; -import tech.powerjob.common.model.DeployedContainerInfo; -import tech.powerjob.common.model.GitRepoInfo; -import tech.powerjob.common.request.ServerDeployContainerRequest; -import tech.powerjob.common.request.ServerDestroyContainerRequest; -import tech.powerjob.common.utils.CommonUtils; -import tech.powerjob.common.serialize.JsonUtils; -import tech.powerjob.common.utils.NetUtils; -import tech.powerjob.common.utils.SegmentLock; -import tech.powerjob.server.common.constants.ContainerSourceType; -import tech.powerjob.server.common.constants.SwitchableStatus; -import tech.powerjob.server.common.utils.OmsFileUtils; -import tech.powerjob.server.extension.LockService; -import tech.powerjob.server.persistence.remote.model.ContainerInfoDO; -import tech.powerjob.server.persistence.remote.repository.ContainerInfoRepository; -import tech.powerjob.server.persistence.mongodb.GridFsManager; -import tech.powerjob.server.remote.transport.TransportService; -import tech.powerjob.server.remote.worker.WorkerClusterQueryService; -import tech.powerjob.server.common.module.WorkerInfo; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; @@ -28,6 +8,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.FileFilterUtils; import org.apache.commons.io.filefilter.IOFileFilter; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.time.DateFormatUtils; import org.apache.maven.shared.invoker.DefaultInvocationRequest; @@ -43,8 +24,29 @@ import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; -import org.apache.commons.lang3.StringUtils; import org.springframework.web.multipart.MultipartFile; +import tech.powerjob.common.OmsConstant; +import tech.powerjob.common.model.DeployedContainerInfo; +import tech.powerjob.common.model.GitRepoInfo; +import tech.powerjob.common.request.ServerDeployContainerRequest; +import tech.powerjob.common.request.ServerDestroyContainerRequest; +import tech.powerjob.common.serialize.JsonUtils; +import tech.powerjob.common.utils.CommonUtils; +import tech.powerjob.common.utils.NetUtils; +import tech.powerjob.common.utils.SegmentLock; +import tech.powerjob.remote.framework.base.URL; +import tech.powerjob.server.common.constants.ContainerSourceType; +import tech.powerjob.server.common.constants.SwitchableStatus; +import tech.powerjob.server.common.module.WorkerInfo; +import tech.powerjob.server.common.utils.OmsFileUtils; +import tech.powerjob.server.extension.LockService; +import tech.powerjob.server.persistence.mongodb.GridFsManager; +import tech.powerjob.server.persistence.remote.model.ContainerInfoDO; +import tech.powerjob.server.persistence.remote.repository.ContainerInfoRepository; +import tech.powerjob.server.remote.server.redirector.DesignateServer; +import tech.powerjob.server.remote.transporter.impl.ServerURLFactory; +import tech.powerjob.server.remote.transporter.TransportService; +import tech.powerjob.server.remote.worker.WorkerClusterQueryService; import javax.annotation.Resource; import javax.websocket.RemoteEndpoint; @@ -128,7 +130,8 @@ public class ContainerService { ServerDestroyContainerRequest destroyRequest = new ServerDestroyContainerRequest(container.getId()); workerClusterQueryService.getAllAliveWorkers(container.getAppId()).forEach(workerInfo -> { - transportService.tell(Protocol.AKKA, workerInfo.getAddress(), destroyRequest); + final URL url = ServerURLFactory.destroyContainer2Worker(workerInfo.getAddress()); + transportService.tell(workerInfo.getProtocol(), url, destroyRequest); }); log.info("[ContainerService] delete container: {}.", container); @@ -245,13 +248,11 @@ public class ContainerService { container.setGmtModified(now); container.setLastDeployTime(now); containerInfoRepository.saveAndFlush(container); + remote.sendText(String.format("SYSTEM: update current container version=%s successfully!", container.getVersion())); // 开始部署(需要分批进行) - Set workerAddressList = workerClusterQueryService.getAllAliveWorkers(container.getAppId()) - .stream() - .map(WorkerInfo::getAddress) - .collect(Collectors.toSet()); - if (workerAddressList.isEmpty()) { + final List allAliveWorkers = workerClusterQueryService.getAllAliveWorkers(container.getAppId()); + if (allAliveWorkers.isEmpty()) { remote.sendText("SYSTEM: there is no worker available now, deploy failed!"); return; } @@ -262,10 +263,12 @@ public class ContainerService { long sleepTime = calculateSleepTime(jarFile.length()); AtomicInteger count = new AtomicInteger(); - workerAddressList.forEach(akkaAddress -> { - transportService.tell(Protocol.AKKA, akkaAddress, req); + allAliveWorkers.forEach(workerInfo -> { - remote.sendText("SYSTEM: send deploy request to " + akkaAddress); + final URL url = ServerURLFactory.deployContainer2Worker(workerInfo.getAddress()); + transportService.tell(workerInfo.getProtocol(), url, req); + + remote.sendText("SYSTEM: send deploy request to " + url.getAddress()); if (count.incrementAndGet() % DEPLOY_BATCH_NUM == 0) { CommonUtils.executeIgnoreException(() -> Thread.sleep(sleepTime)); @@ -285,6 +288,7 @@ public class ContainerService { * @param containerId 容器ID * @return 拼接好的可阅读字符串 */ + @DesignateServer public String fetchDeployedInfo(Long appId, Long containerId) { List infoList = workerClusterQueryService.getDeployedContainerInfos(appId, containerId); @@ -417,7 +421,10 @@ public class ContainerService { FileUtils.copyFile(jarWithDependency, localFile); return localFile; - }finally { + } catch (Throwable t) { + log.error("[ContainerService] prepareJarFile failed for container: {}", container, t); + remote.sendText("SYSTEM: [ERROR] prepare jar file failed: " + ExceptionUtils.getStackTrace(t)); + } finally { // 删除工作区数据 FileUtils.forceDelete(workerDir); } diff --git a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/AbWorkerRequestHandler.java b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/AbWorkerRequestHandler.java index b0e8047c..90c0e59e 100644 --- a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/AbWorkerRequestHandler.java +++ b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/AbWorkerRequestHandler.java @@ -10,6 +10,8 @@ import tech.powerjob.common.request.*; import tech.powerjob.common.response.AskResponse; import tech.powerjob.common.serialize.JsonUtils; import tech.powerjob.common.utils.NetUtils; +import tech.powerjob.remote.framework.actor.Handler; +import tech.powerjob.remote.framework.actor.ProcessType; import tech.powerjob.server.common.constants.SwitchableStatus; import tech.powerjob.server.common.module.WorkerInfo; import tech.powerjob.server.common.utils.SpringUtils; @@ -28,6 +30,8 @@ import java.util.Optional; import java.util.concurrent.RejectedExecutionException; import java.util.stream.Collectors; +import static tech.powerjob.common.RemoteConstant.*; + /** * wrapper monitor for IWorkerRequestHandler * @@ -49,12 +53,13 @@ public abstract class AbWorkerRequestHandler implements IWorkerRequestHandler { protected abstract void processWorkerHeartbeat0(WorkerHeartbeat heartbeat, WorkerHeartbeatEvent event); - protected abstract Optional processTaskTrackerReportInstanceStatus0(TaskTrackerReportInstanceStatusReq req, TtReportInstanceStatusEvent event) throws Exception; + protected abstract AskResponse processTaskTrackerReportInstanceStatus0(TaskTrackerReportInstanceStatusReq req, TtReportInstanceStatusEvent event) throws Exception; protected abstract void processWorkerLogReport0(WorkerLogReportReq req, WorkerLogReportEvent event); @Override + @Handler(path = S4W_HANDLER_WORKER_HEARTBEAT, processType = ProcessType.NO_BLOCKING) public void processWorkerHeartbeat(WorkerHeartbeat heartbeat) { long startMs = System.currentTimeMillis(); WorkerHeartbeatEvent event = new WorkerHeartbeatEvent() @@ -71,7 +76,8 @@ public abstract class AbWorkerRequestHandler implements IWorkerRequestHandler { } @Override - public Optional processTaskTrackerReportInstanceStatus(TaskTrackerReportInstanceStatusReq req) { + @Handler(path = S4W_HANDLER_REPORT_INSTANCE_STATUS, processType = ProcessType.BLOCKING) + public AskResponse processTaskTrackerReportInstanceStatus(TaskTrackerReportInstanceStatusReq req) { long startMs = System.currentTimeMillis(); TtReportInstanceStatusEvent event = new TtReportInstanceStatusEvent() .setAppId(req.getAppId()) @@ -86,7 +92,7 @@ public abstract class AbWorkerRequestHandler implements IWorkerRequestHandler { } catch (Exception e) { event.setServerProcessStatus(TtReportInstanceStatusEvent.Status.FAILED); log.error("[WorkerRequestHandler] processTaskTrackerReportInstanceStatus failed for request: {}", req, e); - return Optional.of(AskResponse.failed(ExceptionUtils.getMessage(e))); + return AskResponse.failed(ExceptionUtils.getMessage(e)); } finally { event.setServerProcessCost(System.currentTimeMillis() - startMs); monitorService.monitor(event); @@ -94,6 +100,7 @@ public abstract class AbWorkerRequestHandler implements IWorkerRequestHandler { } @Override + @Handler(path = S4W_HANDLER_REPORT_LOG, processType = ProcessType.NO_BLOCKING) public void processWorkerLogReport(WorkerLogReportReq req) { WorkerLogReportEvent event = new WorkerLogReportEvent() @@ -113,6 +120,7 @@ public abstract class AbWorkerRequestHandler implements IWorkerRequestHandler { } @Override + @Handler(path = S4W_HANDLER_QUERY_JOB_CLUSTER, processType = ProcessType.BLOCKING) public AskResponse processWorkerQueryExecutorCluster(WorkerQueryExecutorClusterReq req) { AskResponse askResponse; @@ -137,6 +145,7 @@ public abstract class AbWorkerRequestHandler implements IWorkerRequestHandler { } @Override + @Handler(path = S4W_HANDLER_WORKER_NEED_DEPLOY_CONTAINER, processType = ProcessType.BLOCKING) public AskResponse processWorkerNeedDeployContainer(WorkerNeedDeployContainerRequest req) { String port = environment.getProperty("local.server.port"); diff --git a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/IWorkerRequestHandler.java b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/IWorkerRequestHandler.java index 6fe30e70..7b11ded6 100644 --- a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/IWorkerRequestHandler.java +++ b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/IWorkerRequestHandler.java @@ -3,8 +3,6 @@ package tech.powerjob.server.core.handler; import tech.powerjob.common.request.*; import tech.powerjob.common.response.AskResponse; -import java.util.Optional; - /** * 定义 server 与 worker 之间需要处理的协议 * @@ -24,7 +22,7 @@ public interface IWorkerRequestHandler { * @param req 上报请求 * @return 响应信息 */ - Optional processTaskTrackerReportInstanceStatus(TaskTrackerReportInstanceStatusReq req); + AskResponse processTaskTrackerReportInstanceStatus(TaskTrackerReportInstanceStatusReq req); /** * 处理 worker 查询执行器集群 @@ -34,7 +32,7 @@ public interface IWorkerRequestHandler { AskResponse processWorkerQueryExecutorCluster(WorkerQueryExecutorClusterReq req); /** - * 处理 worker 日志推送请求 + * 处理 worker 日志推送请求(内部使用线程池异步处理,非阻塞) * @param req 请求 */ void processWorkerLogReport(WorkerLogReportReq req); diff --git a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/WorkerRequestHandlerHolder.java b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/WorkerRequestHandlerHolder.java deleted file mode 100644 index f9c267cc..00000000 --- a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/WorkerRequestHandlerHolder.java +++ /dev/null @@ -1,27 +0,0 @@ -package tech.powerjob.server.core.handler; - -import org.springframework.stereotype.Component; - - -/** - * WorkerRequestHandlerHolder - * - * @author tjq - * @since 2022/9/11 - */ -@Component -public class WorkerRequestHandlerHolder { - - private static IWorkerRequestHandler workerRequestHandler; - - public WorkerRequestHandlerHolder(IWorkerRequestHandler injectedWorkerRequestHandler) { - workerRequestHandler = injectedWorkerRequestHandler; - } - - public static IWorkerRequestHandler fetchWorkerRequestHandler() { - if (workerRequestHandler == null){ - throw new IllegalStateException("WorkerRequestHandlerHolder not initialized!"); - } - return workerRequestHandler; - } -} diff --git a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/WorkerRequestHandlerImpl.java b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/WorkerRequestHandlerImpl.java index ed4e9206..129571e0 100644 --- a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/WorkerRequestHandlerImpl.java +++ b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/WorkerRequestHandlerImpl.java @@ -4,11 +4,13 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; +import tech.powerjob.common.RemoteConstant; import tech.powerjob.common.enums.InstanceStatus; import tech.powerjob.common.request.TaskTrackerReportInstanceStatusReq; import tech.powerjob.common.request.WorkerHeartbeat; import tech.powerjob.common.request.WorkerLogReportReq; import tech.powerjob.common.response.AskResponse; +import tech.powerjob.remote.framework.actor.Actor; import tech.powerjob.server.core.instance.InstanceLogService; import tech.powerjob.server.core.instance.InstanceManager; import tech.powerjob.server.core.workflow.WorkflowInstanceManager; @@ -20,8 +22,6 @@ import tech.powerjob.server.persistence.remote.repository.ContainerInfoRepositor import tech.powerjob.server.remote.worker.WorkerClusterManagerService; import tech.powerjob.server.remote.worker.WorkerClusterQueryService; -import java.util.Optional; - /** * receive and process worker's request * @@ -30,6 +30,7 @@ import java.util.Optional; */ @Slf4j @Component +@Actor(path = RemoteConstant.S4W_PATH) public class WorkerRequestHandlerImpl extends AbWorkerRequestHandler { private final InstanceManager instanceManager; @@ -52,7 +53,7 @@ public class WorkerRequestHandlerImpl extends AbWorkerRequestHandler { } @Override - protected Optional processTaskTrackerReportInstanceStatus0(TaskTrackerReportInstanceStatusReq req, TtReportInstanceStatusEvent event) throws Exception { + protected AskResponse processTaskTrackerReportInstanceStatus0(TaskTrackerReportInstanceStatusReq req, TtReportInstanceStatusEvent event) throws Exception { // 2021/02/05 如果是工作流中的实例先尝试更新上下文信息,再更新实例状态,这里一定不会有异常 if (req.getWfInstanceId() != null && !CollectionUtils.isEmpty(req.getAppendedWfContext())) { // 更新工作流上下文信息 @@ -63,9 +64,10 @@ public class WorkerRequestHandlerImpl extends AbWorkerRequestHandler { // 结束状态(成功/失败)需要回复消息 if (InstanceStatus.FINISHED_STATUS.contains(req.getInstanceStatus())) { - return Optional.of(AskResponse.succeed(null)); + return AskResponse.succeed(null); } - return Optional.empty(); + + return null; } @Override diff --git a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/impl/Initializer.java b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/impl/Initializer.java deleted file mode 100644 index d34d7469..00000000 --- a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/impl/Initializer.java +++ /dev/null @@ -1,28 +0,0 @@ -package tech.powerjob.server.core.handler.impl; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; -import tech.powerjob.common.RemoteConstant; -import tech.powerjob.server.remote.transport.starter.AkkaStarter; -import tech.powerjob.server.remote.transport.starter.VertXStarter; - -import javax.annotation.PostConstruct; - -/** - * 初始化器 - * - * @author tjq - * @since 2022/9/11 - */ -@Component -@ConditionalOnExpression("'${execution.env}'!='test'") -public class Initializer { - - @PostConstruct - public void initHandler() { - // init akka - AkkaStarter.actorSystem.actorOf(WorkerRequestAkkaHandler.defaultProps(), RemoteConstant.SERVER_ACTOR_NAME); - // init vert.x - VertXStarter.vertx.deployVerticle(new WorkerRequestHttpHandler()); - } -} diff --git a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/impl/WorkerRequestAkkaHandler.java b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/impl/WorkerRequestAkkaHandler.java deleted file mode 100644 index 26f18be0..00000000 --- a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/impl/WorkerRequestAkkaHandler.java +++ /dev/null @@ -1,100 +0,0 @@ -package tech.powerjob.server.core.handler.impl; - -import akka.actor.AbstractActor; -import akka.actor.Props; -import akka.routing.DefaultResizer; -import akka.routing.RoundRobinPool; -import tech.powerjob.common.request.*; -import tech.powerjob.common.response.AskResponse; -import lombok.extern.slf4j.Slf4j; - -import java.util.Optional; - -import static tech.powerjob.server.core.handler.WorkerRequestHandlerHolder.fetchWorkerRequestHandler; - -/** - * 处理 Worker 请求 - * - * @author tjq - * @since 2020/3/30 - */ -@Slf4j -public class WorkerRequestAkkaHandler extends AbstractActor { - - - public static Props defaultProps(){ - return Props.create(WorkerRequestAkkaHandler.class) - .withDispatcher("akka.w-r-c-d") - .withRouter( - new RoundRobinPool(Runtime.getRuntime().availableProcessors() * 4) - .withResizer(new DefaultResizer( - Runtime.getRuntime().availableProcessors() * 4, - Runtime.getRuntime().availableProcessors() * 10, - 1, - 0.2d, - 0.3d, - 0.1d, - 10 - )) - ); - } - - @Override - public Receive createReceive() { - return receiveBuilder() - .match(WorkerHeartbeat.class, hb -> fetchWorkerRequestHandler().processWorkerHeartbeat(hb)) - .match(TaskTrackerReportInstanceStatusReq.class, this::onReceiveTaskTrackerReportInstanceStatusReq) - .match(WorkerLogReportReq.class, req -> fetchWorkerRequestHandler().processWorkerLogReport(req)) - .match(WorkerNeedDeployContainerRequest.class, this::onReceiveWorkerNeedDeployContainerRequest) - .match(WorkerQueryExecutorClusterReq.class, this::onReceiveWorkerQueryExecutorClusterReq) - .matchAny(obj -> log.warn("[WorkerRequestAkkaHandler] receive unknown request: {}.", obj)) - .build(); - } - - @Override - public void preStart() throws Exception { - super.preStart(); - log.debug("[WorkerRequestAkkaHandler]init WorkerRequestActor"); - } - - - @Override - public void postStop() throws Exception { - super.postStop(); - log.debug("[WorkerRequestAkkaHandler]stop WorkerRequestActor"); - } - - /** - * 处理 instance 状态 - * @param req 任务实例的状态上报请求 - */ - private void onReceiveTaskTrackerReportInstanceStatusReq(TaskTrackerReportInstanceStatusReq req) { - - try { - Optional askResponseOpt = fetchWorkerRequestHandler().processTaskTrackerReportInstanceStatus(req); - if (askResponseOpt.isPresent()) { - getSender().tell(AskResponse.succeed(null), getSelf()); - } - }catch (Exception e) { - log.error("[WorkerRequestAkkaHandler] update instance status failed for request: {}.", req, e); - } - } - - /** - * 处理 Worker容器部署请求 - * @param req 容器部署请求 - */ - private void onReceiveWorkerNeedDeployContainerRequest(WorkerNeedDeployContainerRequest req) { - getSender().tell(fetchWorkerRequestHandler().processWorkerNeedDeployContainer(req), getSelf()); - } - - /** - * 处理 worker 请求获取当前任务所有处理器节点的请求 - * @param req jobId + appId - */ - private void onReceiveWorkerQueryExecutorClusterReq(WorkerQueryExecutorClusterReq req) { - - getSender().tell(fetchWorkerRequestHandler().processWorkerQueryExecutorCluster(req), getSelf()); - } - -} diff --git a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/impl/WorkerRequestHttpHandler.java b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/impl/WorkerRequestHttpHandler.java deleted file mode 100644 index 3749158f..00000000 --- a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/handler/impl/WorkerRequestHttpHandler.java +++ /dev/null @@ -1,81 +0,0 @@ -package tech.powerjob.server.core.handler.impl; - -import tech.powerjob.common.OmsConstant; -import tech.powerjob.common.ProtocolConstant; -import tech.powerjob.common.request.TaskTrackerReportInstanceStatusReq; -import tech.powerjob.common.request.WorkerHeartbeat; -import tech.powerjob.common.request.WorkerLogReportReq; -import tech.powerjob.common.response.AskResponse; -import tech.powerjob.common.response.ResultDTO; -import tech.powerjob.server.common.PowerJobServerConfigKey; -import tech.powerjob.server.common.utils.PropertyUtils; -import io.vertx.core.AbstractVerticle; -import io.vertx.core.http.HttpServer; -import io.vertx.core.http.HttpServerOptions; -import io.vertx.core.json.JsonObject; -import io.vertx.ext.web.Router; -import io.vertx.ext.web.RoutingContext; -import io.vertx.ext.web.handler.BodyHandler; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.exception.ExceptionUtils; - -import java.util.Properties; - -import static tech.powerjob.server.core.handler.WorkerRequestHandlerHolder.fetchWorkerRequestHandler; - -/** - * WorkerRequestHandler - * - * @author tjq - * @since 2021/2/8 - */ -@Slf4j -public class WorkerRequestHttpHandler extends AbstractVerticle { - - @Override - public void start() throws Exception { - - Properties properties = PropertyUtils.getProperties(); - int port = Integer.parseInt(properties.getProperty(PowerJobServerConfigKey.HTTP_PORT, String.valueOf(OmsConstant.SERVER_DEFAULT_HTTP_PORT))); - - HttpServerOptions options = new HttpServerOptions(); - HttpServer server = vertx.createHttpServer(options); - - Router router = Router.router(vertx); - router.route().handler(BodyHandler.create()); - router.post(ProtocolConstant.SERVER_PATH_HEARTBEAT) - .handler(ctx -> { - WorkerHeartbeat heartbeat = ctx.getBodyAsJson().mapTo(WorkerHeartbeat.class); - fetchWorkerRequestHandler().processWorkerHeartbeat(heartbeat); - success(ctx); - }); - router.post(ProtocolConstant.SERVER_PATH_STATUS_REPORT) - .blockingHandler(ctx -> { - TaskTrackerReportInstanceStatusReq req = ctx.getBodyAsJson().mapTo(TaskTrackerReportInstanceStatusReq.class); - try { - fetchWorkerRequestHandler().processTaskTrackerReportInstanceStatus(req); - out(ctx, AskResponse.succeed(null)); - } catch (Exception e) { - log.error("[WorkerRequestHttpHandler] update instance status failed for request: {}.", req, e); - out(ctx, AskResponse.failed(ExceptionUtils.getMessage(e))); - } - }); - router.post(ProtocolConstant.SERVER_PATH_LOG_REPORT) - .blockingHandler(ctx -> { - WorkerLogReportReq req = ctx.getBodyAsJson().mapTo(WorkerLogReportReq.class); - fetchWorkerRequestHandler().processWorkerLogReport(req); - success(ctx); - }); - server.requestHandler(router).listen(port); - } - - private static void out(RoutingContext ctx, Object msg) { - ctx.response() - .putHeader(OmsConstant.HTTP_HEADER_CONTENT_TYPE, OmsConstant.JSON_MEDIA_TYPE) - .end(JsonObject.mapFrom(msg).encode()); - } - - private static void success(RoutingContext ctx) { - out(ctx, ResultDTO.success(null)); - } -} diff --git a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/instance/InstanceManager.java b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/instance/InstanceManager.java index 40f066e1..ee810483 100644 --- a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/instance/InstanceManager.java +++ b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/instance/InstanceManager.java @@ -2,15 +2,15 @@ package tech.powerjob.server.core.instance; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; -import org.apache.commons.lang3.StringUtils; import tech.powerjob.common.enums.InstanceStatus; -import tech.powerjob.common.enums.Protocol; import tech.powerjob.common.enums.TimeExpressionType; import tech.powerjob.common.model.LifeCycle; import tech.powerjob.common.request.ServerStopInstanceReq; import tech.powerjob.common.request.TaskTrackerReportInstanceStatusReq; +import tech.powerjob.remote.framework.base.URL; import tech.powerjob.server.common.module.WorkerInfo; import tech.powerjob.server.common.timewheel.holder.HashedWheelTimerHolder; import tech.powerjob.server.common.utils.SpringUtils; @@ -22,10 +22,10 @@ import tech.powerjob.server.persistence.remote.model.InstanceInfoDO; import tech.powerjob.server.persistence.remote.model.JobInfoDO; import tech.powerjob.server.persistence.remote.model.UserInfoDO; import tech.powerjob.server.persistence.remote.repository.InstanceInfoRepository; -import tech.powerjob.server.remote.transport.TransportService; +import tech.powerjob.server.remote.transporter.impl.ServerURLFactory; +import tech.powerjob.server.remote.transporter.TransportService; import tech.powerjob.server.remote.worker.WorkerClusterQueryService; -import javax.annotation.Resource; import java.util.Date; import java.util.List; import java.util.Optional; @@ -176,7 +176,8 @@ public class InstanceManager { if (workerInfoOpt.isPresent()) { ServerStopInstanceReq stopInstanceReq = new ServerStopInstanceReq(instanceId); WorkerInfo workerInfo = workerInfoOpt.get(); - transportService.tell(Protocol.of(workerInfo.getProtocol()), workerInfo.getAddress(), stopInstanceReq); + final URL url = ServerURLFactory.stopInstance2Worker(workerInfo.getAddress()); + transportService.tell(workerInfo.getProtocol(), url, stopInstanceReq); } } diff --git a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/instance/InstanceService.java b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/instance/InstanceService.java index 2d465b96..53071a6d 100644 --- a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/instance/InstanceService.java +++ b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/instance/InstanceService.java @@ -5,15 +5,16 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import tech.powerjob.common.PowerQuery; +import tech.powerjob.common.RemoteConstant; import tech.powerjob.common.SystemInstanceResult; import tech.powerjob.common.enums.InstanceStatus; -import tech.powerjob.common.enums.Protocol; import tech.powerjob.common.exception.PowerJobException; import tech.powerjob.common.model.InstanceDetail; import tech.powerjob.common.request.ServerQueryInstanceStatusReq; import tech.powerjob.common.request.ServerStopInstanceReq; import tech.powerjob.common.response.AskResponse; import tech.powerjob.common.response.InstanceInfoDTO; +import tech.powerjob.remote.framework.base.URL; import tech.powerjob.server.common.constants.InstanceType; import tech.powerjob.server.common.module.WorkerInfo; import tech.powerjob.server.common.timewheel.TimerFuture; @@ -26,12 +27,14 @@ import tech.powerjob.server.persistence.remote.model.JobInfoDO; import tech.powerjob.server.persistence.remote.repository.InstanceInfoRepository; import tech.powerjob.server.persistence.remote.repository.JobInfoRepository; import tech.powerjob.server.remote.server.redirector.DesignateServer; -import tech.powerjob.server.remote.transport.TransportService; +import tech.powerjob.server.remote.transporter.impl.ServerURLFactory; +import tech.powerjob.server.remote.transporter.TransportService; import tech.powerjob.server.remote.worker.WorkerClusterQueryService; import java.util.Date; import java.util.List; import java.util.Optional; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static tech.powerjob.common.enums.InstanceStatus.RUNNING; @@ -136,7 +139,7 @@ public class InstanceService { if (workerInfoOpt.isPresent()) { ServerStopInstanceReq req = new ServerStopInstanceReq(instanceId); WorkerInfo workerInfo = workerInfoOpt.get(); - transportService.tell(Protocol.of(workerInfo.getProtocol()), workerInfo.getAddress(), req); + transportService.tell(workerInfo.getProtocol(), ServerURLFactory.stopInstance2Worker(workerInfo.getAddress()), req); log.info("[Instance-{}] update instanceInfo and send 'stopInstance' request succeed.", instanceId); } else { log.warn("[Instance-{}] update instanceInfo successfully but can't find TaskTracker to stop instance", instanceId); @@ -280,7 +283,10 @@ public class InstanceService { WorkerInfo workerInfo = workerInfoOpt.get(); ServerQueryInstanceStatusReq req = new ServerQueryInstanceStatusReq(instanceId); try { - AskResponse askResponse = transportService.ask(Protocol.of(workerInfo.getProtocol()), workerInfo.getAddress(), req); + final URL url = ServerURLFactory.queryInstance2Worker(workerInfo.getAddress()); + AskResponse askResponse = transportService.ask(workerInfo.getProtocol(), url, req, AskResponse.class) + .toCompletableFuture() + .get(RemoteConstant.DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS); if (askResponse.isSuccess()) { InstanceDetail instanceDetail = askResponse.getData(InstanceDetail.class); instanceDetail.setRunningTimes(instanceInfoDO.getRunningTimes()); diff --git a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/scheduler/InstanceStatusCheckService.java b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/scheduler/InstanceStatusCheckService.java index 879cd749..4652904f 100644 --- a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/scheduler/InstanceStatusCheckService.java +++ b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/scheduler/InstanceStatusCheckService.java @@ -22,9 +22,8 @@ import tech.powerjob.server.persistence.remote.model.WorkflowInfoDO; import tech.powerjob.server.persistence.remote.model.WorkflowInstanceInfoDO; import tech.powerjob.server.persistence.remote.model.brief.BriefInstanceInfo; import tech.powerjob.server.persistence.remote.repository.*; -import tech.powerjob.server.remote.transport.starter.AkkaStarter; +import tech.powerjob.server.remote.transporter.TransportService; -import javax.annotation.Resource; import java.util.*; import java.util.stream.Collectors; @@ -49,6 +48,8 @@ public class InstanceStatusCheckService { public static final long CHECK_INTERVAL = 10000; + private final TransportService transportService; + private final DispatchService dispatchService; private final InstanceManager instanceManager; @@ -61,7 +62,6 @@ public class InstanceStatusCheckService { private final InstanceInfoRepository instanceInfoRepository; - private final WorkflowInfoRepository workflowInfoRepository; private final WorkflowInstanceInfoRepository workflowInstanceInfoRepository; @@ -69,7 +69,7 @@ public class InstanceStatusCheckService { public void checkWorkflowInstance() { Stopwatch stopwatch = Stopwatch.createStarted(); // 查询 DB 获取该 Server 需要负责的 AppGroup - List allAppIds = appInfoRepository.listAppIdByCurrentServer(AkkaStarter.getActorSystemAddress()); + List allAppIds = appInfoRepository.listAppIdByCurrentServer(transportService.defaultProtocol().getAddress()); if (CollectionUtils.isEmpty(allAppIds)) { log.info("[InstanceStatusChecker] current server has no app's job to check"); return; @@ -89,7 +89,7 @@ public class InstanceStatusCheckService { public void checkWaitingDispatchInstance() { Stopwatch stopwatch = Stopwatch.createStarted(); // 查询 DB 获取该 Server 需要负责的 AppGroup - List allAppIds = appInfoRepository.listAppIdByCurrentServer(AkkaStarter.getActorSystemAddress()); + List allAppIds = appInfoRepository.listAppIdByCurrentServer(transportService.defaultProtocol().getAddress()); if (CollectionUtils.isEmpty(allAppIds)) { log.info("[InstanceStatusChecker] current server has no app's job to check"); return; @@ -110,7 +110,7 @@ public class InstanceStatusCheckService { public void checkWaitingWorkerReceiveInstance() { Stopwatch stopwatch = Stopwatch.createStarted(); // 查询 DB 获取该 Server 需要负责的 AppGroup - List allAppIds = appInfoRepository.listAppIdByCurrentServer(AkkaStarter.getActorSystemAddress()); + List allAppIds = appInfoRepository.listAppIdByCurrentServer(transportService.defaultProtocol().getAddress()); if (CollectionUtils.isEmpty(allAppIds)) { log.info("[InstanceStatusChecker] current server has no app's job to check"); return; @@ -131,7 +131,7 @@ public class InstanceStatusCheckService { public void checkRunningInstance() { Stopwatch stopwatch = Stopwatch.createStarted(); // 查询 DB 获取该 Server 需要负责的 AppGroup - List allAppIds = appInfoRepository.listAppIdByCurrentServer(AkkaStarter.getActorSystemAddress()); + List allAppIds = appInfoRepository.listAppIdByCurrentServer(transportService.defaultProtocol().getAddress()); if (CollectionUtils.isEmpty(allAppIds)) { log.info("[InstanceStatusChecker] current server has no app's job to check"); return; diff --git a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/scheduler/PowerScheduleService.java b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/scheduler/PowerScheduleService.java index 33bf97f3..5f65fa07 100644 --- a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/scheduler/PowerScheduleService.java +++ b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/scheduler/PowerScheduleService.java @@ -23,10 +23,9 @@ import tech.powerjob.server.persistence.remote.repository.AppInfoRepository; import tech.powerjob.server.persistence.remote.repository.InstanceInfoRepository; import tech.powerjob.server.persistence.remote.repository.JobInfoRepository; import tech.powerjob.server.persistence.remote.repository.WorkflowInfoRepository; -import tech.powerjob.server.remote.transport.starter.AkkaStarter; +import tech.powerjob.server.remote.transporter.TransportService; import tech.powerjob.server.remote.worker.WorkerClusterManagerService; -import javax.annotation.Resource; import java.util.*; /** @@ -47,6 +46,7 @@ public class PowerScheduleService { */ private static final int MAX_APP_NUM = 10; + private final TransportService transportService; private final DispatchService dispatchService; private final InstanceService instanceService; @@ -72,7 +72,7 @@ public class PowerScheduleService { long start = System.currentTimeMillis(); // 调度 CRON 表达式 JOB try { - final List allAppIds = appInfoRepository.listAppIdByCurrentServer(AkkaStarter.getActorSystemAddress()); + final List allAppIds = appInfoRepository.listAppIdByCurrentServer(transportService.defaultProtocol().getAddress()); if (CollectionUtils.isEmpty(allAppIds)) { log.info("[CronJobSchedule] current server has no app's job to schedule."); return; @@ -92,7 +92,7 @@ public class PowerScheduleService { long start = System.currentTimeMillis(); // 调度 CRON 表达式 WORKFLOW try { - final List allAppIds = appInfoRepository.listAppIdByCurrentServer(AkkaStarter.getActorSystemAddress()); + final List allAppIds = appInfoRepository.listAppIdByCurrentServer(transportService.defaultProtocol().getAddress()); if (CollectionUtils.isEmpty(allAppIds)) { log.info("[CronWorkflowSchedule] current server has no app's workflow to schedule."); return; @@ -113,7 +113,7 @@ public class PowerScheduleService { long start = System.currentTimeMillis(); // 调度 FIX_RATE/FIX_DELAY 表达式 JOB try { - final List allAppIds = appInfoRepository.listAppIdByCurrentServer(AkkaStarter.getActorSystemAddress()); + final List allAppIds = appInfoRepository.listAppIdByCurrentServer(transportService.defaultProtocol().getAddress()); if (CollectionUtils.isEmpty(allAppIds)) { log.info("[FrequentJobSchedule] current server has no app's job to schedule."); return; @@ -132,7 +132,7 @@ public class PowerScheduleService { public void cleanData() { try { - final List allAppIds = appInfoRepository.listAppIdByCurrentServer(AkkaStarter.getActorSystemAddress()); + final List allAppIds = appInfoRepository.listAppIdByCurrentServer(transportService.defaultProtocol().getAddress()); if (allAppIds.isEmpty()) { return; } diff --git a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/service/CacheService.java b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/service/CacheService.java index 491ea328..c4ae22a4 100644 --- a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/service/CacheService.java +++ b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/service/CacheService.java @@ -44,18 +44,22 @@ public class CacheService { jobId2JobNameCache = CacheBuilder.newBuilder() .expireAfterWrite(Duration.ofMinutes(1)) .maximumSize(512) + .softValues() .build(); workflowId2WorkflowNameCache = CacheBuilder.newBuilder() .expireAfterWrite(Duration.ofMinutes(1)) .maximumSize(512) + .softValues() .build(); instanceId2AppId = CacheBuilder.newBuilder() .maximumSize(1024) + .softValues() .build(); jobId2AppId = CacheBuilder.newBuilder() .maximumSize(1024) + .softValues() .build(); } diff --git a/powerjob-server/powerjob-server-extension/pom.xml b/powerjob-server/powerjob-server-extension/pom.xml index 29ab5693..4ed8e548 100644 --- a/powerjob-server/powerjob-server-extension/pom.xml +++ b/powerjob-server/powerjob-server-extension/pom.xml @@ -5,7 +5,7 @@ powerjob-server tech.powerjob - 4.2.1 + 4.3.0 ../pom.xml 4.0.0 diff --git a/powerjob-server/powerjob-server-extension/src/main/java/tech/powerjob/server/extension/defaultimpl/alarm/impl/DingTalkAlarmService.java b/powerjob-server/powerjob-server-extension/src/main/java/tech/powerjob/server/extension/defaultimpl/alarm/impl/DingTalkAlarmService.java index b42d9c05..57eb4d35 100644 --- a/powerjob-server/powerjob-server-extension/src/main/java/tech/powerjob/server/extension/defaultimpl/alarm/impl/DingTalkAlarmService.java +++ b/powerjob-server/powerjob-server-extension/src/main/java/tech/powerjob/server/extension/defaultimpl/alarm/impl/DingTalkAlarmService.java @@ -1,25 +1,24 @@ package tech.powerjob.server.extension.defaultimpl.alarm.impl; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; import tech.powerjob.common.OmsConstant; import tech.powerjob.common.exception.PowerJobException; import tech.powerjob.common.utils.NetUtils; import tech.powerjob.server.common.PowerJobServerConfigKey; import tech.powerjob.server.common.SJ; -import tech.powerjob.server.persistence.remote.model.UserInfoDO; -import tech.powerjob.server.extension.defaultimpl.alarm.module.Alarm; import tech.powerjob.server.extension.Alarmable; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.core.env.Environment; -import org.springframework.stereotype.Service; +import tech.powerjob.server.extension.defaultimpl.alarm.module.Alarm; +import tech.powerjob.server.persistence.remote.model.UserInfoDO; import javax.annotation.PostConstruct; -import javax.annotation.Resource; import java.util.List; import java.util.Set; @@ -108,7 +107,7 @@ public class DingTalkAlarmService implements Alarmable { } this.agentId = Long.valueOf(agentId); dingTalkUtils = new DingTalkUtils(appKey, appSecret); - mobile2UserIdCache = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).build(); + mobile2UserIdCache = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).softValues().build(); log.info("[DingTalkAlarmService] init DingTalkAlarmService successfully!"); } diff --git a/powerjob-server/powerjob-server-migrate/pom.xml b/powerjob-server/powerjob-server-migrate/pom.xml index 9b8b32b6..282cee69 100644 --- a/powerjob-server/powerjob-server-migrate/pom.xml +++ b/powerjob-server/powerjob-server-migrate/pom.xml @@ -5,7 +5,7 @@ powerjob-server tech.powerjob - 4.2.1 + 4.3.0 ../pom.xml 4.0.0 diff --git a/powerjob-server/powerjob-server-monitor/pom.xml b/powerjob-server/powerjob-server-monitor/pom.xml index c54bb0df..ccf24dc4 100644 --- a/powerjob-server/powerjob-server-monitor/pom.xml +++ b/powerjob-server/powerjob-server-monitor/pom.xml @@ -5,7 +5,7 @@ powerjob-server tech.powerjob - 4.2.1 + 4.3.0 ../pom.xml 4.0.0 diff --git a/powerjob-server/powerjob-server-persistence/pom.xml b/powerjob-server/powerjob-server-persistence/pom.xml index a5e6b9f4..bf54efb6 100644 --- a/powerjob-server/powerjob-server-persistence/pom.xml +++ b/powerjob-server/powerjob-server-persistence/pom.xml @@ -5,7 +5,7 @@ powerjob-server tech.powerjob - 4.2.1 + 4.3.0 ../pom.xml 4.0.0 diff --git a/powerjob-server/powerjob-server-remote/pom.xml b/powerjob-server/powerjob-server-remote/pom.xml index 878289d0..62338062 100644 --- a/powerjob-server/powerjob-server-remote/pom.xml +++ b/powerjob-server/powerjob-server-remote/pom.xml @@ -5,7 +5,7 @@ powerjob-server tech.powerjob - 4.2.1 + 4.3.0 ../pom.xml 4.0.0 diff --git a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/FriendActor.java b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/FriendActor.java new file mode 100644 index 00000000..8d7d56e3 --- /dev/null +++ b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/FriendActor.java @@ -0,0 +1,57 @@ +package tech.powerjob.server.remote.server; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.springframework.stereotype.Component; +import tech.powerjob.common.response.AskResponse; +import tech.powerjob.common.serialize.JsonUtils; +import tech.powerjob.remote.framework.actor.Actor; +import tech.powerjob.remote.framework.actor.Handler; +import tech.powerjob.remote.framework.actor.ProcessType; +import tech.powerjob.server.remote.server.election.Ping; +import tech.powerjob.server.remote.server.redirector.RemoteProcessReq; +import tech.powerjob.server.remote.server.redirector.RemoteRequestProcessor; +import tech.powerjob.server.remote.transporter.TransportService; + +import static tech.powerjob.common.RemoteConstant.*; + +/** + * 处理朋友们的信息(处理服务器与服务器之间的通讯) + * + * @author tjq + * @since 2020/4/9 + */ +@Slf4j +@Component +@Actor(path = S4S_PATH) +public class FriendActor { + + private final TransportService transportService; + + public FriendActor(TransportService transportService) { + this.transportService = transportService; + } + + /** + * 处理存活检测的请求 + */ + @Handler(path = S4S_HANDLER_PING, processType = ProcessType.NO_BLOCKING) + public AskResponse onReceivePing(Ping ping) { + return AskResponse.succeed(transportService.allProtocols()); + } + + @Handler(path = S4S_HANDLER_PROCESS, processType = ProcessType.BLOCKING) + public AskResponse onReceiveRemoteProcessReq(RemoteProcessReq req) { + + AskResponse response = new AskResponse(); + response.setSuccess(true); + try { + response.setData(JsonUtils.toBytes(RemoteRequestProcessor.processRemoteRequest(req))); + } catch (Throwable t) { + log.error("[FriendActor] process remote request[{}] failed!", req, t); + response.setSuccess(false); + response.setMessage(ExceptionUtils.getMessage(t)); + } + return response; + } +} diff --git a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/FriendRequestHandler.java b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/FriendRequestHandler.java deleted file mode 100644 index edda488e..00000000 --- a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/FriendRequestHandler.java +++ /dev/null @@ -1,86 +0,0 @@ -package tech.powerjob.server.remote.server; - -import akka.actor.AbstractActor; -import akka.actor.Props; -import akka.routing.DefaultResizer; -import akka.routing.RoundRobinPool; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.exception.ExceptionUtils; -import tech.powerjob.common.response.AskResponse; -import tech.powerjob.common.serialize.JsonUtils; -import tech.powerjob.server.remote.server.election.Ping; -import tech.powerjob.server.remote.server.redirector.RemoteProcessReq; -import tech.powerjob.server.remote.server.redirector.RemoteRequestProcessor; -import tech.powerjob.server.remote.transport.TransportService; - -/** - * 处理朋友们的信息(处理服务器与服务器之间的通讯) - * - * @author tjq - * @since 2020/4/9 - */ -@Slf4j -public class FriendRequestHandler extends AbstractActor { - - - public static Props defaultProps() { - return Props.create(FriendRequestHandler.class) - .withDispatcher("akka.friend-request-actor-dispatcher") - .withRouter( - new RoundRobinPool(Runtime.getRuntime().availableProcessors() * 4) - .withResizer(new DefaultResizer( - Runtime.getRuntime().availableProcessors() * 4, - Runtime.getRuntime().availableProcessors() * 10, - 1, - 0.2d, - 0.3d, - 0.1d, - 10 - )) - ); - } - - @Override - public Receive createReceive() { - return receiveBuilder() - .match(Ping.class, this::onReceivePing) - .match(RemoteProcessReq.class, this::onReceiveRemoteProcessReq) - .matchAny(obj -> log.warn("[FriendActor] receive unknown request: {}.", obj)) - .build(); - } - - - @Override - public void preStart() throws Exception { - super.preStart(); - log.debug("[FriendRequestHandler]init FriendRequestActor"); - } - - - @Override - public void postStop() throws Exception { - super.postStop(); - log.debug("[FriendRequestHandler]stop FriendRequestActor"); - } - - /** - * 处理存活检测的请求 - */ - private void onReceivePing(Ping ping) { - getSender().tell(AskResponse.succeed(TransportService.getAllAddress()), getSelf()); - } - - private void onReceiveRemoteProcessReq(RemoteProcessReq req) { - - AskResponse response = new AskResponse(); - response.setSuccess(true); - try { - response.setData(JsonUtils.toBytes(RemoteRequestProcessor.processRemoteRequest(req))); - } catch (Throwable t) { - log.error("[FriendActor] process remote request[{}] failed!", req, t); - response.setSuccess(false); - response.setMessage(ExceptionUtils.getMessage(t)); - } - getSender().tell(response, getSelf()); - } -} diff --git a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/election/ServerElectionService.java b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/election/ServerElectionService.java index 1db65201..5b0eb819 100644 --- a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/election/ServerElectionService.java +++ b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/election/ServerElectionService.java @@ -1,29 +1,27 @@ package tech.powerjob.server.remote.server.election; -import akka.actor.ActorSelection; -import akka.pattern.Patterns; import com.alibaba.fastjson.JSONObject; -import tech.powerjob.common.exception.PowerJobException; -import tech.powerjob.common.enums.Protocol; -import tech.powerjob.common.response.AskResponse; -import tech.powerjob.common.serialize.JsonUtils; -import tech.powerjob.server.extension.LockService; -import tech.powerjob.server.persistence.remote.model.AppInfoDO; -import tech.powerjob.server.persistence.remote.repository.AppInfoRepository; -import tech.powerjob.server.remote.transport.TransportService; -import tech.powerjob.server.remote.transport.starter.AkkaStarter; import com.google.common.collect.Sets; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import tech.powerjob.common.enums.Protocol; +import tech.powerjob.common.exception.PowerJobException; +import tech.powerjob.common.request.ServerDiscoveryRequest; +import tech.powerjob.common.response.AskResponse; +import tech.powerjob.common.serialize.JsonUtils; +import tech.powerjob.remote.framework.base.URL; +import tech.powerjob.server.extension.LockService; +import tech.powerjob.server.persistence.remote.model.AppInfoDO; +import tech.powerjob.server.persistence.remote.repository.AppInfoRepository; +import tech.powerjob.server.remote.transporter.ProtocolInfo; +import tech.powerjob.server.remote.transporter.impl.ServerURLFactory; +import tech.powerjob.server.remote.transporter.TransportService; -import javax.annotation.Resource; -import java.time.Duration; import java.util.Date; import java.util.Optional; import java.util.Set; -import java.util.concurrent.CompletionStage; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; @@ -56,18 +54,23 @@ public class ServerElectionService { this.accurateSelectServerPercentage = accurateSelectServerPercentage; } - public String elect(Long appId, String protocol, String currentServer) { + public String elect(ServerDiscoveryRequest request) { if (!accurate()) { + final String currentServer = request.getCurrentServer(); // 如果是本机,就不需要查数据库那么复杂的操作了,直接返回成功 - if (getProtocolServerAddress(protocol).equals(currentServer)) { + Optional localProtocolInfoOpt = Optional.ofNullable(transportService.allProtocols().get(request.getProtocol())); + if (localProtocolInfoOpt.isPresent() && localProtocolInfoOpt.get().getAddress().equals(currentServer)) { + log.debug("[ServerElectionService] this server[{}] is worker's current server, skip check", currentServer); return currentServer; } } - return getServer0(appId, protocol); + return getServer0(request); } - private String getServer0(Long appId, String protocol) { + private String getServer0(ServerDiscoveryRequest discoveryRequest) { + final Long appId = discoveryRequest.getAppId(); + final String protocol = discoveryRequest.getProtocol(); Set downServerCache = Sets.newHashSet(); for (int i = 0; i < RETRY_TIMES; i++) { @@ -103,17 +106,20 @@ public class ServerElectionService { return address; } - // 篡位,本机作为Server - // 注意,写入 AppInfoDO#currentServer 的永远是 ActorSystem 的地址,仅在返回的时候特殊处理 - appInfo.setCurrentServer(transportService.getTransporter(Protocol.AKKA).getAddress()); - appInfo.setGmtModified(new Date()); + // 篡位,如果本机存在协议,则作为Server调度该 worker + final ProtocolInfo targetProtocolInfo = transportService.allProtocols().get(protocol); + if (targetProtocolInfo != null) { + // 注意,写入 AppInfoDO#currentServer 的永远是 default 的地址,仅在返回的时候特殊处理为协议地址 + appInfo.setCurrentServer(transportService.defaultProtocol().getAddress()); + appInfo.setGmtModified(new Date()); - appInfoRepository.saveAndFlush(appInfo); - log.info("[ServerElection] this server({}) become the new server for app(appId={}).", appInfo.getCurrentServer(), appId); - return getProtocolServerAddress(protocol); + appInfoRepository.saveAndFlush(appInfo); + log.info("[ServerElection] this server({}) become the new server for app(appId={}).", appInfo.getCurrentServer(), appId); + return targetProtocolInfo.getAddress(); + } }catch (Exception e) { log.error("[ServerElection] write new server to db failed for app {}.", appName, e); - }finally { + } finally { lockService.unlock(lockName); } } @@ -139,16 +145,25 @@ public class ServerElectionService { Ping ping = new Ping(); ping.setCurrentTime(System.currentTimeMillis()); - ActorSelection serverActor = AkkaStarter.getFriendActor(serverAddress); + URL targetUrl = ServerURLFactory.ping2Friend(serverAddress); try { - CompletionStage askCS = Patterns.ask(serverActor, ping, Duration.ofMillis(PING_TIMEOUT_MS)); - AskResponse response = (AskResponse) askCS.toCompletableFuture().get(PING_TIMEOUT_MS, TimeUnit.MILLISECONDS); - downServerCache.remove(serverAddress); + AskResponse response = transportService.ask(Protocol.HTTP.name(), targetUrl, ping, AskResponse.class) + .toCompletableFuture() + .get(PING_TIMEOUT_MS, TimeUnit.MILLISECONDS); if (response.isSuccess()) { - return JsonUtils.parseObject(response.getData(), JSONObject.class).getString(protocol); + // 检测通过的是远程 server 的暴露地址,需要返回 worker 需要的协议地址 + final JSONObject protocolInfo = JsonUtils.parseObject(response.getData(), JSONObject.class).getJSONObject(protocol); + if (protocolInfo != null) { + downServerCache.remove(serverAddress); + final String protocolAddress = protocolInfo.toJavaObject(ProtocolInfo.class).getAddress(); + log.info("[ServerElection] server[{}] is active, it will be the master, final protocol address={}", serverAddress, protocolAddress); + return protocolAddress; + } else { + log.warn("[ServerElection] server[{}] is active but don't have target protocol", serverAddress); + } } }catch (Exception e) { - log.warn("[ServerElection] server({}) was down.", serverAddress); + log.warn("[ServerElection] server[{}] was down.", serverAddress, e); } downServerCache.add(serverAddress); return null; @@ -157,9 +172,4 @@ public class ServerElectionService { private boolean accurate() { return ThreadLocalRandom.current().nextInt(100) < accurateSelectServerPercentage; } - - private String getProtocolServerAddress(String protocol) { - Protocol pt = Protocol.of(protocol); - return TransportService.getAllAddress().get(pt); - } } diff --git a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/redirector/DesignateServerAspect.java b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/redirector/DesignateServerAspect.java index 9b5280b5..ad906487 100644 --- a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/redirector/DesignateServerAspect.java +++ b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/redirector/DesignateServerAspect.java @@ -1,17 +1,9 @@ package tech.powerjob.server.remote.server.redirector; -import akka.pattern.Patterns; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.TypeFactory; import lombok.RequiredArgsConstructor; -import tech.powerjob.common.exception.PowerJobException; -import tech.powerjob.common.RemoteConstant; -import tech.powerjob.common.response.AskResponse; -import org.springframework.core.annotation.Order; -import tech.powerjob.server.persistence.remote.model.AppInfoDO; -import tech.powerjob.server.persistence.remote.repository.AppInfoRepository; -import tech.powerjob.server.remote.transport.starter.AkkaStarter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; @@ -19,16 +11,25 @@ import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; +import tech.powerjob.common.RemoteConstant; +import tech.powerjob.common.enums.Protocol; +import tech.powerjob.common.exception.PowerJobException; +import tech.powerjob.common.response.AskResponse; +import tech.powerjob.remote.framework.base.URL; +import tech.powerjob.server.persistence.remote.model.AppInfoDO; +import tech.powerjob.server.persistence.remote.repository.AppInfoRepository; +import tech.powerjob.server.remote.transporter.impl.ServerURLFactory; +import tech.powerjob.server.remote.transporter.TransportService; -import javax.annotation.Resource; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.time.Duration; import java.util.Arrays; import java.util.Objects; import java.util.concurrent.CompletionStage; +import java.util.concurrent.TimeUnit; /** * 指定服务器运行切面 @@ -43,6 +44,7 @@ import java.util.concurrent.CompletionStage; @RequiredArgsConstructor public class DesignateServerAspect { + private final TransportService transportService; private final AppInfoRepository appInfoRepository; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); @@ -84,7 +86,7 @@ public class DesignateServerAspect { } // 目标IP与本地符合则本地执行 - if (Objects.equals(targetServer, AkkaStarter.getActorSystemAddress())) { + if (Objects.equals(targetServer, transportService.defaultProtocol().getAddress())) { return point.proceed(); } @@ -96,8 +98,10 @@ public class DesignateServerAspect { .setParameterTypes(parameterTypes) .setArgs(args); - CompletionStage askCS = Patterns.ask(AkkaStarter.getFriendActor(targetServer), remoteProcessReq, Duration.ofMillis(RemoteConstant.DEFAULT_TIMEOUT_MS)); - AskResponse askResponse = (AskResponse) askCS.toCompletableFuture().get(); + final URL friendUrl = ServerURLFactory.process2Friend(targetServer); + + CompletionStage askCS = transportService.ask(Protocol.HTTP.name(), friendUrl, remoteProcessReq, AskResponse.class); + AskResponse askResponse = askCS.toCompletableFuture().get(RemoteConstant.DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS); if (!askResponse.isSuccess()) { throw new PowerJobException("remote process failed: " + askResponse.getMessage()); diff --git a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/self/ServerInfoServiceImpl.java b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/self/ServerInfoServiceImpl.java index eb94b58b..8a205e49 100644 --- a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/self/ServerInfoServiceImpl.java +++ b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/server/self/ServerInfoServiceImpl.java @@ -1,22 +1,20 @@ package tech.powerjob.server.remote.server.self; +import com.google.common.base.Stopwatch; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.info.BuildProperties; -import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; import tech.powerjob.common.exception.PowerJobException; import tech.powerjob.common.utils.CommonUtils; import tech.powerjob.common.utils.NetUtils; -import tech.powerjob.server.common.constants.PJThreadPool; import tech.powerjob.server.common.module.ServerInfo; import tech.powerjob.server.extension.LockService; import tech.powerjob.server.persistence.remote.model.ServerInfoDO; import tech.powerjob.server.persistence.remote.repository.ServerInfoRepository; -import com.google.common.base.Stopwatch; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.time.DateUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; import java.util.Date; import java.util.List; @@ -50,6 +48,7 @@ public class ServerInfoServiceImpl implements ServerInfoService { String ip = NetUtils.getLocalHost(); serverInfo.setIp(ip); + serverInfo.setBornTime(System.currentTimeMillis()); this.serverInfoRepository = serverInfoRepository; Stopwatch sw = Stopwatch.createStarted(); diff --git a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/TransportService.java b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/TransportService.java deleted file mode 100644 index 3ee154d5..00000000 --- a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/TransportService.java +++ /dev/null @@ -1,66 +0,0 @@ -package tech.powerjob.server.remote.transport; - -import tech.powerjob.common.PowerSerializable; -import tech.powerjob.common.enums.Protocol; -import tech.powerjob.common.response.AskResponse; -import com.google.common.collect.Maps; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Map; - -/** - * TransportService - * - * @author tjq - * @since 2021/2/7 - */ -@Slf4j -@Service -public class TransportService { - - private static final Map protocol2Address = Maps.newHashMap(); - - @Getter - private final Map protocol2Transporter = Maps.newConcurrentMap(); - - @Autowired - public TransportService(List transporters) { - transporters.forEach(t -> { - log.info("[TransportService] Transporter[protocol:{},address:{}] registration successful!", t.getProtocol(), t.getAddress()); - protocol2Transporter.put(t.getProtocol(), t); - protocol2Address.put(t.getProtocol(), t.getAddress()); - }); - } - - public void tell(Protocol protocol, String address, PowerSerializable object) { - getTransporter(protocol).tell(address, object); - } - - public AskResponse ask(Protocol protocol, String address, PowerSerializable object) throws Exception { - - return getTransporter(protocol).ask(address, object); - } - - public Transporter getTransporter(Protocol protocol) { - Transporter transporter = protocol2Transporter.get(protocol); - if (transporter == null) { - log.error("[TransportService] can't find transporter by protocol[{}], this is a bug!", protocol); - throw new UnknownProtocolException("can't find transporter by protocol: " + protocol); - } - return transporter; - } - - public static class UnknownProtocolException extends RuntimeException { - public UnknownProtocolException(String message) { - super(message); - } - } - - public static Map getAllAddress() { - return protocol2Address; - } -} diff --git a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/Transporter.java b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/Transporter.java deleted file mode 100644 index 6d77dd69..00000000 --- a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/Transporter.java +++ /dev/null @@ -1,22 +0,0 @@ -package tech.powerjob.server.remote.transport; - -import tech.powerjob.common.PowerSerializable; -import tech.powerjob.common.enums.Protocol; -import tech.powerjob.common.response.AskResponse; - -/** - * Transporter - * - * @author tjq - * @since 2021/2/7 - */ -public interface Transporter { - - Protocol getProtocol(); - - String getAddress(); - - void tell(String address, PowerSerializable object); - - AskResponse ask(String address, PowerSerializable object) throws Exception; -} diff --git a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/impl/AkkaTransporter.java b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/impl/AkkaTransporter.java deleted file mode 100644 index 3a7ae9a1..00000000 --- a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/impl/AkkaTransporter.java +++ /dev/null @@ -1,48 +0,0 @@ -package tech.powerjob.server.remote.transport.impl; - -import akka.actor.ActorSelection; -import akka.pattern.Patterns; -import tech.powerjob.common.PowerSerializable; -import tech.powerjob.common.enums.Protocol; -import tech.powerjob.common.RemoteConstant; -import tech.powerjob.common.response.AskResponse; -import tech.powerjob.server.remote.transport.Transporter; -import tech.powerjob.server.remote.transport.starter.AkkaStarter; -import org.springframework.stereotype.Service; - -import java.time.Duration; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.TimeUnit; - -/** - * akka transporter - * - * @author tjq - * @since 2021/2/7 - */ -@Service -public class AkkaTransporter implements Transporter { - - @Override - public Protocol getProtocol() { - return Protocol.AKKA; - } - - @Override - public String getAddress() { - return AkkaStarter.getActorSystemAddress(); - } - - @Override - public void tell(String address, PowerSerializable object) { - ActorSelection taskTrackerActor = AkkaStarter.getWorkerActor(address); - taskTrackerActor.tell(object, null); - } - - @Override - public AskResponse ask(String address, PowerSerializable object) throws Exception { - ActorSelection taskTrackerActor = AkkaStarter.getWorkerActor(address); - CompletionStage askCS = Patterns.ask(taskTrackerActor, object, Duration.ofMillis(RemoteConstant.DEFAULT_TIMEOUT_MS)); - return (AskResponse) askCS.toCompletableFuture().get(RemoteConstant.DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS); - } -} diff --git a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/impl/HttpTransporter.java b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/impl/HttpTransporter.java deleted file mode 100644 index 365b60a5..00000000 --- a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/impl/HttpTransporter.java +++ /dev/null @@ -1,73 +0,0 @@ -package tech.powerjob.server.remote.transport.impl; - -import tech.powerjob.common.PowerSerializable; -import tech.powerjob.common.enums.Protocol; -import tech.powerjob.common.RemoteConstant; -import tech.powerjob.common.response.AskResponse; -import tech.powerjob.common.utils.NetUtils; -import tech.powerjob.server.remote.transport.Transporter; -import tech.powerjob.server.remote.transport.starter.VertXStarter; -import io.vertx.core.Future; -import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; -import io.vertx.ext.web.client.HttpResponse; -import io.vertx.ext.web.client.WebClient; -import io.vertx.ext.web.client.WebClientOptions; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.tuple.Pair; -import org.springframework.stereotype.Service; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; - -/** - * http transporter powered by vert.x - * - * @author tjq - * @since 2021/2/8 - */ -@Slf4j -@Service -public class HttpTransporter implements Transporter { - - private final WebClient webClient; - - public HttpTransporter() { - WebClientOptions options = new WebClientOptions() - .setKeepAlive(false) - .setConnectTimeout((int) RemoteConstant.DEFAULT_TIMEOUT_MS); - webClient = WebClient.create(Vertx.vertx(), options); - } - - @Override - public Protocol getProtocol() { - return Protocol.HTTP; - } - - @Override - public String getAddress() { - return VertXStarter.getAddress(); - } - - @Override - public void tell(String address, PowerSerializable object) { - postRequest(address, object); - } - - @Override - public AskResponse ask(String address, PowerSerializable object) throws Exception { - CompletableFuture> future = postRequest(address, object).toCompletionStage().toCompletableFuture(); - HttpResponse httpResponse = future.get(RemoteConstant.DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS); - return httpResponse.bodyAsJson(AskResponse.class); - } - - private Future> postRequest(String address, PowerSerializable object) { - Pair ipAndPort = NetUtils.splitAddress2IpAndPort(address); - String ip = ipAndPort.getLeft(); - int port = ipAndPort.getRight(); - return webClient.post(port, ip, object.path()) - .sendJson(object) - .onSuccess(res -> log.info("[HttpTransporter] send request to {}{} successfully: {}, response: {}", address, object.path(), object, res)) - .onFailure(t -> log.warn("[HttpTransporter] send request to {}{} failed: {}", address, object.path(), object, t)); - } -} diff --git a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/starter/AkkaStarter.java b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/starter/AkkaStarter.java deleted file mode 100644 index de54e273..00000000 --- a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/starter/AkkaStarter.java +++ /dev/null @@ -1,88 +0,0 @@ -package tech.powerjob.server.remote.transport.starter; - -import akka.actor.ActorSelection; -import akka.actor.ActorSystem; -import com.google.common.base.Stopwatch; -import com.google.common.collect.Maps; -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import tech.powerjob.common.OmsConstant; -import tech.powerjob.common.RemoteConstant; -import tech.powerjob.common.utils.NetUtils; -import tech.powerjob.server.common.PowerJobServerConfigKey; -import tech.powerjob.server.common.utils.PropertyUtils; -import tech.powerjob.server.remote.server.FriendRequestHandler; - -import java.util.Map; -import java.util.Properties; - -/** - * 服务端 ActorSystem 启动器 - * - * @author tjq - * @since 2020/4/2 - */ -@Slf4j -public class AkkaStarter { - - public static ActorSystem actorSystem; - @Getter - private static String actorSystemAddress; - - private static final String AKKA_PATH = "akka://%s@%s/user/%s"; - - public static void init() { - - Stopwatch stopwatch = Stopwatch.createStarted(); - log.info("[PowerJob] PowerJob's akka system start to bootstrap..."); - - // 忽略了一个问题,机器是没办法访问外网的,除非架设自己的NTP服务器 - // TimeUtils.check(); - - // 解析配置文件 - Config akkaFinalConfig = parseConfig(); - actorSystem = ActorSystem.create(RemoteConstant.SERVER_ACTOR_SYSTEM_NAME, akkaFinalConfig); - actorSystem.actorOf(FriendRequestHandler.defaultProps(), RemoteConstant.SERVER_FRIEND_ACTOR_NAME); - log.info("[PowerJob] PowerJob's akka system started successfully, using time {}.", stopwatch); - } - - private static Config parseConfig() { - Properties properties = PropertyUtils.getProperties(); - int port = Integer.parseInt(properties.getProperty(PowerJobServerConfigKey.AKKA_PORT, String.valueOf(OmsConstant.SERVER_DEFAULT_AKKA_PORT))); - String portFromJvm = System.getProperty(PowerJobServerConfigKey.AKKA_PORT); - if (StringUtils.isNotEmpty(portFromJvm)) { - log.info("[PowerJob] use port from jvm params: {}", portFromJvm); - port = Integer.parseInt(portFromJvm); - } - - // 启动 ActorSystem - Map overrideConfig = Maps.newHashMap(); - String localIp = NetUtils.getLocalHost(); - overrideConfig.put("akka.remote.artery.canonical.hostname", localIp); - overrideConfig.put("akka.remote.artery.canonical.port", port); - actorSystemAddress = localIp + ":" + port; - log.info("[PowerJob] akka-remote server address: {}", actorSystemAddress); - - Config akkaBasicConfig = ConfigFactory.load(RemoteConstant.SERVER_AKKA_CONFIG_NAME); - return ConfigFactory.parseMap(overrideConfig).withFallback(akkaBasicConfig); - } - - /** - * 获取 ServerActor 的 ActorSelection - * - * @param address IP:port - * @return ActorSelection - */ - public static ActorSelection getFriendActor(String address) { - String path = String.format(AKKA_PATH, RemoteConstant.SERVER_ACTOR_SYSTEM_NAME, address, RemoteConstant.SERVER_FRIEND_ACTOR_NAME); - return actorSystem.actorSelection(path); - } - - public static ActorSelection getWorkerActor(String address) { - String path = String.format(AKKA_PATH, RemoteConstant.WORKER_ACTOR_SYSTEM_NAME, address, RemoteConstant.WORKER_ACTOR_NAME); - return actorSystem.actorSelection(path); - } -} diff --git a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/starter/VertXStarter.java b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/starter/VertXStarter.java deleted file mode 100644 index 5f419c26..00000000 --- a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transport/starter/VertXStarter.java +++ /dev/null @@ -1,47 +0,0 @@ -package tech.powerjob.server.remote.transport.starter; - -import tech.powerjob.common.OmsConstant; -import tech.powerjob.common.utils.NetUtils; -import tech.powerjob.server.common.PowerJobServerConfigKey; -import tech.powerjob.server.common.utils.PropertyUtils; -import com.google.common.base.Stopwatch; -import io.vertx.core.Vertx; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; - -import java.util.Properties; - -/** - * vert.x starter - * - * @author tjq - * @since 2021/2/8 - */ -@Slf4j -public class VertXStarter { - - public static Vertx vertx; - @Getter - private static String address; - - public static void init() { - Stopwatch stopwatch = Stopwatch.createStarted(); - log.info("[PowerJob] PowerJob's vert.x system start to bootstrap..."); - - Properties properties = PropertyUtils.getProperties(); - int port = Integer.parseInt(properties.getProperty(PowerJobServerConfigKey.HTTP_PORT, String.valueOf(OmsConstant.SERVER_DEFAULT_HTTP_PORT))); - String portFromJVM = System.getProperty(PowerJobServerConfigKey.HTTP_PORT); - if (StringUtils.isNotEmpty(portFromJVM)) { - port = Integer.parseInt(portFromJVM); - } - String localIP = NetUtils.getLocalHost(); - - address = localIP + ":" + port; - log.info("[PowerJob] vert.x server address: {}", address); - - vertx = Vertx.vertx(); - - log.info("[PowerJob] PowerJob's vert.x system started successfully, using time {}.", stopwatch); - } -} diff --git a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transporter/ProtocolInfo.java b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transporter/ProtocolInfo.java new file mode 100644 index 00000000..0802e811 --- /dev/null +++ b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transporter/ProtocolInfo.java @@ -0,0 +1,30 @@ +package tech.powerjob.server.remote.transporter; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import tech.powerjob.remote.framework.transporter.Transporter; + +/** + * ProtocolInfo + * + * @author tjq + * @since 2023/1/21 + */ +@Getter +@Setter +@ToString +public class ProtocolInfo { + + private String protocol; + + private String address; + + private transient Transporter transporter; + + public ProtocolInfo(String protocol, String address, Transporter transporter) { + this.protocol = protocol; + this.address = address; + this.transporter = transporter; + } +} diff --git a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transporter/TransportService.java b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transporter/TransportService.java new file mode 100644 index 00000000..97e390e3 --- /dev/null +++ b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transporter/TransportService.java @@ -0,0 +1,36 @@ +package tech.powerjob.server.remote.transporter; + +import tech.powerjob.common.PowerSerializable; +import tech.powerjob.remote.framework.base.RemotingException; +import tech.powerjob.remote.framework.base.URL; + +import java.util.Map; +import java.util.concurrent.CompletionStage; + +/** + * server 数据传输服务 + * + * @author tjq + * @since 2023/1/21 + */ +public interface TransportService { + + /** + * 自用地址,用于维护 server -> appId 和 server 间通讯 + * 4.3.0 前为 ActorSystem Address(ip:10086) + * 4.3.0 后 PowerJob 将主协议切换为自由协议,默认使用 HTTP address (ip:10010) + * @return 自用地址 + */ + ProtocolInfo defaultProtocol(); + + /** + * 当前支持的全部协议 + * @return allProtocols + */ + Map allProtocols(); + + void tell(String protocol, URL url, PowerSerializable request); + + CompletionStage ask(String protocol, URL url, PowerSerializable request, Class clz) throws RemotingException; + +} diff --git a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transporter/impl/PowerTransportService.java b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transporter/impl/PowerTransportService.java new file mode 100644 index 00000000..d5d2f624 --- /dev/null +++ b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transporter/impl/PowerTransportService.java @@ -0,0 +1,207 @@ +package tech.powerjob.server.remote.transporter.impl; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import tech.powerjob.common.OmsConstant; +import tech.powerjob.common.PowerSerializable; +import tech.powerjob.common.enums.Protocol; +import tech.powerjob.common.utils.NetUtils; +import tech.powerjob.remote.framework.actor.Actor; +import tech.powerjob.remote.framework.base.Address; +import tech.powerjob.remote.framework.base.RemotingException; +import tech.powerjob.remote.framework.base.ServerType; +import tech.powerjob.remote.framework.base.URL; +import tech.powerjob.remote.framework.engine.EngineConfig; +import tech.powerjob.remote.framework.engine.EngineOutput; +import tech.powerjob.remote.framework.engine.RemoteEngine; +import tech.powerjob.remote.framework.engine.impl.PowerJobRemoteEngine; +import tech.powerjob.server.remote.transporter.ProtocolInfo; +import tech.powerjob.server.remote.transporter.TransportService; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletionStage; + +/** + * server 数据传输服务 + * + * @author tjq + * @since 2023/1/21 + */ +@Slf4j +@Service +public class PowerTransportService implements TransportService, InitializingBean, DisposableBean, ApplicationContextAware { + + /** + * server 需要激活的通讯协议,建议激活全部支持的协议 + */ + @Value("${oms.transporter.active.protocols}") + private String activeProtocols; + + /** + * 主要通讯协议,用于 server 与 server 之间的通讯,用户必须保证该协议可用(端口开放)! + */ + @Value("${oms.transporter.main.protocol}") + private String mainProtocol; + + private static final String PROTOCOL_PORT_CONFIG = "oms.%s.port"; + + private final Environment environment; + + private ProtocolInfo defaultProtocol; + private final Map protocolName2Info = Maps.newHashMap(); + + private final List engines = Lists.newArrayList(); + + private ApplicationContext applicationContext; + + public PowerTransportService(Environment environment) { + this.environment = environment; + } + + @Override + public ProtocolInfo defaultProtocol() { + return defaultProtocol; + } + + @Override + public Map allProtocols() { + return protocolName2Info; + } + + private ProtocolInfo fetchProtocolInfo(String protocol) { + // 兼容老版 worker 未上报 protocol 的情况 + protocol = compatibleProtocol(protocol); + final ProtocolInfo protocolInfo = protocolName2Info.get(protocol); + if (protocolInfo == null) { + throw new IllegalArgumentException("can't find Transporter by protocol :" + protocol); + } + return protocolInfo; + } + + @Override + public void tell(String protocol, URL url, PowerSerializable request) { + fetchProtocolInfo(protocol).getTransporter().tell(url, request); + } + + @Override + public CompletionStage ask(String protocol, URL url, PowerSerializable request, Class clz) throws RemotingException { + return fetchProtocolInfo(protocol).getTransporter().ask(url, request, clz); + } + + private void initRemoteFrameWork(String protocol, int port) { + + // 从构造器注入改为从 applicationContext 获取来避免循环依赖 + final Map beansWithAnnotation = applicationContext.getBeansWithAnnotation(Actor.class); + log.info("[PowerTransportService] find Actor num={},names={}", beansWithAnnotation.size(), beansWithAnnotation.keySet()); + + Address address = new Address() + .setHost(NetUtils.getLocalHost()) + .setPort(port); + EngineConfig engineConfig = new EngineConfig() + .setServerType(ServerType.SERVER) + .setType(protocol.toUpperCase()) + .setBindAddress(address) + .setActorList(Lists.newArrayList(beansWithAnnotation.values())); + log.info("[PowerTransportService] start to initialize RemoteEngine[type={},address={}]", protocol, address); + RemoteEngine re = new PowerJobRemoteEngine(); + final EngineOutput engineOutput = re.start(engineConfig); + log.info("[PowerTransportService] start RemoteEngine[type={},address={}] successfully", protocol, address); + + this.engines.add(re); + this.protocolName2Info.put(protocol, new ProtocolInfo(protocol, address.toFullAddress(), engineOutput.getTransporter())); + } + + @Override + public void afterPropertiesSet() throws Exception { + + log.info("[PowerTransportService] start to initialize whole PowerTransportService!"); + log.info("[PowerTransportService] activeProtocols: {}", activeProtocols); + + if (StringUtils.isEmpty(activeProtocols)) { + throw new IllegalArgumentException("activeProtocols can't be empty!"); + } + + for (String protocol : activeProtocols.split(OmsConstant.COMMA)) { + try { + final int port = parseProtocolPort(protocol); + initRemoteFrameWork(protocol, port); + } catch (Throwable t) { + log.error("[PowerTransportService] initialize protocol[{}] failed. If you don't need to use this protocol, you can turn it off by 'oms.transporter.active.protocols'", protocol); + ExceptionUtils.rethrow(t); + } + } + + choseDefault(); + + log.info("[PowerTransportService] initialize successfully!"); + log.info("[PowerTransportService] ALL_PROTOCOLS: {}", protocolName2Info); + } + + /** + * 获取协议端口,考虑兼容性 & 用户仔细扩展的场景,选择动态从 env 获取 port + * @return port + */ + private int parseProtocolPort(String protocol) { + final String key1 = String.format(PROTOCOL_PORT_CONFIG, protocol.toLowerCase()); + final String key2 = String.format(PROTOCOL_PORT_CONFIG, protocol.toUpperCase()); + String portStr = environment.getProperty(key1); + if (StringUtils.isEmpty(portStr)) { + portStr = environment.getProperty(key2); + } + log.info("[PowerTransportService] fetch port for protocol[{}], key={}, value={}", protocol, key1, portStr); + + if (StringUtils.isEmpty(portStr)) { + throw new IllegalArgumentException(String.format("can't find protocol config by key: %s, please check your spring config!", key1)); + } + + return Integer.parseInt(portStr); + } + + private String compatibleProtocol(String p) { + if (p == null) { + return Protocol.AKKA.name(); + } + return p; + } + + /** + * HTTP 优先,否则默认取第一个协议 + */ + private void choseDefault() { + + + this.defaultProtocol = this.protocolName2Info.get(mainProtocol); + log.info("[PowerTransportService] chose [{}] as the default protocol, make sure this protocol can work!", mainProtocol); + + if (this.defaultProtocol == null) { + throw new IllegalArgumentException("can't find default protocol, please check your config!"); + } + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + public void destroy() throws Exception { + engines.forEach(e -> { + try { + e.close(); + } catch (Exception ignore) { + } + }); + } +} diff --git a/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transporter/impl/ServerURLFactory.java b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transporter/impl/ServerURLFactory.java new file mode 100644 index 00000000..0edc3c65 --- /dev/null +++ b/powerjob-server/powerjob-server-remote/src/main/java/tech/powerjob/server/remote/transporter/impl/ServerURLFactory.java @@ -0,0 +1,52 @@ +package tech.powerjob.server.remote.transporter.impl; + +import tech.powerjob.remote.framework.base.Address; +import tech.powerjob.remote.framework.base.HandlerLocation; +import tech.powerjob.remote.framework.base.ServerType; +import tech.powerjob.remote.framework.base.URL; + +import static tech.powerjob.common.RemoteConstant.*; + +/** + * 统一生成地址 + * + * @author tjq + * @since 2023/1/21 + */ +public class ServerURLFactory { + + public static URL dispatchJob2Worker(String address) { + return simileBuild(address, ServerType.WORKER, WORKER_PATH, WTT_HANDLER_RUN_JOB); + } + + public static URL stopInstance2Worker(String address) { + return simileBuild(address, ServerType.WORKER, WORKER_PATH, WTT_HANDLER_STOP_INSTANCE); + } + + public static URL queryInstance2Worker(String address) { + return simileBuild(address, ServerType.WORKER, WORKER_PATH, WTT_HANDLER_QUERY_INSTANCE_STATUS); + } + + public static URL deployContainer2Worker(String address) { + return simileBuild(address, ServerType.WORKER, WORKER_PATH, WORKER_HANDLER_DEPLOY_CONTAINER); + } + + public static URL destroyContainer2Worker(String address) { + return simileBuild(address, ServerType.WORKER, WORKER_PATH, WORKER_HANDLER_DESTROY_CONTAINER); + } + + public static URL ping2Friend(String address) { + return simileBuild(address, ServerType.SERVER, S4S_PATH, S4S_HANDLER_PING); + } + + public static URL process2Friend(String address) { + return simileBuild(address, ServerType.SERVER, S4S_PATH, S4S_HANDLER_PROCESS); + } + + public static URL simileBuild(String address, ServerType type, String rootPath, String handlerPath) { + return new URL() + .setServerType(type) + .setAddress(Address.fromIpv4(address)) + .setLocation(new HandlerLocation().setRootPath(rootPath).setMethodPath(handlerPath)); + } +} diff --git a/powerjob-server/powerjob-server-starter/pom.xml b/powerjob-server/powerjob-server-starter/pom.xml index 8f3f73df..bb3a3b2c 100644 --- a/powerjob-server/powerjob-server-starter/pom.xml +++ b/powerjob-server/powerjob-server-starter/pom.xml @@ -5,7 +5,7 @@ powerjob-server tech.powerjob - 4.2.1 + 4.3.0 ../pom.xml 4.0.0 diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/PowerJobServerApplication.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/PowerJobServerApplication.java index 22a660af..75af8667 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/PowerJobServerApplication.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/PowerJobServerApplication.java @@ -1,8 +1,6 @@ package tech.powerjob.server; import tech.powerjob.server.common.utils.PropertyUtils; -import tech.powerjob.server.remote.transport.starter.AkkaStarter; -import tech.powerjob.server.remote.transport.starter.VertXStarter; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -30,9 +28,6 @@ public class PowerJobServerApplication { pre(); - AkkaStarter.init(); - VertXStarter.init(); - // Start SpringBoot application. try { SpringApplication.run(PowerJobServerApplication.class, args); diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/ContainerController.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/ContainerController.java index 8f13e0ef..02acd5d0 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/ContainerController.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/ContainerController.java @@ -1,20 +1,5 @@ package tech.powerjob.server.web.controller; -import tech.powerjob.common.OmsConstant; -import tech.powerjob.common.response.ResultDTO; -import tech.powerjob.server.remote.transport.starter.AkkaStarter; -import tech.powerjob.server.common.constants.ContainerSourceType; -import tech.powerjob.server.common.constants.SwitchableStatus; -import tech.powerjob.server.core.container.ContainerTemplateGenerator; -import tech.powerjob.server.common.utils.OmsFileUtils; -import tech.powerjob.server.persistence.remote.model.AppInfoDO; -import tech.powerjob.server.persistence.remote.model.ContainerInfoDO; -import tech.powerjob.server.persistence.remote.repository.AppInfoRepository; -import tech.powerjob.server.persistence.remote.repository.ContainerInfoRepository; -import tech.powerjob.server.core.container.ContainerService; -import tech.powerjob.server.web.request.GenerateContainerTemplateRequest; -import tech.powerjob.server.web.request.SaveContainerInfoRequest; -import tech.powerjob.server.web.response.ContainerInfoVO; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateFormatUtils; @@ -22,8 +7,21 @@ import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; +import tech.powerjob.common.OmsConstant; +import tech.powerjob.common.response.ResultDTO; +import tech.powerjob.server.common.constants.ContainerSourceType; +import tech.powerjob.server.common.constants.SwitchableStatus; +import tech.powerjob.server.common.utils.OmsFileUtils; +import tech.powerjob.server.core.container.ContainerService; +import tech.powerjob.server.core.container.ContainerTemplateGenerator; +import tech.powerjob.server.persistence.remote.model.AppInfoDO; +import tech.powerjob.server.persistence.remote.model.ContainerInfoDO; +import tech.powerjob.server.persistence.remote.repository.AppInfoRepository; +import tech.powerjob.server.persistence.remote.repository.ContainerInfoRepository; +import tech.powerjob.server.web.request.GenerateContainerTemplateRequest; +import tech.powerjob.server.web.request.SaveContainerInfoRequest; +import tech.powerjob.server.web.response.ContainerInfoVO; -import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; @@ -62,6 +60,8 @@ public class ContainerController { File file = containerService.fetchContainerJarFile(version); if (file.exists()) { OmsFileUtils.file2HttpResponse(file, response); + } else { + log.error("[Container] can't find container by version[{}], please deploy first!", version); } } @@ -114,17 +114,6 @@ public class ContainerController { return ResultDTO.failed("No workers have even registered!"); } - // 转发 HTTP 请求 - if (!AkkaStarter.getActorSystemAddress().equals(targetServer)) { - String targetIp = targetServer.split(":")[0]; - String url = String.format("http://%s:%d/container/listDeployedWorker?appId=%d&containerId=%d", targetIp, port, appId, containerId); - try { - response.sendRedirect(url); - return ResultDTO.success(null); - }catch (Exception e) { - return ResultDTO.failed(e); - } - } return ResultDTO.success(containerService.fetchDeployedInfo(appId, containerId)); } diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/ServerController.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/ServerController.java index ada346e3..c9c71dcf 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/ServerController.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/ServerController.java @@ -7,13 +7,16 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import tech.powerjob.common.request.ServerDiscoveryRequest; import tech.powerjob.common.response.ResultDTO; import tech.powerjob.common.utils.CommonUtils; import tech.powerjob.common.utils.NetUtils; +import tech.powerjob.server.common.aware.ServerInfoAware; +import tech.powerjob.server.common.module.ServerInfo; import tech.powerjob.server.persistence.remote.model.AppInfoDO; import tech.powerjob.server.persistence.remote.repository.AppInfoRepository; import tech.powerjob.server.remote.server.election.ServerElectionService; -import tech.powerjob.server.remote.transport.TransportService; +import tech.powerjob.server.remote.transporter.TransportService; import tech.powerjob.server.remote.worker.WorkerClusterQueryService; import java.util.Optional; @@ -29,8 +32,9 @@ import java.util.TimeZone; @RestController @RequestMapping("/server") @RequiredArgsConstructor -public class ServerController { +public class ServerController implements ServerInfoAware { + private ServerInfo serverInfo; private final TransportService transportService; private final ServerElectionService serverElectionService; @@ -47,23 +51,33 @@ public class ServerController { } @GetMapping("/acquire") - public ResultDTO acquireServer(Long appId, String protocol, String currentServer) { - return ResultDTO.success(serverElectionService.elect(appId, protocol, currentServer)); + public ResultDTO acquireServer(ServerDiscoveryRequest request) { + return ResultDTO.success(serverElectionService.elect(request)); } @GetMapping("/hello") public ResultDTO ping(@RequestParam(required = false) boolean debug) { JSONObject res = new JSONObject(); res.put("localHost", NetUtils.getLocalHost()); - res.put("communicationSystemInfo", transportService.getProtocol2Transporter()); + res.put("serverInfo", serverInfo); res.put("serverTime", CommonUtils.formatTime(System.currentTimeMillis())); + res.put("serverTimeTs", System.currentTimeMillis()); res.put("serverTimeZone", TimeZone.getDefault().getDisplayName()); res.put("appIds", workerClusterQueryService.getAppId2ClusterStatus().keySet()); if (debug) { res.put("appId2ClusterInfo", JSON.parseObject(JSON.toJSONString(workerClusterQueryService.getAppId2ClusterStatus()))); - } + + try { + res.put("defaultAddress", JSONObject.toJSON(transportService.defaultProtocol())); + } catch (Exception ignore) { + } + return ResultDTO.success(res); } + @Override + public void setServerInfo(ServerInfo serverInfo) { + this.serverInfo = serverInfo; + } } diff --git a/powerjob-server/powerjob-server-starter/src/main/resources/application.properties b/powerjob-server/powerjob-server-starter/src/main/resources/application.properties index 9f65c568..f8cce8b8 100644 --- a/powerjob-server/powerjob-server-starter/src/main/resources/application.properties +++ b/powerjob-server/powerjob-server-starter/src/main/resources/application.properties @@ -16,8 +16,9 @@ spring.servlet.multipart.max-request-size=209715200 # temporary skip circular references check spring.main.allow-circular-references=true -###### PowerJob self-owned configuration (The following properties should exist in application.properties only). ###### -# Akka ActorSystem port. +###### PowerJob transporter configuration ###### +oms.transporter.active.protocols=AKKA,HTTP +oms.transporter.main.protocol=HTTP oms.akka.port=10086 oms.http.port=10010 # Prefix for all tables. Default empty string. Config if you have needs, i.e. pj_ diff --git a/powerjob-server/powerjob-server-starter/src/main/resources/banner.txt b/powerjob-server/powerjob-server-starter/src/main/resources/banner.txt index 6ca0bdbc..e52ab26f 100644 --- a/powerjob-server/powerjob-server-starter/src/main/resources/banner.txt +++ b/powerjob-server/powerjob-server-starter/src/main/resources/banner.txt @@ -11,5 +11,5 @@ ${AnsiColor.BRIGHT_RED} * Maintainer: tengjiqi@gmail.com & Team PowerJob * OfficialWebsite: http://www.powerjob.tech/ * SourceCode: https://github.com/PowerJob/PowerJob -* PoweredBy: SpringBoot${spring-boot.formatted-version} & Akka (v2.6.12) & Vert.x (v4.0.2) +* PoweredBy: SpringBoot${spring-boot.formatted-version} ${AnsiColor.DEFAULT} \ No newline at end of file diff --git a/powerjob-server/powerjob-server-starter/src/main/resources/logback-dev.xml b/powerjob-server/powerjob-server-starter/src/main/resources/logback-dev.xml index 1c58dc7c..9951c3bc 100644 --- a/powerjob-server/powerjob-server-starter/src/main/resources/logback-dev.xml +++ b/powerjob-server/powerjob-server-starter/src/main/resources/logback-dev.xml @@ -12,6 +12,8 @@ + + diff --git a/powerjob-server/powerjob-server-starter/src/main/resources/oms-server.akka.conf b/powerjob-server/powerjob-server-starter/src/main/resources/oms-server.akka.conf deleted file mode 100644 index 95afe889..00000000 --- a/powerjob-server/powerjob-server-starter/src/main/resources/oms-server.akka.conf +++ /dev/null @@ -1,68 +0,0 @@ -akka { - - loggers = ["akka.event.slf4j.Slf4jLogger"] - loglevel = "WARNING" - - actor { - # cluster is better(recommend by official document), but I prefer remote - provider = remote - allow-java-serialization = off - - serializers { - power-serializer = "tech.powerjob.common.serialize.PowerAkkaSerializer" - } - - serialization-bindings { - "tech.powerjob.common.PowerSerializable" = power-serializer - } - } - remote { - artery { - transport = tcp # See Selecting a transport below - # over write by code - canonical.hostname = "127.0.0.1" - canonical.port = 0 - } - } - - # worker-request-core-dispatcher - w-r-c-d { - # Dispatcher is the name of the event-based dispatcher - type = Dispatcher - # What kind of ExecutionService to use - executor = "fork-join-executor" - # Configuration for the fork join pool - fork-join-executor { - # Min number of threads to cap factor-based parallelism number to - parallelism-min = 2 - # Parallelism (threads) ... ceil(available processors * factor) - parallelism-factor = 4.0 - # Max number of threads to cap factor-based parallelism number to - parallelism-max = 128 - } - # Throughput defines the maximum number of messages to be - # processed per actor before the thread jumps to the next actor. - # Set to 1 for as fair as possible. - throughput = 10 - } - - friend-request-actor-dispatcher { - # Dispatcher is the name of the event-based dispatcher - type = Dispatcher - # What kind of ExecutionService to use - executor = "fork-join-executor" - # Configuration for the fork join pool - fork-join-executor { - # Min number of threads to cap factor-based parallelism number to - parallelism-min = 2 - # Parallelism (threads) ... ceil(available processors * factor) - parallelism-factor = 4.0 - # Max number of threads to cap factor-based parallelism number to - parallelism-max = 128 - } - # Throughput defines the maximum number of messages to be - # processed per actor before the thread jumps to the next actor. - # Set to 1 for as fair as possible. - throughput = 5 - } -} \ No newline at end of file diff --git a/powerjob-worker-agent/Dockerfile b/powerjob-worker-agent/Dockerfile index f33b43e1..c7b3160e 100644 --- a/powerjob-worker-agent/Dockerfile +++ b/powerjob-worker-agent/Dockerfile @@ -13,7 +13,10 @@ RUN apt-get update && \ && apt-get clean \ && apt-get autoclean \ && rm -rf /var/lib/apt/lists/* - +# 安装 wait-for-it 脚本 +RUN curl -o wait-for-it.sh https://gitee.com/KFCFans/wait-for-it/raw/master/wait-for-it.sh +RUN chmod +x wait-for-it.sh +# 拷贝主文件 COPY powerjob-agent.jar /powerjob-agent.jar # 暴露端口(AKKA-Client) EXPOSE 27777 diff --git a/powerjob-worker-agent/pom.xml b/powerjob-worker-agent/pom.xml index 7449eabc..6eb753a5 100644 --- a/powerjob-worker-agent/pom.xml +++ b/powerjob-worker-agent/pom.xml @@ -5,19 +5,20 @@ powerjob tech.powerjob - 3.0.0 + 4.0.0 4.0.0 powerjob-worker-agent - 4.2.1 + 4.3.0 jar - 4.2.1 + 4.3.0 1.2.9 4.3.2 + 5.3.23 2.3.4.RELEASE @@ -98,6 +99,13 @@ postgresql ${postgresql.version} + + + + org.springframework + spring-context + ${spring.version} + diff --git a/powerjob-worker-agent/src/main/java/tech/powerjob/agent/MainApplication.java b/powerjob-worker-agent/src/main/java/tech/powerjob/agent/MainApplication.java index b89767f1..4b84bfac 100644 --- a/powerjob-worker-agent/src/main/java/tech/powerjob/agent/MainApplication.java +++ b/powerjob-worker-agent/src/main/java/tech/powerjob/agent/MainApplication.java @@ -57,8 +57,7 @@ public class MainApplication implements Runnable { cfg.setMaxResultLength(length); cfg.setTag(tag); - PowerJobWorker worker = new PowerJobWorker(); - worker.setConfig(cfg); + PowerJobWorker worker = new PowerJobWorker(cfg); worker.init(); }catch (Exception e) { diff --git a/powerjob-worker-samples/pom.xml b/powerjob-worker-samples/pom.xml index 091c275e..00c1d5df 100644 --- a/powerjob-worker-samples/pom.xml +++ b/powerjob-worker-samples/pom.xml @@ -5,16 +5,16 @@ powerjob tech.powerjob - 3.0.0 + 4.0.0 4.0.0 powerjob-worker-samples - 4.2.1 + 4.3.0 2.7.4 - 4.2.1 + 4.3.0 1.2.83 1.2.1 @@ -59,6 +59,13 @@ ${powerjob.official.processors.version} + + + javax.xml.bind + jaxb-api + 2.3.1 + + diff --git a/powerjob-worker-samples/src/main/java/tech/powerjob/samples/PowerJobWorkerConfig.java b/powerjob-worker-samples/src/main/java/tech/powerjob/samples/PowerJobWorkerConfig.java deleted file mode 100644 index 7ec8672b..00000000 --- a/powerjob-worker-samples/src/main/java/tech/powerjob/samples/PowerJobWorkerConfig.java +++ /dev/null @@ -1,41 +0,0 @@ -package tech.powerjob.samples; - -import org.springframework.context.annotation.Configuration; - -/** - * powerjob-worker 配置 - * 代码配置示例,SpringBoot 项目支持使用 starter,只需要在 application.properties 中完成配置即可 - * - * @author tjq - * @since 2020/4/17 - */ -@Configuration -public class PowerJobWorkerConfig { - - /* - - @Bean(name = "worker2") - public OhMyWorker initOMS() throws Exception { - - // 服务器HTTP地址(端口号为 server.port,而不是 ActorSystem port) - List serverAddress = Lists.newArrayList("127.0.0.1:7700", "127.0.0.1:7701"); - - // 1. 创建配置文件 - OhMyConfig config = new OhMyConfig(); - config.setPort(28888); - config.setAppName("powerjob-multi-worker-2"); - config.setServerAddress(serverAddress); - // 如果没有大型 Map/MapReduce 的需求,建议使用内存来加速计算 - config.setStoreStrategy(StoreStrategy.DISK); - - // 2. 创建 Worker 对象,设置配置文件 - OhMyWorker ohMyWorker = new OhMyWorker(); - ohMyWorker.setConfig(config); - return ohMyWorker; - } - - - */ - - -} diff --git a/powerjob-worker-samples/src/main/java/tech/powerjob/samples/PowerJobWorkerInitializer.java b/powerjob-worker-samples/src/main/java/tech/powerjob/samples/PowerJobWorkerInitializer.java new file mode 100644 index 00000000..e6150ee2 --- /dev/null +++ b/powerjob-worker-samples/src/main/java/tech/powerjob/samples/PowerJobWorkerInitializer.java @@ -0,0 +1,44 @@ +package tech.powerjob.samples; + +import org.springframework.context.annotation.Configuration; + +/** + * powerjob-worker 配置 + * 代码配置示例,SpringBoot 项目支持使用 starter,只需要在 application.properties 中完成配置即可 + * + * @author tjq + * @since 2020/4/17 + */ +@Configuration +public class PowerJobWorkerInitializer { + + /* + 手动配置版代码 + 常规 SpringBoot 用户直接使用 starter 配置即可,具体配置见 application.properties + + @Bean + public PowerJobSpringWorker initPowerJobSpringWorkerByCode() { + + // 初始化 PowerJob 配置文件 + PowerJobWorkerConfig config = new PowerJobWorkerConfig(); + // 传输协议,新用户建议直接上 HTTP + config.setProtocol(Protocol.HTTP); + // 传输层端口号 + config.setPort(28888); + // worker 的归组,建议使用项目名称 + config.setAppName("powerjob-multi-worker-2"); + // server 的服务发现地址,支持多IP 或 HTTP 域名 + config.setServerAddress(Lists.newArrayList("127.0.0.1:7700", "127.0.0.1:7701")); + // 如果没有大型 Map/MapReduce 的需求,建议使用内存来加速计算 + config.setStoreStrategy(StoreStrategy.DISK); + // 执行器的自定义标签,可用于指定部分执行器运行。举例:多单元机房将 TAG 设置为单元名称,即可在控制台指定单元运行 + config.setTag("CENTER"); + + // 以上为核心配置,其他配置可直接参考注释 or 官方文档 + + // 注意 Spring 用户请使用 PowerJobSpringWorker 而不是 PowerJobWorker,后者无法使用 Spring 管理的 Bean 作为执行器 + return new PowerJobSpringWorker(config); + } + + */ +} diff --git a/powerjob-worker-samples/src/main/java/tech/powerjob/samples/processors/SimpleProcessor.java b/powerjob-worker-samples/src/main/java/tech/powerjob/samples/processors/SimpleProcessor.java index 97876073..96a8f382 100644 --- a/powerjob-worker-samples/src/main/java/tech/powerjob/samples/processors/SimpleProcessor.java +++ b/powerjob-worker-samples/src/main/java/tech/powerjob/samples/processors/SimpleProcessor.java @@ -5,6 +5,8 @@ import tech.powerjob.worker.core.processor.TaskContext; import tech.powerjob.worker.core.processor.sdk.BasicProcessor; import tech.powerjob.worker.log.OmsLogger; +import java.util.Optional; + /** * @author Echo009 * @since 2022/4/27 @@ -17,11 +19,11 @@ public class SimpleProcessor implements BasicProcessor { OmsLogger logger = context.getOmsLogger(); - String jobParams = context.getJobParams(); + String jobParams = Optional.ofNullable(context.getJobParams()).orElse("S"); logger.info("Current context:{}", context.getWorkflowContext()); logger.info("Current job params:{}", jobParams); - return jobParams.contains("F") ? new ProcessResult(false) : new ProcessResult(true); + return jobParams.contains("F") ? new ProcessResult(false) : new ProcessResult(true, "yeah!"); } } \ No newline at end of file diff --git a/powerjob-worker-samples/src/main/resources/application.properties b/powerjob-worker-samples/src/main/resources/application.properties index 838e6ce3..f2f6e1b0 100644 --- a/powerjob-worker-samples/src/main/resources/application.properties +++ b/powerjob-worker-samples/src/main/resources/application.properties @@ -3,12 +3,14 @@ spring.jpa.open-in-view=false ########### PowerJob-worker properties. ########### # Whether to enable PowerJob Worker, default is true powerjob.worker.enabled=true -# Akka port, default is 27777 -powerjob.worker.akka-port=27777 +# Transport port, default is 27777 +powerjob.worker.port=27777 # Application name, used for grouping applications. Recommend to set the same value as project name. powerjob.worker.app-name=powerjob-worker-samples # Address of PowerJob-server node(s). Ip:port or domain. Multiple addresses should be separated with comma. powerjob.worker.server-address=127.0.0.1:7700,127.0.0.1:7701 +# transport protocol between server and worker +powerjob.worker.protocol=akka # Store strategy of H2 database. disk or memory. Default value is disk. powerjob.worker.store-strategy=disk # Max length of result. Results that are longer than the value will be truncated. diff --git a/powerjob-worker-spring-boot-starter/pom.xml b/powerjob-worker-spring-boot-starter/pom.xml index cc79470f..be0645ce 100644 --- a/powerjob-worker-spring-boot-starter/pom.xml +++ b/powerjob-worker-spring-boot-starter/pom.xml @@ -5,16 +5,16 @@ powerjob tech.powerjob - 3.0.0 + 4.0.0 4.0.0 powerjob-worker-spring-boot-starter - 4.2.1 + 4.3.0 jar - 4.2.1 + 4.3.0 2.7.4 diff --git a/powerjob-worker-spring-boot-starter/src/main/java/tech/powerjob/worker/autoconfigure/PowerJobAutoConfiguration.java b/powerjob-worker-spring-boot-starter/src/main/java/tech/powerjob/worker/autoconfigure/PowerJobAutoConfiguration.java index a5e079c2..77da03f3 100644 --- a/powerjob-worker-spring-boot-starter/src/main/java/tech/powerjob/worker/autoconfigure/PowerJobAutoConfiguration.java +++ b/powerjob-worker-spring-boot-starter/src/main/java/tech/powerjob/worker/autoconfigure/PowerJobAutoConfiguration.java @@ -7,6 +7,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import tech.powerjob.common.utils.CommonUtils; import tech.powerjob.common.utils.NetUtils; +import tech.powerjob.worker.PowerJobSpringWorker; import tech.powerjob.worker.PowerJobWorker; import tech.powerjob.worker.common.PowerJobWorkerConfig; @@ -45,11 +46,15 @@ public class PowerJobAutoConfiguration { /* * Configuration of worker port. Random port is enabled when port is set with non-positive number. */ - int port = worker.getAkkaPort(); - if (port <= 0) { - port = NetUtils.getRandomPort(); + if (worker.getPort() != null) { + config.setPort(worker.getPort()); + } else { + int port = worker.getAkkaPort(); + if (port <= 0) { + port = NetUtils.getRandomPort(); + } + config.setPort(port); } - config.setPort(port); /* * appName, name of the application. Applications should be registered in advance to prevent * error. This property should be the same with what you entered for appName when getting @@ -57,6 +62,7 @@ public class PowerJobAutoConfiguration { */ config.setAppName(worker.getAppName()); config.setServerAddress(serverAddress); + config.setProtocol(worker.getProtocol()); /* * For non-Map/MapReduce tasks, {@code memory} is recommended for speeding up calculation. * Map/MapReduce tasks may produce batches of subtasks, which could lead to OutOfMemory @@ -81,11 +87,9 @@ public class PowerJobAutoConfiguration { config.setHealthReportInterval(worker.getHealthReportInterval()); /* - * Create OhMyWorker object and set properties. + * Create PowerJobSpringWorker object and set properties. */ - PowerJobWorker ohMyWorker = new PowerJobWorker(); - ohMyWorker.setConfig(config); - return ohMyWorker; + return new PowerJobSpringWorker(config); } } diff --git a/powerjob-worker-spring-boot-starter/src/main/java/tech/powerjob/worker/autoconfigure/PowerJobProperties.java b/powerjob-worker-spring-boot-starter/src/main/java/tech/powerjob/worker/autoconfigure/PowerJobProperties.java index 4074c5ff..5ac4181e 100644 --- a/powerjob-worker-spring-boot-starter/src/main/java/tech/powerjob/worker/autoconfigure/PowerJobProperties.java +++ b/powerjob-worker-spring-boot-starter/src/main/java/tech/powerjob/worker/autoconfigure/PowerJobProperties.java @@ -1,6 +1,7 @@ package tech.powerjob.worker.autoconfigure; import tech.powerjob.common.RemoteConstant; +import tech.powerjob.common.enums.Protocol; import tech.powerjob.worker.common.constants.StoreStrategy; import tech.powerjob.worker.core.processor.ProcessResult; import tech.powerjob.worker.core.processor.WorkflowContext; @@ -111,8 +112,14 @@ public class PowerJobProperties { /** * Akka port of Powerjob-worker, optional value. Default value of this property is 27777. * If multiple PowerJob-worker nodes were deployed, different, unique ports should be assigned. + * Deprecated, please use 'port' */ + @Deprecated private int akkaPort = RemoteConstant.DEFAULT_WORKER_PORT; + /** + * port + */ + private Integer port; /** * Address(es) of Powerjob-server node(s). Ip:port or domain. * Example of single Powerjob-server node: @@ -125,6 +132,10 @@ public class PowerJobProperties { *

*/ private String serverAddress; + /** + * Protocol for communication between WORKER and server + */ + private Protocol protocol = Protocol.AKKA; /** * Local store strategy for H2 database. {@code disk} or {@code memory}. */ diff --git a/powerjob-worker/pom.xml b/powerjob-worker/pom.xml index be2e8535..068e1ceb 100644 --- a/powerjob-worker/pom.xml +++ b/powerjob-worker/pom.xml @@ -5,38 +5,55 @@ powerjob tech.powerjob - 3.0.0 + 4.0.0 4.0.0 powerjob-worker - 4.2.1 + 4.3.0 jar 5.3.23 - 4.2.1 2.1.214 4.0.3 5.9.1 1.2.9 + + 4.3.0 + 4.3.0 + 4.3.0 + 4.3.0 - - - org.springframework - spring-context - ${spring.version} - - - + tech.powerjob powerjob-common - ${powerjob.common.version} + ${powerjob-common.version} + + + + + tech.powerjob + powerjob-remote-framework + ${powerjob-remote-framework.version} + + + + + + tech.powerjob + powerjob-remote-impl-akka + ${powerjob-remote-impl-akka.version} + + + tech.powerjob + powerjob-remote-impl-http + ${powerjob-remote-impl-http.version} @@ -52,6 +69,14 @@ ${hikaricp.version} + + + org.springframework + spring-context + ${spring.version} + provided + + org.junit.jupiter diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/PowerJobSpringWorker.java b/powerjob-worker/src/main/java/tech/powerjob/worker/PowerJobSpringWorker.java new file mode 100644 index 00000000..c089fb43 --- /dev/null +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/PowerJobSpringWorker.java @@ -0,0 +1,49 @@ +package tech.powerjob.worker; + +import com.google.common.collect.Lists; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import tech.powerjob.worker.common.PowerJobWorkerConfig; +import tech.powerjob.worker.extension.processor.ProcessorFactory; +import tech.powerjob.worker.processor.impl.BuiltInSpringProcessorFactory; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * Spring 项目中的 Worker 启动器 + * 能够获取到由 Spring IOC 容器管理的 processor + * + * @author tjq + * @since 2023/1/20 + */ +public class PowerJobSpringWorker extends PowerJobWorker implements ApplicationContextAware, InitializingBean, DisposableBean { + + + public PowerJobSpringWorker(PowerJobWorkerConfig config) { + super(config); + } + + @Override + public void afterPropertiesSet() throws Exception { + init(); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + BuiltInSpringProcessorFactory springProcessorFactory = new BuiltInSpringProcessorFactory(applicationContext); + + // append BuiltInSpringProcessorFactory + PowerJobWorkerConfig workerConfig = workerRuntime.getWorkerConfig(); + List processorFactories = Lists.newArrayList( + Optional.ofNullable(workerConfig.getProcessorFactoryList()) + .orElse(Collections.emptyList())); + processorFactories.add(springProcessorFactory); + workerConfig.setProcessorFactoryList(processorFactories); + } + +} diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/PowerJobWorker.java b/powerjob-worker/src/main/java/tech/powerjob/worker/PowerJobWorker.java index ef88197f..61ea7271 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/PowerJobWorker.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/PowerJobWorker.java @@ -1,30 +1,22 @@ package tech.powerjob.worker; -import akka.actor.ActorRef; -import akka.actor.ActorSystem; -import akka.actor.DeadLetter; -import akka.actor.Props; -import akka.routing.RoundRobinPool; import com.google.common.base.Stopwatch; -import com.google.common.collect.Maps; -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; +import com.google.common.collect.Lists; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import tech.powerjob.common.RemoteConstant; import tech.powerjob.common.exception.PowerJobException; import tech.powerjob.common.response.ResultDTO; import tech.powerjob.common.serialize.JsonUtils; import tech.powerjob.common.utils.CommonUtils; import tech.powerjob.common.utils.HttpUtils; import tech.powerjob.common.utils.NetUtils; +import tech.powerjob.remote.framework.base.Address; +import tech.powerjob.remote.framework.base.ServerType; +import tech.powerjob.remote.framework.engine.EngineConfig; +import tech.powerjob.remote.framework.engine.EngineOutput; +import tech.powerjob.remote.framework.engine.RemoteEngine; +import tech.powerjob.remote.framework.engine.impl.PowerJobRemoteEngine; import tech.powerjob.worker.actors.ProcessorTrackerActor; import tech.powerjob.worker.actors.TaskTrackerActor; -import tech.powerjob.worker.actors.TroubleshootingActor; import tech.powerjob.worker.actors.WorkerActor; import tech.powerjob.worker.background.OmsLogHandler; import tech.powerjob.worker.background.ServerDiscoveryService; @@ -32,12 +24,18 @@ import tech.powerjob.worker.background.WorkerHealthReporter; import tech.powerjob.worker.common.PowerBannerPrinter; import tech.powerjob.worker.common.PowerJobWorkerConfig; import tech.powerjob.worker.common.WorkerRuntime; -import tech.powerjob.worker.common.utils.SpringUtils; import tech.powerjob.worker.core.executor.ExecutorManager; +import tech.powerjob.worker.extension.processor.ProcessorFactory; import tech.powerjob.worker.persistence.TaskPersistenceService; +import tech.powerjob.worker.processor.PowerJobProcessorLoader; +import tech.powerjob.worker.processor.ProcessorLoader; +import tech.powerjob.worker.processor.impl.BuiltInDefaultProcessorFactory; +import tech.powerjob.worker.processor.impl.JarContainerProcessorFactory; -import java.util.Map; +import java.util.Collections; +import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -48,20 +46,15 @@ import java.util.concurrent.atomic.AtomicBoolean; * @since 2020/3/16 */ @Slf4j -public class PowerJobWorker implements ApplicationContextAware, InitializingBean, DisposableBean { +public class PowerJobWorker { + private final RemoteEngine remoteEngine; + protected final WorkerRuntime workerRuntime; + private final AtomicBoolean initialized = new AtomicBoolean(false); - private final WorkerRuntime workerRuntime = new WorkerRuntime(); - - private final AtomicBoolean initialized = new AtomicBoolean(); - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - SpringUtils.inject(applicationContext); - } - - @Override - public void afterPropertiesSet() throws Exception { - init(); + public PowerJobWorker(PowerJobWorkerConfig config) { + this.workerRuntime = new WorkerRuntime(); + this.remoteEngine = new PowerJobRemoteEngine(); + workerRuntime.setWorkerConfig(config); } public void init() throws Exception { @@ -75,11 +68,7 @@ public class PowerJobWorker implements ApplicationContextAware, InitializingBean log.info("[PowerJobWorker] start to initialize PowerJobWorker..."); PowerJobWorkerConfig config = workerRuntime.getWorkerConfig(); - - // 打印 worker 配置 - log.info("[PowerJobWorker] worker config: {}", JsonUtils.toJSONString(config)); - - CommonUtils.requireNonNull(config, "can't find OhMyConfig, please set OhMyConfig first"); + CommonUtils.requireNonNull(config, "can't find PowerJobWorkerConfig, please set PowerJobWorkerConfig first"); try { PowerBannerPrinter.print(); @@ -98,17 +87,24 @@ public class PowerJobWorker implements ApplicationContextAware, InitializingBean final ExecutorManager executorManager = new ExecutorManager(workerRuntime.getWorkerConfig()); workerRuntime.setExecutorManager(executorManager); - // 初始化 ActorSystem(macOS上 new ServerSocket 检测端口占用的方法并不生效,可能是AKKA是Scala写的缘故?没办法...只能靠异常重试了) - Map overrideConfig = Maps.newHashMap(); - overrideConfig.put("akka.remote.artery.canonical.hostname", NetUtils.getLocalHost()); - overrideConfig.put("akka.remote.artery.canonical.port", config.getPort()); + // 初始化 ProcessorLoader + ProcessorLoader processorLoader = buildProcessorLoader(workerRuntime); + workerRuntime.setProcessorLoader(processorLoader); - Config akkaBasicConfig = ConfigFactory.load(RemoteConstant.WORKER_AKKA_CONFIG_NAME); - Config akkaFinalConfig = ConfigFactory.parseMap(overrideConfig).withFallback(akkaBasicConfig); + // 初始化 actor + TaskTrackerActor taskTrackerActor = new TaskTrackerActor(workerRuntime); + ProcessorTrackerActor processorTrackerActor = new ProcessorTrackerActor(workerRuntime); + WorkerActor workerActor = new WorkerActor(workerRuntime, taskTrackerActor); - int cores = Runtime.getRuntime().availableProcessors(); - ActorSystem actorSystem = ActorSystem.create(RemoteConstant.WORKER_ACTOR_SYSTEM_NAME, akkaFinalConfig); - workerRuntime.setActorSystem(actorSystem); + // 初始化通讯引擎 + EngineConfig engineConfig = new EngineConfig() + .setType(config.getProtocol().name()) + .setServerType(ServerType.WORKER) + .setBindAddress(new Address().setHost(NetUtils.getLocalHost()).setPort(config.getPort())) + .setActorList(Lists.newArrayList(taskTrackerActor, processorTrackerActor, workerActor)); + + EngineOutput engineOutput = remoteEngine.start(engineConfig); + workerRuntime.setTransporter(engineOutput.getTransporter()); // 连接 server ServerDiscoveryService serverDiscoveryService = new ServerDiscoveryService(workerRuntime.getAppId(), workerRuntime.getWorkerConfig()); @@ -116,25 +112,10 @@ public class PowerJobWorker implements ApplicationContextAware, InitializingBean serverDiscoveryService.start(workerRuntime.getExecutorManager().getCoreExecutor()); workerRuntime.setServerDiscoveryService(serverDiscoveryService); - ActorRef taskTrackerActorRef = actorSystem.actorOf(TaskTrackerActor.props(workerRuntime) - .withDispatcher("akka.task-tracker-dispatcher") - .withRouter(new RoundRobinPool(cores * 2)), RemoteConstant.TASK_TRACKER_ACTOR_NAME); - actorSystem.actorOf(ProcessorTrackerActor.props(workerRuntime) - .withDispatcher("akka.processor-tracker-dispatcher") - .withRouter(new RoundRobinPool(cores)), RemoteConstant.PROCESSOR_TRACKER_ACTOR_NAME); - actorSystem.actorOf(WorkerActor.props(taskTrackerActorRef) - .withDispatcher("akka.worker-common-dispatcher") - .withRouter(new RoundRobinPool(cores)), RemoteConstant.WORKER_ACTOR_NAME); - - // 处理系统中产生的异常情况 - ActorRef troubleshootingActor = actorSystem.actorOf(Props.create(TroubleshootingActor.class), RemoteConstant.TROUBLESHOOTING_ACTOR_NAME); - actorSystem.eventStream().subscribe(troubleshootingActor, DeadLetter.class); - - log.info("[PowerJobWorker] akka-remote listening address: {}", workerAddress); - log.info("[PowerJobWorker] akka ActorSystem({}) initialized successfully.", actorSystem); + log.info("[PowerJobWorker] PowerJobRemoteEngine initialized successfully."); // 初始化日志系统 - OmsLogHandler omsLogHandler = new OmsLogHandler(workerAddress, actorSystem, serverDiscoveryService); + OmsLogHandler omsLogHandler = new OmsLogHandler(workerAddress, workerRuntime.getTransporter(), serverDiscoveryService); workerRuntime.setOmsLogHandler(omsLogHandler); // 初始化存储 @@ -155,10 +136,6 @@ public class PowerJobWorker implements ApplicationContextAware, InitializingBean } } - public void setConfig(PowerJobWorkerConfig config) { - workerRuntime.setWorkerConfig(config); - } - @SuppressWarnings("rawtypes") private void assertAppName() { @@ -191,9 +168,19 @@ public class PowerJobWorker implements ApplicationContextAware, InitializingBean throw new PowerJobException("no server available!"); } - @Override + private ProcessorLoader buildProcessorLoader(WorkerRuntime runtime) { + List customPF = Optional.ofNullable(runtime.getWorkerConfig().getProcessorFactoryList()).orElse(Collections.emptyList()); + List finalPF = Lists.newArrayList(customPF); + + // 后置添加2个系统 ProcessorLoader + finalPF.add(new BuiltInDefaultProcessorFactory()); + finalPF.add(new JarContainerProcessorFactory(runtime)); + + return new PowerJobProcessorLoader(finalPF); + } + public void destroy() throws Exception { workerRuntime.getExecutorManager().shutdown(); - workerRuntime.getActorSystem().terminate(); + remoteEngine.close(); } } diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/actors/ProcessorTrackerActor.java b/powerjob-worker/src/main/java/tech/powerjob/worker/actors/ProcessorTrackerActor.java index 3c0bffbc..846cfa37 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/actors/ProcessorTrackerActor.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/actors/ProcessorTrackerActor.java @@ -1,16 +1,17 @@ package tech.powerjob.worker.actors; -import akka.actor.AbstractActor; -import akka.actor.Props; +import lombok.extern.slf4j.Slf4j; +import tech.powerjob.common.RemoteConstant; +import tech.powerjob.common.utils.CollectionUtils; +import tech.powerjob.remote.framework.actor.Actor; +import tech.powerjob.remote.framework.actor.Handler; +import tech.powerjob.remote.framework.actor.ProcessType; import tech.powerjob.worker.common.WorkerRuntime; -import tech.powerjob.worker.core.tracker.processor.ProcessorTracker; import tech.powerjob.worker.core.tracker.manager.ProcessorTrackerManager; +import tech.powerjob.worker.core.tracker.processor.ProcessorTracker; import tech.powerjob.worker.persistence.TaskDO; import tech.powerjob.worker.pojo.request.TaskTrackerStartTaskReq; import tech.powerjob.worker.pojo.request.TaskTrackerStopInstanceReq; -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.util.CollectionUtils; import java.util.List; @@ -21,29 +22,21 @@ import java.util.List; * @since 2020/3/17 */ @Slf4j -@AllArgsConstructor -public class ProcessorTrackerActor extends AbstractActor { +@Actor(path = RemoteConstant.WPT_PATH) +public class ProcessorTrackerActor { private final WorkerRuntime workerRuntime; - public static Props props(WorkerRuntime workerRuntime) { - return Props.create(ProcessorTrackerActor.class, () -> new ProcessorTrackerActor(workerRuntime)); - } - - @Override - public Receive createReceive() { - return receiveBuilder() - .match(TaskTrackerStartTaskReq.class, this::onReceiveTaskTrackerStartTaskReq) - .match(TaskTrackerStopInstanceReq.class, this::onReceiveTaskTrackerStopInstanceReq) - .matchAny(obj -> log.warn("[ProcessorTrackerActor] receive unknown request: {}.", obj)) - .build(); + public ProcessorTrackerActor(WorkerRuntime workerRuntime) { + this.workerRuntime = workerRuntime; } /** * 处理来自TaskTracker的task执行请求 * @param req 请求 */ - private void onReceiveTaskTrackerStartTaskReq(TaskTrackerStartTaskReq req) { + @Handler(path = RemoteConstant.WPT_HANDLER_START_TASK, processType = ProcessType.NO_BLOCKING) + public void onReceiveTaskTrackerStartTaskReq(TaskTrackerStartTaskReq req) { Long instanceId = req.getInstanceInfo().getInstanceId(); @@ -68,7 +61,8 @@ public class ProcessorTrackerActor extends AbstractActor { * 处理来自TaskTracker停止任务的请求 * @param req 请求 */ - private void onReceiveTaskTrackerStopInstanceReq(TaskTrackerStopInstanceReq req) { + @Handler(path = RemoteConstant.WPT_HANDLER_STOP_INSTANCE) + public void onReceiveTaskTrackerStopInstanceReq(TaskTrackerStopInstanceReq req) { Long instanceId = req.getInstanceId(); List removedPts = ProcessorTrackerManager.removeProcessorTracker(instanceId); diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/actors/TaskTrackerActor.java b/powerjob-worker/src/main/java/tech/powerjob/worker/actors/TaskTrackerActor.java index c95a585b..917f3abb 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/actors/TaskTrackerActor.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/actors/TaskTrackerActor.java @@ -1,9 +1,6 @@ package tech.powerjob.worker.actors; -import akka.actor.AbstractActor; -import akka.actor.Props; import com.google.common.collect.Lists; -import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import tech.powerjob.common.enums.ExecuteType; import tech.powerjob.common.enums.TimeExpressionType; @@ -12,6 +9,8 @@ import tech.powerjob.common.request.ServerQueryInstanceStatusReq; import tech.powerjob.common.request.ServerScheduleJobReq; import tech.powerjob.common.request.ServerStopInstanceReq; import tech.powerjob.common.response.AskResponse; +import tech.powerjob.remote.framework.actor.Actor; +import tech.powerjob.remote.framework.actor.Handler; import tech.powerjob.worker.common.WorkerRuntime; import tech.powerjob.worker.common.constants.TaskStatus; import tech.powerjob.worker.core.tracker.manager.HeavyTaskTrackerManager; @@ -26,6 +25,8 @@ import tech.powerjob.worker.pojo.request.ProcessorTrackerStatusReportReq; import java.util.List; +import static tech.powerjob.common.RemoteConstant.*; + /** * worker 的 master 节点,处理来自 server 的 jobInstance 请求和来自 worker 的task 请求 * @@ -33,47 +34,29 @@ import java.util.List; * @since 2020/3/17 */ @Slf4j -@AllArgsConstructor -public class TaskTrackerActor extends AbstractActor { +@Actor(path = WTT_PATH) +public class TaskTrackerActor { private final WorkerRuntime workerRuntime; - public static Props props(WorkerRuntime workerRuntime) { - return Props.create(TaskTrackerActor.class, () -> new TaskTrackerActor(workerRuntime)); + public TaskTrackerActor(WorkerRuntime workerRuntime) { + this.workerRuntime = workerRuntime; } - @Override - public Receive createReceive() { - return receiveBuilder() - .match(ProcessorReportTaskStatusReq.class, this::onReceiveProcessorReportTaskStatusReq) - .match(ServerScheduleJobReq.class, this::onReceiveServerScheduleJobReq) - .match(ProcessorMapTaskRequest.class, this::onReceiveProcessorMapTaskRequest) - .match(ProcessorTrackerStatusReportReq.class, this::onReceiveProcessorTrackerStatusReportReq) - .match(ServerStopInstanceReq.class, this::onReceiveServerStopInstanceReq) - .match(ServerQueryInstanceStatusReq.class, this::onReceiveServerQueryInstanceStatusReq) - .matchAny(obj -> log.warn("[ServerRequestActor] receive unknown request: {}.", obj)) - .build(); - } - - /** * 子任务状态上报 处理器 */ - private void onReceiveProcessorReportTaskStatusReq(ProcessorReportTaskStatusReq req) { + @Handler(path = WTT_HANDLER_REPORT_TASK_STATUS) + public AskResponse onReceiveProcessorReportTaskStatusReq(ProcessorReportTaskStatusReq req) { int taskStatus = req.getStatus(); // 只有重量级任务才会有两级任务状态上报的机制 HeavyTaskTracker taskTracker = HeavyTaskTrackerManager.getTaskTracker(req.getInstanceId()); - // 结束状态需要回复接受成功 - if (TaskStatus.FINISHED_STATUS.contains(taskStatus)) { - AskResponse askResponse = AskResponse.succeed(null); - getSender().tell(askResponse, getSelf()); - } // 手动停止 TaskTracker 的情况下会出现这种情况 if (taskTracker == null) { log.warn("[TaskTrackerActor] receive ProcessorReportTaskStatusReq({}) but system can't find TaskTracker.", req); - return; + return null; } if (ProcessorReportTaskStatusReq.BROADCAST.equals(req.getCmd())) { @@ -84,17 +67,25 @@ public class TaskTrackerActor extends AbstractActor { // 更新工作流上下文 taskTracker.updateAppendedWfContext(req.getAppendedWfContext()); + + // 结束状态需要回复接受成功 + if (TaskStatus.FINISHED_STATUS.contains(taskStatus)) { + return AskResponse.succeed(null); + } + + return null; } /** * 子任务 map 处理器 */ - private void onReceiveProcessorMapTaskRequest(ProcessorMapTaskRequest req) { + @Handler(path = WTT_HANDLER_MAP_TASK) + public AskResponse onReceiveProcessorMapTaskRequest(ProcessorMapTaskRequest req) { HeavyTaskTracker taskTracker = HeavyTaskTrackerManager.getTaskTracker(req.getInstanceId()); if (taskTracker == null) { log.warn("[TaskTrackerActor] receive ProcessorMapTaskRequest({}) but system can't find TaskTracker.", req); - return; + return null; } boolean success = false; @@ -121,13 +112,14 @@ public class TaskTrackerActor extends AbstractActor { AskResponse response = new AskResponse(); response.setSuccess(success); - getSender().tell(response, getSelf()); + return response; } /** * 服务器任务调度处理器 */ - private void onReceiveServerScheduleJobReq(ServerScheduleJobReq req) { + @Handler(path = WTT_HANDLER_RUN_JOB) + public void onReceiveServerScheduleJobReq(ServerScheduleJobReq req) { log.debug("[TaskTrackerActor] server schedule job by request: {}.", req); Long instanceId = req.getInstanceId(); // 区分轻量级任务模型以及重量级任务模型 @@ -168,7 +160,8 @@ public class TaskTrackerActor extends AbstractActor { /** * ProcessorTracker 心跳处理器 */ - private void onReceiveProcessorTrackerStatusReportReq(ProcessorTrackerStatusReportReq req) { + @Handler(path = WTT_HANDLER_REPORT_PROCESSOR_TRACKER_STATUS) + public void onReceiveProcessorTrackerStatusReportReq(ProcessorTrackerStatusReportReq req) { HeavyTaskTracker taskTracker = HeavyTaskTrackerManager.getTaskTracker(req.getInstanceId()); if (taskTracker == null) { @@ -181,8 +174,8 @@ public class TaskTrackerActor extends AbstractActor { /** * 停止任务实例 */ - private void onReceiveServerStopInstanceReq(ServerStopInstanceReq req) { - + @Handler(path = WTT_HANDLER_STOP_INSTANCE) + public void onReceiveServerStopInstanceReq(ServerStopInstanceReq req) { log.info("[TaskTrackerActor] receive ServerStopInstanceReq({}).", req); HeavyTaskTracker heavyTaskTracker = HeavyTaskTrackerManager.getTaskTracker(req.getInstanceId()); @@ -201,7 +194,8 @@ public class TaskTrackerActor extends AbstractActor { /** * 查询任务实例运行状态 */ - private void onReceiveServerQueryInstanceStatusReq(ServerQueryInstanceStatusReq req) { + @Handler(path = WTT_HANDLER_QUERY_INSTANCE_STATUS) + public AskResponse onReceiveServerQueryInstanceStatusReq(ServerQueryInstanceStatusReq req) { AskResponse askResponse; TaskTracker taskTracker = HeavyTaskTrackerManager.getTaskTracker(req.getInstanceId()); if (taskTracker == null && (taskTracker = LightTaskTrackerManager.getTaskTracker(req.getInstanceId())) == null) { @@ -211,7 +205,7 @@ public class TaskTrackerActor extends AbstractActor { InstanceDetail instanceDetail = taskTracker.fetchRunningStatus(); askResponse = AskResponse.succeed(instanceDetail); } - getSender().tell(askResponse, getSelf()); + return askResponse; } diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/actors/WorkerActor.java b/powerjob-worker/src/main/java/tech/powerjob/worker/actors/WorkerActor.java index 8edf7cf7..052b2a4a 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/actors/WorkerActor.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/actors/WorkerActor.java @@ -1,12 +1,14 @@ package tech.powerjob.worker.actors; -import akka.actor.AbstractActor; -import akka.actor.ActorRef; -import akka.actor.Props; -import tech.powerjob.worker.container.OmsContainerFactory; -import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import tech.powerjob.common.request.*; +import tech.powerjob.common.response.AskResponse; +import tech.powerjob.remote.framework.actor.Actor; +import tech.powerjob.remote.framework.actor.Handler; +import tech.powerjob.worker.common.WorkerRuntime; +import tech.powerjob.worker.container.OmsContainerFactory; + +import static tech.powerjob.common.RemoteConstant.*; /** * Worker节点Actor,接受服务器请求 @@ -15,36 +17,38 @@ import tech.powerjob.common.request.*; * @since 2020/3/24 */ @Slf4j -@AllArgsConstructor -public class WorkerActor extends AbstractActor { +@Actor(path = WORKER_PATH) +public class WorkerActor { - private final ActorRef taskTrackerActorRef; + private final WorkerRuntime workerRuntime; + private final TaskTrackerActor taskTrackerActor; - public static Props props(ActorRef taskTrackerActorRef) { - return Props.create(WorkerActor.class, () -> new WorkerActor(taskTrackerActorRef)); + + public WorkerActor(WorkerRuntime workerRuntime, TaskTrackerActor taskTrackerActor) { + this.workerRuntime = workerRuntime; + this.taskTrackerActor = taskTrackerActor; } - @Override - public Receive createReceive() { - return receiveBuilder() - .match(ServerDeployContainerRequest.class, this::onReceiveServerDeployContainerRequest) - .match(ServerDestroyContainerRequest.class, this::onReceiveServerDestroyContainerRequest) - .match(ServerScheduleJobReq.class, this::forward2TaskTracker) - .match(ServerStopInstanceReq.class, this::forward2TaskTracker) - .match(ServerQueryInstanceStatusReq.class, this::forward2TaskTracker) - .matchAny(obj -> log.warn("[WorkerActor] receive unknown request: {}.", obj)) - .build(); - } - - private void onReceiveServerDeployContainerRequest(ServerDeployContainerRequest request) { + @Handler(path = WORKER_HANDLER_DEPLOY_CONTAINER) + public void onReceiveServerDeployContainerRequest(ServerDeployContainerRequest request) { OmsContainerFactory.deployContainer(request); } - private void onReceiveServerDestroyContainerRequest(ServerDestroyContainerRequest request) { + @Handler(path = WORKER_HANDLER_DESTROY_CONTAINER) + public void onReceiveServerDestroyContainerRequest(ServerDestroyContainerRequest request) { OmsContainerFactory.destroyContainer(request.getContainerId()); } - private void forward2TaskTracker(Object obj) { - taskTrackerActorRef.forward(obj, getContext()); + @Handler(path = WTT_HANDLER_RUN_JOB) + public void onReceiveServerScheduleJobReq(ServerScheduleJobReq req) { + taskTrackerActor.onReceiveServerScheduleJobReq(req); + } + @Handler(path = WTT_HANDLER_STOP_INSTANCE) + public void onReceiveServerStopInstanceReq(ServerStopInstanceReq req) { + taskTrackerActor.onReceiveServerStopInstanceReq(req); + } + @Handler(path = WTT_HANDLER_QUERY_INSTANCE_STATUS) + public AskResponse onReceiveServerQueryInstanceStatusReq(ServerQueryInstanceStatusReq req) { + return taskTrackerActor.onReceiveServerQueryInstanceStatusReq(req); } } diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/background/OmsLogHandler.java b/powerjob-worker/src/main/java/tech/powerjob/worker/background/OmsLogHandler.java index 54010994..5ea219cc 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/background/OmsLogHandler.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/background/OmsLogHandler.java @@ -1,15 +1,14 @@ package tech.powerjob.worker.background; -import akka.actor.ActorSelection; -import akka.actor.ActorSystem; import tech.powerjob.common.enums.LogLevel; import tech.powerjob.common.model.InstanceLogContent; import tech.powerjob.common.request.WorkerLogReportReq; -import tech.powerjob.worker.common.utils.AkkaUtils; +import tech.powerjob.remote.framework.transporter.Transporter; import com.google.common.collect.Lists; import com.google.common.collect.Queues; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import tech.powerjob.worker.common.utils.TransportUtils; import java.util.List; import java.util.concurrent.BlockingQueue; @@ -27,7 +26,7 @@ import java.util.concurrent.locks.ReentrantLock; public class OmsLogHandler { private final String workerAddress; - private final ActorSystem actorSystem; + private final Transporter transporter; private final ServerDiscoveryService serverDiscoveryService; // 处理线程,需要通过线程池启动 @@ -42,9 +41,9 @@ public class OmsLogHandler { // 本地囤积阈值 private static final int REPORT_SIZE = 1024; - public OmsLogHandler(String workerAddress, ActorSystem actorSystem, ServerDiscoveryService serverDiscoveryService) { + public OmsLogHandler(String workerAddress, Transporter transporter, ServerDiscoveryService serverDiscoveryService) { this.workerAddress = workerAddress; - this.actorSystem = actorSystem; + this.transporter = transporter; this.serverDiscoveryService = serverDiscoveryService; } @@ -81,9 +80,9 @@ public class OmsLogHandler { try { - String serverPath = AkkaUtils.getServerActorPath(serverDiscoveryService.getCurrentServerAddress()); + final String currentServerAddress = serverDiscoveryService.getCurrentServerAddress(); // 当前无可用 Server - if (StringUtils.isEmpty(serverPath)) { + if (StringUtils.isEmpty(currentServerAddress)) { if (!logQueue.isEmpty()) { logQueue.clear(); log.warn("[OmsLogHandler] because there is no available server to report logs which leads to queue accumulation, oms discarded all logs."); @@ -91,7 +90,6 @@ public class OmsLogHandler { return; } - ActorSelection serverActor = actorSystem.actorSelection(serverPath); List logs = Lists.newLinkedList(); while (!logQueue.isEmpty()) { @@ -102,7 +100,7 @@ public class OmsLogHandler { if (logs.size() >= BATCH_SIZE) { WorkerLogReportReq req = new WorkerLogReportReq(workerAddress, Lists.newLinkedList(logs)); // 不可靠请求,WEB日志不追求极致 - serverActor.tell(req, null); + TransportUtils.reportLogs(req, currentServerAddress, transporter); logs.clear(); } @@ -113,7 +111,7 @@ public class OmsLogHandler { if (!logs.isEmpty()) { WorkerLogReportReq req = new WorkerLogReportReq(workerAddress, logs); - serverActor.tell(req, null); + TransportUtils.reportLogs(req, currentServerAddress, transporter); } }finally { diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/background/ServerDiscoveryService.java b/powerjob-worker/src/main/java/tech/powerjob/worker/background/ServerDiscoveryService.java index 7e25a62f..e7e1752c 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/background/ServerDiscoveryService.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/background/ServerDiscoveryService.java @@ -1,17 +1,20 @@ package tech.powerjob.worker.background; +import com.google.common.base.Joiner; import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.springframework.util.CollectionUtils; +import tech.powerjob.common.OmsConstant; import tech.powerjob.common.exception.PowerJobException; +import tech.powerjob.common.request.ServerDiscoveryRequest; import tech.powerjob.common.response.ResultDTO; import tech.powerjob.common.serialize.JsonUtils; +import tech.powerjob.common.utils.CollectionUtils; import tech.powerjob.common.utils.CommonUtils; import tech.powerjob.common.utils.HttpUtils; import tech.powerjob.worker.common.PowerJobWorkerConfig; -import tech.powerjob.worker.core.tracker.task.heavy.HeavyTaskTracker; import tech.powerjob.worker.core.tracker.manager.HeavyTaskTrackerManager; +import tech.powerjob.worker.core.tracker.task.heavy.HeavyTaskTracker; import java.util.List; import java.util.Map; @@ -37,7 +40,7 @@ public class ServerDiscoveryService { /** * 服务发现地址 */ - private static final String DISCOVERY_URL = "http://%s/server/acquire?appId=%d¤tServer=%s&protocol=AKKA"; + private static final String DISCOVERY_URL = "http://%s/server/acquire?%s"; /** * 失败次数 */ @@ -55,7 +58,7 @@ public class ServerDiscoveryService { public void start(ScheduledExecutorService timingPool) { this.currentServerAddress = discovery(); - if (org.springframework.util.StringUtils.isEmpty(this.currentServerAddress) && !config.isEnableTestMode()) { + if (StringUtils.isEmpty(this.currentServerAddress) && !config.isEnableTestMode()) { throw new PowerJobException("can't find any available server, this worker has been quarantined."); } // 这里必须保证成功 @@ -131,7 +134,7 @@ public class ServerDiscoveryService { @SuppressWarnings("rawtypes") private String acquire(String httpServerAddress) { String result = null; - String url = String.format(DISCOVERY_URL, httpServerAddress, appId, currentServerAddress); + String url = buildServerDiscoveryUrl(httpServerAddress); try { result = CommonUtils.executeWithRetry0(() -> HttpUtils.get(url)); }catch (Exception ignore) { @@ -147,4 +150,15 @@ public class ServerDiscoveryService { } return null; } + + private String buildServerDiscoveryUrl(String address) { + + ServerDiscoveryRequest serverDiscoveryRequest = new ServerDiscoveryRequest() + .setAppId(appId) + .setCurrentServer(currentServerAddress) + .setProtocol(config.getProtocol().name().toUpperCase()); + + String query = Joiner.on(OmsConstant.AND).withKeyValueSeparator(OmsConstant.EQUAL).join(serverDiscoveryRequest.toMap()); + return String.format(DISCOVERY_URL, address, query); + } } diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/background/WorkerHealthReporter.java b/powerjob-worker/src/main/java/tech/powerjob/worker/background/WorkerHealthReporter.java index 50d50f20..645d74ed 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/background/WorkerHealthReporter.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/background/WorkerHealthReporter.java @@ -1,17 +1,15 @@ package tech.powerjob.worker.background; -import akka.actor.ActorSelection; import lombok.RequiredArgsConstructor; -import tech.powerjob.common.enums.Protocol; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import tech.powerjob.common.model.SystemMetrics; import tech.powerjob.common.request.WorkerHeartbeat; import tech.powerjob.worker.common.PowerJobWorkerVersion; import tech.powerjob.worker.common.WorkerRuntime; -import tech.powerjob.worker.common.utils.AkkaUtils; import tech.powerjob.worker.common.utils.SystemInfoUtils; +import tech.powerjob.worker.common.utils.TransportUtils; import tech.powerjob.worker.container.OmsContainerFactory; -import lombok.extern.slf4j.Slf4j; -import org.springframework.util.StringUtils; import tech.powerjob.worker.core.tracker.manager.HeavyTaskTrackerManager; import tech.powerjob.worker.core.tracker.manager.LightTaskTrackerManager; @@ -54,8 +52,8 @@ public class WorkerHealthReporter implements Runnable { heartbeat.setAppId(workerRuntime.getAppId()); heartbeat.setHeartbeatTime(System.currentTimeMillis()); heartbeat.setVersion(PowerJobWorkerVersion.getVersion()); - heartbeat.setProtocol(Protocol.AKKA.name()); - heartbeat.setClient("Atlantis"); + heartbeat.setProtocol(workerRuntime.getWorkerConfig().getProtocol().name()); + heartbeat.setClient("KingPenguin"); heartbeat.setTag(workerRuntime.getWorkerConfig().getTag()); // 上报 Tracker 数量 @@ -68,8 +66,7 @@ public class WorkerHealthReporter implements Runnable { // 获取当前加载的容器列表 heartbeat.setContainerInfos(OmsContainerFactory.getDeployedContainerInfos()); // 发送请求 - String serverPath = AkkaUtils.getServerActorPath(currentServer); - if (StringUtils.isEmpty(serverPath)) { + if (StringUtils.isEmpty(currentServer)) { return; } // log @@ -82,7 +79,7 @@ public class WorkerHealthReporter implements Runnable { workerRuntime.getWorkerConfig().getMaxHeavyweightTaskNum(), heartbeat.getHeavyTaskTrackerNum() ); - ActorSelection actorSelection = workerRuntime.getActorSystem().actorSelection(serverPath); - actorSelection.tell(heartbeat, null); + + TransportUtils.reportWorkerHeartbeat(heartbeat, currentServer, workerRuntime.getTransporter()); } } diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/common/PowerJobWorkerConfig.java b/powerjob-worker/src/main/java/tech/powerjob/worker/common/PowerJobWorkerConfig.java index 961aacec..c9238d1c 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/common/PowerJobWorkerConfig.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/common/PowerJobWorkerConfig.java @@ -4,10 +4,12 @@ import com.google.common.collect.Lists; import lombok.Getter; import lombok.Setter; import tech.powerjob.common.RemoteConstant; +import tech.powerjob.common.enums.Protocol; import tech.powerjob.worker.common.constants.StoreStrategy; import tech.powerjob.worker.core.processor.ProcessResult; import tech.powerjob.worker.core.processor.WorkflowContext; import tech.powerjob.worker.extension.SystemMetricsCollector; +import tech.powerjob.worker.extension.processor.ProcessorFactory; import java.util.List; @@ -35,6 +37,10 @@ public class PowerJobWorkerConfig { * Do not mistake for ActorSystem port. Do not add any prefix, i.e. http://. */ private List serverAddress = Lists.newArrayList(); + /** + * Protocol for communication between WORKER and server + */ + private Protocol protocol = Protocol.AKKA; /** * Max length of response result. Result that is longer than the value will be truncated. * {@link ProcessResult} max length for #msg @@ -60,9 +66,14 @@ public class PowerJobWorkerConfig { * {@link WorkflowContext} max length for #appendedContextData */ private int maxAppendedWfContextLength = 8192; - - + /** + * user-customized system metrics collector + */ private SystemMetricsCollector systemMetricsCollector; + /** + * Processor factory for custom logic, generally used for IOC framework processor bean injection that is not officially supported by PowerJob + */ + private List processorFactoryList; private String tag; /** diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/common/WorkerRuntime.java b/powerjob-worker/src/main/java/tech/powerjob/worker/common/WorkerRuntime.java index f7b07c41..19524e6f 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/common/WorkerRuntime.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/common/WorkerRuntime.java @@ -1,12 +1,12 @@ package tech.powerjob.worker.common; -import akka.actor.ActorSystem; +import lombok.Data; +import tech.powerjob.remote.framework.transporter.Transporter; import tech.powerjob.worker.background.OmsLogHandler; import tech.powerjob.worker.background.ServerDiscoveryService; -import tech.powerjob.worker.background.WorkerHealthReporter; import tech.powerjob.worker.core.executor.ExecutorManager; import tech.powerjob.worker.persistence.TaskPersistenceService; -import lombok.Data; +import tech.powerjob.worker.processor.ProcessorLoader; /** * store worker's runtime @@ -18,14 +18,22 @@ import lombok.Data; public class WorkerRuntime { private Long appId; - + /** + * 当前执行器地址 + */ private String workerAddress; - + /** + * 用户配置 + */ private PowerJobWorkerConfig workerConfig; - - private ActorSystem actorSystem; - - private WorkerHealthReporter healthReporter; + /** + * 通讯器 + */ + private Transporter transporter; + /** + * 处理器加载器 + */ + private ProcessorLoader processorLoader; private ExecutorManager executorManager; diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/common/utils/AkkaUtils.java b/powerjob-worker/src/main/java/tech/powerjob/worker/common/utils/AkkaUtils.java deleted file mode 100644 index c94d2f2f..00000000 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/common/utils/AkkaUtils.java +++ /dev/null @@ -1,64 +0,0 @@ -package tech.powerjob.worker.common.utils; - -import akka.actor.ActorSelection; -import akka.pattern.Patterns; -import tech.powerjob.common.exception.PowerJobException; -import tech.powerjob.common.response.AskResponse; -import tech.powerjob.common.RemoteConstant; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; - -import java.time.Duration; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.TimeUnit; - -/** - * AKKA 工具类 - * - * @author tjq - * @since 2020/3/17 - */ -@Slf4j -public class AkkaUtils { - - /** - * akka://@:/ - */ - private static final String AKKA_NODE_PATH = "akka://%s@%s/user/%s"; - - public static String getAkkaWorkerPath(String address, String actorName) { - return String.format(AKKA_NODE_PATH, RemoteConstant.WORKER_ACTOR_SYSTEM_NAME, address, actorName); - } - - public static String getServerActorPath(String serverAddress) { - if (StringUtils.isEmpty(serverAddress)) { - return null; - } - return String.format(AKKA_NODE_PATH, RemoteConstant.SERVER_ACTOR_SYSTEM_NAME, serverAddress, RemoteConstant.SERVER_ACTOR_NAME); - } - - /** - * 可靠传输 - * @param remote 远程 AKKA 节点 - * @param msg 需要传输的对象 - * @return true: 对方接收成功 / false: 对方接收失败(可能传输成功但对方处理失败,需要协同处理 AskResponse 返回值) - */ - public static boolean reliableTransmit(ActorSelection remote, Object msg) { - try { - return easyAsk(remote, msg).isSuccess(); - }catch (Exception e) { - log.warn("[PowerTransmitter] transmit {} failed", msg, e); - } - return false; - } - - public static AskResponse easyAsk(ActorSelection remote, Object msg) { - try { - CompletionStage ask = Patterns.ask(remote, msg, Duration.ofMillis(RemoteConstant.DEFAULT_TIMEOUT_MS)); - return (AskResponse) ask.toCompletableFuture().get(RemoteConstant.DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS); - }catch (Exception e) { - throw new PowerJobException(e); - } - } - -} diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/common/utils/PowerFileUtils.java b/powerjob-worker/src/main/java/tech/powerjob/worker/common/utils/PowerFileUtils.java new file mode 100644 index 00000000..1d0331ce --- /dev/null +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/common/utils/PowerFileUtils.java @@ -0,0 +1,30 @@ +package tech.powerjob.worker.common.utils; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import tech.powerjob.common.PowerJobDKey; + +/** + * 文件工具 + * + * @author tjq + * @since 2023/1/22 + */ +@Slf4j +public class PowerFileUtils { + + /** + * 获取工作目录 + * @return 允许用户通过启动配置文件自定义存储目录,默认为 user.home + */ + public static String workspace() { + String workspaceByDKey = System.getProperty(PowerJobDKey.WORKER_WORK_SPACE); + if (StringUtils.isNotEmpty(workspaceByDKey)) { + log.info("[PowerFileUtils] [workspace] use custom workspace: {}", workspaceByDKey); + return workspaceByDKey; + } + final String userHome = System.getProperty("user.home").concat("/powerjob/worker"); + log.info("[PowerFileUtils] [workspace] use user.home as workspace: {}", userHome); + return userHome; + } +} diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/common/utils/SpringUtils.java b/powerjob-worker/src/main/java/tech/powerjob/worker/common/utils/SpringUtils.java deleted file mode 100644 index 953e4713..00000000 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/common/utils/SpringUtils.java +++ /dev/null @@ -1,51 +0,0 @@ -package tech.powerjob.worker.common.utils; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.ApplicationContext; - -/** - * Spring ApplicationContext 工具类 - * - * @author tjq - * @since 2020/3/16 - */ -@Slf4j -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 getBean(Class clz) { - return context.getBean(clz); - } - - @SuppressWarnings("unchecked") - public static T getBean(String className) throws Exception { - - // 1. ClassLoader 存在,则直接使用 clz 加载 - ClassLoader classLoader = context.getClassLoader(); - if (classLoader != null) { - return (T) context.getBean(classLoader.loadClass(className)); - } - // 2. ClassLoader 不存在(系统类加载器不可见),尝试用类名称小写加载 - String[] split = className.split("\\."); - String beanName = split[split.length - 1]; - // 小写转大写 - char[] cs = beanName.toCharArray(); - cs[0] += 32; - String beanName0 = String.valueOf(cs); - log.warn("[SpringUtils] can't get ClassLoader from context[{}], try to load by beanName:{}", context, beanName0); - return (T) context.getBean(beanName0); - } - -} diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/common/utils/TransportUtils.java b/powerjob-worker/src/main/java/tech/powerjob/worker/common/utils/TransportUtils.java new file mode 100644 index 00000000..3a7f1127 --- /dev/null +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/common/utils/TransportUtils.java @@ -0,0 +1,117 @@ +package tech.powerjob.worker.common.utils; + +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import tech.powerjob.common.PowerSerializable; +import tech.powerjob.common.RemoteConstant; +import tech.powerjob.common.exception.PowerJobCheckedException; +import tech.powerjob.common.request.*; +import tech.powerjob.common.response.AskResponse; +import tech.powerjob.remote.framework.base.Address; +import tech.powerjob.remote.framework.base.HandlerLocation; +import tech.powerjob.remote.framework.base.ServerType; +import tech.powerjob.remote.framework.base.URL; +import tech.powerjob.remote.framework.transporter.Transporter; +import tech.powerjob.worker.common.WorkerRuntime; +import tech.powerjob.worker.pojo.request.*; + +import java.util.concurrent.CompletionStage; +import java.util.concurrent.TimeUnit; + +import static tech.powerjob.common.RemoteConstant.*; + +/** + * 通讯工具 + * + * @author tjq + * @since 2023/1/20 + */ +@Slf4j +public class TransportUtils { + + public static void ttReportInstanceStatus(TaskTrackerReportInstanceStatusReq req, String address, Transporter transporter) { + final URL url = easyBuildUrl(ServerType.SERVER, S4W_PATH, S4W_HANDLER_REPORT_INSTANCE_STATUS, address); + transporter.tell(url, req); + } + + public static void ttStartPtTask(TaskTrackerStartTaskReq req, String address, Transporter transporter) { + final URL url = easyBuildUrl(ServerType.WORKER, WPT_PATH, WPT_HANDLER_START_TASK, address); + transporter.tell(url, req); + } + + public static void ttStopPtInstance(TaskTrackerStopInstanceReq req, String address, Transporter transporter) { + final URL url = easyBuildUrl(ServerType.WORKER, WPT_PATH, WPT_HANDLER_STOP_INSTANCE, address); + transporter.tell(url, req); + } + + public static void ptReportTask(ProcessorReportTaskStatusReq req, String address, WorkerRuntime workerRuntime) { + final URL url = easyBuildUrl(ServerType.WORKER, WTT_PATH, WTT_HANDLER_REPORT_TASK_STATUS, address); + workerRuntime.getTransporter().tell(url, req); + } + + public static void ptReportSelfStatus(ProcessorTrackerStatusReportReq req, String address, WorkerRuntime workerRuntime) { + final URL url = easyBuildUrl(ServerType.WORKER, WTT_PATH, WTT_HANDLER_REPORT_PROCESSOR_TRACKER_STATUS, address); + workerRuntime.getTransporter().tell(url, req); + } + + public static void reportLogs(WorkerLogReportReq req, String address, Transporter transporter) { + final URL url = easyBuildUrl(ServerType.SERVER, S4W_PATH, S4W_HANDLER_REPORT_LOG, address); + transporter.tell(url, req); + } + + public static void reportWorkerHeartbeat(WorkerHeartbeat req, String address, Transporter transporter) { + final URL url = easyBuildUrl(ServerType.SERVER, S4W_PATH, S4W_HANDLER_WORKER_HEARTBEAT, address); + transporter.tell(url, req); + } + + public static boolean reliablePtReportTask(ProcessorReportTaskStatusReq req, String address, WorkerRuntime workerRuntime) { + try { + return reliableAsk(ServerType.WORKER, WTT_PATH, WTT_HANDLER_REPORT_TASK_STATUS, address, req, workerRuntime.getTransporter()).isSuccess(); + } catch (Exception e) { + log.warn("[PowerJobTransport] reliablePtReportTask failed: {}", req, e); + return false; + } + } + + public static boolean reliableMapTask(ProcessorMapTaskRequest req, String address, WorkerRuntime workerRuntime) throws PowerJobCheckedException { + try { + return reliableAsk(ServerType.WORKER, WTT_PATH, WTT_HANDLER_MAP_TASK, address, req, workerRuntime.getTransporter()).isSuccess(); + } catch (Throwable throwable) { + throw new PowerJobCheckedException(throwable); + } + } + + @SneakyThrows + public static boolean reliableTtReportInstanceStatus(TaskTrackerReportInstanceStatusReq req, String address, Transporter transporter) { + return reliableAsk(ServerType.SERVER, S4W_PATH, S4W_HANDLER_REPORT_INSTANCE_STATUS, address, req, transporter).isSuccess(); + } + + @SneakyThrows + public static AskResponse reliableQueryJobCluster(WorkerQueryExecutorClusterReq req, String address, Transporter transporter) { + return reliableAsk(ServerType.SERVER, S4W_PATH, S4W_HANDLER_QUERY_JOB_CLUSTER, address, req, transporter); + } + + @SneakyThrows + public static AskResponse reliableQueryContainerInfo(WorkerNeedDeployContainerRequest req, String address, Transporter transporter) { + return reliableAsk(ServerType.SERVER, S4W_PATH, S4W_HANDLER_WORKER_NEED_DEPLOY_CONTAINER, address, req, transporter); + } + + private static AskResponse reliableAsk(ServerType t, String rootPath, String handlerPath, String address, PowerSerializable req, Transporter transporter) throws Exception { + final URL url = easyBuildUrl(t, rootPath, handlerPath, address); + final CompletionStage completionStage = transporter.ask(url, req, AskResponse.class); + return completionStage + .toCompletableFuture() + .get(RemoteConstant.DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + public static URL easyBuildUrl(ServerType serverType, String rootPath, String handlerPath, String address) { + HandlerLocation handlerLocation = new HandlerLocation() + .setRootPath(rootPath) + .setMethodPath(handlerPath); + return new URL() + .setServerType(serverType) + .setAddress(Address.fromIpv4(address)) + .setLocation(handlerLocation); + } + +} diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/container/OmsContainerFactory.java b/powerjob-worker/src/main/java/tech/powerjob/worker/container/OmsContainerFactory.java index 6993af34..f857e43c 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/container/OmsContainerFactory.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/container/OmsContainerFactory.java @@ -1,8 +1,5 @@ package tech.powerjob.worker.container; -import akka.actor.ActorSelection; -import akka.pattern.Patterns; -import tech.powerjob.common.RemoteConstant; import tech.powerjob.common.model.DeployedContainerInfo; import tech.powerjob.common.request.ServerDeployContainerRequest; import tech.powerjob.common.request.WorkerNeedDeployContainerRequest; @@ -12,14 +9,14 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; +import tech.powerjob.worker.common.WorkerRuntime; +import tech.powerjob.worker.common.utils.PowerFileUtils; +import tech.powerjob.worker.common.utils.TransportUtils; import java.io.File; import java.net.URL; -import java.time.Duration; import java.util.List; import java.util.Map; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.TimeUnit; /** * 容器工厂 @@ -30,25 +27,23 @@ import java.util.concurrent.TimeUnit; @Slf4j public class OmsContainerFactory { - private static final String CONTAINER_DIR = System.getProperty("user.home") + "/powerjob/worker/container/"; + private static final String CONTAINER_DIR = PowerFileUtils.workspace() + "/container/"; private static final Map CARGO = Maps.newConcurrentMap(); /** * 获取容器 * @param containerId 容器ID - * @param serverActor 当容器不存在且 serverActor 非空时,尝试从服务端重新拉取容器 + * @param workerRuntime 当容器不存在且 serverActor 非空时,尝试从服务端重新拉取容器 * @return 容器示例,可能为 null */ - public static OmsContainer fetchContainer(Long containerId, ActorSelection serverActor) { + public static OmsContainer fetchContainer(Long containerId, WorkerRuntime workerRuntime) { OmsContainer omsContainer = CARGO.get(containerId); if (omsContainer != null) { return omsContainer; } - if (serverActor == null) { - return null; - } + final String currentServerAddress = workerRuntime.getServerDiscoveryService().getCurrentServerAddress(); // 尝试从 server 加载 log.info("[OmsContainer-{}] can't find the container in factory, try to deploy from server.", containerId); @@ -56,8 +51,7 @@ public class OmsContainerFactory { try { - CompletionStage askCS = Patterns.ask(serverActor, request, Duration.ofMillis(RemoteConstant.DEFAULT_TIMEOUT_MS)); - AskResponse askResponse = (AskResponse) askCS.toCompletableFuture().get(RemoteConstant.DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + AskResponse askResponse = TransportUtils.reliableQueryContainerInfo(request, currentServerAddress, workerRuntime.getTransporter()); if (askResponse.isSuccess()) { ServerDeployContainerRequest deployRequest = askResponse.getData(ServerDeployContainerRequest.class); diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/container/OmsJarContainer.java b/powerjob-worker/src/main/java/tech/powerjob/worker/container/OmsJarContainer.java index 2bd27648..8265ac83 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/container/OmsJarContainer.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/container/OmsJarContainer.java @@ -38,7 +38,7 @@ public class OmsJarContainer implements OmsContainer { private OhMyClassLoader containerClassLoader; private ClassPathXmlApplicationContext container; - private Map processorCache = Maps.newConcurrentMap(); + private final Map processorCache = Maps.newConcurrentMap(); public OmsJarContainer(Long containerId, String name, String version, File localJarFile) { this.containerId = containerId; diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/core/ProcessorBeanFactory.java b/powerjob-worker/src/main/java/tech/powerjob/worker/core/ProcessorBeanFactory.java deleted file mode 100644 index d86cdfd7..00000000 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/core/ProcessorBeanFactory.java +++ /dev/null @@ -1,62 +0,0 @@ -package tech.powerjob.worker.core; - -import tech.powerjob.worker.core.processor.sdk.BasicProcessor; -import com.google.common.collect.Maps; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.exception.ExceptionUtils; - -import java.util.Map; - -/** - * 处理器工厂 - * - * @author tjq - * @since 2020/3/23 - */ -@Slf4j -public class ProcessorBeanFactory { - - /** - * key(用来防止不同jar包同名类的冲突) -> (className -> Processor) - */ - private final Map> cache; - - private static final String LOCAL_KEY = "local"; - - private static volatile ProcessorBeanFactory processorBeanFactory; - - public ProcessorBeanFactory() { - - // 初始化对象缓存 - cache = Maps.newConcurrentMap(); - Map className2Processor = Maps.newConcurrentMap(); - cache.put(LOCAL_KEY, className2Processor); - } - - public BasicProcessor getLocalProcessor(String className) { - return cache.get(LOCAL_KEY).computeIfAbsent(className, ignore -> { - try { - - Class clz = Class.forName(className); - return (BasicProcessor) clz.getDeclaredConstructor().newInstance(); - - }catch (Exception e) { - log.warn("[ProcessorBeanFactory] load local Processor(className = {}) failed.", className, e); - ExceptionUtils.rethrow(e); - } - return null; - }); - } - - public static ProcessorBeanFactory getInstance() { - if (processorBeanFactory != null) { - return processorBeanFactory; - } - synchronized (ProcessorBeanFactory.class) { - if (processorBeanFactory == null) { - processorBeanFactory = new ProcessorBeanFactory(); - } - } - return processorBeanFactory; - } -} diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/core/processor/ProcessorInfo.java b/powerjob-worker/src/main/java/tech/powerjob/worker/core/processor/ProcessorInfo.java deleted file mode 100644 index a971911e..00000000 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/core/processor/ProcessorInfo.java +++ /dev/null @@ -1,22 +0,0 @@ -package tech.powerjob.worker.core.processor; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import tech.powerjob.worker.core.processor.sdk.BasicProcessor; - -/** - * @author Echo009 - * @since 2022/9/23 - */ -@RequiredArgsConstructor -@Getter -public class ProcessorInfo { - - private final BasicProcessor basicProcessor; - - private final ClassLoader classLoader; - - public static ProcessorInfo of(BasicProcessor basicProcessor, ClassLoader classLoader) { - return new ProcessorInfo(basicProcessor, classLoader); - } -} diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/core/processor/ProcessorLoader.java b/powerjob-worker/src/main/java/tech/powerjob/worker/core/processor/ProcessorLoader.java deleted file mode 100644 index 9256f337..00000000 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/core/processor/ProcessorLoader.java +++ /dev/null @@ -1,89 +0,0 @@ -package tech.powerjob.worker.core.processor; - -import akka.actor.ActorSelection; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.exception.ExceptionUtils; -import tech.powerjob.common.enums.ProcessorType; -import tech.powerjob.common.exception.PowerJobException; -import tech.powerjob.worker.common.WorkerRuntime; -import tech.powerjob.worker.common.utils.AkkaUtils; -import tech.powerjob.worker.common.utils.SpringUtils; -import tech.powerjob.worker.container.OmsContainer; -import tech.powerjob.worker.container.OmsContainerFactory; -import tech.powerjob.worker.core.ProcessorBeanFactory; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author Echo009 - * @since 2022/9/19 - */ -@Slf4j -public class ProcessorLoader { - - - private static final Map CACHE; - - - static { - // init - CACHE = new ConcurrentHashMap<>(128); - } - - /** - * 获取处理器 - * @param workerRuntime 运行时 - * @param processorType 处理器类型 - * @param processorInfo 处理器 id ,一般是全限定类名 - * @return processor - */ - public static ProcessorInfo loadProcessor(WorkerRuntime workerRuntime, String processorType, String processorInfo) { - ProcessorInfo processorInfoHolder = null; - ProcessorType type = ProcessorType.valueOf(processorType); - - switch (type) { - case BUILT_IN: - // 先从缓存中取 - processorInfoHolder = CACHE.computeIfAbsent(processorInfo, ignore -> { - // 先使用 Spring 加载 - if (SpringUtils.supportSpringBean()) { - try { - return ProcessorInfo.of(SpringUtils.getBean(processorInfo),workerRuntime.getClass().getClassLoader()); - } catch (Exception e) { - log.warn("[ProcessorLoader] no spring bean of processor(className={}), reason is {}.", processorInfo, ExceptionUtils.getMessage(e)); - } - } - // 反射加载 - return ProcessorInfo.of(ProcessorBeanFactory.getInstance().getLocalProcessor(processorInfo),workerRuntime.getClass().getClassLoader()); - }); - break; - case EXTERNAL: - String[] split = processorInfo.split("#"); - log.info("[ProcessorLoader] try to load processor({}) in container({})", split[1], split[0]); - - String serverPath = AkkaUtils.getServerActorPath(workerRuntime.getServerDiscoveryService().getCurrentServerAddress()); - ActorSelection actorSelection = workerRuntime.getActorSystem().actorSelection(serverPath); - OmsContainer omsContainer = OmsContainerFactory.fetchContainer(Long.valueOf(split[0]), actorSelection); - if (omsContainer != null) { - processorInfoHolder = ProcessorInfo.of(omsContainer.getProcessor(split[1]), omsContainer.getContainerClassLoader()); - } else { - log.warn("[ProcessorLoader] load container failed. processor info : {}", processorInfo); - } - break; - default: - log.warn("[ProcessorLoader] unknown processor type: {}.", processorType); - throw new PowerJobException("unknown processor type of " + processorType); - } - - if (processorInfoHolder == null) { - log.warn("[ProcessorLoader] fetch Processor(type={},info={}) failed.", processorType, processorInfo); - throw new PowerJobException("fetch Processor failed, please check your processorType and processorInfo config"); - } - - return processorInfoHolder; - - } - - -} diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/core/processor/runnable/HeavyProcessorRunnable.java b/powerjob-worker/src/main/java/tech/powerjob/worker/core/processor/runnable/HeavyProcessorRunnable.java index d42ed61b..af28b72d 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/core/processor/runnable/HeavyProcessorRunnable.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/core/processor/runnable/HeavyProcessorRunnable.java @@ -1,13 +1,16 @@ package tech.powerjob.worker.core.processor.runnable; -import akka.actor.ActorSelection; +import com.google.common.base.Stopwatch; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import tech.powerjob.common.enums.ExecuteType; -import tech.powerjob.worker.common.WorkerRuntime; +import tech.powerjob.common.serialize.SerializerUtils; import tech.powerjob.worker.common.ThreadLocalStore; +import tech.powerjob.worker.common.WorkerRuntime; import tech.powerjob.worker.common.constants.TaskConstant; import tech.powerjob.worker.common.constants.TaskStatus; -import tech.powerjob.worker.common.utils.AkkaUtils; -import tech.powerjob.common.serialize.SerializerUtils; +import tech.powerjob.worker.common.utils.TransportUtils; import tech.powerjob.worker.common.utils.WorkflowContextUtils; import tech.powerjob.worker.core.processor.ProcessResult; import tech.powerjob.worker.core.processor.TaskContext; @@ -16,15 +19,11 @@ import tech.powerjob.worker.core.processor.WorkflowContext; import tech.powerjob.worker.core.processor.sdk.BasicProcessor; import tech.powerjob.worker.core.processor.sdk.BroadcastProcessor; import tech.powerjob.worker.core.processor.sdk.MapReduceProcessor; +import tech.powerjob.worker.extension.processor.ProcessorBean; import tech.powerjob.worker.log.OmsLogger; import tech.powerjob.worker.persistence.TaskDO; import tech.powerjob.worker.pojo.model.InstanceInfo; import tech.powerjob.worker.pojo.request.ProcessorReportTaskStatusReq; -import com.google.common.base.Stopwatch; -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeanUtils; -import org.apache.commons.lang3.StringUtils; import java.util.Collections; import java.util.List; @@ -45,14 +44,10 @@ public class HeavyProcessorRunnable implements Runnable { private final InstanceInfo instanceInfo; - private final ActorSelection taskTrackerActor; + private final String taskTrackerAddress; private final TaskDO task; - private final BasicProcessor processor; + private final ProcessorBean processorBean; private final OmsLogger omsLogger; - /** - * 类加载器 - */ - private final ClassLoader classLoader; /** * 重试队列,ProcessorTracker 将会定期重新上报处理结果 */ @@ -61,6 +56,8 @@ public class HeavyProcessorRunnable implements Runnable { public void innerRun() throws InterruptedException { + final BasicProcessor processor = processorBean.getProcessor(); + String taskId = task.getTaskId(); Long instanceId = task.getInstanceId(); @@ -107,8 +104,11 @@ public class HeavyProcessorRunnable implements Runnable { private TaskContext constructTaskContext() { TaskContext taskContext = new TaskContext(); - BeanUtils.copyProperties(task, taskContext); taskContext.setJobId(instanceInfo.getJobId()); + taskContext.setInstanceId(task.getInstanceId()); + taskContext.setSubInstanceId(task.getSubInstanceId()); + taskContext.setTaskId(task.getTaskId()); + taskContext.setTaskName(task.getTaskName()); taskContext.setMaxRetryTimes(instanceInfo.getTaskRetryNum()); taskContext.setCurrentRetryTimes(task.getFailedCnt()); taskContext.setJobParams(instanceInfo.getJobParams()); @@ -122,7 +122,7 @@ public class HeavyProcessorRunnable implements Runnable { } private WorkflowContext constructWorkflowContext() { - return new WorkflowContext(instanceInfo.getWfInstanceId(),instanceInfo.getInstanceParams()); + return new WorkflowContext(instanceInfo.getWfInstanceId(), instanceInfo.getInstanceParams()); } /** @@ -131,6 +131,7 @@ public class HeavyProcessorRunnable implements Runnable { * MAP_REDUCE => {@link MapReduceProcessor#reduce} */ private void handleLastTask(String taskId, Long instanceId, TaskContext taskContext, ExecuteType executeType) { + final BasicProcessor processor = processorBean.getProcessor(); ProcessResult processResult; Stopwatch stopwatch = Stopwatch.createStarted(); log.debug("[ProcessorRunnable-{}] the last task(taskId={}) start to process.", instanceId, taskId); @@ -175,6 +176,7 @@ public class HeavyProcessorRunnable implements Runnable { * 即执行 {@link BroadcastProcessor#preProcess},并通知 TaskerTracker 创建广播子任务 */ private void handleBroadcastRootTask(Long instanceId, TaskContext taskContext) { + BasicProcessor processor = processorBean.getProcessor(); ProcessResult processResult; // 广播执行的第一个 task 只执行 preProcess 部分 if (processor instanceof BroadcastProcessor) { @@ -222,14 +224,14 @@ public class HeavyProcessorRunnable implements Runnable { // 最终结束状态要求可靠发送 if (TaskStatus.FINISHED_STATUS.contains(status.getValue())) { - boolean success = AkkaUtils.reliableTransmit(taskTrackerActor, req); + boolean success = TransportUtils.reliablePtReportTask(req, taskTrackerAddress, workerRuntime); if (!success) { // 插入重试队列,等待重试 statusReportRetryQueue.add(req); log.warn("[ProcessorRunnable-{}] report task(id={},status={},result={}) failed, will retry later", task.getInstanceId(), task.getTaskId(), status, result); } } else { - taskTrackerActor.tell(req, null); + TransportUtils.ptReportTask(req, taskTrackerAddress, workerRuntime); } } @@ -237,7 +239,7 @@ public class HeavyProcessorRunnable implements Runnable { @SuppressWarnings("squid:S2142") public void run() { // 切换线程上下文类加载器(否则用的是 Worker 类加载器,不存在容器类,在序列化/反序列化时会报 ClassNotFoundException) - Thread.currentThread().setContextClassLoader(classLoader); + Thread.currentThread().setContextClassLoader(processorBean.getClassLoader()); try { innerRun(); } catch (InterruptedException ignore) { diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/core/processor/sdk/MapProcessor.java b/powerjob-worker/src/main/java/tech/powerjob/worker/core/processor/sdk/MapProcessor.java index d8d8e752..1ec00177 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/core/processor/sdk/MapProcessor.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/core/processor/sdk/MapProcessor.java @@ -2,13 +2,12 @@ package tech.powerjob.worker.core.processor.sdk; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.util.CollectionUtils; -import tech.powerjob.common.RemoteConstant; import tech.powerjob.common.exception.PowerJobCheckedException; +import tech.powerjob.common.utils.CollectionUtils; import tech.powerjob.worker.common.ThreadLocalStore; import tech.powerjob.worker.common.WorkerRuntime; import tech.powerjob.worker.common.constants.TaskConstant; -import tech.powerjob.worker.common.utils.AkkaUtils; +import tech.powerjob.worker.common.utils.TransportUtils; import tech.powerjob.worker.persistence.TaskDO; import tech.powerjob.worker.pojo.request.ProcessorMapTaskRequest; @@ -55,8 +54,7 @@ public interface MapProcessor extends BasicProcessor { ProcessorMapTaskRequest req = new ProcessorMapTaskRequest(task, taskList, taskName); // 2. 可靠发送请求(任务不允许丢失,需要使用 ask 方法,失败抛异常) - String akkaRemotePath = AkkaUtils.getAkkaWorkerPath(task.getAddress(), RemoteConstant.TASK_TRACKER_ACTOR_NAME); - boolean requestSucceed = AkkaUtils.reliableTransmit(workerRuntime.getActorSystem().actorSelection(akkaRemotePath), req); + boolean requestSucceed = TransportUtils.reliableMapTask(req, task.getAddress(), workerRuntime); if (requestSucceed) { log.info("[Map-{}] map task[name={},num={}] successfully!", task.getInstanceId(), taskName, taskList.size()); diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/manager/LightTaskTrackerManager.java b/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/manager/LightTaskTrackerManager.java index eef65f39..5ed0f60e 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/manager/LightTaskTrackerManager.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/manager/LightTaskTrackerManager.java @@ -21,8 +21,11 @@ public class LightTaskTrackerManager { return INSTANCE_ID_2_TASK_TRACKER.get(instanceId); } - public static LightTaskTracker removeTaskTracker(Long instanceId) { - return INSTANCE_ID_2_TASK_TRACKER.remove(instanceId); + public static void removeTaskTracker(Long instanceId) { + // 忽略印度的 IDE 警告,这个判断非常有用!!!不加这个判断会导致:如果创建 TT(先执行 computeIfAbsent 正在将TT添加到 HashMap) 时报错,TT 主动调用 destroy 销毁(从 HashMap移除该 TT)时死锁 + if (INSTANCE_ID_2_TASK_TRACKER.containsKey(instanceId)) { + INSTANCE_ID_2_TASK_TRACKER.remove(instanceId); + } } public static void atomicCreateTaskTracker(Long instanceId, Function creator) { diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/processor/ProcessorTracker.java b/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/processor/ProcessorTracker.java index f3a54543..7bc79f68 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/processor/ProcessorTracker.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/processor/ProcessorTracker.java @@ -1,28 +1,23 @@ package tech.powerjob.worker.core.tracker.processor; -import akka.actor.ActorSelection; import com.google.common.collect.Queues; import com.google.common.util.concurrent.ThreadFactoryBuilder; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.springframework.util.CollectionUtils; -import tech.powerjob.common.RemoteConstant; import tech.powerjob.common.enums.ExecuteType; import tech.powerjob.common.enums.ProcessorType; import tech.powerjob.common.enums.TimeExpressionType; -import tech.powerjob.common.model.LogConfig; -import tech.powerjob.common.serialize.JsonUtils; +import tech.powerjob.common.utils.CollectionUtils; import tech.powerjob.common.utils.CommonUtils; import tech.powerjob.worker.common.WorkerRuntime; import tech.powerjob.worker.common.constants.TaskStatus; -import tech.powerjob.worker.common.utils.AkkaUtils; -import tech.powerjob.worker.core.processor.ProcessorInfo; +import tech.powerjob.worker.common.utils.TransportUtils; import tech.powerjob.worker.core.processor.runnable.HeavyProcessorRunnable; -import tech.powerjob.worker.core.processor.ProcessorLoader; import tech.powerjob.worker.core.tracker.manager.ProcessorTrackerManager; +import tech.powerjob.worker.extension.processor.ProcessorBean; +import tech.powerjob.worker.extension.processor.ProcessorDefinition; import tech.powerjob.worker.log.OmsLogger; import tech.powerjob.worker.log.OmsLoggerFactory; -import tech.powerjob.worker.log.impl.OmsServerLogger; import tech.powerjob.worker.persistence.TaskDO; import tech.powerjob.worker.pojo.model.InstanceInfo; import tech.powerjob.worker.pojo.request.ProcessorReportTaskStatusReq; @@ -56,7 +51,7 @@ public class ProcessorTracker { */ private Long instanceId; - private ProcessorInfo processorInfo; + private ProcessorBean processorBean; /** * 在线日志 */ @@ -76,8 +71,6 @@ public class ProcessorTracker { private String taskTrackerAddress; - private ActorSelection taskTrackerActorRef; - private ThreadPoolExecutor threadPool; private ScheduledExecutorService timingPool; @@ -107,9 +100,6 @@ public class ProcessorTracker { this.instanceId = request.getInstanceInfo().getInstanceId(); this.taskTrackerAddress = request.getTaskTrackerAddress(); - String akkaRemotePath = AkkaUtils.getAkkaWorkerPath(taskTrackerAddress, RemoteConstant.TASK_TRACKER_ACTOR_NAME); - this.taskTrackerActorRef = workerRuntime.getActorSystem().actorSelection(akkaRemotePath); - this.omsLogger = OmsLoggerFactory.build(instanceId, request.getLogConfig(), workerRuntime); this.statusReportRetryQueue = Queues.newLinkedBlockingQueue(); this.lastIdleTime = -1L; @@ -120,7 +110,7 @@ public class ProcessorTracker { // 初始化定时任务 initTimingJob(); // 初始化 Processor - processorInfo = ProcessorLoader.loadProcessor(workerRuntime, instanceInfo.getProcessorType(), instanceInfo.getProcessorInfo()); + processorBean = workerRuntime.getProcessorLoader().load(new ProcessorDefinition().setProcessorType(instanceInfo.getProcessorType()).setProcessorInfo(instanceInfo.getProcessorInfo())); log.info("[ProcessorTracker-{}] ProcessorTracker was successfully created!", instanceId); } catch (Throwable t) { log.warn("[ProcessorTracker-{}] create ProcessorTracker failed, all tasks submitted here will fail.", instanceId, t); @@ -152,7 +142,7 @@ public class ProcessorTracker { .setResult(lethalReason) .setReportTime(System.currentTimeMillis()); - taskTrackerActorRef.tell(report, null); + TransportUtils.ptReportTask(report, taskTrackerAddress, workerRuntime); return; } @@ -161,8 +151,7 @@ public class ProcessorTracker { newTask.setInstanceId(instanceInfo.getInstanceId()); newTask.setAddress(taskTrackerAddress); - ClassLoader classLoader = processorInfo.getClassLoader(); - HeavyProcessorRunnable heavyProcessorRunnable = new HeavyProcessorRunnable(instanceInfo, taskTrackerActorRef, newTask, processorInfo.getBasicProcessor(), omsLogger, classLoader, statusReportRetryQueue, workerRuntime); + HeavyProcessorRunnable heavyProcessorRunnable = new HeavyProcessorRunnable(instanceInfo, taskTrackerAddress, newTask, processorBean, omsLogger, statusReportRetryQueue, workerRuntime); try { threadPool.submit(heavyProcessorRunnable); success = true; @@ -182,7 +171,7 @@ public class ProcessorTracker { reportReq.setStatus(TaskStatus.WORKER_RECEIVED.getValue()); reportReq.setReportTime(System.currentTimeMillis()); - taskTrackerActorRef.tell(reportReq, null); + TransportUtils.ptReportTask(reportReq, taskTrackerAddress, workerRuntime); log.debug("[ProcessorTracker-{}] submit task(taskId={}, taskName={}) success, current queue size: {}.", instanceId, newTask.getTaskId(), newTask.getTaskName(), threadPool.getQueue().size()); @@ -203,7 +192,6 @@ public class ProcessorTracker { }); // 2. 去除顶层引用,送入GC世界 - taskTrackerActorRef = null; statusReportRetryQueue.clear(); ProcessorTrackerManager.removeProcessorTracker(instanceId); @@ -281,7 +269,7 @@ public class ProcessorTracker { // 不可靠通知,如果该请求失败,则整个任务处理集群缺失一个 ProcessorTracker,影响可接受 ProcessorTrackerStatusReportReq statusReportReq = ProcessorTrackerStatusReportReq.buildIdleReport(instanceId); statusReportReq.setAddress(workerRuntime.getWorkerAddress()); - taskTrackerActorRef.tell(statusReportReq, null); + TransportUtils.ptReportSelfStatus(statusReportReq, taskTrackerAddress, workerRuntime); destroy(); return; } @@ -293,7 +281,7 @@ public class ProcessorTracker { ProcessorReportTaskStatusReq req = statusReportRetryQueue.poll(); if (req != null) { req.setReportTime(System.currentTimeMillis()); - if (!AkkaUtils.reliableTransmit(taskTrackerActorRef, req)) { + if (!TransportUtils.reliablePtReportTask(req, taskTrackerAddress, workerRuntime)) { statusReportRetryQueue.add(req); log.warn("[ProcessorRunnable-{}] retry report finished task status failed: {}", instanceId, req); return; @@ -305,7 +293,7 @@ public class ProcessorTracker { long waitingNum = threadPool.getQueue().size(); ProcessorTrackerStatusReportReq statusReportReq = ProcessorTrackerStatusReportReq.buildLoadReport(instanceId, waitingNum); statusReportReq.setAddress(workerRuntime.getWorkerAddress()); - taskTrackerActorRef.tell(statusReportReq, null); + TransportUtils.ptReportSelfStatus(statusReportReq, taskTrackerAddress, workerRuntime); log.debug("[ProcessorTracker-{}] send heartbeat to TaskTracker, current waiting task num is {}.", instanceId, waitingNum); } diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/TaskTracker.java b/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/TaskTracker.java index f3546db1..a1edebc4 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/TaskTracker.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/TaskTracker.java @@ -1,24 +1,17 @@ package tech.powerjob.worker.core.tracker.task; -import akka.actor.ActorSelection; -import akka.pattern.Patterns; import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeanUtils; import tech.powerjob.common.enums.InstanceStatus; import tech.powerjob.common.model.InstanceDetail; import tech.powerjob.common.request.ServerScheduleJobReq; import tech.powerjob.common.request.TaskTrackerReportInstanceStatusReq; -import tech.powerjob.common.response.AskResponse; import tech.powerjob.worker.common.WorkerRuntime; -import tech.powerjob.worker.common.utils.AkkaUtils; +import tech.powerjob.worker.common.utils.TransportUtils; import tech.powerjob.worker.pojo.model.InstanceInfo; -import java.time.Duration; import java.util.Collections; import java.util.Map; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -67,7 +60,20 @@ public abstract class TaskTracker { this.instanceId = req.getInstanceId(); this.instanceInfo = new InstanceInfo(); - BeanUtils.copyProperties(req, instanceInfo); + + // PowerJob 值拷贝场景不多,引入三方值拷贝类库可能引入类冲突等问题,综合评估手写 ROI 最高 + instanceInfo.setJobId(req.getJobId()); + instanceInfo.setInstanceId(req.getInstanceId()); + instanceInfo.setWfInstanceId(req.getWfInstanceId()); + instanceInfo.setExecuteType(req.getExecuteType()); + instanceInfo.setProcessorType(req.getProcessorType()); + instanceInfo.setProcessorInfo(req.getProcessorInfo()); + instanceInfo.setJobParams(req.getJobParams()); + instanceInfo.setInstanceParams(req.getInstanceParams()); + instanceInfo.setThreadConcurrency(req.getThreadConcurrency()); + instanceInfo.setTaskRetryNum(req.getTaskRetryNum()); + instanceInfo.setLogConfig(req.getLogConfig()); + // 特殊处理超时时间 if (instanceInfo.getInstanceTimeoutMS() <= 0) { instanceInfo.setInstanceTimeoutMS(Integer.MAX_VALUE); @@ -100,25 +106,27 @@ public abstract class TaskTracker { log.warn("[TaskTracker-{}] create TaskTracker from request({}) failed.", req.getInstanceId(), req, e); // 直接发送失败请求 TaskTrackerReportInstanceStatusReq response = new TaskTrackerReportInstanceStatusReq(); - BeanUtils.copyProperties(req, response); + + response.setAppId(workerRuntime.getAppId()); + response.setJobId(req.getJobId()); + response.setInstanceId(req.getInstanceId()); + response.setWfInstanceId(req.getWfInstanceId()); + response.setInstanceStatus(InstanceStatus.FAILED.getV()); response.setResult(String.format("init TaskTracker failed, reason: %s", e.toString())); response.setReportTime(System.currentTimeMillis()); response.setStartTime(System.currentTimeMillis()); response.setSourceAddress(workerRuntime.getWorkerAddress()); - String serverPath = AkkaUtils.getServerActorPath(workerRuntime.getServerDiscoveryService().getCurrentServerAddress()); - ActorSelection serverActor = workerRuntime.getActorSystem().actorSelection(serverPath); - serverActor.tell(response, null); + TransportUtils.ttReportInstanceStatus(response, workerRuntime.getServerDiscoveryService().getCurrentServerAddress(), workerRuntime.getTransporter()); } - protected void reportFinalStatusThenDestroy(ActorSelection serverActor, TaskTrackerReportInstanceStatusReq reportInstanceStatusReq) { + protected void reportFinalStatusThenDestroy(WorkerRuntime workerRuntime, TaskTrackerReportInstanceStatusReq reportInstanceStatusReq) { + String currentServerAddress = workerRuntime.getServerDiscoveryService().getCurrentServerAddress(); // 最终状态需要可靠上报 - CompletionStage ask = Patterns.ask(serverActor, reportInstanceStatusReq, Duration.ofSeconds(15)); boolean serverAccepted = false; try { - AskResponse askResponse = (AskResponse) ask.toCompletableFuture().get(15, TimeUnit.SECONDS); - serverAccepted = askResponse.isSuccess(); + serverAccepted = TransportUtils.reliableTtReportInstanceStatus(reportInstanceStatusReq, currentServerAddress, workerRuntime.getTransporter()); } catch (Exception e) { log.warn("[TaskTracker-{}] report finished status failed, req={}.", instanceId, reportInstanceStatusReq, e); } diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/heavy/CommonTaskTracker.java b/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/heavy/CommonTaskTracker.java index ef9c2e0a..8853f310 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/heavy/CommonTaskTracker.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/heavy/CommonTaskTracker.java @@ -1,12 +1,9 @@ package tech.powerjob.worker.core.tracker.task.heavy; -import akka.actor.ActorSelection; -import akka.pattern.Patterns; import com.google.common.collect.Lists; import com.google.common.util.concurrent.ThreadFactoryBuilder; import lombok.ToString; import lombok.extern.slf4j.Slf4j; -import org.springframework.util.CollectionUtils; import tech.powerjob.common.PowerJobDKey; import tech.powerjob.common.RemoteConstant; import tech.powerjob.common.SystemInstanceResult; @@ -16,17 +13,15 @@ import tech.powerjob.common.exception.PowerJobException; import tech.powerjob.common.model.InstanceDetail; import tech.powerjob.common.request.ServerScheduleJobReq; import tech.powerjob.common.request.TaskTrackerReportInstanceStatusReq; -import tech.powerjob.common.response.AskResponse; +import tech.powerjob.common.utils.CollectionUtils; import tech.powerjob.worker.common.WorkerRuntime; import tech.powerjob.worker.common.constants.TaskConstant; import tech.powerjob.worker.common.constants.TaskStatus; -import tech.powerjob.worker.common.utils.AkkaUtils; +import tech.powerjob.worker.common.utils.TransportUtils; import tech.powerjob.worker.persistence.TaskDO; -import java.time.Duration; import java.util.List; import java.util.Optional; -import java.util.concurrent.CompletionStage; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; @@ -178,7 +173,6 @@ public class CommonTaskTracker extends HeavyTaskTracker { finished.set(true); List allTask = taskPersistenceService.getAllTask(instanceId, instanceId); if (CollectionUtils.isEmpty(allTask) || allTask.size() > 1) { - success = false; result = SystemInstanceResult.UNKNOWN_BUG; log.warn("[TaskTracker-{}] there must have some bug in TaskTracker.", instanceId); } else { @@ -229,22 +223,19 @@ public class CommonTaskTracker extends HeavyTaskTracker { result = SystemInstanceResult.INSTANCE_EXECUTE_TIMEOUT; } - String serverPath = AkkaUtils.getServerActorPath(workerRuntime.getServerDiscoveryService().getCurrentServerAddress()); - ActorSelection serverActor = workerRuntime.getActorSystem().actorSelection(serverPath); - // 4. 执行完毕,报告服务器 if (finished.get()) { req.setResult(result); // 上报追加的工作流上下文信息 req.setAppendedWfContext(appendedWfContext); req.setInstanceStatus(success ? InstanceStatus.SUCCEED.getV() : InstanceStatus.FAILED.getV()); - reportFinalStatusThenDestroy(serverActor,req); + reportFinalStatusThenDestroy(workerRuntime, req); return; } // 5. 未完成,上报状态 req.setInstanceStatus(InstanceStatus.RUNNING.getV()); - serverActor.tell(req, null); + TransportUtils.ttReportInstanceStatus(req, workerRuntime.getServerDiscoveryService().getCurrentServerAddress(), workerRuntime.getTransporter()); // 6.1 定期检查 -> 重试派发后未确认的任务 long currentMS = System.currentTimeMillis(); diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/heavy/FrequentTaskTracker.java b/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/heavy/FrequentTaskTracker.java index f46ea3de..891ce021 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/heavy/FrequentTaskTracker.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/heavy/FrequentTaskTracker.java @@ -1,6 +1,5 @@ package tech.powerjob.worker.core.tracker.task.heavy; -import akka.actor.ActorSelection; import com.fasterxml.jackson.core.JsonProcessingException; import com.google.common.base.Stopwatch; import com.google.common.collect.Lists; @@ -9,7 +8,6 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.BeanUtils; import tech.powerjob.common.enums.ExecuteType; import tech.powerjob.common.enums.InstanceStatus; import tech.powerjob.common.enums.TimeExpressionType; @@ -22,8 +20,8 @@ import tech.powerjob.common.serialize.JsonUtils; import tech.powerjob.worker.common.WorkerRuntime; import tech.powerjob.worker.common.constants.TaskConstant; import tech.powerjob.worker.common.constants.TaskStatus; -import tech.powerjob.worker.common.utils.AkkaUtils; import tech.powerjob.worker.common.utils.LRUCache; +import tech.powerjob.worker.common.utils.TransportUtils; import tech.powerjob.worker.persistence.TaskDO; import java.util.*; @@ -136,10 +134,14 @@ public class FrequentTaskTracker extends HeavyTaskTracker { List history = Lists.newLinkedList(); recentSubInstanceInfo.forEach((subId, subInstanceInfo) -> { InstanceDetail.SubInstanceDetail subDetail = new InstanceDetail.SubInstanceDetail(); - BeanUtils.copyProperties(subInstanceInfo, subDetail); + + subDetail.setSubInstanceId(subId); + subDetail.setStartTime(subInstanceInfo.getStartTime()); + subDetail.setFinishedTime(subInstanceInfo.getFinishedTime()); + subDetail.setResult(subInstanceInfo.getResult()); + InstanceStatus status = InstanceStatus.of(subInstanceInfo.status); subDetail.setStatus(status.getV()); - subDetail.setSubInstanceId(subId); history.add(subDetail); }); @@ -358,13 +360,8 @@ public class FrequentTaskTracker extends HeavyTaskTracker { log.warn("[FQTaskTracker-{}] report alert req,time:{}", instanceId, req.getReportTime()); } - String serverPath = AkkaUtils.getServerActorPath(currentServerAddress); - if (StringUtils.isEmpty(serverPath)) { - return; - } // 非可靠通知,Server挂掉后任务的kill工作交由其他线程去做 - ActorSelection serverActor = workerRuntime.getActorSystem().actorSelection(serverPath); - serverActor.tell(req, null); + TransportUtils.ttReportInstanceStatus(req, currentServerAddress, workerRuntime.getTransporter()); } /** diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/heavy/HeavyTaskTracker.java b/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/heavy/HeavyTaskTracker.java index ab459392..4f75e0ab 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/heavy/HeavyTaskTracker.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/heavy/HeavyTaskTracker.java @@ -1,24 +1,28 @@ package tech.powerjob.worker.core.tracker.task.heavy; -import akka.actor.ActorSelection; import com.fasterxml.jackson.core.type.TypeReference; +import com.google.common.base.Stopwatch; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.collect.Lists; import lombok.AllArgsConstructor; -import tech.powerjob.common.enums.ExecuteType; -import tech.powerjob.common.enums.InstanceStatus; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import tech.powerjob.common.RemoteConstant; +import tech.powerjob.common.enums.ExecuteType; import tech.powerjob.common.enums.TimeExpressionType; -import tech.powerjob.common.model.InstanceDetail; import tech.powerjob.common.request.ServerScheduleJobReq; -import tech.powerjob.common.request.TaskTrackerReportInstanceStatusReq; import tech.powerjob.common.request.WorkerQueryExecutorClusterReq; import tech.powerjob.common.response.AskResponse; -import tech.powerjob.common.utils.CommonUtils; import tech.powerjob.common.serialize.JsonUtils; +import tech.powerjob.common.utils.CollectionUtils; +import tech.powerjob.common.utils.CommonUtils; import tech.powerjob.common.utils.SegmentLock; import tech.powerjob.worker.common.WorkerRuntime; import tech.powerjob.worker.common.constants.TaskConstant; import tech.powerjob.worker.common.constants.TaskStatus; -import tech.powerjob.worker.common.utils.AkkaUtils; +import tech.powerjob.worker.common.utils.TransportUtils; import tech.powerjob.worker.common.utils.WorkflowContextUtils; import tech.powerjob.worker.core.ha.ProcessorTrackerStatusHolder; import tech.powerjob.worker.core.tracker.manager.HeavyTaskTrackerManager; @@ -28,19 +32,8 @@ import tech.powerjob.worker.persistence.TaskPersistenceService; import tech.powerjob.worker.pojo.request.ProcessorTrackerStatusReportReq; import tech.powerjob.worker.pojo.request.TaskTrackerStartTaskReq; import tech.powerjob.worker.pojo.request.TaskTrackerStopInstanceReq; -import com.google.common.base.Stopwatch; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeanUtils; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; import javax.annotation.Nullable; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; @@ -365,11 +358,9 @@ public abstract class HeavyTaskTracker extends TaskTracker { // 1. 通知 ProcessorTracker 释放资源 TaskTrackerStopInstanceReq stopRequest = new TaskTrackerStopInstanceReq(); stopRequest.setInstanceId(instanceId); - ptStatusHolder.getAllProcessorTrackers().forEach(ptIP -> { - String ptPath = AkkaUtils.getAkkaWorkerPath(ptIP, RemoteConstant.PROCESSOR_TRACKER_ACTOR_NAME); - ActorSelection ptActor = workerRuntime.getActorSystem().actorSelection(ptPath); + ptStatusHolder.getAllProcessorTrackers().forEach(ptAddress -> { // 不可靠通知,ProcessorTracker 也可以靠自己的定时任务/问询等方式关闭 - ptActor.tell(stopRequest, null); + TransportUtils.ttStopPtInstance(stopRequest, ptAddress, workerRuntime.getTransporter()); }); // 2. 删除所有数据库数据 @@ -425,9 +416,7 @@ public abstract class HeavyTaskTracker extends TaskTracker { // 4. 任务派发 TaskTrackerStartTaskReq startTaskReq = new TaskTrackerStartTaskReq(instanceInfo, task, workerRuntime.getWorkerAddress()); - String ptActorPath = AkkaUtils.getAkkaWorkerPath(processorTrackerAddress, RemoteConstant.PROCESSOR_TRACKER_ACTOR_NAME); - ActorSelection ptActor = workerRuntime.getActorSystem().actorSelection(ptActorPath); - ptActor.tell(startTaskReq, null); + TransportUtils.ttStartPtTask(startTaskReq, processorTrackerAddress, workerRuntime.getTransporter()); log.debug("[TaskTracker-{}] dispatch task(taskId={},taskName={}) successfully.", instanceId, task.getTaskId(), task.getTaskName()); } @@ -524,22 +513,24 @@ public abstract class HeavyTaskTracker extends TaskTracker { return; } - String serverPath = AkkaUtils.getServerActorPath(workerRuntime.getServerDiscoveryService().getCurrentServerAddress()); - if (StringUtils.isEmpty(serverPath)) { + final String currentServerAddress = workerRuntime.getServerDiscoveryService().getCurrentServerAddress(); + if (StringUtils.isEmpty(currentServerAddress)) { log.warn("[TaskTracker-{}] no server available, won't start worker detective!", instanceId); return; } - WorkerQueryExecutorClusterReq req = new WorkerQueryExecutorClusterReq(workerRuntime.getAppId(), instanceInfo.getJobId()); - AskResponse response = AkkaUtils.easyAsk(workerRuntime.getActorSystem().actorSelection(serverPath), req); - if (!response.isSuccess()) { - log.warn("[TaskTracker-{}] detective failed due to ask failed, message is {}", instanceId, response.getMessage()); - return; - } + try { + WorkerQueryExecutorClusterReq req = new WorkerQueryExecutorClusterReq(workerRuntime.getAppId(), instanceInfo.getJobId()); + AskResponse response = TransportUtils.reliableQueryJobCluster(req, currentServerAddress, workerRuntime.getTransporter()); + if (!response.isSuccess()) { + log.warn("[TaskTracker-{}] detective failed due to ask failed, message is {}", instanceId, response.getMessage()); + return; + } + List workerList = JsonUtils.parseObject(response.getData(), new TypeReference>() {}); ptStatusHolder.register(workerList); } catch (Exception e) { - log.warn("[TaskTracker-{}] detective failed!", instanceId, e); + log.warn("[TaskTracker-{}] detective failed, currentServer: {}", instanceId, currentServerAddress, e); } } } diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/light/LightTaskTracker.java b/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/light/LightTaskTracker.java index d46fc5dd..66da591b 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/light/LightTaskTracker.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/core/tracker/task/light/LightTaskTracker.java @@ -1,6 +1,5 @@ package tech.powerjob.worker.core.tracker.task.light; -import akka.actor.ActorSelection; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomUtils; import org.apache.commons.lang3.StringUtils; @@ -13,10 +12,12 @@ import tech.powerjob.common.request.TaskTrackerReportInstanceStatusReq; import tech.powerjob.worker.common.WorkerRuntime; import tech.powerjob.worker.common.constants.TaskConstant; import tech.powerjob.worker.common.constants.TaskStatus; -import tech.powerjob.worker.common.utils.AkkaUtils; +import tech.powerjob.worker.common.utils.TransportUtils; import tech.powerjob.worker.core.processor.*; import tech.powerjob.worker.core.tracker.manager.LightTaskTrackerManager; import tech.powerjob.worker.core.tracker.task.TaskTracker; +import tech.powerjob.worker.extension.processor.ProcessorBean; +import tech.powerjob.worker.extension.processor.ProcessorDefinition; import tech.powerjob.worker.log.OmsLoggerFactory; import java.util.concurrent.Future; @@ -50,7 +51,7 @@ public class LightTaskTracker extends TaskTracker { /** * 处理器信息 */ - private final ProcessorInfo processorInfo; + private final ProcessorBean processorBean; /** * 上下文 */ @@ -86,7 +87,7 @@ public class LightTaskTracker extends TaskTracker { // 等待处理 status = TaskStatus.WORKER_RECEIVED; // 加载 Processor - processorInfo = ProcessorLoader.loadProcessor(workerRuntime, req.getProcessorType(), req.getProcessorInfo()); + processorBean = workerRuntime.getProcessorLoader().load(new ProcessorDefinition().setProcessorType(req.getProcessorType()).setProcessorInfo(req.getProcessorInfo())); executeThread = new AtomicReference<>(); long delay = Integer.parseInt(System.getProperty(PowerJobDKey.WORKER_STATUS_CHECK_PERIOD, "15")) * 1000L; // 初始延迟加入随机值,避免在高并发场景下所有请求集中在一个时间段 @@ -107,7 +108,7 @@ public class LightTaskTracker extends TaskTracker { // 提交任务到线程池 processFuture = workerRuntime.getExecutorManager().getLightweightTaskExecutorService().submit(this::processTask); } catch (Exception e) { - log.warn("[TaskTracker-{}] fail to create TaskTracker for req:{} ", instanceId, req); + log.error("[TaskTracker-{}] fail to create TaskTracker for req:{} ", instanceId, req); destroy(); throw e; } @@ -200,14 +201,14 @@ public class LightTaskTracker extends TaskTracker { // 开始执行时,提交任务判断是否超时 ProcessResult res = null; do { - Thread.currentThread().setContextClassLoader(processorInfo.getClassLoader()); + Thread.currentThread().setContextClassLoader(processorBean.getClassLoader()); if (res != null && !res.isSuccess()) { // 重试 taskContext.setCurrentRetryTimes(taskContext.getCurrentRetryTimes() + 1); log.warn("[TaskTracker-{}] process failed, TaskTracker will have a retry,current retryTimes : {}", instanceId, taskContext.getCurrentRetryTimes()); } try { - res = processorInfo.getBasicProcessor().process(taskContext); + res = processorBean.getProcessor().process(taskContext); } catch (InterruptedException e) { log.warn("[TaskTracker-{}] task has been interrupted !", instanceId, e); Thread.currentThread().interrupt(); @@ -249,8 +250,6 @@ public class LightTaskTracker extends TaskTracker { log.info("[TaskTracker-{}] has been destroyed,final status is {},needn't to report status!", instanceId, status); return; } - String serverPath = AkkaUtils.getServerActorPath(workerRuntime.getServerDiscoveryService().getCurrentServerAddress()); - ActorSelection serverActor = workerRuntime.getActorSystem().actorSelection(serverPath); TaskTrackerReportInstanceStatusReq reportInstanceStatusReq = new TaskTrackerReportInstanceStatusReq(); reportInstanceStatusReq.setAppId(workerRuntime.getAppId()); reportInstanceStatusReq.setJobId(instanceInfo.getJobId()); @@ -303,13 +302,13 @@ public class LightTaskTracker extends TaskTracker { reportInstanceStatusReq.setEndTime(taskEndTime); // 微操一下,上报最终状态时重新设置下时间,并且增加一小段偏移,保证在并发上报运行中状态以及最终状态时,最终状态的上报时间晚于运行中的状态 reportInstanceStatusReq.setReportTime(System.currentTimeMillis() + 1); - reportFinalStatusThenDestroy(serverActor, reportInstanceStatusReq); + reportFinalStatusThenDestroy(workerRuntime, reportInstanceStatusReq); return; } // 未完成的任务,只需要上报状态 reportInstanceStatusReq.setInstanceStatus(InstanceStatus.RUNNING.getV()); log.info("[TaskTracker-{}] report status({}) success,real status is {}", instanceId, reportInstanceStatusReq, status); - serverActor.tell(reportInstanceStatusReq, null); + TransportUtils.ttReportInstanceStatus(reportInstanceStatusReq, workerRuntime.getServerDiscoveryService().getCurrentServerAddress(), workerRuntime.getTransporter()); } private void timeoutCheck() { diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/extension/processor/ProcessorBean.java b/powerjob-worker/src/main/java/tech/powerjob/worker/extension/processor/ProcessorBean.java new file mode 100644 index 00000000..c7238f33 --- /dev/null +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/extension/processor/ProcessorBean.java @@ -0,0 +1,28 @@ +package tech.powerjob.worker.extension.processor; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import tech.powerjob.worker.core.processor.sdk.BasicProcessor; + +/** + * 处理器对象 + * + * @author Echo009 + * @since 2022/9/23 + */ +@Getter +@Setter +@Accessors(chain = true) +public class ProcessorBean { + + /** + * 真正用来执行逻辑的处理器对象 + */ + private transient BasicProcessor processor; + /** + * 加载该处理器对象的 classLoader,可空,空则使用 {@link Object#getClass()#getClassLoader() 代替} + */ + private transient ClassLoader classLoader; + +} diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/extension/processor/ProcessorDefinition.java b/powerjob-worker/src/main/java/tech/powerjob/worker/extension/processor/ProcessorDefinition.java new file mode 100644 index 00000000..aa89ca7e --- /dev/null +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/extension/processor/ProcessorDefinition.java @@ -0,0 +1,49 @@ +package tech.powerjob.worker.extension.processor; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Objects; + +/** + * 处理器定义 + * 对外暴露的对象尽量不要直接使用构造器等不方便后续扩展的 API,Getter & Setter 保兼容 + * + * @author Echo009 + * @since 2023/1/17 + */ +@Getter +@Setter +@ToString +@Accessors(chain = true) +public class ProcessorDefinition implements Serializable { + + /** + * 后台配置的处理器类型 + */ + private String processorType; + /** + * 后台配置的处理器信息 + */ + private String processorInfo; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ProcessorDefinition that = (ProcessorDefinition) o; + return Objects.equals(processorType, that.processorType) && Objects.equals(processorInfo, that.processorInfo); + } + + @Override + public int hashCode() { + return Objects.hash(processorType, processorInfo); + } +} diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/extension/processor/ProcessorFactory.java b/powerjob-worker/src/main/java/tech/powerjob/worker/extension/processor/ProcessorFactory.java new file mode 100644 index 00000000..67596aab --- /dev/null +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/extension/processor/ProcessorFactory.java @@ -0,0 +1,31 @@ +package tech.powerjob.worker.extension.processor; + +import tech.powerjob.common.enums.ProcessorType; + +import java.util.Set; + +/** + * 处理器工厂 + * 考虑到当前是一个百花齐放的生态,各种 IOC 框架层出不穷。PowerJob 决定在 4.3.0 剥离对 Spring 的强依赖,并允许开发者自定义 Bean 的初始化逻辑 + * 不知道怎么用的话,可以看看官方提供的 3 个默认实现,比如对接第三方 IOC 框架就类似于 BuiltInSpringProcessorFactory + * + * @author tjq + * @since 2023/1/17 + */ +public interface ProcessorFactory { + + /** + * 支持的处理器类型,类型不匹配则跳过该 ProcessorFactory 的加载逻辑 + * 对应的是控制台的'处理器类型' TAB,不做任何定制的情况下,取值范围为 {@link ProcessorType#name()} + * @return 支持的处理器类型 + */ + Set supportTypes(); + + /** + * 根据处理器定义构建处理器对象 + * 注意:Processor 为单例对象,即 PowerJob 对每一个 ProcessorBean 只调用一次 build 方法 + * @param processorDefinition 处理器定义 + * @return null or ProcessorBean + */ + ProcessorBean build(ProcessorDefinition processorDefinition); +} diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/persistence/ConnectionFactory.java b/powerjob-worker/src/main/java/tech/powerjob/worker/persistence/ConnectionFactory.java index 60013e7e..dfbe2cd0 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/persistence/ConnectionFactory.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/persistence/ConnectionFactory.java @@ -8,6 +8,7 @@ import com.zaxxer.hikari.HikariDataSource; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.h2.Driver; +import tech.powerjob.worker.common.utils.PowerFileUtils; import javax.sql.DataSource; import java.io.File; @@ -25,7 +26,7 @@ public class ConnectionFactory { private volatile DataSource dataSource; - private final String H2_PATH = System.getProperty("user.home") + "/powerjob/worker/h2/" + CommonUtils.genUUID() + "/"; + private final String H2_PATH = PowerFileUtils.workspace() + "/h2/" + CommonUtils.genUUID() + "/"; private final String DISK_JDBC_URL = String.format("jdbc:h2:file:%spowerjob_worker_db;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false", H2_PATH); private final String MEMORY_JDBC_URL = String.format("jdbc:h2:mem:%spowerjob_worker_db;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false", H2_PATH); diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/persistence/TaskPersistenceService.java b/powerjob-worker/src/main/java/tech/powerjob/worker/persistence/TaskPersistenceService.java index 97c5794d..f490146c 100644 --- a/powerjob-worker/src/main/java/tech/powerjob/worker/persistence/TaskPersistenceService.java +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/persistence/TaskPersistenceService.java @@ -1,17 +1,17 @@ package tech.powerjob.worker.persistence; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; import tech.powerjob.common.RemoteConstant; +import tech.powerjob.common.utils.CollectionUtils; import tech.powerjob.common.utils.CommonUtils; import tech.powerjob.common.utils.SupplierPlus; import tech.powerjob.worker.common.constants.StoreStrategy; import tech.powerjob.worker.common.constants.TaskConstant; import tech.powerjob.worker.common.constants.TaskStatus; import tech.powerjob.worker.core.processor.TaskResult; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import lombok.extern.slf4j.Slf4j; -import org.springframework.util.CollectionUtils; import java.util.Collections; import java.util.List; diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/processor/PowerJobProcessorLoader.java b/powerjob-worker/src/main/java/tech/powerjob/worker/processor/PowerJobProcessorLoader.java new file mode 100644 index 00000000..00be4fa5 --- /dev/null +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/processor/PowerJobProcessorLoader.java @@ -0,0 +1,56 @@ +package tech.powerjob.worker.processor; + +import lombok.extern.slf4j.Slf4j; +import tech.powerjob.common.exception.PowerJobException; +import tech.powerjob.worker.extension.processor.ProcessorBean; +import tech.powerjob.worker.extension.processor.ProcessorDefinition; +import tech.powerjob.worker.extension.processor.ProcessorFactory; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * PowerJobProcessorLoader + * + * @author tjq + * @since 2023/1/17 + */ +@Slf4j +public class PowerJobProcessorLoader implements ProcessorLoader { + + private final List processorFactoryList; + private final Map def2Bean = new ConcurrentHashMap<>(128); + + public PowerJobProcessorLoader(List processorFactoryList) { + this.processorFactoryList = processorFactoryList; + } + + @Override + public ProcessorBean load(ProcessorDefinition definition) { + return def2Bean.computeIfAbsent(definition, ignore -> { + final String processorType = definition.getProcessorType(); + log.info("[ProcessorFactory] start to load Processor: {}", definition); + for (ProcessorFactory pf : processorFactoryList) { + final String pfName = pf.getClass().getSimpleName(); + if (!Optional.ofNullable(pf.supportTypes()).orElse(Collections.emptySet()).contains(processorType)) { + log.info("[ProcessorFactory] [{}] can't load type={}, skip!", pfName, processorType); + continue; + } + log.info("[ProcessorFactory] [{}] try to load processor: {}", pfName, definition); + try { + ProcessorBean processorBean = pf.build(definition); + if (processorBean != null) { + log.info("[ProcessorFactory] [{}] load processor successfully: {}", pfName, definition); + return processorBean; + } + } catch (Throwable t) { + log.error("[ProcessorFactory] [{}] load processor failed: {}", pfName, definition, t); + } + } + throw new PowerJobException("fetch Processor failed, please check your processorType and processorInfo config"); + }); + } +} diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/processor/ProcessorLoader.java b/powerjob-worker/src/main/java/tech/powerjob/worker/processor/ProcessorLoader.java new file mode 100644 index 00000000..d37a3303 --- /dev/null +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/processor/ProcessorLoader.java @@ -0,0 +1,15 @@ +package tech.powerjob.worker.processor; + +import tech.powerjob.worker.extension.processor.ProcessorBean; +import tech.powerjob.worker.extension.processor.ProcessorDefinition; + +/** + * 内部使用的 Processor 加载器 + * + * @author Echo009 + * @since 2023/1/20 + */ +public interface ProcessorLoader { + + ProcessorBean load(ProcessorDefinition definition); +} diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/processor/impl/BuiltInDefaultProcessorFactory.java b/powerjob-worker/src/main/java/tech/powerjob/worker/processor/impl/BuiltInDefaultProcessorFactory.java new file mode 100644 index 00000000..764df546 --- /dev/null +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/processor/impl/BuiltInDefaultProcessorFactory.java @@ -0,0 +1,43 @@ +package tech.powerjob.worker.processor.impl; + +import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; +import tech.powerjob.common.enums.ProcessorType; +import tech.powerjob.worker.core.processor.sdk.BasicProcessor; +import tech.powerjob.worker.extension.processor.ProcessorBean; +import tech.powerjob.worker.extension.processor.ProcessorDefinition; +import tech.powerjob.worker.extension.processor.ProcessorFactory; + +import java.util.Set; + +/** + * 内建的默认处理器工厂,通过全限定类名加载处理器,但无法享受 IOC 框架的 DI 功能 + * + * @author tjq + * @since 2023/1/17 + */ +@Slf4j +public class BuiltInDefaultProcessorFactory implements ProcessorFactory { + + @Override + public Set supportTypes() { + return Sets.newHashSet(ProcessorType.BUILT_IN.name()); + } + + @Override + public ProcessorBean build(ProcessorDefinition processorDefinition) { + + String className = processorDefinition.getProcessorInfo(); + + try { + Class clz = Class.forName(className); + BasicProcessor basicProcessor = (BasicProcessor) clz.getDeclaredConstructor().newInstance(); + return new ProcessorBean() + .setProcessor(basicProcessor) + .setClassLoader(basicProcessor.getClass().getClassLoader()); + }catch (Exception e) { + log.warn("[ProcessorFactory] load local Processor(className = {}) failed.", className, e); + } + return null; + } +} diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/processor/impl/BuiltInSpringProcessorFactory.java b/powerjob-worker/src/main/java/tech/powerjob/worker/processor/impl/BuiltInSpringProcessorFactory.java new file mode 100644 index 00000000..e93afe27 --- /dev/null +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/processor/impl/BuiltInSpringProcessorFactory.java @@ -0,0 +1,83 @@ +package tech.powerjob.worker.processor.impl; + +import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationContext; +import tech.powerjob.common.enums.ProcessorType; +import tech.powerjob.worker.core.processor.sdk.BasicProcessor; +import tech.powerjob.worker.extension.processor.ProcessorBean; +import tech.powerjob.worker.extension.processor.ProcessorDefinition; +import tech.powerjob.worker.extension.processor.ProcessorFactory; + +import java.util.Set; + +/** + * 内建的 SpringBean 处理器工厂,用于加载 Spring 相关的Bean,非核心依赖 + * + * @author tjq + * @since 2023/1/17 + */ +@Slf4j +public class BuiltInSpringProcessorFactory implements ProcessorFactory { + + private final ApplicationContext applicationContext; + + public BuiltInSpringProcessorFactory(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Override + public Set supportTypes() { + return Sets.newHashSet(ProcessorType.BUILT_IN.name()); + } + + @Override + public ProcessorBean build(ProcessorDefinition processorDefinition) { + + try { + boolean canLoad = checkCanLoad(); + if (!canLoad) { + log.info("[ProcessorFactory] can't find Spring env, this processor can't load by 'BuiltInSpringProcessorFactory'"); + return null; + } + + BasicProcessor basicProcessor = getBean(processorDefinition.getProcessorInfo(), applicationContext); + return new ProcessorBean() + .setProcessor(basicProcessor) + .setClassLoader(basicProcessor.getClass().getClassLoader()); + } catch (Throwable t) { + log.warn("[ProcessorFactory] load by BuiltInSpringProcessorFactory failed. If you are using Spring, make sure this bean was managed by Spring", t); + } + + return null; + } + + private boolean checkCanLoad() { + try { + ApplicationContext.class.getClassLoader(); + return applicationContext != null; + } catch (Throwable ignore) { + } + return false; + } + + + @SuppressWarnings("unchecked") + private static T getBean(String className, ApplicationContext ctx) throws Exception { + // 1. ClassLoader 存在,则直接使用 clz 加载 + ClassLoader classLoader = ctx.getClassLoader(); + if (classLoader != null) { + return (T) ctx.getBean(classLoader.loadClass(className)); + } + // 2. ClassLoader 不存在(系统类加载器不可见),尝试用类名称小写加载 + String[] split = className.split("\\."); + String beanName = split[split.length - 1]; + // 小写转大写 + char[] cs = beanName.toCharArray(); + cs[0] += 32; + String beanName0 = String.valueOf(cs); + log.warn("[SpringUtils] can't get ClassLoader from context[{}], try to load by beanName:{}", ctx, beanName0); + return (T) ctx.getBean(beanName0); + } + +} diff --git a/powerjob-worker/src/main/java/tech/powerjob/worker/processor/impl/JarContainerProcessorFactory.java b/powerjob-worker/src/main/java/tech/powerjob/worker/processor/impl/JarContainerProcessorFactory.java new file mode 100644 index 00000000..26762be5 --- /dev/null +++ b/powerjob-worker/src/main/java/tech/powerjob/worker/processor/impl/JarContainerProcessorFactory.java @@ -0,0 +1,55 @@ +package tech.powerjob.worker.processor.impl; + +import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; +import tech.powerjob.common.enums.ProcessorType; +import tech.powerjob.worker.common.WorkerRuntime; +import tech.powerjob.worker.container.OmsContainer; +import tech.powerjob.worker.container.OmsContainerFactory; +import tech.powerjob.worker.extension.processor.ProcessorBean; +import tech.powerjob.worker.extension.processor.ProcessorDefinition; +import tech.powerjob.worker.extension.processor.ProcessorFactory; + +import java.util.Set; + +/** + * 加载容器处理器 + * + * @author tjq + * @since 2023/1/17 + */ +@Slf4j +public class JarContainerProcessorFactory implements ProcessorFactory { + + private final WorkerRuntime workerRuntime; + + public JarContainerProcessorFactory(WorkerRuntime workerRuntime) { + this.workerRuntime = workerRuntime; + } + + @Override + public Set supportTypes() { + return Sets.newHashSet(ProcessorType.EXTERNAL.name()); + } + + @Override + public ProcessorBean build(ProcessorDefinition processorDefinition) { + + String processorInfo = processorDefinition.getProcessorInfo(); + String[] split = processorInfo.split("#"); + String containerName = split[0]; + String className = split[1]; + + log.info("[ProcessorFactory] try to load processor({}) in container({})", className, containerName); + + OmsContainer omsContainer = OmsContainerFactory.fetchContainer(Long.valueOf(containerName), workerRuntime); + if (omsContainer != null) { + return new ProcessorBean() + .setProcessor(omsContainer.getProcessor(className)) + .setClassLoader(omsContainer.getContainerClassLoader()); + } else { + log.warn("[ProcessorFactory] load container failed. processor info : {}", processorInfo); + } + return null; + } +} diff --git a/powerjob-worker/src/main/resources/oms-worker.akka.conf b/powerjob-worker/src/main/resources/oms-worker.akka.conf deleted file mode 100644 index 16f7b911..00000000 --- a/powerjob-worker/src/main/resources/oms-worker.akka.conf +++ /dev/null @@ -1,70 +0,0 @@ -akka { - - loggers = ["akka.event.slf4j.Slf4jLogger"] - loglevel = "WARNING" - - actor { - # cluster is better(recommend by official document), but I prefer remote - provider = remote - allow-java-serialization = off - - serializers { - power-serializer = "tech.powerjob.common.serialize.PowerAkkaSerializer" - } - - serialization-bindings { - "tech.powerjob.common.PowerSerializable" = power-serializer - } - } - remote { - artery { - transport = tcp # See Selecting a transport below - # over write by code - canonical.hostname = "127.0.0.1" - canonical.port = 25520 - } - } - - # dispatcher - task-tracker-dispatcher { - # Dispatcher is the name of the event-based dispatcher - type = Dispatcher - # What kind of ExecutionService to use - executor = "fork-join-executor" - # Configuration for the fork join pool - fork-join-executor { - # Min number of threads to cap factor-based parallelism number to - parallelism-min = 2 - # Parallelism (threads) ... ceil(available processors * factor) - parallelism-factor = 4.0 - # Max number of threads to cap factor-based parallelism number to - parallelism-max = 64 - } - # Throughput defines the maximum number of messages to be - # processed per actor before the thread jumps to the next actor. - # Set to 1 for as fair as possible. - throughput = 10 - } - - processor-tracker-dispatcher { - type = Dispatcher - executor = "fork-join-executor" - fork-join-executor { - parallelism-min = 2 - parallelism-factor = 2.0 - parallelism-max = 64 - } - throughput = 10 - } - - worker-common-dispatcher { - type = Dispatcher - executor = "fork-join-executor" - fork-join-executor { - parallelism-min = 2 - parallelism-factor = 2.0 - parallelism-max = 8 - } - throughput = 10 - } -} \ No newline at end of file diff --git a/powerjob-worker/src/test/java/tech/powerjob/worker/test/CommonTaskTrackerTest.java b/powerjob-worker/src/test/java/tech/powerjob/worker/test/CommonTaskTrackerTest.java deleted file mode 100644 index d3becf1f..00000000 --- a/powerjob-worker/src/test/java/tech/powerjob/worker/test/CommonTaskTrackerTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package tech.powerjob.worker.test; - -import akka.actor.ActorSelection; -import akka.actor.ActorSystem; -import tech.powerjob.common.RemoteConstant; -import tech.powerjob.common.enums.ExecuteType; -import tech.powerjob.common.enums.TimeExpressionType; -import tech.powerjob.worker.PowerJobWorker; -import tech.powerjob.worker.common.PowerJobWorkerConfig; -import tech.powerjob.worker.common.utils.AkkaUtils; -import tech.powerjob.common.utils.NetUtils; -import com.google.common.collect.Lists; -import com.typesafe.config.ConfigFactory; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -/** - * 测试完整的 JobInstance 执行流程 - * - * @author tjq - * @since 2020/3/25 - */ -public class CommonTaskTrackerTest { - - private static ActorSelection remoteTaskTracker; - - @BeforeAll - public static void init() throws Exception { - - PowerJobWorkerConfig workerConfig = new PowerJobWorkerConfig(); - workerConfig.setAppName("oms-test"); - workerConfig.setServerAddress(Lists.newArrayList("127.0.0.1:7700")); - workerConfig.setEnableTestMode(true); - - PowerJobWorker worker = new PowerJobWorker(); - worker.setConfig(workerConfig); - worker.init(); - - ActorSystem testAS = ActorSystem.create("oms-test", ConfigFactory.load("oms-akka-test.conf")); - String akkaRemotePath = AkkaUtils.getAkkaWorkerPath(NetUtils.getLocalHost() + ":" + RemoteConstant.DEFAULT_WORKER_PORT, RemoteConstant.TASK_TRACKER_ACTOR_NAME); - remoteTaskTracker = testAS.actorSelection(akkaRemotePath); - } - - @Test - public void justStartWorkerToTestServer() throws Exception { - Thread.sleep(277277277); - } - - @Test - public void testStandaloneJob() throws Exception { - - remoteTaskTracker.tell(TestUtils.genServerScheduleJobReq(ExecuteType.STANDALONE, TimeExpressionType.CRON), null); - Thread.sleep(5000000); - } - - @Test - public void testMapReduceJob() throws Exception { - remoteTaskTracker.tell(TestUtils.genServerScheduleJobReq(ExecuteType.MAP_REDUCE, TimeExpressionType.CRON), null); - Thread.sleep(5000000); - } - - @Test - public void testBroadcast() throws Exception { - remoteTaskTracker.tell(TestUtils.genServerScheduleJobReq(ExecuteType.BROADCAST, TimeExpressionType.CRON), null); - Thread.sleep(5000000); - } - -} diff --git a/powerjob-worker/src/test/java/tech/powerjob/worker/test/CommonTest.java b/powerjob-worker/src/test/java/tech/powerjob/worker/test/CommonTest.java index 093ff9df..080bdf74 100644 --- a/powerjob-worker/src/test/java/tech/powerjob/worker/test/CommonTest.java +++ b/powerjob-worker/src/test/java/tech/powerjob/worker/test/CommonTest.java @@ -1,19 +1,10 @@ package tech.powerjob.worker.test; -import akka.actor.ActorSelection; -import akka.actor.ActorSystem; import tech.powerjob.common.enums.ExecuteType; import tech.powerjob.common.enums.ProcessorType; -import tech.powerjob.common.RemoteConstant; import tech.powerjob.common.utils.NetUtils; -import tech.powerjob.worker.PowerJobWorker; -import tech.powerjob.worker.common.PowerJobWorkerConfig; -import tech.powerjob.worker.common.utils.AkkaUtils; import tech.powerjob.worker.pojo.model.InstanceInfo; import tech.powerjob.worker.pojo.request.TaskTrackerStartTaskReq; -import com.typesafe.config.ConfigFactory; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; /** * 启动公共服务 @@ -23,30 +14,6 @@ import org.junit.jupiter.api.BeforeAll; */ public class CommonTest { - protected static ActorSelection remoteProcessorTracker; - protected static ActorSelection remoteTaskTracker; - - @BeforeAll - public static void startWorker() throws Exception { - PowerJobWorkerConfig workerConfig = new PowerJobWorkerConfig(); - workerConfig.setAppName("oms-test"); - workerConfig.setEnableTestMode(true); - - PowerJobWorker worker = new PowerJobWorker(); - worker.setConfig(workerConfig); - worker.init(); - - ActorSystem testAS = ActorSystem.create("oms-test", ConfigFactory.load("oms-akka-test.conf")); - String address = NetUtils.getLocalHost() + ":27777"; - - remoteProcessorTracker = testAS.actorSelection(AkkaUtils.getAkkaWorkerPath(address, RemoteConstant.PROCESSOR_TRACKER_ACTOR_NAME)); - remoteTaskTracker = testAS.actorSelection(AkkaUtils.getAkkaWorkerPath(address, RemoteConstant.TASK_TRACKER_ACTOR_NAME)); - } - - @AfterAll - public static void stop() throws Exception { - Thread.sleep(120000); - } public static TaskTrackerStartTaskReq genTaskTrackerStartTaskReq(String processor) { diff --git a/powerjob-worker/src/test/java/tech/powerjob/worker/test/FrequentTaskTrackerTest.java b/powerjob-worker/src/test/java/tech/powerjob/worker/test/FrequentTaskTrackerTest.java deleted file mode 100644 index 0bf630c5..00000000 --- a/powerjob-worker/src/test/java/tech/powerjob/worker/test/FrequentTaskTrackerTest.java +++ /dev/null @@ -1,53 +0,0 @@ -package tech.powerjob.worker.test; - -import akka.actor.ActorSelection; -import akka.actor.ActorSystem; -import tech.powerjob.common.enums.ExecuteType; -import tech.powerjob.common.RemoteConstant; -import tech.powerjob.common.enums.TimeExpressionType; -import tech.powerjob.common.utils.NetUtils; -import tech.powerjob.worker.PowerJobWorker; -import tech.powerjob.worker.common.PowerJobWorkerConfig; -import tech.powerjob.worker.common.utils.AkkaUtils; -import com.google.common.collect.Lists; -import com.typesafe.config.ConfigFactory; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -/** - * description - * - * @author tjq - * @since 2020/4/9 - */ -public class FrequentTaskTrackerTest { - - private static ActorSelection remoteTaskTracker; - - @BeforeAll - public static void init() throws Exception { - - PowerJobWorkerConfig workerConfig = new PowerJobWorkerConfig(); - workerConfig.setAppName("oms-test"); - workerConfig.setServerAddress(Lists.newArrayList("127.0.0.1:7700")); - PowerJobWorker worker = new PowerJobWorker(); - worker.setConfig(workerConfig); - worker.init(); - - ActorSystem testAS = ActorSystem.create("oms-test", ConfigFactory.load("oms-akka-test.conf")); - String akkaRemotePath = AkkaUtils.getAkkaWorkerPath(NetUtils.getLocalHost() + ":" + RemoteConstant.DEFAULT_WORKER_PORT, RemoteConstant.TASK_TRACKER_ACTOR_NAME); - remoteTaskTracker = testAS.actorSelection(akkaRemotePath); - } - - @Test - public void testFixRateJob() throws Exception { - remoteTaskTracker.tell(TestUtils.genServerScheduleJobReq(ExecuteType.STANDALONE, TimeExpressionType.FIXED_RATE), null); - Thread.sleep(5000000); - } - - @Test - public void testFixDelayJob() throws Exception { - remoteTaskTracker.tell(TestUtils.genServerScheduleJobReq(ExecuteType.MAP_REDUCE, TimeExpressionType.FIXED_DELAY), null); - Thread.sleep(5000000); - } -} diff --git a/powerjob-worker/src/test/java/tech/powerjob/worker/test/ProcessorTrackerTest.java b/powerjob-worker/src/test/java/tech/powerjob/worker/test/ProcessorTrackerTest.java deleted file mode 100644 index 3a52cd33..00000000 --- a/powerjob-worker/src/test/java/tech/powerjob/worker/test/ProcessorTrackerTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package tech.powerjob.worker.test; - -import tech.powerjob.worker.pojo.request.TaskTrackerStartTaskReq; -import org.junit.jupiter.api.Test; - - -/** - * 测试任务的启动 - * - * @author tjq - * @since 2020/3/24 - */ -public class ProcessorTrackerTest extends CommonTest { - - @Test - public void testBasicProcessor() throws Exception { - - TaskTrackerStartTaskReq req = genTaskTrackerStartTaskReq("tech.powerjob.worker.test.processors.TestBasicProcessor"); - remoteProcessorTracker.tell(req, null); - Thread.sleep(30000); - } - - @Test - public void testMapReduceProcessor() throws Exception { - TaskTrackerStartTaskReq req = genTaskTrackerStartTaskReq("tech.powerjob.worker.test.processors.TestMapReduceProcessor"); - remoteProcessorTracker.tell(req, null); - Thread.sleep(30000); - } -} diff --git a/powerjob-worker/src/test/resources/oms-akka-test.conf b/powerjob-worker/src/test/resources/oms-akka-test.conf deleted file mode 100644 index 7615d41a..00000000 --- a/powerjob-worker/src/test/resources/oms-akka-test.conf +++ /dev/null @@ -1,18 +0,0 @@ -akka { - actor { - # for test - provider = remote - allow-java-serialization = off - - serialization-bindings { - "OmsSerializable" = jackson-cbor - } - } - remote { - artery { - transport = tcp # See Selecting a transport below - canonical.hostname = "127.0.0.1" - canonical.port = 25521 - } - } -} \ No newline at end of file