diff --git a/README.en.md b/README.en.md
deleted file mode 100644
index 9981530d30efc717f134ceaf4ac34439cc7818c2..0000000000000000000000000000000000000000
--- a/README.en.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# RAS-分布式注册中心
-
-#### Description
-RAS-分布式注册中心:对服务进行注册和发现,提供服务端和客户端运行机制和通信
-
-#### Software Architecture
-Software architecture description
-
-#### Installation
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### Instructions
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### Contribution
-
-1. Fork the repository
-2. Create Feat_xxx branch
-3. Commit your code
-4. Create Pull Request
-
-
-#### Gitee Feature
-
-1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
-2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
-3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
-4. The most valuable open source project [GVP](https://gitee.com/gvp)
-5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
-6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
diff --git a/admin/pom.xml b/admin/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e2db2a98829a795d7e44978654d6986b296d0c64
--- /dev/null
+++ b/admin/pom.xml
@@ -0,0 +1,19 @@
+
+
+
+ ras-parent
+ cn.icanci.loopstack.ras
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ ras-admin
+
+
+ 8
+ 8
+
+
+
\ No newline at end of file
diff --git a/client/pom.xml b/client/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..94241e2eaebbc395184798f0313ca5d28374062e
--- /dev/null
+++ b/client/pom.xml
@@ -0,0 +1,78 @@
+
+
+
+ ras-parent
+ cn.icanci.loopstack.ras
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ ras-client
+
+
+ 8
+ 8
+
+
+
+
+ cn.icanci.loopstack.ras
+ ras-common
+ ${parent.version}
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+
+
+ org.aspectj
+ aspectjweaver
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ compile
+ true
+
+
+ org.apache.commons
+ commons-lang3
+
+
+ org.apache.commons
+ commons-collections4
+
+
+ com.google.guava
+ guava
+
+
+ io.netty
+ netty-all
+
+
+ cn.icanci.loopstack
+ lsi-api
+
+
+ cn.icanci.loopstack
+ lsi-common
+
+
+ cn.icanci.loopstack
+ lsi-utils
+
+
+
\ No newline at end of file
diff --git a/client/src/main/java/cn/icanci/loopstack/ras/client/RasClientAutoConfig.java b/client/src/main/java/cn/icanci/loopstack/ras/client/RasClientAutoConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..e017fa77403f240e0c70b0cdc63fece0017d2fc5
--- /dev/null
+++ b/client/src/main/java/cn/icanci/loopstack/ras/client/RasClientAutoConfig.java
@@ -0,0 +1,19 @@
+package cn.icanci.loopstack.ras.client;
+
+import cn.icanci.loopstack.ras.client.properties.RasProperties;
+
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author icanci
+ * @since 1.0 Created in 2023/01/06 22:27
+ */
+@Configuration
+@ComponentScan({ "cn.icanci.loopstack.ras.client" })
+@EnableConfigurationProperties(RasProperties.class)
+@AutoConfigureBefore
+public class RasClientAutoConfig {
+}
diff --git a/client/src/main/java/cn/icanci/loopstack/ras/client/aop/RpcCallAop.java b/client/src/main/java/cn/icanci/loopstack/ras/client/aop/RpcCallAop.java
new file mode 100644
index 0000000000000000000000000000000000000000..6e39d12b03e4a895a01fe25b41ce44962e8da34a
--- /dev/null
+++ b/client/src/main/java/cn/icanci/loopstack/ras/client/aop/RpcCallAop.java
@@ -0,0 +1,33 @@
+package cn.icanci.loopstack.ras.client.aop;
+
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.stereotype.Component;
+
+/**
+ * AOP拦截
+ *
+ * @author icanci
+ * @since 1.0 Created in 2023/01/16 19:56
+ */
+@Aspect
+@Component
+public class RpcCallAop {
+
+ @Pointcut("execution(public * cn.icanci.loopstack.ras.client.facade.RpcCallFacade.call(..))")
+ private void callAop() {
+
+ }
+
+ @Around("callAop()")
+ public Object doBefore(ProceedingJoinPoint pjp) throws Throwable {
+ Object[] args = pjp.getArgs();
+ long startTime = System.currentTimeMillis();
+ Object returnVal = pjp.proceed();
+ long endTime = System.currentTimeMillis();
+ // 只记录执行时间即可
+ return returnVal;
+ }
+}
diff --git a/client/src/main/java/cn/icanci/loopstack/ras/client/exception/RpcCallException.java b/client/src/main/java/cn/icanci/loopstack/ras/client/exception/RpcCallException.java
new file mode 100644
index 0000000000000000000000000000000000000000..1f914a5068c4cdf904736da2ac009b80bea6545b
--- /dev/null
+++ b/client/src/main/java/cn/icanci/loopstack/ras/client/exception/RpcCallException.java
@@ -0,0 +1,29 @@
+package cn.icanci.loopstack.ras.client.exception;
+
+/**
+ * RPC调用异常
+ *
+ * @author icanci
+ * @since 1.0 Created in 2023/01/19 19:53
+ */
+public class RpcCallException extends RuntimeException {
+ public RpcCallException() {
+ super();
+ }
+
+ public RpcCallException(String message) {
+ super(message);
+ }
+
+ public RpcCallException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public RpcCallException(Throwable cause) {
+ super(cause);
+ }
+
+ protected RpcCallException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/client/src/main/java/cn/icanci/loopstack/ras/client/exception/ServiceNotFoundException.java b/client/src/main/java/cn/icanci/loopstack/ras/client/exception/ServiceNotFoundException.java
new file mode 100644
index 0000000000000000000000000000000000000000..32d051018dfe54ab7a1101b3eaee890454534cc9
--- /dev/null
+++ b/client/src/main/java/cn/icanci/loopstack/ras/client/exception/ServiceNotFoundException.java
@@ -0,0 +1,29 @@
+package cn.icanci.loopstack.ras.client.exception;
+
+/**
+ * 服务不存异常
+ *
+ * @author icanci
+ * @since 1.0 Created in 2023/01/19 20:15
+ */
+public class ServiceNotFoundException extends RuntimeException {
+ public ServiceNotFoundException() {
+ super();
+ }
+
+ public ServiceNotFoundException(String message) {
+ super(message);
+ }
+
+ public ServiceNotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ServiceNotFoundException(Throwable cause) {
+ super(cause);
+ }
+
+ protected ServiceNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/client/src/main/java/cn/icanci/loopstack/ras/client/facade/RpcCallFacade.java b/client/src/main/java/cn/icanci/loopstack/ras/client/facade/RpcCallFacade.java
new file mode 100644
index 0000000000000000000000000000000000000000..77bc2d68c09b53d7427a8540182be85e77de0a93
--- /dev/null
+++ b/client/src/main/java/cn/icanci/loopstack/ras/client/facade/RpcCallFacade.java
@@ -0,0 +1,148 @@
+package cn.icanci.loopstack.ras.client.facade;
+
+import cn.hutool.http.Method;
+import cn.icanci.loopstack.api.client.Client;
+import cn.icanci.loopstack.api.client.http.HttpClientImpl;
+import cn.icanci.loopstack.ras.client.exception.RpcCallException;
+import cn.icanci.loopstack.ras.client.holder.RasRepositoryHolder;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import com.google.common.collect.Maps;
+
+/**
+ * 请求说明
+ * - relativePath: 指的是暴露服务的路径,比如服务地址 => http://127.0.0.1:8080/ddk/condig/query
+ * 其暴露的服务相对路径为 => /ddk/condig/query
+ *
+ * @author icanci
+ * @since 1.0 Created in 2023/01/19 19:26
+ */
+@Service
+public class RpcCallFacade {
+
+ private static final Map DEFAULT_HEADERS = Maps.newHashMap();
+
+ private static final Method DEFAULT_METHOD = Method.POST;
+
+ private static final int DEFAULT_READ_TIMEOUT = 3;
+
+ private static final int DEFAULT_RETRY = 0;
+
+ /**
+ * RPC 请求客户端
+ */
+ private static final Client CLIENT = HttpClientImpl.getInstance();
+
+ @Resource
+ private RasRepositoryHolder rasRepositoryHolder;
+
+ /**
+ * Get请求调用
+ *
+ * @param appId appId
+ * @param relativePath 请求相对路径
+ * @param request 请求
+ * @param headers 请求头
+ * @param readTimeOut 请求超时时间(单位 秒)
+ * @param retry 重试次数
+ * @param clazz 请求返回类型
+ * @param 请求返回泛型
+ * @return 返回请求返回数据
+ */
+ public T getCall(String appId, String relativePath, Object request, Map headers, int readTimeOut, int retry, Class clazz) {
+ return call(appId, relativePath, request, headers, Method.GET, readTimeOut, retry, clazz);
+ }
+
+ /**
+ * post请求调用
+ *
+ * @param appId appId
+ * @param relativePath 请求相对路径
+ * @param request 请求
+ * @param headers 请求头
+ * @param readTimeOut 请求超时时间(单位 秒)
+ * @param retry 重试次数
+ * @param clazz 请求返回类型
+ * @param 请求返回泛型
+ * @return 返回请求返回数据
+ */
+ public T postCall(String appId, String relativePath, Object request, Map headers, int readTimeOut, int retry, Class clazz) {
+ return call(appId, relativePath, request, headers, Method.POST, readTimeOut, retry, clazz);
+ }
+
+ /**
+ * 默认get请求调用
+ *
+ * @param appId appId
+ * @param relativePath 请求相对路径
+ * @param request 请求
+ * @param clazz 请求返回类型
+ * @param 请求返回泛型
+ * @return 返回请求返回数据
+ */
+ public T defaultGetCall(String appId, String relativePath, Object request, Class clazz) {
+ return call(appId, relativePath, request, DEFAULT_HEADERS, Method.GET, DEFAULT_READ_TIMEOUT, DEFAULT_RETRY, clazz);
+ }
+
+ /**
+ * 默认post请求调用
+ *
+ * @param appId appId
+ * @param relativePath 请求相对路径
+ * @param request 请求
+ * @param clazz 请求返回类型
+ * @param 请求返回泛型
+ * @return 返回请求返回数据
+ */
+ public T defaultPostCall(String appId, String relativePath, Object request, Class clazz) {
+ return call(appId, relativePath, request, DEFAULT_HEADERS, Method.POST, DEFAULT_READ_TIMEOUT, DEFAULT_RETRY, clazz);
+ }
+
+ /**
+ * 请求调用
+ *
+ * @param appId appId
+ * @param relativePath 请求相对路径
+ * @param request 请求
+ * @param headers 请求头
+ * @param method 请求方法
+ * @param readTimeOut 请求超时时间(单位 秒)
+ * @param retry 重试次数
+ * @param clazz 请求返回类型
+ * @param 请求返回泛型
+ * @return 返回请求返回数据
+ */
+ public T call(String appId, String relativePath, Object request, Map headers, Method method, int readTimeOut, int retry, Class clazz) {
+ // 构建请求参数
+ Client.RpcRequest rpcRequest = new Client.RpcRequest(resolvingRequestUrl(appId, relativePath), request, headers == null ? DEFAULT_HEADERS : headers, method, readTimeOut,
+ TimeUnit.SECONDS, retry);
+
+ // 调用
+ return CLIENT.call(rpcRequest, clazz);
+ }
+
+ /**
+ * 从仓储中获取此次执行的请求数据
+ *
+ * @param appId appId
+ * @param relativePath 请求相对路径
+ * @return 返回需要执行的请求地址
+ */
+ private String resolvingRequestUrl(String appId, String relativePath) {
+ if (StringUtils.isBlank(relativePath)) {
+ throw new RpcCallException("The relativePath is null! Please check your config!");
+ }
+
+ String prefixUrl = rasRepositoryHolder.routingRequestAddress(appId);
+
+ return prefixUrl + relativePath;
+ }
+
+}
diff --git a/client/src/main/java/cn/icanci/loopstack/ras/client/holder/ApplicationHolder.java b/client/src/main/java/cn/icanci/loopstack/ras/client/holder/ApplicationHolder.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f7d5da0aecb7022fb5b98d3a90a0e6694b7be33
--- /dev/null
+++ b/client/src/main/java/cn/icanci/loopstack/ras/client/holder/ApplicationHolder.java
@@ -0,0 +1,14 @@
+package cn.icanci.loopstack.ras.client.holder;
+
+import cn.icanci.loopstack.ras.common.enums.LoadBalanceTypeEnum;
+
+/**
+ * @author icanci
+ * @since 1.0 Created in 2023/01/19 17:25
+ */
+public class ApplicationHolder {
+ /**
+ * 负载均衡模式
+ */
+ private LoadBalanceTypeEnum loadBalanceType;
+}
diff --git a/client/src/main/java/cn/icanci/loopstack/ras/client/holder/RasRepositoryHolder.java b/client/src/main/java/cn/icanci/loopstack/ras/client/holder/RasRepositoryHolder.java
new file mode 100644
index 0000000000000000000000000000000000000000..f8bd91f221efe26010186dd0be20e54115d08155
--- /dev/null
+++ b/client/src/main/java/cn/icanci/loopstack/ras/client/holder/RasRepositoryHolder.java
@@ -0,0 +1,147 @@
+package cn.icanci.loopstack.ras.client.holder;
+
+import cn.hutool.http.Method;
+import cn.hutool.json.JSONUtil;
+import cn.icanci.loopstack.api.client.Client;
+import cn.icanci.loopstack.api.client.http.HttpClientImpl;
+import cn.icanci.loopstack.lsi.common.result.R;
+import cn.icanci.loopstack.ras.client.properties.RasProperties;
+import cn.icanci.loopstack.ras.client.server.NamedNettyServerHandler;
+import cn.icanci.loopstack.ras.client.utils.RandomAddressUtils;
+import cn.icanci.loopstack.ras.common.exception.ServerOfflineException;
+import cn.icanci.loopstack.ras.common.model.Application;
+import cn.icanci.loopstack.ras.common.socket.RasLoadRequestDTO;
+import cn.icanci.loopstack.ras.common.socket.RasRefreshDTO;
+import cn.icanci.loopstack.ras.common.socket.UriConstant;
+import io.netty.util.internal.ThrowableUtil;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Resource;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.stereotype.Service;
+
+import com.google.common.collect.Maps;
+
+/**
+ * 客户端知晓的服务端信息
+ * -
+ * @author icanci
+ * @since 1.0 Created in 2023/01/19 17:06
+ */
+@Service
+public class RasRepositoryHolder implements InitializingBean {
+
+ private static final Logger logger = LoggerFactory.getLogger(RasRepositoryHolder.class);
+
+ @Resource
+ private RasProperties rasProperties;
+
+ /**
+ * http://{address}:port+UriConstant.ToClient.LOAD
+ */
+ private static final String LOAD_REQUEST_FORMAT = "http://%s:%s" + UriConstant.ToServer.LOAD;
+ /**
+ * 请求地址
+ */
+ private static final String REQUEST_FORMAT = "http://%s:%s";
+
+ /**
+ * 客户端
+ */
+ private static final Client CLIENT = HttpClientImpl.getInstance();
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ // 1.加载数据
+ toLoad();
+ // 2.注入对象
+ NamedNettyServerHandler.setRasRepositoryHolder(this);
+ }
+
+ /**
+ * 服务启动加载数据
+ */
+ private void toLoad() {
+ RasRefreshDTO rasRefresh = loadConfigs();
+ refresh(rasRefresh);
+ }
+
+ /**
+ * 加载远程数据
+ */
+ private RasRefreshDTO loadConfigs() {
+ String serverIps = rasProperties.getServerIps();
+ int serverPort = rasProperties.getServerPort();
+ int clientPort = rasProperties.getClientPort();
+ String appId = rasProperties.getAppId();
+
+ String[] serverAddress = serverIps.split(",");
+
+ RandomAddressUtils.randomAddress(serverAddress);
+
+ for (String address : serverAddress) {
+ try {
+ String reqUrl = String.format(LOAD_REQUEST_FORMAT, address, serverPort);
+
+ RasLoadRequestDTO requestDTO = new RasLoadRequestDTO(appId, address, clientPort);
+
+ Client.RpcRequest rpcRequest = new Client.RpcRequest(reqUrl, requestDTO, Maps.newHashMap(), Method.POST, 3, TimeUnit.SECONDS, 3);
+
+ R call = CLIENT.call(rpcRequest, R.class);
+
+ logger.info("[RasRepositoryHolder][call] Load result:{}", JSONUtil.toJsonStr(call));
+
+ return JSONUtil.toBean(call.getData().get("response").toString(), RasRefreshDTO.class);
+ } catch (Exception ex) {
+ logger.error("[RasRepositoryHolder][call] Ex error message:{}", ThrowableUtil.stackTraceToString(ex));
+ }
+ }
+ throw new ServerOfflineException("All Server IP are Requested, and All failed!!! Please Check your Config, The Server IPs are: " + serverIps);
+ }
+
+ /**
+ * 服务信息缓存刷新
+ *
+ * @param refresh refresh
+ */
+ public void refresh(RasRefreshDTO refresh) {
+ // 先刷新服务端信息
+ refreshServerInfo(refresh.getServerApplication());
+ // 再刷新客户端信息
+ refreshClientInfo(refresh.getClientApplications());
+ }
+
+ /**
+ * 刷新服务端信息
+ *
+ * @param serverApplication serverApplication
+ */
+ private void refreshServerInfo(Application serverApplication) {
+ // 这里服务端的信息不可能为,因为数据是从服务端请求而来的
+ }
+
+ /**
+ * 刷新客户端信息
+ *
+ * @param clientApplications clientApplications
+ */
+ private void refreshClientInfo(List clientApplications) {
+
+ }
+
+ /**
+ * 返回请求执行路由地址
+ *
+ * @param appId 应用id
+ * @return 返回请求地址 ip+port
+ */
+ public String routingRequestAddress(String appId) {
+ // REQUEST_FORMAT
+ return null;
+ }
+}
diff --git a/client/src/main/java/cn/icanci/loopstack/ras/client/package-info.java b/client/src/main/java/cn/icanci/loopstack/ras/client/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..6c030bec0beddc56e2243806a60370ac678adbde
--- /dev/null
+++ b/client/src/main/java/cn/icanci/loopstack/ras/client/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * @author icanci
+ * @since 1.0 Created in 2023/01/19 08:48
+ */
+package cn.icanci.loopstack.ras.client;
\ No newline at end of file
diff --git a/client/src/main/java/cn/icanci/loopstack/ras/client/properties/RasProperties.java b/client/src/main/java/cn/icanci/loopstack/ras/client/properties/RasProperties.java
new file mode 100644
index 0000000000000000000000000000000000000000..434448780fbba914dbd3e01c76efc3797ee3e689
--- /dev/null
+++ b/client/src/main/java/cn/icanci/loopstack/ras/client/properties/RasProperties.java
@@ -0,0 +1,62 @@
+package cn.icanci.loopstack.ras.client.properties;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author icanci
+ * @since 1.0 Created in 2023/01/06 22:28
+ */
+@Component
+@ConfigurationProperties(prefix = "ras")
+public class RasProperties {
+ /**
+ * appId
+ */
+ private String appId;
+
+ /**
+ * 客户端注册的port
+ */
+ private int clientPort = 11000;
+ /**
+ * 服务端ip,以,分隔
+ */
+ private String serverIps = "127.0.0.1";
+ /**
+ * 服务端port
+ */
+ private int serverPort = 9995;
+
+ public String getAppId() {
+ return appId;
+ }
+
+ public void setAppId(String appId) {
+ this.appId = appId;
+ }
+
+ public int getClientPort() {
+ return clientPort;
+ }
+
+ public void setClientPort(int clientPort) {
+ this.clientPort = clientPort;
+ }
+
+ public String getServerIps() {
+ return serverIps;
+ }
+
+ public void setServerIps(String serverIps) {
+ this.serverIps = serverIps;
+ }
+
+ public int getServerPort() {
+ return serverPort;
+ }
+
+ public void setServerPort(int serverPort) {
+ this.serverPort = serverPort;
+ }
+}
diff --git a/client/src/main/java/cn/icanci/loopstack/ras/client/server/NamedNettyServerHandler.java b/client/src/main/java/cn/icanci/loopstack/ras/client/server/NamedNettyServerHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..15d8307d2f0ce6b2e75f443ae155f596f2e83eef
--- /dev/null
+++ b/client/src/main/java/cn/icanci/loopstack/ras/client/server/NamedNettyServerHandler.java
@@ -0,0 +1,135 @@
+package cn.icanci.loopstack.ras.client.server;
+
+import cn.hutool.json.JSONUtil;
+import cn.icanci.loopstack.ras.client.holder.RasRepositoryHolder;
+import cn.icanci.loopstack.ras.common.socket.RasRefreshDTO;
+import cn.icanci.loopstack.ras.common.socket.SocketMessage;
+import cn.icanci.loopstack.ras.common.socket.UriConstant;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.handler.codec.http.*;
+import io.netty.handler.timeout.IdleStateEvent;
+import io.netty.util.CharsetUtil;
+import io.netty.util.internal.ThrowableUtil;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author icanci
+ * @since 1.0 Created in 2023/01/06 22:49
+ */
+@SuppressWarnings("all")
+public class NamedNettyServerHandler extends SimpleChannelInboundHandler {
+
+ private static final Logger logger = LoggerFactory.getLogger(NamedNettyServerHandler.class);
+
+ private static RasRepositoryHolder rasRepositoryHolder;
+
+ private static final int CORE_SIZE = Runtime.getRuntime().availableProcessors();
+
+ private static final ThreadPoolExecutor POOL = new ThreadPoolExecutor(CORE_SIZE, //
+ CORE_SIZE << 1, //
+ 60L, //
+ TimeUnit.SECONDS, //
+ new LinkedBlockingQueue<>(2000), //
+ runnable -> new Thread(runnable, "RasServerThread Pool-" + runnable.hashCode()), //
+ (r, executor) -> {
+ throw new RuntimeException("RasServerThread Pool is EXHAUSTED!");
+ });;
+
+ public NamedNettyServerHandler() {
+ }
+
+ public static void setRasRepositoryHolder(RasRepositoryHolder rasRepositoryHolder) {
+ NamedNettyServerHandler.rasRepositoryHolder = rasRepositoryHolder;
+ }
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
+ String requestData = msg.content().toString(CharsetUtil.UTF_8);
+ String uri = msg.uri();
+ HttpMethod httpMethod = msg.method();
+ boolean keepAlive = HttpUtil.isKeepAlive(msg);
+ // 对于这种配置数据,会有很频繁的变更
+ POOL.execute(() -> {
+ Object responseObj = process(httpMethod, uri, requestData);
+
+ String responseJson = JSONUtil.toJsonStr(responseObj);
+
+ writeResponse(ctx, keepAlive, responseJson);
+ });
+ }
+
+ /**
+ * 后置处理
+ *
+ * @param httpMethod httpMethod
+ * @param uri uri
+ * @param requestData requestData
+ * @return Object
+ */
+ private Object process(HttpMethod httpMethod, String uri, String requestData) {
+ if (HttpMethod.POST != httpMethod) {
+ return SocketMessage.fail("Only post requests are supported");
+ }
+ if (StringUtils.isBlank(uri)) {
+ return SocketMessage.fail("Request uri is null");
+ }
+ try {
+ switch (uri) {
+ case UriConstant.ToClient.HEARTBEAT:
+ logger.info("[{}][NamedNettyServerHandler][process] heartbeat", Thread.currentThread().getName());
+ return SocketMessage.success();
+ case UriConstant.ToClient.REFRESH:
+ // 在进行刷新的时候,如果是第一次,则是全局进行加载
+ // 如果是增量数据,则是部分刷新,针对Client来说,对其来说只是刷新本地的数据,对到来的是什么数据不关心
+ // 因此可以使用同一个方法进行处理
+ rasRepositoryHolder.refresh(JSONUtil.toBean(requestData, RasRefreshDTO.class));
+ logger.info("[{}][NamedNettyServerHandler][process] {} was refreshed!", Thread.currentThread().getName(), requestData);
+ return SocketMessage.success();
+ default:
+ return SocketMessage.fail("Invalid request, uri-mapping(" + uri + ") not found");
+ }
+ } catch (Throwable e) {
+ logger.error("[{}][NamedNettyServerHandler][process] ex,error msg:{}", e, Thread.currentThread().getName(), ThrowableUtil.stackTraceToString(e));
+ return SocketMessage.fail(ThrowableUtil.stackTraceToString(e));
+ }
+ }
+
+ private void writeResponse(ChannelHandlerContext ctx, boolean keepAlive, String responseJson) {
+ FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(responseJson, CharsetUtil.UTF_8));
+ response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=UTF-8");
+ response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
+ if (keepAlive) {
+ response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
+ }
+ ctx.writeAndFlush(response);
+ }
+
+ @Override
+ public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
+ ctx.flush();
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+ logger.error(ThrowableUtil.stackTraceToString(cause));
+ ctx.close();
+ }
+
+ @Override
+ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+ if (evt instanceof IdleStateEvent) {
+ ctx.channel().close();
+ } else {
+ super.userEventTriggered(ctx, evt);
+ }
+ }
+}
diff --git a/client/src/main/java/cn/icanci/loopstack/ras/client/server/NamedServer.java b/client/src/main/java/cn/icanci/loopstack/ras/client/server/NamedServer.java
new file mode 100644
index 0000000000000000000000000000000000000000..d42e6d8a0f25acb2664e955a25e89940a86a8866
--- /dev/null
+++ b/client/src/main/java/cn/icanci/loopstack/ras/client/server/NamedServer.java
@@ -0,0 +1,109 @@
+package cn.icanci.loopstack.ras.client.server;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.*;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpServerCodec;
+import io.netty.handler.timeout.IdleStateHandler;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.LockSupport;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author icanci
+ * @since 1.0 Created in 2023/01/01 09:55
+ */
+@SuppressWarnings("all")
+public class NamedServer {
+
+ private static final Logger logger = LoggerFactory.getLogger(NamedServer.class);
+
+ private static RegisterServer registerServer;
+
+ public static void setRegisterService(RegisterServer registerServer) {
+ NamedServer.registerServer = registerServer;
+ }
+
+ public static void startClient(String serverIps, int serverPort, int clientPort) {
+ // 启动时候注册
+ startClient0(serverIps, serverPort, clientPort);
+ // 自动进行注册
+ // Tips: 项目启动了,但是没有配置项目信息,此时注册失败,如果不自动注册,则需要进行重启才能注册。因此开启自助注册
+ autoRegister(serverIps, serverPort, clientPort);
+ }
+
+ private static void startClient0(String serverAddress, int serverPort, int clientPort) {
+ Thread rasThread = new Thread(() -> {
+ EventLoopGroup bossGroup = new NioEventLoopGroup();
+ EventLoopGroup workerGroup = new NioEventLoopGroup();
+
+ ServerBootstrap bootstrap = new ServerBootstrap();
+
+ bootstrap.group(bossGroup, workerGroup) //
+ .channel(NioServerSocketChannel.class) //
+ .childHandler(new ChannelInitializer() {
+ @Override
+ public void initChannel(SocketChannel channel) throws Exception {
+ ChannelPipeline pipeline = channel.pipeline();
+ pipeline.addLast(new IdleStateHandler(0, 0, 30, TimeUnit.SECONDS));
+ pipeline.addLast(new HttpServerCodec());
+ pipeline.addLast(new HttpObjectAggregator(5 * 1024 * 1024));
+ pipeline.addLast(new NamedNettyServerHandler());
+ }
+ }).childOption(ChannelOption.SO_KEEPALIVE, true);
+
+ try {
+ ChannelFuture future = bootstrap.bind(clientPort).sync();
+
+ doRegistry(serverAddress, serverPort, clientPort);
+
+ future.channel().closeFuture().sync();
+
+ } catch (InterruptedException e) {
+ logger.info("RAS remoting server interruptedException", e);
+ } catch (Exception e) {
+ logger.info("RAS remoting server error", e);
+ } finally {
+ workerGroup.shutdownGracefully();
+ bossGroup.shutdownGracefully();
+ }
+
+ });
+ rasThread.setDaemon(true);
+ rasThread.start();
+ }
+
+ /**
+ * 将SDK所在服务注册到注册中心
+ *
+ * @param serverAddress 服务端ip地址
+ * @param serverPort 服务端端口
+ * @param clientPort 客户端端口
+ */
+ private static void doRegistry(String serverAddress, int serverPort, int clientPort) {
+ registerServer.register(serverAddress, serverPort, clientPort);
+ }
+
+ /**
+ * 自动注册
+ *
+ * @param serverAddress 服务端ip地址
+ * @param serverPort 服务端端口
+ * @param clientPort 客户端端口
+ */
+ private static void autoRegister(String serverAddress, int serverPort, int clientPort) {
+ Thread autoRegisterThread = new Thread(() -> {
+ // 每120秒刷新注册一次
+ LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(120));
+ doRegistry(serverAddress, serverPort, clientPort);
+ });
+ autoRegisterThread.setDaemon(true);
+ autoRegisterThread.start();
+ }
+}
diff --git a/client/src/main/java/cn/icanci/loopstack/ras/client/server/RegisterServer.java b/client/src/main/java/cn/icanci/loopstack/ras/client/server/RegisterServer.java
new file mode 100644
index 0000000000000000000000000000000000000000..875f8fa1423bee689140ab05a7749a61d4aa89fe
--- /dev/null
+++ b/client/src/main/java/cn/icanci/loopstack/ras/client/server/RegisterServer.java
@@ -0,0 +1,121 @@
+package cn.icanci.loopstack.ras.client.server;
+
+import cn.hutool.http.Method;
+import cn.hutool.json.JSONUtil;
+import cn.icanci.loopstack.api.client.Client;
+import cn.icanci.loopstack.api.client.http.HttpClientImpl;
+import cn.icanci.loopstack.lsi.common.result.R;
+import cn.icanci.loopstack.ras.client.properties.RasProperties;
+import cn.icanci.loopstack.ras.client.utils.RandomAddressUtils;
+import cn.icanci.loopstack.ras.common.socket.RegisterDTO;
+import cn.icanci.loopstack.ras.common.socket.UriConstant;
+import cn.icanci.loopstack.utils.IPUtils;
+
+import java.util.concurrent.*;
+
+import javax.annotation.Resource;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.stereotype.Service;
+
+import com.google.common.collect.Maps;
+
+/**
+ * @author icanci
+ * @since 1.0 Created in 2023/01/06 22:30
+ */
+@Service
+@SuppressWarnings("all")
+public class RegisterServer implements InitializingBean {
+ private static final Logger logger = LoggerFactory.getLogger(RegisterServer.class);
+ /** http实例 */
+ private static final Client CLIENT = HttpClientImpl.getInstance();
+ @Resource
+ private RasProperties rasProperties;
+
+ private static final int CORE_SIZE = Runtime.getRuntime().availableProcessors();
+
+ private static final ThreadPoolExecutor REGISTER_POOL = new ThreadPoolExecutor(CORE_SIZE, //
+ CORE_SIZE << 1, //
+ 60L, //
+ TimeUnit.SECONDS, //
+ new LinkedBlockingQueue<>(2000), //
+ runnable -> new Thread(runnable, "RegisterClient Biz Pool-" + runnable.hashCode()), //
+ (r, executor) -> {
+ throw new RuntimeException("RegisterClient Biz Pool is EXHAUSTED!");
+ });
+
+ /** 注册请求地址 */
+ private static final String REQ_URL_FORMAT = "http://%s:%s%s";
+
+ /**
+ * 将SDK所在服务注册到注册中心
+ *
+ * @param serverAddress 服务端ip地址
+ * @param serverPort 服务端端口
+ * @param clientPort 客户端端口
+ */
+ public void register(String serverAddress, int serverPort, int clientPort) {
+ String appId = rasProperties.getAppId();
+ String[] addresses = serverAddress.split(",");
+ // 注册地址打散,防止压力在同一个机器上
+ RandomAddressUtils.randomAddress(addresses);
+ // 执行注册
+ for (String address : addresses) {
+ try {
+ FutureTask task = new FutureTask<>(new RegisterCallable(address, clientPort, appId, serverPort));
+ REGISTER_POOL.execute(task);
+ R r = task.get(10, TimeUnit.SECONDS);
+ if (r.isOk()) {
+ break;
+ } else {
+ logger.error("Task Register Exception:{}", r.getMessage());
+ }
+ } catch (ExecutionException | InterruptedException | TimeoutException e) {
+ logger.warn("Register Exception:{}", e.getMessage());
+ }
+ }
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+
+ // 注入注册bean
+ NamedServer.setRegisterService(this);
+
+ // 启动服务器
+ NamedServer.startClient(rasProperties.getServerIps(), rasProperties.getServerPort(), rasProperties.getClientPort());
+ }
+
+ /** 注册器 */
+ private static class RegisterCallable implements Callable {
+
+ private final String address;
+ private final int clientPort;
+ private final String appId;
+ private final int serverPort;
+
+ public RegisterCallable(String address, int clientPort, String appId, int serverPort) {
+ this.address = address;
+ this.clientPort = clientPort;
+ this.appId = appId;
+ this.serverPort = serverPort;
+ }
+
+ @Override
+ public R call() throws Exception {
+ RegisterDTO registerDTO = new RegisterDTO(IPUtils.getHostIpAddress(), clientPort, appId);
+ String reqUrl = String.format(REQ_URL_FORMAT, address, serverPort, UriConstant.ToServer.REGISTER);
+
+ Client.RpcRequest rpcRequest = new Client.RpcRequest(reqUrl, registerDTO, Maps.newHashMap(), Method.POST, 3, TimeUnit.SECONDS, 3);
+
+ R call = CLIENT.call(rpcRequest, R.class);
+
+ logger.info("[RegisterCallable][call] Register result:{}", JSONUtil.toJsonStr(call));
+
+ return call;
+ }
+ }
+}
diff --git a/client/src/main/java/cn/icanci/loopstack/ras/client/utils/RandomAddressUtils.java b/client/src/main/java/cn/icanci/loopstack/ras/client/utils/RandomAddressUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..db0ceb5e3d53cf6b4d3646b86f7d81ecb358ade2
--- /dev/null
+++ b/client/src/main/java/cn/icanci/loopstack/ras/client/utils/RandomAddressUtils.java
@@ -0,0 +1,28 @@
+package cn.icanci.loopstack.ras.client.utils;
+
+/**
+ * @author icanci
+ * @since 1.0 Created in 2023/01/19 20:51
+ */
+public class RandomAddressUtils {
+ private RandomAddressUtils() {
+ }
+
+ /**
+ * randomAddress、
+ *
+ * @param address address
+ */
+ public static void randomAddress(String[] address) {
+ if (address.length == 1) {
+ return;
+ }
+ int length = address.length;
+ for (int i = 0; i < length; i++) {
+ int iRandNum = (int) (Math.random() * length);
+ String temp = address[iRandNum];
+ address[iRandNum] = address[i];
+ address[i] = temp;
+ }
+ }
+}
diff --git a/common/pom.xml b/common/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ae9541403af27023c8d13e003fff6d486de3f2e8
--- /dev/null
+++ b/common/pom.xml
@@ -0,0 +1,19 @@
+
+
+
+ ras-parent
+ cn.icanci.loopstack.ras
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ ras-common
+
+
+ 8
+ 8
+
+
+
\ No newline at end of file
diff --git a/common/src/main/java/cn/icanci/loopstack/ras/common/enums/LoadBalanceTypeEnum.java b/common/src/main/java/cn/icanci/loopstack/ras/common/enums/LoadBalanceTypeEnum.java
new file mode 100644
index 0000000000000000000000000000000000000000..36e5fd9abc657906158ae41d1d9b44be236e7f6f
--- /dev/null
+++ b/common/src/main/java/cn/icanci/loopstack/ras/common/enums/LoadBalanceTypeEnum.java
@@ -0,0 +1,59 @@
+package cn.icanci.loopstack.ras.common.enums;
+
+/**
+ * @author icanci
+ * @since 1.0 Created in 2023/01/19 17:28
+ */
+public enum LoadBalanceTypeEnum {
+ /**
+ * 第一个
+ */
+ FIRST("FIRST", "第一个"),
+ /**
+ * 最后一个
+ */
+ LAST("LAST", "最后一个"),
+ /**
+ * 按顺序
+ */
+ IN_ORDER("IN_ORDER", "按顺序"),
+ /**
+ * 随机
+ */
+ RANDOM("RANDOM", "随机"),
+ /**
+ * 一致性哈希
+ */
+ CONSISTENCY_HASH("CONSISTENCY_HASH", "一致性哈希"),
+ /**
+ * 最不经常使用
+ */
+ LEAST_FREQUENTLY_USED("LEAST_FREQUENTLY_USED", "最不经常使用"),
+ /**
+ * 最近最久未使用
+ */
+ LEAST_RECENTLY_USED("LEAST_RECENTLY_USED", "最近最久未使用"),
+ /**
+ * 调用速度最快
+ */
+ FASTEST_CALL_SPEED("FASTEST_CALL_SPEED", "调用速度最快"),
+
+ //
+ ;
+
+ LoadBalanceTypeEnum(String code, String desc) {
+ this.code = code;
+ this.desc = desc;
+ }
+
+ private final String code;
+ private final String desc;
+
+ public String getCode() {
+ return code;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+}
diff --git a/common/src/main/java/cn/icanci/loopstack/ras/common/exception/ServerOfflineException.java b/common/src/main/java/cn/icanci/loopstack/ras/common/exception/ServerOfflineException.java
new file mode 100644
index 0000000000000000000000000000000000000000..b3992a062edf2bbc4b7949cc5f4e4c2a9ed102e5
--- /dev/null
+++ b/common/src/main/java/cn/icanci/loopstack/ras/common/exception/ServerOfflineException.java
@@ -0,0 +1,27 @@
+package cn.icanci.loopstack.ras.common.exception;
+
+/**
+ * @author icanci
+ * @since 1.0 Created in 2023/01/19 18:43
+ */
+public class ServerOfflineException extends RuntimeException {
+ public ServerOfflineException() {
+ super();
+ }
+
+ public ServerOfflineException(String message) {
+ super(message);
+ }
+
+ public ServerOfflineException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ServerOfflineException(Throwable cause) {
+ super(cause);
+ }
+
+ protected ServerOfflineException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/common/src/main/java/cn/icanci/loopstack/ras/common/model/Application.java b/common/src/main/java/cn/icanci/loopstack/ras/common/model/Application.java
new file mode 100644
index 0000000000000000000000000000000000000000..352ab52cff026a733c25f1687ba98fe093f93e74
--- /dev/null
+++ b/common/src/main/java/cn/icanci/loopstack/ras/common/model/Application.java
@@ -0,0 +1,46 @@
+package cn.icanci.loopstack.ras.common.model;
+
+import java.util.Set;
+
+/**
+ * @author icanci
+ * @since 1.0 Created in 2023/01/18 22:06
+ */
+public class Application {
+ /**
+ * AppId
+ */
+ private String appId;
+ /**
+ * 负载均衡算法
+ */
+ private String loadBalanceType;
+ /**
+ * 注册的App
+ */
+ private Set instances;
+
+ public String getAppId() {
+ return appId;
+ }
+
+ public void setAppId(String appId) {
+ this.appId = appId;
+ }
+
+ public String getLoadBalanceType() {
+ return loadBalanceType;
+ }
+
+ public void setLoadBalanceType(String loadBalanceType) {
+ this.loadBalanceType = loadBalanceType;
+ }
+
+ public Set getInstances() {
+ return instances;
+ }
+
+ public void setInstances(Set instances) {
+ this.instances = instances;
+ }
+}
diff --git a/common/src/main/java/cn/icanci/loopstack/ras/common/model/Instance.java b/common/src/main/java/cn/icanci/loopstack/ras/common/model/Instance.java
new file mode 100644
index 0000000000000000000000000000000000000000..0332ceae8de3e40c85c3805e2ccc614f9e0065b6
--- /dev/null
+++ b/common/src/main/java/cn/icanci/loopstack/ras/common/model/Instance.java
@@ -0,0 +1,117 @@
+package cn.icanci.loopstack.ras.common.model;
+
+import java.util.Date;
+import java.util.Objects;
+import java.util.StringJoiner;
+
+/**
+ * @author icanci
+ * @since 1.0 Created in 2023/01/18 22:06
+ */
+public class Instance {
+ /**
+ * AppId 唯一
+ */
+ private String appId;
+ /**
+ * 客户端/服务端注册地址
+ */
+ private String address;
+ /**
+ * 客户端/服务端注册端口
+ */
+ private int port;
+ /**
+ * 创建时间
+ */
+ private Date createTime;
+ /**
+ * 更新时间
+ */
+ private Date updateTime;
+ /**
+ * 是否删除:状态 1无效,0有效(主动设置)
+ */
+ private int isDelete;
+ /**
+ * 是否在线 1不在线,0在线(客户端注册处理)
+ */
+ private int online;
+
+ public String getAppId() {
+ return appId;
+ }
+
+ public void setAppId(String appId) {
+ this.appId = appId;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public Date getCreateTime() {
+ return createTime;
+ }
+
+ public void setCreateTime(Date createTime) {
+ this.createTime = createTime;
+ }
+
+ public Date getUpdateTime() {
+ return updateTime;
+ }
+
+ public void setUpdateTime(Date updateTime) {
+ this.updateTime = updateTime;
+ }
+
+ public int getIsDelete() {
+ return isDelete;
+ }
+
+ public void setIsDelete(int isDelete) {
+ this.isDelete = isDelete;
+ }
+
+ public int getOnline() {
+ return online;
+ }
+
+ public void setOnline(int online) {
+ this.online = online;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ Instance instance = (Instance) o;
+ return port == instance.port && Objects.equals(appId, instance.appId) && Objects.equals(address, instance.address);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(appId, address, port);
+ }
+
+ @Override
+ public String toString() {
+ return new StringJoiner(",").add("appId=" + appId).add("address=" + address).add("port=" + port).add("createTime=" + createTime).add("updateTime=" + updateTime)
+ .add("isDelete=" + isDelete).add("online=" + online).toString();
+ }
+}
diff --git a/common/src/main/java/cn/icanci/loopstack/ras/common/package-info.java b/common/src/main/java/cn/icanci/loopstack/ras/common/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..2c864d7e7e7e4d5a022b000925b4e7ec98e226c7
--- /dev/null
+++ b/common/src/main/java/cn/icanci/loopstack/ras/common/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * @author icanci
+ * @since 1.0 Created in 2023/01/18 22:06
+ */
+package cn.icanci.loopstack.ras.common;
\ No newline at end of file
diff --git a/common/src/main/java/cn/icanci/loopstack/ras/common/socket/RasLoadRequestDTO.java b/common/src/main/java/cn/icanci/loopstack/ras/common/socket/RasLoadRequestDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..3616eda7b5c742ca9943bf48ae29785891cd3093
--- /dev/null
+++ b/common/src/main/java/cn/icanci/loopstack/ras/common/socket/RasLoadRequestDTO.java
@@ -0,0 +1,55 @@
+package cn.icanci.loopstack.ras.common.socket;
+
+import java.io.Serializable;
+
+/**
+ * @author icanci
+ * @since 1.0 Created in 2023/01/19 18:40
+ */
+public class RasLoadRequestDTO implements Serializable {
+ /**
+ * 当前服务id
+ */
+ private String appId;
+ /**
+ * 当前服务地址
+ */
+ private String address;
+ /**
+ * 当前服务的服务端口
+ */
+ private int port;
+
+ public RasLoadRequestDTO() {
+ }
+
+ public RasLoadRequestDTO(String appId, String address, int port) {
+ this.appId = appId;
+ this.address = address;
+ this.port = port;
+ }
+
+ public String getAppId() {
+ return appId;
+ }
+
+ public void setAppId(String appId) {
+ this.appId = appId;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+}
diff --git a/common/src/main/java/cn/icanci/loopstack/ras/common/socket/RasRefreshDTO.java b/common/src/main/java/cn/icanci/loopstack/ras/common/socket/RasRefreshDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..6e27be753648b560ac36037461c1f2bb25857cbd
--- /dev/null
+++ b/common/src/main/java/cn/icanci/loopstack/ras/common/socket/RasRefreshDTO.java
@@ -0,0 +1,37 @@
+package cn.icanci.loopstack.ras.common.socket;
+
+import cn.icanci.loopstack.ras.common.model.Application;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author icanci
+ * @since 1.0 Created in 2023/01/19 17:10
+ */
+public class RasRefreshDTO implements Serializable {
+ /**
+ * 服务Application,只会一个Application
+ */
+ private Application serverApplication;
+ /**
+ * 客户端Application
+ */
+ private List clientApplications;
+
+ public Application getServerApplication() {
+ return serverApplication;
+ }
+
+ public void setServerApplication(Application serverApplication) {
+ this.serverApplication = serverApplication;
+ }
+
+ public List getClientApplications() {
+ return clientApplications;
+ }
+
+ public void setClientApplications(List clientApplications) {
+ this.clientApplications = clientApplications;
+ }
+}
diff --git a/common/src/main/java/cn/icanci/loopstack/ras/common/socket/RegisterDTO.java b/common/src/main/java/cn/icanci/loopstack/ras/common/socket/RegisterDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..63fbf46df2a3adb52d44941cb8614952a76ed67c
--- /dev/null
+++ b/common/src/main/java/cn/icanci/loopstack/ras/common/socket/RegisterDTO.java
@@ -0,0 +1,58 @@
+package cn.icanci.loopstack.ras.common.socket;
+
+import java.io.Serializable;
+
+/**
+ * @author icanci
+ * @since 1.0 Created in 2023/01/06 22:42
+ */
+public class RegisterDTO implements Serializable {
+ private static final long serialVersionUID = -2030921699127783725L;
+
+ /**
+ * SDK 服务ip地址
+ */
+ private String clientAddress;
+ /**
+ * SDK 服务端口地址
+ */
+ private int clientPort;
+ /**
+ * SDK 服务服务ID
+ */
+ private String appId;
+
+ public RegisterDTO() {
+ }
+
+ public RegisterDTO(String clientAddress, int clientPort, String appId) {
+ this.clientAddress = clientAddress;
+ this.clientPort = clientPort;
+ this.appId = appId;
+ }
+
+ public String getClientAddress() {
+ return clientAddress;
+ }
+
+ public void setClientAddress(String clientAddress) {
+ this.clientAddress = clientAddress;
+ }
+
+ public int getClientPort() {
+ return clientPort;
+ }
+
+ public void setClientPort(int clientPort) {
+ this.clientPort = clientPort;
+ }
+
+ public String getAppId() {
+ return appId;
+ }
+
+ public void setAppId(String appId) {
+ this.appId = appId;
+ }
+
+}
diff --git a/common/src/main/java/cn/icanci/loopstack/ras/common/socket/SocketMessage.java b/common/src/main/java/cn/icanci/loopstack/ras/common/socket/SocketMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..d507a73b8a56f780bf7ba791ba6aa6da0648878c
--- /dev/null
+++ b/common/src/main/java/cn/icanci/loopstack/ras/common/socket/SocketMessage.java
@@ -0,0 +1,49 @@
+package cn.icanci.loopstack.ras.common.socket;
+
+/**
+ * @author icanci
+ * @since 1.0 Created in 2022/11/20 21:01
+ */
+@SuppressWarnings("all")
+public class SocketMessage {
+ private boolean success;
+ private String errorMessage;
+ private T content;
+
+ public static SocketMessage fail(String errorMessage) {
+ SocketMessage message = new SocketMessage();
+ message.setSuccess(false);
+ message.setErrorMessage(errorMessage);
+ return message;
+ }
+
+ public static SocketMessage success() {
+ SocketMessage message = new SocketMessage();
+ message.setSuccess(true);
+ return message;
+ }
+
+ public boolean isSuccess() {
+ return success;
+ }
+
+ public void setSuccess(boolean success) {
+ this.success = success;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ public T getContent() {
+ return content;
+ }
+
+ public void setContent(T content) {
+ this.content = content;
+ }
+}
diff --git a/common/src/main/java/cn/icanci/loopstack/ras/common/socket/UriConstant.java b/common/src/main/java/cn/icanci/loopstack/ras/common/socket/UriConstant.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8b99005a3dcfd000d0f8fb222eff6d06c8527bd
--- /dev/null
+++ b/common/src/main/java/cn/icanci/loopstack/ras/common/socket/UriConstant.java
@@ -0,0 +1,31 @@
+package cn.icanci.loopstack.ras.common.socket;
+
+/**
+ * TODO 发布的处理
+ *
+ * @author icanci
+ * @since 1.0 Created in 2023/01/06 22:41
+ */
+public interface UriConstant {
+ /**
+ * ToClient
+ */
+ interface ToClient {
+ /** 心跳 */
+ String HEARTBEAT = "/ras/register/heartbeat";
+ /** 刷新 */
+ String REFRESH = "/ras/register/refresh";
+ }
+
+ /**
+ * ToServer
+ */
+ interface ToServer {
+ /** 注册 */
+ String REGISTER = "/ras/register/doRegister";
+ /** 加载客户端和注册中心的信息 */
+ String LOAD = "/ras/register/load";
+ /** 刷新服务的状态信息:上线、下线等 */
+ String TO_REFRESH_DELETE_STATUS = "/ras/register/refreshDeleteStatus";
+ }
+}
diff --git a/pom.xml b/pom.xml
index dba608abec6ece12bdb78f393ec74a90eb24ceae..28b8ac126abd2de2b1f54160baf5cdab8d843485 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,11 +6,428 @@
cn.icanci.loopstack.ras
ras-parent
+ pom
1.0-SNAPSHOT
+
+ client
+ common
+ admin
+ server
+
+
+ UTF-8
+
+ 3.8.1
+ 3.0.0-M1
+ 3.2.1
+ 3.2.0
+ 1.6.8
+ 1.6
+ 3.0.0-M1
+
+ 1.8
+ UTF-8
8
8
+
+ 4.13.2
+
+ 1.7.30
+ 2.12.1
+ 2.13.3
+ 2.13.3
+ 3.3.4
+
+ 2.2.2.RELEASE
+ 2.2.2
+
+ 3.4
+ 2.4
+ 4.3
+ 19.0
+
+ 2.11.2
+ 1.2.70
+
+ 1.4.2.Final
+ 5.4.2
+
+ 4.1.63.Final
+
+ 0.0.0.1-SNAPSHOT
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+ ${spring.boot.version}
+ import
+ pom
+
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ ${spring.boot.mybatis.version.version}
+
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons.lang3.version}
+
+
+ commons-io
+ commons-io
+ ${commons.io.version}
+
+
+ org.apache.commons
+ commons-collections4
+ ${commons.collections4.version}
+
+
+ com.google.guava
+ guava
+ ${guava.version}
+
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ ${jackson.version}
+
+
+ com.alibaba
+ fastjson
+ ${fastjson.version}
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+
+
+
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+
+
+ org.slf4j
+ slf4j-api
+ ${org.slf4j.version}
+
+
+
+ org.apache.logging.log4j
+ log4j-slf4j-impl
+ ${log4j-slf4j-impl.version}
+
+
+
+ org.apache.logging.log4j
+ log4j-api
+ ${log4j-api.version}
+
+
+
+ org.apache.logging.log4j
+ log4j-core
+ ${log4j-core.version}
+
+
+
+ com.lmax
+ disruptor
+ ${disruptor.version}
+
+
+
+ org.mapstruct
+ mapstruct
+ ${mapstruct.version}
+
+
+
+ cn.hutool
+ hutool-all
+ ${hutool.version}
+
+
+ io.netty
+ netty-all
+ ${netty-all.version}
+
+
+
+ cn.icanci.loopstack
+ lsi-api
+ ${lsi.version}
+
+
+ cn.icanci.loopstack
+ lsi-common
+ ${lsi.version}
+
+
+ cn.icanci.loopstack
+ lsi-utils
+ ${lsi.version}
+
+
+
+
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+ org.apache.logging.log4j
+ log4j-slf4j-impl
+
+
+
+ org.apache.logging.log4j
+ log4j-api
+
+
+
+ org.apache.logging.log4j
+ log4j-core
+
+
+
+ com.lmax
+ disruptor
+
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+
+
+ junit
+ junit
+ test
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+
+
+ com.alibaba
+ fastjson
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ cn.hutool
+ hutool-all
+
+
+
+
+
+ The Apache Software License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+
+ icanci
+ icanci@foxmail.com
+ https://gitee.com/icanci
+ +8
+
+
+
+
+
+ ${project.artifactId}-${project.version}
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.0.1
+
+
+ attach-sources
+ verify
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${maven-deploy-plugin.version}
+
+ ${javadoc.opts}
+
+
+
+
+
+
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ ${maven.compiler.target}
+ ${maven.compiler.source}
+ ${project.build.sourceEncoding}
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ package
+
+ jar-no-fork
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ package
+
+ jar
+
+
+
+
+
+
+
+
+
+
+ disable-javadoc-doclint
+
+ [1.8,)
+
+
+ -Xdoclint:none
+
+
+
+ release
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ package
+
+ jar-no-fork
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ package
+
+ jar
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.6
+
+
+ verify
+
+ sign
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ ${nexus-staging-maven-plugin.version}
+ true
+
+ oss-release
+ https://s01.oss.sonatype.org/
+ true
+
+
+
+
+
+
+ sonatype-snapshots
+ https://s01.oss.sonatype.org/content/repositories/snapshots
+
+
+ sonatype-release
+ https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
+
+
+
+
+
+ sonatype-snapshots
+ https://s01.oss.sonatype.org/content/repositories/snapshots
+
+
+ sonatype-release
+ https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
\ No newline at end of file
diff --git a/server/pom.xml b/server/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..22784812caf7afe5ffda2fd174971087eed658e3
--- /dev/null
+++ b/server/pom.xml
@@ -0,0 +1,73 @@
+
+
+
+ ras-parent
+ cn.icanci.loopstack.ras
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ ras-server
+
+
+ 8
+ 8
+
+
+
+ cn.icanci.loopstack.ras
+ ras-common
+ ${parent.version}
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ compile
+ true
+
+
+ org.apache.commons
+ commons-lang3
+
+
+ org.apache.commons
+ commons-collections4
+
+
+ com.google.guava
+ guava
+
+
+ io.netty
+ netty-all
+
+
+ cn.icanci.loopstack
+ lsi-api
+
+
+ cn.icanci.loopstack
+ lsi-common
+
+
+ cn.icanci.loopstack
+ lsi-utils
+
+
+
\ No newline at end of file
diff --git a/server/src/main/java/cn/icanci/loopstack/ras/server/package-info.java b/server/src/main/java/cn/icanci/loopstack/ras/server/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..87a177b001b3b2fb53a376b68a89b92c2f1bd385
--- /dev/null
+++ b/server/src/main/java/cn/icanci/loopstack/ras/server/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * @author icanci
+ * @since 1.0 Created in 2023/01/19 17:22
+ */
+package cn.icanci.loopstack.ras.server;
\ No newline at end of file