---初始化项目
This commit is contained in:
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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<HandlerInfo> handlerInfos;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package tech.powerjob.remote.framework.actor;
|
||||
|
||||
/**
|
||||
* 处理器类型
|
||||
*
|
||||
* @author tjq
|
||||
* @since 2023/1/1
|
||||
*/
|
||||
public enum ProcessType {
|
||||
|
||||
/**
|
||||
* 阻塞式
|
||||
*/
|
||||
BLOCKING,
|
||||
/**
|
||||
* 非阻塞式
|
||||
*/
|
||||
NO_BLOCKING
|
||||
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package tech.powerjob.remote.framework.actor;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 内置一个用来通用测试的 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";
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
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 toFullAddress(host, port);
|
||||
}
|
||||
|
||||
public static Address fromIpv4(String ipv4) {
|
||||
String[] split = ipv4.split(":");
|
||||
return new Address()
|
||||
.setHost(split[0])
|
||||
.setPort(Integer.parseInt(split[1]));
|
||||
}
|
||||
|
||||
public static String toFullAddress(String host, int port) {
|
||||
return String.format("%s:%d", host, port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toFullAddress();
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package tech.powerjob.remote.framework.base;
|
||||
|
||||
/**
|
||||
* RemotingException
|
||||
*
|
||||
* @author tjq
|
||||
* @since 2022/12/31
|
||||
*/
|
||||
public class RemotingException extends RuntimeException {
|
||||
|
||||
public RemotingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public RemotingException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package tech.powerjob.remote.framework.base;
|
||||
|
||||
/**
|
||||
* 服务器类型类型
|
||||
*
|
||||
* @author tjq
|
||||
* @since 2022/12/31
|
||||
*/
|
||||
public enum ServerType {
|
||||
SERVER,
|
||||
WORKER
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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<ActorInfo> actorInfos);
|
||||
|
||||
void close() throws IOException;
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
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;
|
||||
/**
|
||||
* 外部地址(需要 NAT 等情况存在)
|
||||
*/
|
||||
private Address externalAddress;
|
||||
|
||||
private ServerType serverType;
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
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;
|
||||
/**
|
||||
* 外部地址(需要 NAT 等情况存在)
|
||||
*/
|
||||
private Address externalAddress;
|
||||
/**
|
||||
* actor实例,交由使用侧自己实例化以便自行注入各种 bean
|
||||
*/
|
||||
private List<Object> actorList;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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<ActorInfo> load(List<Object> actorList) {
|
||||
|
||||
List<ActorInfo> 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<HandlerInfo> loadHandlerInfos4Actor(ActorInfo actorInfo) {
|
||||
List<HandlerInfo> 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<HandlerInfo> 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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,119 @@
|
||||
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.enums.Protocol;
|
||||
import tech.powerjob.common.exception.PowerJobException;
|
||||
import tech.powerjob.remote.framework.cs.CSInitializer;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* build CSInitializer
|
||||
*
|
||||
* @author tjq
|
||||
* @since 2022/12/31
|
||||
*/
|
||||
@Slf4j
|
||||
class CSInitializerFactory {
|
||||
|
||||
private static final String OFFICIAL_HTTP_CS_INITIALIZER = "tech.powerjob.remote.http.HttpVertxCSInitializer";
|
||||
/**
|
||||
* 未来底层框架摆脱 vertx 时可能会用这个 classname,or 开发者自己实现的 http 协议也可以用这个 classname,总之预留战未来
|
||||
*/
|
||||
private static final String OFFICIAL_HTTP_CS_INITIALIZER2 = "tech.powerjob.remote.http.HttpCSInitializer";
|
||||
private static final String OFFICIAL_AKKA_CS_INITIALIZER = "tech.powerjob.remote.akka.AkkaCSInitializer";
|
||||
private static final String OFFICIAL_MU_CS_INITIALIZER = "tech.powerjob.remote.mu.MuCSInitializer";
|
||||
|
||||
private static final String EXTEND_CS_INITIALIZER_PATTERN = "tech.powerjob.remote.%s.CSInitializer";
|
||||
|
||||
static CSInitializer build(String targetType) {
|
||||
|
||||
CSInitializer officialCSInitializer = tryLoadCSInitializerByClassName(targetType);
|
||||
if (officialCSInitializer != null) {
|
||||
return officialCSInitializer;
|
||||
}
|
||||
|
||||
log.info("[CSInitializerFactory] try load CSInitializerFactory by name failed, start to use Reflections!");
|
||||
|
||||
// JAVA SPI 机制太笨了,短期内继续保留 Reflections 官网下高版本兼容性
|
||||
Reflections reflections = new Reflections(OmsConstant.PACKAGE);
|
||||
Set<Class<? extends CSInitializer>> cSInitializerClzSet = reflections.getSubTypesOf(CSInitializer.class);
|
||||
|
||||
log.info("[CSInitializerFactory] scan subTypeOf CSInitializer: {}", cSInitializerClzSet);
|
||||
|
||||
for (Class<? extends CSInitializer> 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 官方组件直接使用固定类名尝试加载,确保 reflections 不兼容情况下,至少能使用官方通讯协议
|
||||
* @param targetType 协议类型
|
||||
* @return CSInitializer
|
||||
*/
|
||||
private static CSInitializer tryLoadCSInitializerByClassName(String targetType) {
|
||||
|
||||
if (Protocol.HTTP.name().equalsIgnoreCase(targetType)) {
|
||||
Optional<CSInitializer> httpCsIOpt = tryLoadCSInitializerByClzName(OFFICIAL_HTTP_CS_INITIALIZER);
|
||||
if (httpCsIOpt.isPresent()) {
|
||||
return httpCsIOpt.get();
|
||||
}
|
||||
Optional<CSInitializer> httpCsIOpt2 = tryLoadCSInitializerByClzName(OFFICIAL_HTTP_CS_INITIALIZER2);
|
||||
if (httpCsIOpt2.isPresent()) {
|
||||
return httpCsIOpt2.get();
|
||||
}
|
||||
}
|
||||
|
||||
if (Protocol.AKKA.name().equalsIgnoreCase(targetType)) {
|
||||
Optional<CSInitializer> akkaCSIOpt = tryLoadCSInitializerByClzName(OFFICIAL_AKKA_CS_INITIALIZER);
|
||||
if (akkaCSIOpt.isPresent()) {
|
||||
return akkaCSIOpt.get();
|
||||
}
|
||||
}
|
||||
|
||||
if (Protocol.MU.name().equalsIgnoreCase(targetType)) {
|
||||
Optional<CSInitializer> muCSIOpt = tryLoadCSInitializerByClzName(OFFICIAL_MU_CS_INITIALIZER);
|
||||
if (muCSIOpt.isPresent()) {
|
||||
return muCSIOpt.get();
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试加载按规范命名的处理器,比如使用方自定义了 http2 协议,将其类名定为 tech.powerjob.remote.http2.CSInitializer 依然可确保在 Reflections 不可用的情况下完成加载
|
||||
String clz = String.format(EXTEND_CS_INITIALIZER_PATTERN, targetType);
|
||||
Optional<CSInitializer> extOpt = tryLoadCSInitializerByClzName(clz);
|
||||
return extOpt.orElse(null);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static Optional<CSInitializer> tryLoadCSInitializerByClzName(String clzName) {
|
||||
try {
|
||||
log.info("[CSInitializerFactory] try to load CSInitializer by classname: {}", clzName);
|
||||
Class<?> clz = Class.forName(clzName);
|
||||
CSInitializer o = (CSInitializer) clz.getDeclaredConstructor().newInstance();
|
||||
log.info("[CSInitializerFactory] load CSInitializer[{}] successfully, obj: {}", clzName, o);
|
||||
return Optional.of(o);
|
||||
} catch (ClassNotFoundException ce) {
|
||||
log.warn("[CSInitializerFactory] load CSInitializer by classname[{}] failed due to ClassNotFound: {}", clzName, ExceptionUtils.getMessage(ce));
|
||||
} catch (Exception e) {
|
||||
log.warn("[CSInitializerFactory] load CSInitializer by classname[{}] failed.", clzName, e);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
package tech.powerjob.remote.framework.engine.impl;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import tech.powerjob.common.utils.SysUtils;
|
||||
import tech.powerjob.remote.framework.actor.ActorInfo;
|
||||
import tech.powerjob.remote.framework.actor.TestActor;
|
||||
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) {
|
||||
|
||||
reConfig(engineConfig);
|
||||
|
||||
final String engineType = engineConfig.getType();
|
||||
EngineOutput engineOutput = new EngineOutput();
|
||||
log.info("[PowerJobRemoteEngine] [{}] start remote engine with config: {}", engineType, engineConfig);
|
||||
|
||||
List<ActorInfo> 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())
|
||||
.setExternalAddress(engineConfig.getExternalAddress())
|
||||
.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();
|
||||
}
|
||||
|
||||
private static void reConfig(EngineConfig engineConfig) {
|
||||
boolean testEnv = SysUtils.isTestEnv();
|
||||
if (testEnv) {
|
||||
log.info("[PowerJobRemoteEngine] add 'TestActor' due to current is test env");
|
||||
engineConfig.getActorList().add(new TestActor());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* PowerJob 网络框架层
|
||||
*
|
||||
* @author tjq
|
||||
* @since 2022/12/31
|
||||
*/
|
||||
package tech.powerjob.remote.framework;
|
||||
@ -0,0 +1,16 @@
|
||||
package tech.powerjob.remote.framework.transporter;
|
||||
|
||||
/**
|
||||
* 通讯协议
|
||||
*
|
||||
* @author tjq
|
||||
* @since 2022/12/31
|
||||
*/
|
||||
public interface Protocol {
|
||||
|
||||
/**
|
||||
* 通讯协议名称
|
||||
* @return 协议名称
|
||||
*/
|
||||
String name();
|
||||
}
|
||||
@ -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
|
||||
*/
|
||||
<T> CompletionStage<T> ask(URL url, PowerSerializable request, Class<T> clz) throws RemotingException;
|
||||
}
|
||||
@ -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<Class<?>> 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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
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.TestActor;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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");
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -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<ActorInfo> actorInfos) {
|
||||
log.info("TestCSInitializer#actorInfos");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
log.info("TestCSInitializer#close");
|
||||
}
|
||||
}
|
||||
@ -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<Class<?>> notContainsResult = RemoteUtils.findPowerSerialize(notContains);
|
||||
log.info("[RemoteUtilsTest] notContainsResult: {}", notContainsResult);
|
||||
final Optional<Class<?>> containsResult = RemoteUtils.findPowerSerialize(contains);
|
||||
log.info("[RemoteUtilsTest] containsResult: {}", containsResult);
|
||||
|
||||
assert !notContainsResult.isPresent();
|
||||
assert containsResult.isPresent();
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user