diff --git a/cim-client-sdk/pom.xml b/cim-client-sdk/pom.xml
new file mode 100644
index 00000000..8d353a39
--- /dev/null
+++ b/cim-client-sdk/pom.xml
@@ -0,0 +1,51 @@
+
+
+ 4.0.0
+
+ com.crossoverjie.netty
+ cim
+ 1.0.0-SNAPSHOT
+
+
+ cim-client-sdk
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+
+ com.crossoverjie.netty
+ cim-common
+
+
+
+ com.crossoverjie.netty
+ cim-rout-api
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ test
+
+
+ com.crossoverjie.netty
+ cim-integration-test
+ ${project.version}
+ test
+
+
+
+
\ No newline at end of file
diff --git a/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/Client.java b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/Client.java
new file mode 100644
index 00000000..c83ad281
--- /dev/null
+++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/Client.java
@@ -0,0 +1,39 @@
+package com.crossoverjie.cim.client.sdk;
+
+import com.crossoverjie.cim.client.sdk.impl.ClientBuilderImpl;
+import com.crossoverjie.cim.client.sdk.impl.ClientConfigurationData;
+import com.crossoverjie.cim.common.pojo.CIMUserInfo;
+import com.crossoverjie.cim.route.api.vo.req.P2PReqVO;
+import com.crossoverjie.cim.route.api.vo.res.CIMServerResVO;
+import java.io.Closeable;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+
+public interface Client extends Closeable {
+
+ static ClientBuilder builder() {
+ return new ClientBuilderImpl();
+ }
+
+ default void sendP2P(P2PReqVO p2PReqVO) throws Exception{
+ sendP2PAsync(p2PReqVO).get();
+ };
+
+ CompletableFuture sendP2PAsync(P2PReqVO p2PReqVO);
+
+ default void sendGroup(String msg) throws Exception{
+ sendGroupAsync(msg).get();
+ };
+
+ CompletableFuture sendGroupAsync(String msg);
+
+ ClientState.State getState();
+
+ ClientConfigurationData.Auth getAuth();
+
+ Set getOnlineUser() throws Exception;
+
+ Optional getServerInfo();
+
+}
diff --git a/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/ClientBuilder.java b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/ClientBuilder.java
new file mode 100644
index 00000000..4ef802b0
--- /dev/null
+++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/ClientBuilder.java
@@ -0,0 +1,23 @@
+package com.crossoverjie.cim.client.sdk;
+
+import com.crossoverjie.cim.client.sdk.impl.ClientConfigurationData;
+import com.crossoverjie.cim.client.sdk.io.MessageListener;
+import com.crossoverjie.cim.client.sdk.io.ReconnectCheck;
+import java.util.concurrent.ThreadPoolExecutor;
+import okhttp3.OkHttpClient;
+
+/**
+ * @author crossoverJie
+ */
+public interface ClientBuilder {
+
+ Client build();
+ ClientBuilder auth(ClientConfigurationData.Auth auth);
+ ClientBuilder routeUrl(String routeUrl);
+ ClientBuilder loginRetryCount(int loginRetryCount);
+ ClientBuilder event(Event event);
+ ClientBuilder reconnectCheck(ReconnectCheck reconnectCheck);
+ ClientBuilder okHttpClient(OkHttpClient okHttpClient);
+ ClientBuilder messageListener(MessageListener messageListener);
+ ClientBuilder callbackThreadPool(ThreadPoolExecutor callbackThreadPool);
+}
diff --git a/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/ClientState.java b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/ClientState.java
new file mode 100644
index 00000000..7a953732
--- /dev/null
+++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/ClientState.java
@@ -0,0 +1,23 @@
+package com.crossoverjie.cim.client.sdk;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+public abstract class ClientState {
+
+ private static final AtomicReference STATE = new AtomicReference<>(State.Initialized);
+
+ public enum State {
+ /**
+ * Client state
+ */
+ Initialized, Reconnecting, Ready, Closed, Failed
+ }
+
+ public void setState(State s) {
+ STATE.set(s);
+ }
+
+ public State getState() {
+ return STATE.get();
+ }
+}
diff --git a/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/Event.java b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/Event.java
new file mode 100644
index 00000000..f2d3378d
--- /dev/null
+++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/Event.java
@@ -0,0 +1,36 @@
+package com.crossoverjie.cim.client.sdk;
+
+public interface Event {
+ void debug(String msg, Object... replace);
+ void info(String msg, Object... replace);
+ void warn(String msg, Object... replace);
+ void error(String msg, Object... replace);
+ void fatal(Client client);
+
+ class DefaultEvent implements Event {
+ @Override
+ public void debug(String msg, Object... replace) {
+ System.out.println(msg);
+ }
+
+ @Override
+ public void info(String msg, Object... replace) {
+ System.out.println(msg);
+ }
+
+ @Override
+ public void warn(String msg, Object... replace) {
+ System.out.println(msg);
+ }
+
+ @Override
+ public void error(String msg, Object... replace) {
+ System.err.println(msg);
+ }
+
+ @Override
+ public void fatal(Client client) {
+
+ }
+ }
+}
diff --git a/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/ReConnectManager.java b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/ReConnectManager.java
new file mode 100644
index 00000000..1db4876e
--- /dev/null
+++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/ReConnectManager.java
@@ -0,0 +1,69 @@
+package com.crossoverjie.cim.client.sdk;
+
+import com.crossoverjie.cim.client.sdk.impl.ClientImpl;
+import com.crossoverjie.cim.common.kit.HeartBeatHandler;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import io.netty.channel.ChannelHandlerContext;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+public final class ReConnectManager {
+
+ private ScheduledExecutorService scheduledExecutorService;
+
+ /**
+ * Trigger reconnect job
+ *
+ * @param ctx
+ */
+ public void reConnect(ChannelHandlerContext ctx) {
+ buildExecutor();
+ scheduledExecutorService.scheduleAtFixedRate(() -> {
+ try {
+ ClientImpl.getClient().getHeartBeatHandler().process(ctx);
+ } catch (Exception e) {
+ ClientImpl.getClient().getConf().getEvent().error("ReConnectManager reConnect error", e);
+ }
+ },
+ 0, 10, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Close reconnect job if reconnect success.
+ */
+ public void reConnectSuccess() {
+ scheduledExecutorService.shutdown();
+ }
+
+
+ /***
+ * build a thread executor
+ */
+ private void buildExecutor() {
+ if (scheduledExecutorService == null || scheduledExecutorService.isShutdown()) {
+ ThreadFactory factory = new ThreadFactoryBuilder()
+ .setNameFormat("reConnect-job-%d")
+ .setDaemon(true)
+ .build();
+ scheduledExecutorService = new ScheduledThreadPoolExecutor(1, factory);
+ }
+ }
+
+ private static class ClientHeartBeatHandle implements HeartBeatHandler {
+
+ @Override
+ public void process(ChannelHandlerContext ctx) throws Exception {
+ ClientImpl.getClient().reconnect();
+ }
+ }
+
+ public static ReConnectManager createReConnectManager() {
+ return new ReConnectManager();
+ }
+
+ public static HeartBeatHandler createHeartBeatHandler() {
+ return new ClientHeartBeatHandle();
+ }
+}
diff --git a/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/RouteManager.java b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/RouteManager.java
new file mode 100644
index 00000000..dd896857
--- /dev/null
+++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/RouteManager.java
@@ -0,0 +1,81 @@
+package com.crossoverjie.cim.client.sdk;
+
+import com.crossoverjie.cim.client.sdk.impl.ClientImpl;
+import com.crossoverjie.cim.common.core.proxy.RpcProxyManager;
+import com.crossoverjie.cim.common.enums.StatusEnum;
+import com.crossoverjie.cim.common.exception.CIMException;
+import com.crossoverjie.cim.common.pojo.CIMUserInfo;
+import com.crossoverjie.cim.common.res.BaseResponse;
+import com.crossoverjie.cim.common.res.NULLBody;
+import com.crossoverjie.cim.route.api.RouteApi;
+import com.crossoverjie.cim.route.api.vo.req.ChatReqVO;
+import com.crossoverjie.cim.route.api.vo.req.LoginReqVO;
+import com.crossoverjie.cim.route.api.vo.req.P2PReqVO;
+import com.crossoverjie.cim.route.api.vo.res.CIMServerResVO;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import okhttp3.OkHttpClient;
+
+public class RouteManager {
+
+
+ private final RouteApi routeApi;
+ private final Event event;
+
+ public RouteManager(String routeUrl, OkHttpClient okHttpClient, Event event) {
+ routeApi = RpcProxyManager.create(RouteApi.class, routeUrl, okHttpClient);
+ this.event = event;
+ }
+
+ public CIMServerResVO getServer(LoginReqVO loginReqVO) throws Exception {
+ BaseResponse cimServerResVO = routeApi.login(loginReqVO);
+
+ // repeat fail
+ if (!cimServerResVO.getCode().equals(StatusEnum.SUCCESS.getCode())) {
+ event.info(cimServerResVO.getMessage());
+
+ // when client in Reconnecting state, could exit.
+ if (ClientImpl.getClient().getState() == ClientState.State.Reconnecting) {
+ event.warn("###{}###", StatusEnum.RECONNECT_FAIL.getMessage());
+ throw new CIMException(StatusEnum.RECONNECT_FAIL);
+ }
+ }
+ return cimServerResVO.getDataBody();
+ }
+
+ public CompletableFuture sendP2P(CompletableFuture future, P2PReqVO p2PReqVO) {
+ return CompletableFuture.runAsync(() -> {
+ try {
+ BaseResponse response = routeApi.p2pRoute(p2PReqVO);
+ if (response.getCode().equals(StatusEnum.OFF_LINE.getCode())) {
+ future.completeExceptionally(new CIMException(StatusEnum.OFF_LINE));
+ }
+ future.complete(null);
+ } catch (Exception e) {
+ future.completeExceptionally(e);
+ event.error("send p2p msg error", e);
+ }
+ });
+ }
+
+ public CompletableFuture sendGroupMsg(ChatReqVO chatReqVO) {
+ return CompletableFuture.runAsync(() -> {
+ try {
+ routeApi.groupRoute(chatReqVO);
+ } catch (Exception e) {
+ event.error("send group msg error", e);
+ }
+ });
+ }
+
+ public void offLine(Long userId) {
+ ChatReqVO vo = new ChatReqVO(userId, "offLine");
+ routeApi.offLine(vo);
+ }
+
+ public Set onlineUser() throws Exception {
+ BaseResponse> onlineUsersResVO = routeApi.onlineUser();
+ return onlineUsersResVO.getDataBody();
+ }
+}
diff --git a/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/impl/ClientBuilderImpl.java b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/impl/ClientBuilderImpl.java
new file mode 100644
index 00000000..8779f755
--- /dev/null
+++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/impl/ClientBuilderImpl.java
@@ -0,0 +1,82 @@
+package com.crossoverjie.cim.client.sdk.impl;
+
+import com.crossoverjie.cim.client.sdk.Client;
+import com.crossoverjie.cim.client.sdk.ClientBuilder;
+import com.crossoverjie.cim.client.sdk.Event;
+import com.crossoverjie.cim.client.sdk.io.MessageListener;
+import com.crossoverjie.cim.client.sdk.io.ReconnectCheck;
+import com.crossoverjie.cim.common.util.StringUtil;
+import java.util.concurrent.ThreadPoolExecutor;
+import okhttp3.OkHttpClient;
+
+public class ClientBuilderImpl implements ClientBuilder {
+
+
+ private ClientConfigurationData conf;
+
+ public ClientBuilderImpl() {
+ this(new ClientConfigurationData());
+ }
+ public ClientBuilderImpl(ClientConfigurationData conf) {
+ this.conf = conf;
+ }
+
+ @Override
+ public Client build() {
+ return new ClientImpl(conf);
+ }
+
+ @Override
+ public ClientBuilder auth(ClientConfigurationData.Auth auth) {
+ if (auth.getUserId() <= 0 || StringUtil.isEmpty(auth.getUserName())){
+ throw new IllegalArgumentException("userId and userName must be set");
+ }
+ this.conf.setAuth(auth);
+ return this;
+ }
+
+ @Override
+ public ClientBuilder routeUrl(String routeUrl) {
+ if (StringUtil.isEmpty(routeUrl)) {
+ throw new IllegalArgumentException("routeUrl must be set");
+ }
+ this.conf.setRouteUrl(routeUrl);
+ return this;
+ }
+
+ @Override
+ public ClientBuilder loginRetryCount(int loginRetryCount) {
+ this.conf.setLoginRetryCount(loginRetryCount);
+ return this;
+ }
+
+ @Override
+ public ClientBuilder event(Event event) {
+ this.conf.setEvent(event);
+ return this;
+ }
+
+ @Override
+ public ClientBuilder reconnectCheck(ReconnectCheck reconnectCheck) {
+ this.conf.setReconnectCheck(reconnectCheck);
+ return this;
+ }
+
+ @Override
+ public ClientBuilder okHttpClient(OkHttpClient okHttpClient) {
+ this.conf.setOkHttpClient(okHttpClient);
+ return this;
+ }
+
+ @Override
+ public ClientBuilder messageListener(MessageListener messageListener) {
+ this.conf.setMessageListener(messageListener);
+ return this;
+ }
+
+ @Override
+ public ClientBuilder callbackThreadPool(ThreadPoolExecutor callbackThreadPool) {
+ this.conf.setCallbackThreadPool(callbackThreadPool);
+ return this;
+ }
+}
diff --git a/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/impl/ClientConfigurationData.java b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/impl/ClientConfigurationData.java
new file mode 100644
index 00000000..0ed24417
--- /dev/null
+++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/impl/ClientConfigurationData.java
@@ -0,0 +1,49 @@
+package com.crossoverjie.cim.client.sdk.impl;
+
+import com.crossoverjie.cim.client.sdk.Event;
+import com.crossoverjie.cim.client.sdk.io.MessageListener;
+import com.crossoverjie.cim.client.sdk.io.ReconnectCheck;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import java.util.concurrent.ThreadPoolExecutor;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import okhttp3.OkHttpClient;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ClientConfigurationData {
+
+ private Auth auth;
+
+ @Data
+ @AllArgsConstructor
+ @Builder
+ public static class Auth{
+ private long userId;
+ private String userName;
+ }
+
+ private String routeUrl;
+ private int loginRetryCount = 5;
+
+ @JsonIgnore
+ private Event event = new Event.DefaultEvent();
+
+ @JsonIgnore
+ private MessageListener messageListener =
+ (client, msg) -> System.out.printf("id:[%s] msg:[%s]%n \n", client.getAuth(), msg);
+
+ @JsonIgnore
+ private OkHttpClient okHttpClient = new OkHttpClient();
+
+ @JsonIgnore
+ private ThreadPoolExecutor callbackThreadPool;
+
+ @JsonIgnore
+ private ReconnectCheck reconnectCheck = (client) -> true;
+}
diff --git a/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/impl/ClientImpl.java b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/impl/ClientImpl.java
new file mode 100644
index 00000000..dbd3c0ca
--- /dev/null
+++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/impl/ClientImpl.java
@@ -0,0 +1,258 @@
+package com.crossoverjie.cim.client.sdk.impl;
+
+import static com.crossoverjie.cim.common.enums.StatusEnum.RECONNECT_FAIL;
+import com.crossoverjie.cim.client.sdk.Client;
+import com.crossoverjie.cim.client.sdk.ClientState;
+import com.crossoverjie.cim.client.sdk.ReConnectManager;
+import com.crossoverjie.cim.client.sdk.RouteManager;
+import com.crossoverjie.cim.client.sdk.io.CIMClientHandleInitializer;
+import com.crossoverjie.cim.common.constant.Constants;
+import com.crossoverjie.cim.common.exception.CIMException;
+import com.crossoverjie.cim.common.kit.HeartBeatHandler;
+import com.crossoverjie.cim.common.pojo.CIMUserInfo;
+import com.crossoverjie.cim.common.protocol.CIMRequestProto;
+import com.crossoverjie.cim.route.api.vo.req.ChatReqVO;
+import com.crossoverjie.cim.route.api.vo.req.LoginReqVO;
+import com.crossoverjie.cim.route.api.vo.req.P2PReqVO;
+import com.crossoverjie.cim.route.api.vo.res.CIMServerResVO;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.util.concurrent.DefaultThreadFactory;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class ClientImpl extends ClientState implements Client {
+
+ @Getter
+ private final ClientConfigurationData conf;
+ private static final int CALLBACK_QUEUE_SIZE = 1024;
+ private static final int CALLBACK_POOL_SIZE = 10;
+
+ // ======= private ========
+ private int errorCount;
+ private SocketChannel channel;
+
+ private final RouteManager routeManager;
+
+ @Getter
+ private final HeartBeatHandler heartBeatHandler = ReConnectManager.createHeartBeatHandler();
+ @Getter
+ private final ReConnectManager reConnectManager = ReConnectManager.createReConnectManager();
+
+ @Getter
+ private static ClientImpl client;
+ @Getter
+ private final CIMRequestProto.CIMReqProtocol heartBeatPacket;
+
+ // Client connected server info
+ private CIMServerResVO serverInfo;
+
+ public ClientImpl(ClientConfigurationData conf) {
+ this.conf = conf;
+
+ if (this.conf.getCallbackThreadPool() == null) {
+ BlockingQueue queue = new LinkedBlockingQueue<>(CALLBACK_QUEUE_SIZE);
+ ThreadFactory factory = new ThreadFactoryBuilder()
+ .setNameFormat("msg-callback-%d")
+ .setDaemon(true)
+ .build();
+ this.conf.setCallbackThreadPool(
+ new ThreadPoolExecutor(CALLBACK_POOL_SIZE, CALLBACK_POOL_SIZE, 1, TimeUnit.SECONDS, queue,
+ factory));
+ }
+
+ routeManager = new RouteManager(conf.getRouteUrl(), conf.getOkHttpClient(), conf.getEvent());
+
+ heartBeatPacket = CIMRequestProto.CIMReqProtocol.newBuilder()
+ .setRequestId(this.conf.getAuth().getUserId())
+ .setReqMsg("ping")
+ .setType(Constants.CommandType.PING)
+ .build();
+ client = this;
+
+ connectServer(v -> this.conf.getEvent().info("Login success!"));
+ }
+
+ private void connectServer(Consumer success) {
+ doConnectServer().whenComplete((r, e) -> {
+ if (r) {
+ success.accept(null);
+ }
+ if (e != null) {
+ if (e instanceof CIMException cimException && cimException.getErrorCode()
+ .equals(RECONNECT_FAIL.getCode())) {
+ this.conf.getEvent().fatal(this);
+ } else {
+ if (errorCount++ >= this.conf.getLoginRetryCount()) {
+ this.conf.getEvent()
+ .error("The maximum number of reconnections has been reached[{}]times, exit cim client!",
+ errorCount);
+ this.conf.getEvent().fatal(this);
+ }
+ }
+ }
+
+ });
+ }
+
+ /**
+ * 1. User login and get target server
+ * 2. Connect target server
+ * 3. send login cmd to server
+ */
+ private CompletableFuture doConnectServer() {
+ CompletableFuture future = new CompletableFuture<>();
+ this.userLogin(future).ifPresentOrElse((cimServer) -> {
+ this.doConnectServer(cimServer, future);
+ this.loginServer();
+ this.serverInfo = cimServer;
+ future.complete(true);
+ }, () -> {
+ this.conf.getEvent().error("Login fail!, cannot get server info!");
+ this.conf.getEvent().fatal(this);
+ future.complete(false);
+ });
+ return future;
+ }
+
+ /**
+ * Login and get server info
+ *
+ * @return Server info
+ */
+ private Optional userLogin(CompletableFuture future) {
+ LoginReqVO loginReqVO = new LoginReqVO(conf.getAuth().getUserId(),
+ conf.getAuth().getUserName());
+
+ CIMServerResVO cimServer = null;
+ try {
+ cimServer = routeManager.getServer(loginReqVO);
+ log.info("cimServer=[{}]", cimServer);
+ } catch (Exception e) {
+ log.error("login fail", e);
+ future.completeExceptionally(e);
+ }
+ return Optional.ofNullable(cimServer);
+ }
+
+ private final EventLoopGroup group = new NioEventLoopGroup(0, new DefaultThreadFactory("cim-work"));
+
+ private void doConnectServer(CIMServerResVO cimServer, CompletableFuture future) {
+ Bootstrap bootstrap = new Bootstrap();
+ bootstrap.group(group)
+ .channel(NioSocketChannel.class)
+ .handler(new CIMClientHandleInitializer());
+ ChannelFuture sync;
+ try {
+ sync = bootstrap.connect(cimServer.getIp(), cimServer.getCimServerPort()).sync();
+ if (sync.isSuccess()) {
+ this.conf.getEvent().info("Start cim client success!");
+ channel = (SocketChannel) sync.channel();
+ }
+ } catch (InterruptedException e) {
+ future.completeExceptionally(e);
+ }
+ }
+
+ /**
+ * Send login cmd to server
+ */
+ private void loginServer() {
+ CIMRequestProto.CIMReqProtocol login = CIMRequestProto.CIMReqProtocol.newBuilder()
+ .setRequestId(this.conf.getAuth().getUserId())
+ .setReqMsg(this.conf.getAuth().getUserName())
+ .setType(Constants.CommandType.LOGIN)
+ .build();
+ channel.writeAndFlush(login)
+ .addListener((ChannelFutureListener) channelFuture ->
+ this.conf.getEvent().info("Registry cim server success!")
+ );
+ }
+
+ /**
+ * 1. clear route information.
+ * 2. reconnect.
+ * 3. shutdown reconnect job.
+ * 4. reset reconnect state.
+ * @throws Exception
+ */
+ public void reconnect() throws Exception {
+ if (channel != null && channel.isActive()) {
+ return;
+ }
+ this.serverInfo = null;
+ // clear route information.
+ this.routeManager.offLine(this.getConf().getAuth().getUserId());
+
+ this.conf.getEvent().info("cim trigger reconnecting....");
+
+ // TODO: 2024/9/13 need a backoff interface
+ int random = (int) (Math.random() * 7 + 3);
+ TimeUnit.SECONDS.sleep(random);
+
+ // don't set State ready, because when connect success, the State will be set to ready automate.
+ connectServer(v -> {
+ this.reConnectManager.reConnectSuccess();
+ this.conf.getEvent().info("Great! reConnect success!!!");
+ });
+ }
+
+ @Override
+ public void close() {
+ if (channel != null) {
+ channel.close();
+ channel = null;
+ }
+ super.setState(ClientState.State.Closed);
+ this.routeManager.offLine(this.getAuth().getUserId());
+ }
+
+ @Override
+ public CompletableFuture sendP2PAsync(P2PReqVO p2PReqVO) {
+ CompletableFuture future = new CompletableFuture<>();
+ p2PReqVO.setUserId(this.conf.getAuth().getUserId());
+ return routeManager.sendP2P(future, p2PReqVO);
+ }
+
+ @Override
+ public CompletableFuture sendGroupAsync(String msg) {
+ // TODO: 2024/9/12 return messageId
+ return this.routeManager.sendGroupMsg(new ChatReqVO(this.conf.getAuth().getUserId(), msg));
+ }
+
+ @Override
+ public ClientConfigurationData.Auth getAuth() {
+ return this.conf.getAuth();
+ }
+
+ @Override
+ public ClientState.State getState() {
+ return super.getState();
+ }
+
+ @Override
+ public Set getOnlineUser() throws Exception {
+ return routeManager.onlineUser();
+ }
+
+ @Override
+ public Optional getServerInfo() {
+ return Optional.ofNullable(this.serverInfo);
+ }
+}
diff --git a/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/CIMClientHandle.java b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/CIMClientHandle.java
new file mode 100644
index 00000000..4b9709f9
--- /dev/null
+++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/CIMClientHandle.java
@@ -0,0 +1,83 @@
+package com.crossoverjie.cim.client.sdk.io;
+
+import com.crossoverjie.cim.client.sdk.ClientState;
+import com.crossoverjie.cim.client.sdk.impl.ClientImpl;
+import com.crossoverjie.cim.common.constant.Constants;
+import com.crossoverjie.cim.common.protocol.CIMResponseProto;
+import com.crossoverjie.cim.common.util.NettyAttrUtil;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.handler.timeout.IdleState;
+import io.netty.handler.timeout.IdleStateEvent;
+import lombok.extern.slf4j.Slf4j;
+
+@ChannelHandler.Sharable
+@Slf4j
+public class CIMClientHandle extends SimpleChannelInboundHandler {
+
+ @Override
+ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+
+ if (evt instanceof IdleStateEvent idleStateEvent) {
+
+ if (idleStateEvent.state() == IdleState.WRITER_IDLE) {
+
+ ctx.writeAndFlush(ClientImpl.getClient().getHeartBeatPacket()).addListeners((ChannelFutureListener) future -> {
+ if (!future.isSuccess()) {
+ log.error("heart beat error,close Channel");
+ ClientImpl.getClient().getConf().getEvent().warn("heart beat error,close Channel");
+ future.channel().close();
+ }
+ });
+ }
+
+ }
+
+ super.userEventTriggered(ctx, evt);
+ }
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) {
+ ClientImpl.getClient().getConf().getEvent().debug("ChannelActive");
+ ClientImpl.getClient().setState(ClientState.State.Ready);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) {
+
+ if (!ClientImpl.getClient().getConf().getReconnectCheck().isNeedReconnect(ClientImpl.getClient())) {
+ return;
+ }
+ ClientImpl.getClient().setState(ClientState.State.Closed);
+
+ ClientImpl.getClient().getConf().getEvent().warn("Client inactive, let's reconnect");
+ ClientImpl.getClient().getReConnectManager().reConnect(ctx);
+ }
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, CIMResponseProto.CIMResProtocol msg) {
+
+
+ if (msg.getType() == Constants.CommandType.PING) {
+ ClientImpl.getClient().getConf().getEvent().debug("received ping from server");
+ NettyAttrUtil.updateReaderTime(ctx.channel(), System.currentTimeMillis());
+ }
+
+ if (msg.getType() != Constants.CommandType.PING) {
+ // callback
+ ClientImpl.getClient().getConf().getEvent().info(msg.getResMsg());
+ ClientImpl.getClient().getConf().getCallbackThreadPool().execute(() -> {
+ ClientImpl.getClient().getConf().getMessageListener().received(ClientImpl.getClient(), msg.getResMsg());
+ });
+ }
+
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+ ClientImpl.getClient().getConf().getEvent().error(cause.getCause().toString());
+ ctx.close();
+ }
+}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/init/CIMClientHandleInitializer.java b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/CIMClientHandleInitializer.java
similarity index 64%
rename from cim-client/src/main/java/com/crossoverjie/cim/client/init/CIMClientHandleInitializer.java
rename to cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/CIMClientHandleInitializer.java
index c68bd67b..c8ef03ad 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/init/CIMClientHandleInitializer.java
+++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/CIMClientHandleInitializer.java
@@ -1,6 +1,5 @@
-package com.crossoverjie.cim.client.init;
+package com.crossoverjie.cim.client.sdk.io;
-import com.crossoverjie.cim.client.handle.CIMClientHandle;
import com.crossoverjie.cim.common.protocol.CIMResponseProto;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
@@ -10,32 +9,18 @@
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.timeout.IdleStateHandler;
-/**
- * Function:
- *
- * @author crossoverJie
- * Date: 23/02/2018 22:47
- * @since JDK 1.8
- */
public class CIMClientHandleInitializer extends ChannelInitializer {
private final CIMClientHandle cimClientHandle = new CIMClientHandle();
@Override
- protected void initChannel(Channel ch) throws Exception {
+ protected void initChannel(Channel ch) {
ch.pipeline()
- //10 秒没发送消息 将IdleStateHandler 添加到 ChannelPipeline 中
.addLast(new IdleStateHandler(0, 10, 0))
- //心跳解码
- //.addLast(new HeartbeatEncode())
-
- // google Protobuf 编解码
- //拆包解码
+ // google Protobuf
.addLast(new ProtobufVarint32FrameDecoder())
.addLast(new ProtobufDecoder(CIMResponseProto.CIMResProtocol.getDefaultInstance()))
- //
- //拆包编码
.addLast(new ProtobufVarint32LengthFieldPrepender())
.addLast(new ProtobufEncoder())
.addLast(cimClientHandle)
diff --git a/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/MessageListener.java b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/MessageListener.java
new file mode 100644
index 00000000..7fa21c87
--- /dev/null
+++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/MessageListener.java
@@ -0,0 +1,12 @@
+package com.crossoverjie.cim.client.sdk.io;
+
+import com.crossoverjie.cim.client.sdk.Client;
+
+public interface MessageListener {
+
+ /**
+ * @param client client
+ * @param msg msgs
+ */
+ void received(Client client, String msg);
+}
diff --git a/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/ReconnectCheck.java b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/ReconnectCheck.java
new file mode 100644
index 00000000..3984cfcb
--- /dev/null
+++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/ReconnectCheck.java
@@ -0,0 +1,12 @@
+package com.crossoverjie.cim.client.sdk.io;
+
+import com.crossoverjie.cim.client.sdk.Client;
+
+public interface ReconnectCheck {
+
+ /**
+ * By the default, the client will reconnect to the server when the connection is close(inactive).
+ * @return false if the client should not reconnect to the server.
+ */
+ boolean isNeedReconnect(Client client);
+}
diff --git a/cim-client-sdk/src/test/java/com/crossoverjie/cim/client/sdk/ClientTest.java b/cim-client-sdk/src/test/java/com/crossoverjie/cim/client/sdk/ClientTest.java
new file mode 100644
index 00000000..7685698f
--- /dev/null
+++ b/cim-client-sdk/src/test/java/com/crossoverjie/cim/client/sdk/ClientTest.java
@@ -0,0 +1,398 @@
+package com.crossoverjie.cim.client.sdk;
+
+import com.crossoverjie.cim.client.sdk.impl.ClientConfigurationData;
+import com.crossoverjie.cim.client.sdk.route.AbstractRouteBaseTest;
+import com.crossoverjie.cim.common.pojo.CIMUserInfo;
+import com.crossoverjie.cim.route.api.vo.req.P2PReqVO;
+import com.crossoverjie.cim.route.api.vo.res.CIMServerResVO;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import lombok.Cleanup;
+import lombok.extern.slf4j.Slf4j;
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientTest extends AbstractRouteBaseTest {
+
+
+ @AfterEach
+ public void tearDown() {
+ super.close();
+ }
+
+ @Test
+ public void groupChat() throws Exception {
+ super.starSingleServer();
+ super.startRoute();
+ String routeUrl = "http://localhost:8083";
+ String cj = "crossoverJie";
+ String zs = "zs";
+ Long id = super.registerAccount(cj);
+ Long zsId = super.registerAccount(zs);
+ var auth1 = ClientConfigurationData.Auth.builder()
+ .userId(id)
+ .userName(cj)
+ .build();
+ var auth2 = ClientConfigurationData.Auth.builder()
+ .userId(zsId)
+ .userName(zs)
+ .build();
+
+ @Cleanup
+ Client client1 = Client.builder()
+ .auth(auth1)
+ .routeUrl(routeUrl)
+ .build();
+ TimeUnit.SECONDS.sleep(3);
+ ClientState.State state = client1.getState();
+ Awaitility.await().atMost(10, TimeUnit.SECONDS)
+ .untilAsserted(() -> Assertions.assertEquals(ClientState.State.Ready, state));
+ Optional serverInfo = client1.getServerInfo();
+ Assertions.assertTrue(serverInfo.isPresent());
+ System.out.println("client1 serverInfo = " + serverInfo.get());
+
+
+ AtomicReference client2Receive = new AtomicReference<>();
+ @Cleanup
+ Client client2 = Client.builder()
+ .auth(auth2)
+ .routeUrl(routeUrl)
+ .messageListener((client, message) -> client2Receive.set(message))
+ .build();
+ TimeUnit.SECONDS.sleep(3);
+ ClientState.State state2 = client2.getState();
+ Awaitility.await().atMost(10, TimeUnit.SECONDS)
+ .untilAsserted(() -> Assertions.assertEquals(ClientState.State.Ready, state2));
+
+ Optional serverInfo2 = client2.getServerInfo();
+ Assertions.assertTrue(serverInfo2.isPresent());
+ System.out.println("client2 serverInfo = " + serverInfo2.get());
+
+ // send msg
+ String msg = "hello";
+ client1.sendGroup(msg);
+
+ Set onlineUser = client1.getOnlineUser();
+ Assertions.assertEquals(onlineUser.size(), 2);
+ onlineUser.forEach(userInfo -> {
+ log.info("online user = {}", userInfo);
+ Long userId = userInfo.getUserId();
+ if (userId.equals(id)) {
+ Assertions.assertEquals(cj, userInfo.getUserName());
+ } else if (userId.equals(zsId)) {
+ Assertions.assertEquals(zs, userInfo.getUserName());
+ }
+ });
+
+ Awaitility.await().untilAsserted(
+ () -> Assertions.assertEquals(String.format("crossoverJie:%s", msg), client2Receive.get()));
+ super.stopSingle();
+ }
+
+ @Test
+ public void testP2PChat() throws Exception {
+ super.starSingleServer();
+ super.startRoute();
+ String routeUrl = "http://localhost:8083";
+ String cj = "cj";
+ String zs = "zs";
+ String ls = "ls";
+ Long cjId = super.registerAccount(cj);
+ Long zsId = super.registerAccount(zs);
+ Long lsId = super.registerAccount(ls);
+ var auth1 = ClientConfigurationData.Auth.builder()
+ .userName(cj)
+ .userId(cjId)
+ .build();
+ var auth2 = ClientConfigurationData.Auth.builder()
+ .userName(zs)
+ .userId(zsId)
+ .build();
+ var auth3 = ClientConfigurationData.Auth.builder()
+ .userName(ls)
+ .userId(lsId)
+ .build();
+
+ @Cleanup
+ Client client1 = Client.builder()
+ .auth(auth1)
+ .routeUrl(routeUrl)
+ .build();
+ TimeUnit.SECONDS.sleep(3);
+ ClientState.State state = client1.getState();
+ Awaitility.await().atMost(10, TimeUnit.SECONDS)
+ .untilAsserted(() -> Assertions.assertEquals(ClientState.State.Ready, state));
+ Optional serverInfo = client1.getServerInfo();
+ Assertions.assertTrue(serverInfo.isPresent());
+ System.out.println("client1 serverInfo = " + serverInfo.get());
+
+
+ // client2
+ AtomicReference client2Receive = new AtomicReference<>();
+ @Cleanup
+ Client client2 = Client.builder()
+ .auth(auth2)
+ .routeUrl(routeUrl)
+ .messageListener((client, message) -> client2Receive.set(message))
+ .build();
+ TimeUnit.SECONDS.sleep(3);
+ ClientState.State state2 = client2.getState();
+ Awaitility.await().atMost(10, TimeUnit.SECONDS)
+ .untilAsserted(() -> Assertions.assertEquals(ClientState.State.Ready, state2));
+
+ Optional serverInfo2 = client2.getServerInfo();
+ Assertions.assertTrue(serverInfo2.isPresent());
+ System.out.println("client2 serverInfo = " + serverInfo2.get());
+
+ // client3
+ AtomicReference client3Receive = new AtomicReference<>();
+ @Cleanup
+ Client client3 = Client.builder()
+ .auth(auth3)
+ .routeUrl(routeUrl)
+ .messageListener((client, message) -> {
+ log.info("client3 receive message = {}", message);
+ client3Receive.set(message);
+ })
+ .build();
+ TimeUnit.SECONDS.sleep(3);
+ ClientState.State state3 = client3.getState();
+ Awaitility.await().atMost(10, TimeUnit.SECONDS)
+ .untilAsserted(() -> Assertions.assertEquals(ClientState.State.Ready, state3));
+
+ Optional serverInfo3 = client3.getServerInfo();
+ Assertions.assertTrue(serverInfo3.isPresent());
+ System.out.println("client3 serverInfo = " + serverInfo3.get());
+
+ // send msg to client3
+ String msg = "hello";
+ client1.sendP2P(P2PReqVO.builder()
+ .receiveUserId(lsId)
+ .msg(msg)
+ .build());
+
+ Set onlineUser = client1.getOnlineUser();
+ Assertions.assertEquals(onlineUser.size(), 3);
+ onlineUser.forEach(userInfo -> {
+ log.info("online user = {}", userInfo);
+ Long userId = userInfo.getUserId();
+ if (userId.equals(cjId)) {
+ Assertions.assertEquals(cj, userInfo.getUserName());
+ } else if (userId.equals(zsId)) {
+ Assertions.assertEquals(zs, userInfo.getUserName());
+ } else if (userId.equals(lsId)) {
+ Assertions.assertEquals(ls, userInfo.getUserName());
+ }
+ });
+
+ Awaitility.await().untilAsserted(
+ () -> Assertions.assertEquals(String.format("%s:%s", cj, msg), client3Receive.get()));
+ Awaitility.await().untilAsserted(
+ () -> Assertions.assertNull(client2Receive.get()));
+ super.stopSingle();
+ }
+
+ /**
+ * 1. Start two servers.
+ * 2. Start two client, and send message.
+ * 3. Stop one server which is connected by client1.
+ * 4. Wait for client1 reconnect.
+ * 5. Send message again.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testReconnect() throws Exception {
+ super.startTwoServer();
+ super.startRoute();
+
+ String routeUrl = "http://localhost:8083";
+ String cj = "cj";
+ String zs = "zs";
+ Long cjId = super.registerAccount(cj);
+ Long zsId = super.registerAccount(zs);
+ var auth1 = ClientConfigurationData.Auth.builder()
+ .userName(cj)
+ .userId(cjId)
+ .build();
+ var auth2 = ClientConfigurationData.Auth.builder()
+ .userName(zs)
+ .userId(zsId)
+ .build();
+
+ @Cleanup
+ Client client1 = Client.builder()
+ .auth(auth1)
+ .routeUrl(routeUrl)
+ .build();
+ TimeUnit.SECONDS.sleep(3);
+ ClientState.State state = client1.getState();
+ Awaitility.await().atMost(10, TimeUnit.SECONDS)
+ .untilAsserted(() -> Assertions.assertEquals(ClientState.State.Ready, state));
+
+
+ AtomicReference client2Receive = new AtomicReference<>();
+ @Cleanup
+ Client client2 = Client.builder()
+ .auth(auth2)
+ .routeUrl(routeUrl)
+ .messageListener((client, message) -> client2Receive.set(message))
+ .build();
+ TimeUnit.SECONDS.sleep(3);
+ ClientState.State state2 = client2.getState();
+ Awaitility.await().atMost(10, TimeUnit.SECONDS)
+ .untilAsserted(() -> Assertions.assertEquals(ClientState.State.Ready, state2));
+
+ Optional serverInfo2 = client2.getServerInfo();
+ Assertions.assertTrue(serverInfo2.isPresent());
+ System.out.println("client2 serverInfo = " + serverInfo2.get());
+
+ // send msg
+ String msg = "hello";
+ client1.sendGroup(msg);
+ Awaitility.await()
+ .untilAsserted(() -> Assertions.assertEquals(String.format("cj:%s", msg), client2Receive.get()));
+ client2Receive.set("");
+
+
+ System.out.println("ready to restart server");
+ TimeUnit.SECONDS.sleep(3);
+ Optional serverInfo = client1.getServerInfo();
+ Assertions.assertTrue(serverInfo.isPresent());
+ System.out.println("server info = " + serverInfo.get());
+
+ super.stopServer(serverInfo.get().getCimServerPort());
+ System.out.println("stop server success! " + serverInfo.get());
+
+
+ // Waiting server stopped, and client reconnect.
+ TimeUnit.SECONDS.sleep(30);
+ System.out.println("reconnect state: " + client1.getState());
+ Awaitility.await().atMost(15, TimeUnit.SECONDS)
+ .untilAsserted(() -> Assertions.assertEquals(ClientState.State.Ready, state));
+ serverInfo = client1.getServerInfo();
+ Assertions.assertTrue(serverInfo.isPresent());
+ System.out.println("client1 reconnect server info = " + serverInfo.get());
+
+ // Send message again.
+ log.info("send message again, client2Receive = {}", client2Receive.get());
+ client1.sendGroup(msg);
+ Awaitility.await()
+ .untilAsserted(() -> Assertions.assertEquals(String.format("cj:%s", msg), client2Receive.get()));
+ super.stopTwoServer();
+ }
+
+ @Test
+ public void offLineAndOnline() throws Exception {
+ super.starSingleServer();
+ super.startRoute();
+ String routeUrl = "http://localhost:8083";
+ String cj = "crossoverJie";
+ String zs = "zs";
+ Long id = super.registerAccount(cj);
+ Long zsId = super.registerAccount(zs);
+ var auth1 = ClientConfigurationData.Auth.builder()
+ .userId(id)
+ .userName(cj)
+ .build();
+ var auth2 = ClientConfigurationData.Auth.builder()
+ .userId(zsId)
+ .userName(zs)
+ .build();
+
+ @Cleanup
+ Client client1 = Client.builder()
+ .auth(auth1)
+ .routeUrl(routeUrl)
+ .build();
+ TimeUnit.SECONDS.sleep(3);
+ ClientState.State state = client1.getState();
+ Awaitility.await().atMost(10, TimeUnit.SECONDS)
+ .untilAsserted(() -> Assertions.assertEquals(ClientState.State.Ready, state));
+ Optional serverInfo = client1.getServerInfo();
+ Assertions.assertTrue(serverInfo.isPresent());
+ System.out.println("client1 serverInfo = " + serverInfo.get());
+
+
+ AtomicReference client2Receive = new AtomicReference<>();
+ Client client2 = Client.builder()
+ .auth(auth2)
+ .routeUrl(routeUrl)
+ .messageListener((client, message) -> client2Receive.set(message))
+ // Avoid auto reconnect, this test will manually close client.
+ .reconnectCheck((client) -> false)
+ .build();
+ TimeUnit.SECONDS.sleep(3);
+ ClientState.State state2 = client2.getState();
+ Awaitility.await().atMost(10, TimeUnit.SECONDS)
+ .untilAsserted(() -> Assertions.assertEquals(ClientState.State.Ready, state2));
+
+ Optional serverInfo2 = client2.getServerInfo();
+ Assertions.assertTrue(serverInfo2.isPresent());
+ System.out.println("client2 serverInfo = " + serverInfo2.get());
+
+ // send msg
+ String msg = "hello";
+ client1.sendGroup(msg);
+ Awaitility.await().untilAsserted(
+ () -> Assertions.assertEquals(String.format("crossoverJie:%s", msg), client2Receive.get()));
+ client2Receive.set("");
+
+ // Manually offline
+ client2.close();
+ TimeUnit.SECONDS.sleep(10);
+ client2 = Client.builder()
+ .auth(auth2)
+ .routeUrl(routeUrl)
+ .messageListener((client, message) -> client2Receive.set(message))
+ // Avoid to auto reconnect, this test will manually close client.
+ .reconnectCheck((client) -> false)
+ .build();
+ ClientState.State state3 = client2.getState();
+ Awaitility.await().atMost(10, TimeUnit.SECONDS)
+ .untilAsserted(() -> Assertions.assertEquals(ClientState.State.Ready, state3));
+
+ // send msg again
+ client1.sendGroup(msg);
+ Awaitility.await().untilAsserted(
+ () -> Assertions.assertEquals(String.format("crossoverJie:%s", msg), client2Receive.get()));
+
+ super.stopSingle();
+ }
+
+ @Test
+ public void testClose() throws Exception {
+ super.starSingleServer();
+ super.startRoute();
+ String routeUrl = "http://localhost:8083";
+ String cj = "crossoverJie";
+ Long id = super.registerAccount(cj);
+ var auth1 = ClientConfigurationData.Auth.builder()
+ .userId(id)
+ .userName(cj)
+ .build();
+
+ Client client1 = Client.builder()
+ .auth(auth1)
+ .routeUrl(routeUrl)
+ .build();
+ TimeUnit.SECONDS.sleep(3);
+ ClientState.State state = client1.getState();
+ Awaitility.await().atMost(10, TimeUnit.SECONDS)
+ .untilAsserted(() -> Assertions.assertEquals(ClientState.State.Ready, state));
+ Optional serverInfo = client1.getServerInfo();
+ Assertions.assertTrue(serverInfo.isPresent());
+ System.out.println("client1 serverInfo = " + serverInfo.get());
+
+ client1.close();
+ ClientState.State state1 = client1.getState();
+ Assertions.assertEquals(ClientState.State.Closed, state1);
+ super.stopSingle();
+ }
+
+}
\ No newline at end of file
diff --git a/cim-client-sdk/src/test/resources/application-route.yaml b/cim-client-sdk/src/test/resources/application-route.yaml
new file mode 100644
index 00000000..305a9aa1
--- /dev/null
+++ b/cim-client-sdk/src/test/resources/application-route.yaml
@@ -0,0 +1,49 @@
+spring:
+ application:
+ name:
+ cim-forward-route
+ data:
+ redis:
+ host: 127.0.0.1
+ port: 6379
+ jedis:
+ pool:
+ max-active: 100
+ max-idle: 100
+ max-wait: 1000
+ min-idle: 10
+# web port
+server:
+ port: 8083
+
+logging:
+ level:
+ root: info
+
+ # enable swagger
+springdoc:
+ swagger-ui:
+ enabled: true
+
+app:
+ zk:
+ connect:
+ timeout: 30000
+ root: /route
+
+ # route strategy
+ #app.route.way=com.crossoverjie.cim.common.route.algorithm.loop.LoopHandle
+
+ # route strategy
+ #app.route.way=com.crossoverjie.cim.common.route.algorithm.random.RandomHandle
+
+ # route strategy
+ route:
+ way:
+ handler: com.crossoverjie.cim.common.route.algorithm.loop.LoopHandle
+
+ #app.route.way.consitenthash=com.crossoverjie.cim.common.route.algorithm.consistenthash.SortArrayMapConsistentHash
+
+ consitenthash: com.crossoverjie.cim.common.route.algorithm.consistenthash.TreeMapConsistentHash
+
+
diff --git a/cim-client/pom.xml b/cim-client/pom.xml
index ba3958ca..9e628ddb 100644
--- a/cim-client/pom.xml
+++ b/cim-client/pom.xml
@@ -32,6 +32,11 @@
cim-common
+
+ com.crossoverjie.netty
+ cim-client-sdk
+
+
org.springframework.boot
spring-boot-starter-web
@@ -88,5 +93,22 @@
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/CIMClientApplication.java b/cim-client/src/main/java/com/crossoverjie/cim/client/CIMClientApplication.java
index 8c81cdb4..092b95ca 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/CIMClientApplication.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/CIMClientApplication.java
@@ -1,9 +1,7 @@
package com.crossoverjie.cim.client;
import com.crossoverjie.cim.client.scanner.Scan;
-import com.crossoverjie.cim.client.service.impl.ClientInfo;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -15,19 +13,17 @@
@SpringBootApplication
public class CIMClientApplication implements CommandLineRunner{
- @Autowired
- private ClientInfo clientInfo ;
+
public static void main(String[] args) {
SpringApplication.run(CIMClientApplication.class, args);
- log.info("启动 Client 服务成功");
+ log.info("Client start success");
}
@Override
- public void run(String... args) throws Exception {
+ public void run(String... args) {
Scan scan = new Scan() ;
Thread thread = new Thread(scan);
thread.setName("scan-thread");
thread.start();
- clientInfo.saveStartDate();
}
}
\ No newline at end of file
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/client/CIMClient.java b/cim-client/src/main/java/com/crossoverjie/cim/client/client/CIMClient.java
deleted file mode 100644
index f1d8de25..00000000
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/client/CIMClient.java
+++ /dev/null
@@ -1,233 +0,0 @@
-package com.crossoverjie.cim.client.client;
-
-import com.crossoverjie.cim.client.config.AppConfiguration;
-import com.crossoverjie.cim.client.init.CIMClientHandleInitializer;
-import com.crossoverjie.cim.client.service.EchoService;
-import com.crossoverjie.cim.client.service.ReConnectManager;
-import com.crossoverjie.cim.client.service.RouteRequest;
-import com.crossoverjie.cim.client.service.impl.ClientInfo;
-import com.crossoverjie.cim.client.thread.ContextHolder;
-import com.crossoverjie.cim.client.vo.req.GoogleProtocolVO;
-import com.crossoverjie.cim.route.api.vo.req.LoginReqVO;
-import com.crossoverjie.cim.route.api.vo.res.CIMServerResVO;
-import com.crossoverjie.cim.common.constant.Constants;
-import com.crossoverjie.cim.common.protocol.CIMRequestProto;
-import io.netty.bootstrap.Bootstrap;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-import io.netty.channel.ChannelFuture;
-import io.netty.channel.ChannelFutureListener;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.channel.socket.SocketChannel;
-import io.netty.channel.socket.nio.NioSocketChannel;
-import io.netty.util.concurrent.DefaultThreadFactory;
-import jakarta.annotation.PostConstruct;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
-import org.springframework.stereotype.Component;
-
-/**
- * Function:
- *
- * @author crossoverJie
- * Date: 22/05/2018 14:19
- * @since JDK 1.8
- */
-@Component
-@Slf4j
-@ConditionalOnWebApplication
-public class CIMClient {
-
-
- private EventLoopGroup group = new NioEventLoopGroup(0, new DefaultThreadFactory("cim-work"));
-
- @Value("${cim.user.id}")
- private long userId;
-
- @Value("${cim.user.userName}")
- private String userName;
-
- private SocketChannel channel;
-
- @Autowired
- private EchoService echoService ;
-
- @Autowired
- private RouteRequest routeRequest;
-
- @Autowired
- private AppConfiguration configuration;
-
-
- @Autowired
- private ClientInfo clientInfo;
-
- @Autowired
- private ReConnectManager reConnectManager ;
-
- /**
- * 重试次数
- */
- private int errorCount;
-
- @PostConstruct
- public void start() throws Exception {
-
- //登录 + 获取可以使用的服务器 ip+port
- CIMServerResVO cimServer = userLogin();
-
- //启动客户端
- startClient(cimServer);
-
- //向服务端注册
- loginCIMServer();
-
-
- }
-
- /**
- * 启动客户端
- *
- * @param cimServer
- * @throws Exception
- */
- private void startClient(CIMServerResVO cimServer) {
- Bootstrap bootstrap = new Bootstrap();
- bootstrap.group(group)
- .channel(NioSocketChannel.class)
- .handler(new CIMClientHandleInitializer())
- ;
-
- ChannelFuture future = null;
- try {
- future = bootstrap.connect(cimServer.getIp(), cimServer.getCimServerPort()).sync();
- } catch (Exception e) {
- errorCount++;
-
- if (errorCount >= configuration.getErrorCount()) {
- log.error("连接失败次数达到上限[{}]次", errorCount);
-// shutdownService.closeMsgHandle();
- }
- log.error("Connect fail!", e);
- }
- if (future.isSuccess()) {
- echoService.echo("Start cim client success!");
- log.info("启动 cim client 成功");
- }
- channel = (SocketChannel) future.channel();
- }
-
- /**
- * 登录+路由服务器
- *
- * @return 路由服务器信息
- * @throws Exception
- */
- private CIMServerResVO userLogin() {
- LoginReqVO loginReqVO = new LoginReqVO(userId, userName);
- CIMServerResVO cimServer = null;
- try {
- cimServer = routeRequest.getCIMServer(loginReqVO);
-
- //保存系统信息
- clientInfo.saveServiceInfo(cimServer.getIp() + ":" + cimServer.getCimServerPort())
- .saveUserInfo(userId, userName);
-
- log.info("cimServer=[{}]", cimServer);
- } catch (Exception e) {
- errorCount++;
-
- if (errorCount >= configuration.getErrorCount()) {
- echoService.echo("The maximum number of reconnections has been reached[{}]times, close cim client!", errorCount);
-// shutdownService.closeMsgHandle();
- }
- log.error("login fail", e);
- }
- return cimServer;
- }
-
- /**
- * 向服务器注册
- */
- private void loginCIMServer() {
- CIMRequestProto.CIMReqProtocol login = CIMRequestProto.CIMReqProtocol.newBuilder()
- .setRequestId(userId)
- .setReqMsg(userName)
- .setType(Constants.CommandType.LOGIN)
- .build();
- ChannelFuture future = channel.writeAndFlush(login);
- future.addListener((ChannelFutureListener) channelFuture ->
- echoService.echo("Registry cim server success!")
- );
- }
-
- /**
- * 发送消息字符串
- *
- * @param msg
- */
- public void sendStringMsg(String msg) {
- ByteBuf message = Unpooled.buffer(msg.getBytes().length);
- message.writeBytes(msg.getBytes());
- ChannelFuture future = channel.writeAndFlush(message);
- future.addListener((ChannelFutureListener) channelFuture ->
- log.info("客户端手动发消息成功={}", msg));
-
- }
-
- /**
- * 发送 Google Protocol 编解码字符串
- *
- * @param googleProtocolVO
- */
- public void sendGoogleProtocolMsg(GoogleProtocolVO googleProtocolVO) {
-
- CIMRequestProto.CIMReqProtocol protocol = CIMRequestProto.CIMReqProtocol.newBuilder()
- .setRequestId(googleProtocolVO.getRequestId())
- .setReqMsg(googleProtocolVO.getMsg())
- .setType(Constants.CommandType.MSG)
- .build();
-
-
- ChannelFuture future = channel.writeAndFlush(protocol);
- future.addListener((ChannelFutureListener) channelFuture ->
- log.info("客户端手动发送 Google Protocol 成功={}", googleProtocolVO.toString()));
-
- }
-
-
- /**
- * 1. clear route information.
- * 2. reconnect.
- * 3. shutdown reconnect job.
- * 4. reset reconnect state.
- * @throws Exception
- */
- public void reconnect() throws Exception {
- if (channel != null && channel.isActive()) {
- return;
- }
- //首先清除路由信息,下线
- routeRequest.offLine();
-
- echoService.echo("cim server shutdown, reconnecting....");
- start();
- echoService.echo("Great! reConnect success!!!");
- reConnectManager.reConnectSuccess();
- ContextHolder.clear();
- }
-
- /**
- * 关闭
- *
- * @throws InterruptedException
- */
- public void close() throws InterruptedException {
- if (channel != null){
- channel.close();
- }
- }
-}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/config/AppConfiguration.java b/cim-client/src/main/java/com/crossoverjie/cim/client/config/AppConfiguration.java
index 9bc86e49..a0ffad31 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/config/AppConfiguration.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/config/AppConfiguration.java
@@ -1,5 +1,6 @@
package com.crossoverjie.cim.client.config;
+import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@@ -11,6 +12,7 @@
* @since JDK 1.8
*/
@Component
+@Data
public class AppConfiguration {
@Value("${cim.user.id}")
@@ -22,54 +24,16 @@ public class AppConfiguration {
@Value("${cim.msg.logger.path}")
private String msgLoggerPath ;
-
@Value("${cim.heartbeat.time}")
private long heartBeatTime ;
@Value("${cim.reconnect.count}")
- private int errorCount ;
-
- public Long getUserId() {
- return userId;
- }
-
- public void setUserId(Long userId) {
- this.userId = userId;
- }
-
- public String getUserName() {
- return userName;
- }
-
- public void setUserName(String userName) {
- this.userName = userName;
- }
-
- public String getMsgLoggerPath() {
- return msgLoggerPath;
- }
-
- public void setMsgLoggerPath(String msgLoggerPath) {
- this.msgLoggerPath = msgLoggerPath;
- }
-
-
- public long getHeartBeatTime() {
- return heartBeatTime;
- }
-
- public void setHeartBeatTime(long heartBeatTime) {
- this.heartBeatTime = heartBeatTime;
- }
-
-
-
-
- public int getErrorCount() {
- return errorCount;
- }
-
- public void setErrorCount(int errorCount) {
- this.errorCount = errorCount;
- }
+ private int reconnectCount;
+
+ @Value("${cim.route.url}")
+ private String routeUrl;
+ @Value("${cim.callback.thread.queue.size}")
+ private int queueSize;
+ @Value("${cim.callback.thread.pool.size}")
+ private int poolSize;
}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/config/BeanConfig.java b/cim-client/src/main/java/com/crossoverjie/cim/client/config/BeanConfig.java
index e86e9697..2845dcba 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/config/BeanConfig.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/config/BeanConfig.java
@@ -1,55 +1,72 @@
package com.crossoverjie.cim.client.config;
-import com.crossoverjie.cim.client.handle.MsgHandleCaller;
+import com.crossoverjie.cim.client.sdk.Client;
+import com.crossoverjie.cim.client.sdk.Event;
+import com.crossoverjie.cim.client.sdk.impl.ClientConfigurationData;
+import com.crossoverjie.cim.client.service.MsgLogger;
+import com.crossoverjie.cim.client.service.ShutDownSign;
import com.crossoverjie.cim.client.service.impl.MsgCallBackListener;
-import com.crossoverjie.cim.common.constant.Constants;
import com.crossoverjie.cim.common.data.construct.RingBufferWheel;
-import com.crossoverjie.cim.common.protocol.CIMRequestProto;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import jakarta.annotation.Resource;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
-import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import java.util.concurrent.*;
-
/**
* Function:bean 配置
*
* @author crossoverJie
- * Date: 24/05/2018 15:55
+ * Date: 24/05/2018 15:55
* @since JDK 1.8
*/
@Configuration
public class BeanConfig {
- @Value("${cim.user.id}")
- private long userId;
+ @Resource
+ private AppConfiguration appConfiguration;
- @Value("${cim.callback.thread.queue.size}")
- private int queueSize;
+ @Resource
+ private ShutDownSign shutDownSign;
- @Value("${cim.callback.thread.pool.size}")
- private int poolSize;
+ @Resource
+ private MsgLogger msgLogger;
- /**
- * 创建心跳单例
- * @return
- */
- @Bean(value = "heartBeat")
- public CIMRequestProto.CIMReqProtocol heartBeat() {
- CIMRequestProto.CIMReqProtocol heart = CIMRequestProto.CIMReqProtocol.newBuilder()
- .setRequestId(userId)
- .setReqMsg("ping")
- .setType(Constants.CommandType.PING)
+ @Bean
+ public Client buildClient(@Qualifier("callBackThreadPool") ThreadPoolExecutor callbackThreadPool,
+ Event event) {
+ OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(3, TimeUnit.SECONDS)
+ .readTimeout(3, TimeUnit.SECONDS)
+ .writeTimeout(3, TimeUnit.SECONDS)
+ .retryOnConnectionFailure(true).build();
+
+ return Client.builder()
+ .auth(ClientConfigurationData.Auth.builder()
+ .userName(appConfiguration.getUserName())
+ .userId(appConfiguration.getUserId())
+ .build())
+ .routeUrl(appConfiguration.getRouteUrl())
+ .loginRetryCount(appConfiguration.getReconnectCount())
+ .event(event)
+ .reconnectCheck(client -> !shutDownSign.checkStatus())
+ .okHttpClient(okHttpClient)
+ .messageListener(new MsgCallBackListener(msgLogger))
+ .callbackThreadPool(callbackThreadPool)
.build();
- return heart;
}
-
/**
* http client
+ *
* @return okHttp
*/
@Bean
@@ -57,54 +74,33 @@ public OkHttpClient okHttpClient() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(3, TimeUnit.SECONDS)
.readTimeout(3, TimeUnit.SECONDS)
- .writeTimeout(3,TimeUnit.SECONDS)
+ .writeTimeout(3, TimeUnit.SECONDS)
.retryOnConnectionFailure(true);
return builder.build();
}
/**
- * 创建回调线程池
+ * Create callback thread pool
+ *
* @return
*/
@Bean("callBackThreadPool")
- public ThreadPoolExecutor buildCallerThread(){
- BlockingQueue queue = new LinkedBlockingQueue(queueSize);
- ThreadFactory product = new ThreadFactoryBuilder()
+ public ThreadPoolExecutor buildCallerThread() {
+ BlockingQueue queue = new LinkedBlockingQueue<>(appConfiguration.getQueueSize());
+ ThreadFactory executor = new ThreadFactoryBuilder()
.setNameFormat("msg-callback-%d")
.setDaemon(true)
.build();
- ThreadPoolExecutor productExecutor = new ThreadPoolExecutor(poolSize, poolSize, 1, TimeUnit.MILLISECONDS, queue,product);
- return productExecutor ;
- }
-
-
- @Bean("scheduledTask")
- public ScheduledExecutorService buildSchedule(){
- ThreadFactory sche = new ThreadFactoryBuilder()
- .setNameFormat("reConnect-job-%d")
- .setDaemon(true)
- .build();
- ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1,sche) ;
- return scheduledExecutorService ;
- }
-
- /**
- * 回调 bean
- * @return
- */
- @Bean
- public MsgHandleCaller buildCaller(){
- MsgHandleCaller caller = new MsgHandleCaller(new MsgCallBackListener()) ;
-
- return caller ;
+ return new ThreadPoolExecutor(appConfiguration.getPoolSize(), appConfiguration.getPoolSize(), 1,
+ TimeUnit.MILLISECONDS, queue, executor);
}
@Bean
- public RingBufferWheel bufferWheel(){
- ExecutorService executorService = Executors.newFixedThreadPool(2) ;
- return new RingBufferWheel(executorService) ;
+ public RingBufferWheel bufferWheel() {
+ ExecutorService executorService = Executors.newFixedThreadPool(2);
+ return new RingBufferWheel(executorService);
}
}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/constant/Emoji.java b/cim-client/src/main/java/com/crossoverjie/cim/client/constant/Emoji.java
deleted file mode 100644
index cc7c9f57..00000000
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/constant/Emoji.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.crossoverjie.cim.client.constant;
-
-/**
- * Function:
- *
- * @author crossoverJie
- * Date: 2019-08-24 22:53
- * @since JDK 1.8
- */
-public class Emoji {
-
-}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/controller/IndexController.java b/cim-client/src/main/java/com/crossoverjie/cim/client/controller/IndexController.java
deleted file mode 100644
index 5c250563..00000000
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/controller/IndexController.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package com.crossoverjie.cim.client.controller;
-
-import com.crossoverjie.cim.client.client.CIMClient;
-import com.crossoverjie.cim.client.service.RouteRequest;
-import com.crossoverjie.cim.client.vo.req.GoogleProtocolVO;
-import com.crossoverjie.cim.client.vo.req.StringReqVO;
-import com.crossoverjie.cim.client.vo.res.SendMsgResVO;
-import com.crossoverjie.cim.common.enums.StatusEnum;
-import com.crossoverjie.cim.common.res.BaseResponse;
-import com.crossoverjie.cim.common.res.NULLBody;
-import com.crossoverjie.cim.route.api.vo.req.ChatReqVO;
-import io.swagger.v3.oas.annotations.Operation;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.ResponseBody;
-
-/**
- * Function:
- *
- * @author crossoverJie
- * Date: 22/05/2018 14:46
- * @since JDK 1.8
- */
-@Controller
-@RequestMapping("/")
-@ConditionalOnWebApplication
-public class IndexController {
-
-
- @Autowired
- private CIMClient heartbeatClient ;
-
-
-
- @Autowired
- private RouteRequest routeRequest ;
-
-
- /**
- * 向服务端发消息 字符串
- * @param stringReqVO
- * @return
- */
- @Operation(summary = "客户端发送消息,字符串")
- @RequestMapping(value = "sendStringMsg", method = RequestMethod.POST)
- @ResponseBody
- public BaseResponse sendStringMsg(@RequestBody StringReqVO stringReqVO){
- BaseResponse res = new BaseResponse();
-
- for (int i = 0; i < 100; i++) {
- heartbeatClient.sendStringMsg(stringReqVO.getMsg()) ;
- }
-
- // TODO: 2024/5/30 metrics
-
- SendMsgResVO sendMsgResVO = new SendMsgResVO() ;
- sendMsgResVO.setMsg("OK") ;
- res.setCode(StatusEnum.SUCCESS.getCode()) ;
- res.setMessage(StatusEnum.SUCCESS.getMessage()) ;
- return res ;
- }
-
- /**
- * 向服务端发消息 Google ProtoBuf
- * @param googleProtocolVO
- * @return
- */
- @Operation(summary = "向服务端发消息 Google ProtoBuf")
- @RequestMapping(value = "sendProtoBufMsg", method = RequestMethod.POST)
- @ResponseBody
- public BaseResponse sendProtoBufMsg(@RequestBody GoogleProtocolVO googleProtocolVO){
- BaseResponse res = new BaseResponse();
-
- for (int i = 0; i < 100; i++) {
- heartbeatClient.sendGoogleProtocolMsg(googleProtocolVO) ;
- }
-
- // TODO: 2024/5/30 metrics
-
- SendMsgResVO sendMsgResVO = new SendMsgResVO() ;
- sendMsgResVO.setMsg("OK") ;
- res.setCode(StatusEnum.SUCCESS.getCode()) ;
- res.setMessage(StatusEnum.SUCCESS.getMessage()) ;
- return res ;
- }
-
-
-
- /**
- * 群发消息
- * @param chatReqVO
- * @return
- */
- @Operation(summary = "群发消息")
- @RequestMapping(value = "sendGroupMsg",method = RequestMethod.POST)
- @ResponseBody
- public BaseResponse sendGroupMsg(@RequestBody ChatReqVO chatReqVO) throws Exception {
- BaseResponse res = new BaseResponse();
- routeRequest.sendGroupMsg(chatReqVO) ;
-
- // TODO: 2024/5/30 metrics
-
- res.setCode(StatusEnum.SUCCESS.getCode()) ;
- res.setMessage(StatusEnum.SUCCESS.getMessage()) ;
- return res ;
- }
-}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/handle/CIMClientHandle.java b/cim-client/src/main/java/com/crossoverjie/cim/client/handle/CIMClientHandle.java
deleted file mode 100644
index 791fd019..00000000
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/handle/CIMClientHandle.java
+++ /dev/null
@@ -1,141 +0,0 @@
-package com.crossoverjie.cim.client.handle;
-
-import com.crossoverjie.cim.client.service.EchoService;
-import com.crossoverjie.cim.client.service.ReConnectManager;
-import com.crossoverjie.cim.client.service.ShutDownMsg;
-import com.crossoverjie.cim.client.service.impl.EchoServiceImpl;
-import com.crossoverjie.cim.client.util.SpringBeanFactory;
-import com.crossoverjie.cim.common.constant.Constants;
-import com.crossoverjie.cim.common.protocol.CIMRequestProto;
-import com.crossoverjie.cim.common.protocol.CIMResponseProto;
-import com.crossoverjie.cim.common.util.NettyAttrUtil;
-import com.vdurmont.emoji.EmojiParser;
-import io.netty.channel.ChannelFutureListener;
-import io.netty.channel.ChannelHandler;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.SimpleChannelInboundHandler;
-import io.netty.handler.timeout.IdleState;
-import io.netty.handler.timeout.IdleStateEvent;
-import lombok.extern.slf4j.Slf4j;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ThreadPoolExecutor;
-
-/**
- * Function:
- *
- * @author crossoverJie
- * Date: 16/02/2018 18:09
- * @since JDK 1.8
- */
-@ChannelHandler.Sharable
-@Slf4j
-public class CIMClientHandle extends SimpleChannelInboundHandler {
-
-
- private MsgHandleCaller caller;
-
- private ThreadPoolExecutor threadPoolExecutor;
-
- private ScheduledExecutorService scheduledExecutorService;
-
- private ReConnectManager reConnectManager;
-
- private ShutDownMsg shutDownMsg;
-
- private EchoService echoService;
-
-
- @Override
- public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
-
- if (evt instanceof IdleStateEvent) {
- IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
-
- if (idleStateEvent.state() == IdleState.WRITER_IDLE) {
- CIMRequestProto.CIMReqProtocol heartBeat = SpringBeanFactory.getBean("heartBeat",
- CIMRequestProto.CIMReqProtocol.class);
- ctx.writeAndFlush(heartBeat).addListeners((ChannelFutureListener) future -> {
- if (!future.isSuccess()) {
- log.error("IO error,close Channel");
- future.channel().close();
- }
- });
- }
-
- }
-
- super.userEventTriggered(ctx, evt);
- }
-
- @Override
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
- //客户端和服务端建立连接时调用
- log.info("cim server connect success!");
- }
-
- @Override
- public void channelInactive(ChannelHandlerContext ctx) throws Exception {
-
- if (shutDownMsg == null) {
- shutDownMsg = SpringBeanFactory.getBean(ShutDownMsg.class);
- }
-
- //用户主动退出,不执行重连逻辑
- if (shutDownMsg.checkStatus()) {
- return;
- }
-
- if (scheduledExecutorService == null) {
- scheduledExecutorService = SpringBeanFactory.getBean("scheduledTask", ScheduledExecutorService.class);
- reConnectManager = SpringBeanFactory.getBean(ReConnectManager.class);
- }
- log.info("客户端断开了,重新连接!");
- reConnectManager.reConnect(ctx);
- }
-
- @Override
- protected void channelRead0(ChannelHandlerContext ctx, CIMResponseProto.CIMResProtocol msg) throws Exception {
- if (echoService == null) {
- echoService = SpringBeanFactory.getBean(EchoServiceImpl.class);
- }
-
-
- //心跳更新时间
- if (msg.getType() == Constants.CommandType.PING) {
- //LOGGER.info("收到服务端心跳!!!");
- NettyAttrUtil.updateReaderTime(ctx.channel(), System.currentTimeMillis());
- }
-
- if (msg.getType() != Constants.CommandType.PING) {
- //回调消息
- callBackMsg(msg.getResMsg());
-
- //将消息中的 emoji 表情格式化为 Unicode 编码以便在终端可以显示
- String response = EmojiParser.parseToUnicode(msg.getResMsg());
- echoService.echo(response);
- }
-
-
- }
-
- /**
- * 回调消息
- *
- * @param msg
- */
- private void callBackMsg(String msg) {
- threadPoolExecutor = SpringBeanFactory.getBean("callBackThreadPool", ThreadPoolExecutor.class);
- threadPoolExecutor.execute(() -> {
- caller = SpringBeanFactory.getBean(MsgHandleCaller.class);
- caller.getMsgHandleListener().handle(msg);
- });
-
- }
-
- @Override
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
- //异常时断开连接
- cause.printStackTrace();
- ctx.close();
- }
-}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/handle/MsgHandleCaller.java b/cim-client/src/main/java/com/crossoverjie/cim/client/handle/MsgHandleCaller.java
deleted file mode 100644
index 2f0f37ed..00000000
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/handle/MsgHandleCaller.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.crossoverjie.cim.client.handle;
-
-import com.crossoverjie.cim.client.service.CustomMsgHandleListener;
-
-/**
- * Function:消息回调 bean
- *
- * @author crossoverJie
- * Date: 2018/12/26 17:37
- * @since JDK 1.8
- */
-public class MsgHandleCaller {
-
- /**
- * 回调接口
- */
- private CustomMsgHandleListener msgHandleListener ;
-
- public MsgHandleCaller(CustomMsgHandleListener msgHandleListener) {
- this.msgHandleListener = msgHandleListener;
- }
-
- public CustomMsgHandleListener getMsgHandleListener() {
- return msgHandleListener;
- }
-
- public void setMsgHandleListener(CustomMsgHandleListener msgHandleListener) {
- this.msgHandleListener = msgHandleListener;
- }
-}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/scanner/Scan.java b/cim-client/src/main/java/com/crossoverjie/cim/client/scanner/Scan.java
index fbd07485..de9188ce 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/scanner/Scan.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/scanner/Scan.java
@@ -1,12 +1,9 @@
package com.crossoverjie.cim.client.scanner;
-import com.crossoverjie.cim.client.config.AppConfiguration;
-import com.crossoverjie.cim.client.service.EchoService;
+import com.crossoverjie.cim.client.sdk.Event;
import com.crossoverjie.cim.client.service.MsgHandle;
import com.crossoverjie.cim.client.service.MsgLogger;
import com.crossoverjie.cim.client.util.SpringBeanFactory;
-import com.vdurmont.emoji.EmojiParser;
-
import java.util.Scanner;
import lombok.SneakyThrows;
@@ -20,22 +17,15 @@
public class Scan implements Runnable {
- /**
- * 系统参数
- */
- private AppConfiguration configuration;
-
- private MsgHandle msgHandle ;
-
- private MsgLogger msgLogger ;
+ private final MsgHandle msgHandle ;
- private EchoService echoService ;
+ private final MsgLogger msgLogger ;
+ private final Event event ;
public Scan() {
- this.configuration = SpringBeanFactory.getBean(AppConfiguration.class);
this.msgHandle = SpringBeanFactory.getBean(MsgHandle.class) ;
this.msgLogger = SpringBeanFactory.getBean(MsgLogger.class) ;
- this.echoService = SpringBeanFactory.getBean(EchoService.class) ;
+ this.event = SpringBeanFactory.getBean(Event.class) ;
}
@SneakyThrows
@@ -45,23 +35,21 @@ public void run() {
while (true) {
String msg = sc.nextLine();
- //检查消息
if (msgHandle.checkMsg(msg)) {
continue;
}
- //系统内置命令
+ // internal cmd
if (msgHandle.innerCommand(msg)){
continue;
}
- //真正的发送消息
msgHandle.sendMsg(msg) ;
- //写入聊天记录
+ // write to log
msgLogger.log(msg) ;
- echoService.echo(EmojiParser.parseToUnicode(msg));
+ event.info(msg);
}
}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/CustomMsgHandleListener.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/CustomMsgHandleListener.java
deleted file mode 100644
index ff92d9a1..00000000
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/CustomMsgHandleListener.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.crossoverjie.cim.client.service;
-
-/**
- * Function: 自定义消息回调
- *
- * @author crossoverJie
- * Date: 2018/12/26 17:24
- * @since JDK 1.8
- */
-public interface CustomMsgHandleListener {
-
- /**
- * 消息回调
- * @param msg
- */
- void handle(String msg);
-}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/EchoService.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/EchoService.java
deleted file mode 100644
index f4a57532..00000000
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/EchoService.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.crossoverjie.cim.client.service;
-
-/**
- * Function:
- *
- * @author crossoverJie
- * Date: 2019-08-27 22:35
- * @since JDK 1.8
- */
-public interface EchoService {
-
- /**
- * echo msg to terminal
- * @param msg message
- * @param replace
- */
- void echo(String msg, Object... replace) ;
-}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/MsgHandle.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/MsgHandle.java
index 7cd89be0..358e55d8 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/MsgHandle.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/service/MsgHandle.java
@@ -7,61 +7,44 @@
* Function:消息处理器
*
* @author crossoverJie
- * Date: 2018/12/26 11:11
+ * Date: 2018/12/26 11:11
* @since JDK 1.8
*/
public interface MsgHandle {
/**
* 统一的发送接口,包含了 groupChat p2pChat
+ *
* @param msg
*/
- void sendMsg(String msg) ;
+ void sendMsg(String msg) throws Exception;
- /**
- * 群聊
- * @param groupReqVO 群聊消息 其中的 userId 为发送者的 userID
- * @throws Exception
- */
- void groupChat(ChatReqVO groupReqVO) throws Exception ;
- /**
- * 私聊
- * @param p2PReqVO 私聊请求
- * @throws Exception
- */
- void p2pChat(P2PReqVO p2PReqVO) throws Exception;
-
-
- // TODO: 2018/12/26 后续对消息的处理可以优化为责任链模式
/**
* 校验消息
+ *
* @param msg
* @return 不能为空,后续可以加上一些敏感词
* @throws Exception
*/
- boolean checkMsg(String msg) ;
+ boolean checkMsg(String msg);
/**
* 执行内部命令
+ *
* @param msg
* @return 是否应当跳过当前消息(包含了":" 就需要跳过)
*/
boolean innerCommand(String msg) throws Exception;
- /**
- * 关闭系统
- */
- void shutdown() throws Exception;
-
/**
* 开启 AI 模式
*/
- void openAIModel() ;
+ void openAIModel();
/**
* 关闭 AI 模式
*/
- void closeAIModel() ;
+ void closeAIModel();
}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/MsgLogger.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/MsgLogger.java
index d2f27c44..822a1b23 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/MsgLogger.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/service/MsgLogger.java
@@ -10,7 +10,7 @@
public interface MsgLogger {
/**
- * 异步写入消息
+ * write log
* @param msg
*/
void log(String msg) ;
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/ReConnectManager.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/ReConnectManager.java
deleted file mode 100644
index 3e977e67..00000000
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/ReConnectManager.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.crossoverjie.cim.client.service;
-
-import com.crossoverjie.cim.client.thread.ReConnectJob;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
-import io.netty.channel.ChannelHandlerContext;
-import org.springframework.stereotype.Component;
-
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Function:
- *
- * @author crossoverJie
- * Date: 2020-04-15 00:26
- * @since JDK 1.8
- */
-@Component
-public final class ReConnectManager {
-
- private ScheduledExecutorService scheduledExecutorService;
-
- /**
- * Trigger reconnect job
- * @param ctx
- */
- public void reConnect(ChannelHandlerContext ctx) {
- buildExecutor() ;
- scheduledExecutorService.scheduleAtFixedRate(new ReConnectJob(ctx),0,10, TimeUnit.SECONDS) ;
- }
-
- /**
- * Close reconnect job if reconnect success.
- */
- public void reConnectSuccess(){
- scheduledExecutorService.shutdown();
- }
-
-
- /***
- * build an thread executor
- * @return
- */
- private ScheduledExecutorService buildExecutor() {
- if (scheduledExecutorService == null || scheduledExecutorService.isShutdown()) {
- ThreadFactory sche = new ThreadFactoryBuilder()
- .setNameFormat("reConnect-job-%d")
- .setDaemon(true)
- .build();
- scheduledExecutorService = new ScheduledThreadPoolExecutor(1, sche);
- return scheduledExecutorService;
- } else {
- return scheduledExecutorService;
- }
- }
-}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/RouteRequest.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/RouteRequest.java
deleted file mode 100644
index 3abeb3e0..00000000
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/RouteRequest.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.crossoverjie.cim.client.service;
-
-import com.crossoverjie.cim.common.pojo.CIMUserInfo;
-import com.crossoverjie.cim.route.api.vo.req.ChatReqVO;
-import com.crossoverjie.cim.route.api.vo.req.LoginReqVO;
-import com.crossoverjie.cim.route.api.vo.req.P2PReqVO;
-import com.crossoverjie.cim.route.api.vo.res.CIMServerResVO;
-import java.util.Set;
-
-/**
- * Function:
- *
- * @author crossoverJie
- * Date: 2018/12/22 22:26
- * @since JDK 1.8
- */
-public interface RouteRequest {
-
- /**
- * 群发消息
- * @param chatReqVO 消息
- * @throws Exception
- */
- void sendGroupMsg(ChatReqVO chatReqVO) throws Exception;
-
-
- /**
- * 私聊
- * @param p2PReqVO
- * @throws Exception
- */
- void sendP2PMsg(P2PReqVO p2PReqVO)throws Exception;
-
- /**
- * 获取服务器
- * @return 服务ip+port
- * @param loginReqVO
- * @throws Exception
- */
- CIMServerResVO getCIMServer(LoginReqVO loginReqVO) throws Exception;
-
- /**
- * 获取所有在线用户
- * @return
- * @throws Exception
- */
- Set onlineUsers()throws Exception ;
-
-
- void offLine() throws Exception;
-
-}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/ShutDownMsg.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/ShutDownSign.java
similarity index 85%
rename from cim-client/src/main/java/com/crossoverjie/cim/client/service/ShutDownMsg.java
rename to cim-client/src/main/java/com/crossoverjie/cim/client/service/ShutDownSign.java
index abebc7df..d7bd7088 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/ShutDownMsg.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/service/ShutDownSign.java
@@ -10,11 +10,11 @@
* @since JDK 1.8
*/
@Component
-public class ShutDownMsg {
+public class ShutDownSign {
private boolean isCommand ;
/**
- * 置为用户主动退出状态
+ * Set user exit sign.
*/
public void shutdown(){
isCommand = true ;
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/AsyncMsgLogger.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/AsyncMsgLogger.java
index 5dc10db9..3e87ad8a 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/AsyncMsgLogger.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/AsyncMsgLogger.java
@@ -2,6 +2,10 @@
import com.crossoverjie.cim.client.config.AppConfiguration;
import com.crossoverjie.cim.client.service.MsgLogger;
+import jakarta.annotation.Resource;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -33,17 +37,17 @@ public class AsyncMsgLogger implements MsgLogger {
* The default buffer size.
*/
private static final int DEFAULT_QUEUE_SIZE = 16;
- private BlockingQueue blockingQueue = new ArrayBlockingQueue(DEFAULT_QUEUE_SIZE);
+ private final BlockingQueue blockingQueue = new ArrayBlockingQueue(DEFAULT_QUEUE_SIZE);
private volatile boolean started = false;
- private Worker worker = new Worker();
+ private final Worker worker = new Worker();
- @Autowired
+ @Resource
private AppConfiguration appConfiguration;
@Override
public void log(String msg) {
- //开始消费
+ // start worker
startMsgLogger();
try {
// TODO: 2019/1/6 消息堆满是否阻塞线程?
@@ -88,9 +92,9 @@ private void writeLog(String msg) {
Files.createDirectories(Paths.get(dir));
}
- List lines = Arrays.asList(msg);
+ List lines = Collections.singletonList(msg);
- Files.write(file, lines, Charset.forName("UTF-8"), StandardOpenOption.CREATE, StandardOpenOption.APPEND);
+ Files.write(file, lines, StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
} catch (IOException e) {
log.info("IOException", e);
}
@@ -98,7 +102,7 @@ private void writeLog(String msg) {
}
/**
- * 开始工作
+ * Begin worker
*/
private void startMsgLogger() {
if (started) {
@@ -125,8 +129,9 @@ public String query(String key) {
Path path = Paths.get(appConfiguration.getMsgLoggerPath() + appConfiguration.getUserName() + "/");
try {
+ @Cleanup
Stream list = Files.list(path);
- List collect = list.collect(Collectors.toList());
+ List collect = list.toList();
for (Path file : collect) {
List strings = Files.readAllLines(file);
for (String msg : strings) {
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/ClientHeartBeatHandlerImpl.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/ClientHeartBeatHandlerImpl.java
deleted file mode 100644
index 8be70e17..00000000
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/ClientHeartBeatHandlerImpl.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.crossoverjie.cim.client.service.impl;
-
-import com.crossoverjie.cim.client.client.CIMClient;
-import com.crossoverjie.cim.client.thread.ContextHolder;
-import com.crossoverjie.cim.common.kit.HeartBeatHandler;
-import io.netty.channel.ChannelHandlerContext;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
-import org.springframework.stereotype.Service;
-
-/**
- * Function:
- *
- * @author crossoverJie
- * Date: 2019-01-20 17:16
- * @since JDK 1.8
- */
-@Service
-@ConditionalOnWebApplication
-public class ClientHeartBeatHandlerImpl implements HeartBeatHandler {
-
- @Autowired
- private CIMClient cimClient;
-
-
- @Override
- public void process(ChannelHandlerContext ctx) throws Exception {
-
- //重连
- ContextHolder.setReconnect(true);
- cimClient.reconnect();
-
- }
-
-
-}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/ClientInfo.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/ClientInfo.java
deleted file mode 100644
index eedb2de8..00000000
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/ClientInfo.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package com.crossoverjie.cim.client.service.impl;
-
-
-import org.springframework.stereotype.Component;
-
-import java.util.Date;
-
-/**
- * Function:
- *
- * @author crossoverJie
- * Date: 2019-01-21 23:35
- * @since JDK 1.8
- */
-@Component
-public class ClientInfo {
-
- private Info info = new Info() ;
-
- public Info get(){
- return info ;
- }
-
- public ClientInfo saveUserInfo(long userId,String userName){
- info.setUserId(userId);
- info.setUserName(userName);
- return this;
- }
-
-
- public ClientInfo saveServiceInfo(String serviceInfo){
- info.setServiceInfo(serviceInfo);
- return this;
- }
-
- public ClientInfo saveStartDate(){
- info.setStartDate(new Date());
- return this;
- }
-
- public class Info{
- private String userName;
- private long userId ;
- private String serviceInfo ;
- private Date startDate ;
-
- public Info() {
- }
-
- public String getUserName() {
- return userName;
- }
-
- public void setUserName(String userName) {
- this.userName = userName;
- }
-
- public long getUserId() {
- return userId;
- }
-
- public void setUserId(long userId) {
- this.userId = userId;
- }
-
- public String getServiceInfo() {
- return serviceInfo;
- }
-
- public void setServiceInfo(String serviceInfo) {
- this.serviceInfo = serviceInfo;
- }
-
- public Date getStartDate() {
- return startDate;
- }
-
- public void setStartDate(Date startDate) {
- this.startDate = startDate;
- }
- }
-}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/EchoServiceImpl.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/EchoServiceImpl.java
index de9f0d25..841a6543 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/EchoServiceImpl.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/EchoServiceImpl.java
@@ -1,12 +1,13 @@
package com.crossoverjie.cim.client.service.impl;
import com.crossoverjie.cim.client.config.AppConfiguration;
-import com.crossoverjie.cim.client.service.EchoService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
+import com.crossoverjie.cim.client.sdk.Client;
+import com.crossoverjie.cim.client.sdk.Event;
+import com.vdurmont.emoji.EmojiParser;
+import jakarta.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalTime;
+import org.springframework.stereotype.Service;
/**
* Function:
@@ -16,16 +17,23 @@
* @since JDK 1.8
*/
@Service
-public class EchoServiceImpl implements EchoService {
+public class EchoServiceImpl implements Event {
private static final String PREFIX = "$";
- @Autowired
+ @Resource
private AppConfiguration appConfiguration;
@Override
- public void echo(String msg, Object... replace) {
- String date = LocalDate.now().toString() + " " + LocalTime.now().withNano(0).toString();
+ public void debug(String msg, Object... replace) {
+ info(String.format("Debug[%s]", msg), replace);
+ }
+
+ @Override
+ public void info(String msg, Object... replace) {
+ // Make terminal can display the emoji
+ msg = EmojiParser.parseToUnicode(msg);
+ String date = LocalDate.now() + " " + LocalTime.now().withNano(0).toString();
msg = "[" + date + "] \033[31;4m" + appConfiguration.getUserName() + PREFIX + "\033[0m" + " " + msg;
@@ -34,6 +42,21 @@ public void echo(String msg, Object... replace) {
System.out.println(log);
}
+ @Override
+ public void warn(String msg, Object... replace) {
+ info(String.format("Warn##%s##", msg), replace);
+ }
+
+ @Override
+ public void error(String msg, Object... replace) {
+ info(String.format("Error!!%s!!", msg), replace);
+ }
+
+ @Override
+ public void fatal(Client client) {
+ info("{} fatal error, shutdown client", client.getAuth());
+ }
+
/**
* print msg
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/MsgCallBackListener.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/MsgCallBackListener.java
index 5eae7a2e..3d395b08 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/MsgCallBackListener.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/MsgCallBackListener.java
@@ -1,27 +1,28 @@
package com.crossoverjie.cim.client.service.impl;
-import com.crossoverjie.cim.client.service.CustomMsgHandleListener;
+import com.crossoverjie.cim.client.sdk.Client;
+import com.crossoverjie.cim.client.sdk.io.MessageListener;
import com.crossoverjie.cim.client.service.MsgLogger;
-import com.crossoverjie.cim.client.util.SpringBeanFactory;
/**
* Function:自定义收到消息回调
*
* @author crossoverJie
- * Date: 2019/1/6 17:49
+ * Date: 2019/1/6 17:49
* @since JDK 1.8
*/
-public class MsgCallBackListener implements CustomMsgHandleListener {
+public class MsgCallBackListener implements MessageListener {
- private MsgLogger msgLogger ;
+ private final MsgLogger msgLogger;
- public MsgCallBackListener() {
- this.msgLogger = SpringBeanFactory.getBean(MsgLogger.class) ;
+ public MsgCallBackListener(MsgLogger msgLogger) {
+ this.msgLogger = msgLogger;
}
+
@Override
- public void handle(String msg) {
- msgLogger.log(msg) ;
+ public void received(Client client, String msg) {
+ msgLogger.log(msg);
}
}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/MsgHandler.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/MsgHandler.java
index f9149b21..32201763 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/MsgHandler.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/MsgHandler.java
@@ -1,19 +1,13 @@
package com.crossoverjie.cim.client.service.impl;
-import com.crossoverjie.cim.client.config.AppConfiguration;
+import com.crossoverjie.cim.client.sdk.Client;
import com.crossoverjie.cim.client.service.InnerCommand;
import com.crossoverjie.cim.client.service.InnerCommandContext;
import com.crossoverjie.cim.client.service.MsgHandle;
-import com.crossoverjie.cim.client.service.MsgLogger;
-import com.crossoverjie.cim.client.service.RouteRequest;
import com.crossoverjie.cim.common.util.StringUtil;
-import com.crossoverjie.cim.route.api.vo.req.ChatReqVO;
import com.crossoverjie.cim.route.api.vo.req.P2PReqVO;
import jakarta.annotation.Resource;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
@@ -26,29 +20,18 @@
@Slf4j
@Service
public class MsgHandler implements MsgHandle {
- @Autowired
- private RouteRequest routeRequest;
- @Autowired
- private AppConfiguration configuration;
- @Resource(name = "callBackThreadPool")
- private ThreadPoolExecutor executor;
-
-
- @Autowired
- private MsgLogger msgLogger;
-
- @Autowired
- private ClientInfo clientInfo;
-
- @Autowired
+ @Resource
private InnerCommandContext innerCommandContext ;
+ @Resource
+ private Client client;
+
private boolean aiModel = false;
@Override
- public void sendMsg(String msg) {
+ public void sendMsg(String msg) throws Exception {
if (aiModel) {
aiChat(msg);
} else {
@@ -56,33 +39,16 @@ public void sendMsg(String msg) {
}
}
- /**
- * 正常聊天
- *
- * @param msg
- */
- private void normalChat(String msg) {
+ private void normalChat(String msg) throws Exception {
String[] totalMsg = msg.split(";;");
if (totalMsg.length > 1) {
- //私聊
P2PReqVO p2PReqVO = new P2PReqVO();
- p2PReqVO.setUserId(configuration.getUserId());
p2PReqVO.setReceiveUserId(Long.parseLong(totalMsg[0]));
p2PReqVO.setMsg(totalMsg[1]);
- try {
- p2pChat(p2PReqVO);
- } catch (Exception e) {
- log.error("Exception", e);
- }
+ client.sendP2P(p2PReqVO);
} else {
- //群聊
- ChatReqVO groupReqVO = new ChatReqVO(configuration.getUserId(), msg);
- try {
- groupChat(groupReqVO);
- } catch (Exception e) {
- log.error("Exception", e);
- }
+ client.sendGroup(msg);
}
}
@@ -100,18 +66,6 @@ private void aiChat(String msg) {
System.out.println("AI:\033[31;4m" + msg + "\033[0m");
}
- @Override
- public void groupChat(ChatReqVO groupReqVO) throws Exception {
- routeRequest.sendGroupMsg(groupReqVO);
- }
-
- @Override
- public void p2pChat(P2PReqVO p2PReqVO) throws Exception {
-
- routeRequest.sendP2PMsg(p2PReqVO);
-
- }
-
@Override
public boolean checkMsg(String msg) {
if (StringUtil.isEmpty(msg)) {
@@ -134,28 +88,6 @@ public boolean innerCommand(String msg) throws Exception {
} else {
return false;
}
-
-
- }
-
- /**
- * 关闭系统
- */
- @Override
- public void shutdown() throws Exception {
- log.info("系统关闭中。。。。");
- routeRequest.offLine();
- msgLogger.stop();
- executor.shutdown();
- try {
- while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
- log.info("线程池关闭中。。。。");
- }
-// shutdownService.closeCIMClient();
- } catch (InterruptedException e) {
- log.error("InterruptedException", e);
- }
- System.exit(0);
}
@Override
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/RouteRequestImpl.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/RouteRequestImpl.java
deleted file mode 100644
index 8165ac60..00000000
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/RouteRequestImpl.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package com.crossoverjie.cim.client.service.impl;
-
-import com.crossoverjie.cim.client.config.AppConfiguration;
-import com.crossoverjie.cim.client.service.EchoService;
-import com.crossoverjie.cim.client.service.RouteRequest;
-import com.crossoverjie.cim.client.thread.ContextHolder;
-import com.crossoverjie.cim.common.core.proxy.RpcProxyManager;
-import com.crossoverjie.cim.common.enums.StatusEnum;
-import com.crossoverjie.cim.common.exception.CIMException;
-import com.crossoverjie.cim.common.pojo.CIMUserInfo;
-import com.crossoverjie.cim.common.res.BaseResponse;
-import com.crossoverjie.cim.common.res.NULLBody;
-import com.crossoverjie.cim.route.api.RouteApi;
-import com.crossoverjie.cim.route.api.vo.req.ChatReqVO;
-import com.crossoverjie.cim.route.api.vo.req.LoginReqVO;
-import com.crossoverjie.cim.route.api.vo.req.P2PReqVO;
-import com.crossoverjie.cim.route.api.vo.res.CIMServerResVO;
-import java.util.Set;
-import lombok.extern.slf4j.Slf4j;
-import okhttp3.OkHttpClient;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-
-/**
- * Function:
- *
- * @author crossoverJie
- * Date: 2018/12/22 22:27
- * @since JDK 1.8
- */
-@Slf4j
-@Service
-public class RouteRequestImpl implements RouteRequest {
-
- @Autowired
- private OkHttpClient okHttpClient;
-
- @Value("${cim.route.url}")
- private String routeUrl;
-
- @Autowired
- private EchoService echoService;
-
-
- @Autowired
- private AppConfiguration appConfiguration;
-
- @Override
- public void sendGroupMsg(ChatReqVO chatReqVO) throws Exception {
- RouteApi routeApi = RpcProxyManager.create(RouteApi.class, routeUrl, okHttpClient);
- routeApi.groupRoute(chatReqVO);
- }
-
- @Override
- public void sendP2PMsg(P2PReqVO p2PReqVO) throws Exception {
- RouteApi routeApi = RpcProxyManager.create(RouteApi.class, routeUrl, okHttpClient);
- BaseResponse response = routeApi.p2pRoute(p2PReqVO);
- // account offline.
- if (response.getCode().equals(StatusEnum.OFF_LINE.getCode())) {
- log.error(p2PReqVO.getReceiveUserId() + ":" + StatusEnum.OFF_LINE.getMessage());
- }
- }
-
- @Override
- public CIMServerResVO getCIMServer(LoginReqVO loginReqVO) throws Exception {
-
- RouteApi routeApi = RpcProxyManager.create(RouteApi.class, routeUrl, okHttpClient);
- BaseResponse cimServerResVO = routeApi.login(loginReqVO);
-
- // repeat fail
- if (!cimServerResVO.getCode().equals(StatusEnum.SUCCESS.getCode())) {
- echoService.echo(cimServerResVO.getMessage());
-
- // when client in reConnect state, could not exit.
- if (ContextHolder.getReconnect()) {
- echoService.echo("###{}###", StatusEnum.RECONNECT_FAIL.getMessage());
- throw new CIMException(StatusEnum.RECONNECT_FAIL);
- }
-
- System.exit(-1);
- }
-
-
- return cimServerResVO.getDataBody();
- }
-
- @Override
- public Set onlineUsers() throws Exception {
- RouteApi routeApi = RpcProxyManager.create(RouteApi.class, routeUrl, okHttpClient);
- BaseResponse> onlineUsersResVO = routeApi.onlineUser();
- return onlineUsersResVO.getDataBody();
- }
-
- @Override
- public void offLine() throws Exception {
- RouteApi routeApi = RpcProxyManager.create(RouteApi.class, routeUrl, okHttpClient);
- ChatReqVO vo = new ChatReqVO(appConfiguration.getUserId(), "offLine");
- routeApi.offLine(vo);
- }
-}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/CloseAIModelCommand.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/CloseAIModelCommand.java
index 37b85f55..318ff476 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/CloseAIModelCommand.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/CloseAIModelCommand.java
@@ -1,9 +1,9 @@
package com.crossoverjie.cim.client.service.impl.command;
-import com.crossoverjie.cim.client.service.EchoService;
+import com.crossoverjie.cim.client.sdk.Event;
import com.crossoverjie.cim.client.service.InnerCommand;
import com.crossoverjie.cim.client.service.MsgHandle;
-import org.springframework.beans.factory.annotation.Autowired;
+import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
/**
@@ -17,16 +17,16 @@
public class CloseAIModelCommand implements InnerCommand {
- @Autowired
+ @Resource
private MsgHandle msgHandle ;
- @Autowired
- private EchoService echoService ;
+ @Resource
+ private Event event ;
@Override
public void process(String msg) {
msgHandle.closeAIModel();
- echoService.echo("\033[31;4m" + "。゚(゚´ω`゚)゚。 AI 下线了!" + "\033[0m");
+ event.info("\033[31;4m" + "。゚(゚´ω`゚)゚。 AI 下线了!" + "\033[0m");
}
}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/DelayMsgCommand.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/DelayMsgCommand.java
index c5f37e4a..b69a1c5b 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/DelayMsgCommand.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/DelayMsgCommand.java
@@ -1,11 +1,11 @@
package com.crossoverjie.cim.client.service.impl.command;
-import com.crossoverjie.cim.client.service.EchoService;
+import com.crossoverjie.cim.client.sdk.Event;
import com.crossoverjie.cim.client.service.InnerCommand;
import com.crossoverjie.cim.client.service.MsgHandle;
import com.crossoverjie.cim.common.data.construct.RingBufferWheel;
-import com.vdurmont.emoji.EmojiParser;
-import org.springframework.beans.factory.annotation.Autowired;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
@@ -16,31 +16,32 @@
* @since JDK 1.8
*/
@Service
+@Slf4j
public class DelayMsgCommand implements InnerCommand {
- @Autowired
- private EchoService echoService ;
+ @Resource
+ private Event event;
- @Autowired
+ @Resource
private MsgHandle msgHandle ;
- @Autowired
+ @Resource
private RingBufferWheel ringBufferWheel ;
@Override
public void process(String msg) {
if (msg.split(" ").length <=2){
- echoService.echo("incorrect commond, :delay [msg] [delayTime]") ;
+ event.info("incorrect commond, :delay [msg] [delayTime]") ;
return ;
}
String message = msg.split(" ")[1] ;
- Integer delayTime = Integer.valueOf(msg.split(" ")[2]);
+ int delayTime = Integer.parseInt(msg.split(" ")[2]);
RingBufferWheel.Task task = new DelayMsgJob(message) ;
task.setKey(delayTime);
ringBufferWheel.addTask(task);
- echoService.echo(EmojiParser.parseToUnicode(msg));
+ event.info(msg);
}
@@ -55,7 +56,11 @@ public DelayMsgJob(String msg) {
@Override
public void run() {
- msgHandle.sendMsg(msg);
+ try {
+ msgHandle.sendMsg(msg);
+ } catch (Exception e) {
+ log.error("Delay message send error",e);
+ }
}
}
}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/EchoInfoCommand.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/EchoInfoCommand.java
index b6891692..e5bce11b 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/EchoInfoCommand.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/EchoInfoCommand.java
@@ -1,9 +1,9 @@
package com.crossoverjie.cim.client.service.impl.command;
-import com.crossoverjie.cim.client.service.EchoService;
+import com.crossoverjie.cim.client.sdk.Client;
+import com.crossoverjie.cim.client.sdk.Event;
import com.crossoverjie.cim.client.service.InnerCommand;
-import com.crossoverjie.cim.client.service.impl.ClientInfo;
-import org.springframework.beans.factory.annotation.Autowired;
+import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
/**
@@ -16,16 +16,16 @@
@Service
public class EchoInfoCommand implements InnerCommand {
- @Autowired
- private ClientInfo clientInfo;
+ @Resource
+ private Client client;
- @Autowired
- private EchoService echoService ;
+ @Resource
+ private Event event ;
@Override
public void process(String msg) {
- echoService.echo("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
- echoService.echo("client info={}", clientInfo.get().getUserName());
- echoService.echo("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
+ event.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
+ event.info("client info={}", client.getAuth());
+ event.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
}
}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/EmojiCommand.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/EmojiCommand.java
index 943a743a..b94e93de 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/EmojiCommand.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/EmojiCommand.java
@@ -1,14 +1,13 @@
package com.crossoverjie.cim.client.service.impl.command;
-import com.crossoverjie.cim.client.service.EchoService;
+import com.crossoverjie.cim.client.sdk.Event;
import com.crossoverjie.cim.client.service.InnerCommand;
import com.vdurmont.emoji.Emoji;
import com.vdurmont.emoji.EmojiManager;
import com.vdurmont.emoji.EmojiParser;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
+import jakarta.annotation.Resource;
import java.util.List;
+import org.springframework.stereotype.Service;
/**
* Function:
@@ -20,24 +19,24 @@
@Service
public class EmojiCommand implements InnerCommand {
- @Autowired
- private EchoService echoService ;
+ @Resource
+ private Event event ;
@Override
public void process(String msg) {
if (msg.split(" ").length <=1){
- echoService.echo("incorrect commond, :emoji [option]") ;
+ event.info("incorrect commond, :emoji [option]") ;
return ;
}
String value = msg.split(" ")[1];
if (value != null) {
- Integer index = Integer.parseInt(value);
+ int index = Integer.parseInt(value);
List all = (List) EmojiManager.getAll();
all = all.subList(5 * index, 5 * index + 5);
for (Emoji emoji : all) {
- echoService.echo(EmojiParser.parseToAliases(emoji.getUnicode()) + "--->" + emoji.getUnicode());
+ event.info(EmojiParser.parseToAliases(emoji.getUnicode()) + "--->" + emoji.getUnicode());
}
}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/PrefixSearchCommand.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/PrefixSearchCommand.java
index ab1a06e8..a0082bf8 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/PrefixSearchCommand.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/PrefixSearchCommand.java
@@ -1,14 +1,14 @@
package com.crossoverjie.cim.client.service.impl.command;
-import com.crossoverjie.cim.client.service.EchoService;
+import com.crossoverjie.cim.client.sdk.Client;
+import com.crossoverjie.cim.client.sdk.Event;
import com.crossoverjie.cim.client.service.InnerCommand;
-import com.crossoverjie.cim.client.service.RouteRequest;
import com.crossoverjie.cim.common.data.construct.TrieTree;
import com.crossoverjie.cim.common.pojo.CIMUserInfo;
+import jakarta.annotation.Resource;
import java.util.List;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
@@ -23,15 +23,15 @@
public class PrefixSearchCommand implements InnerCommand {
- @Autowired
- private RouteRequest routeRequest ;
- @Autowired
- private EchoService echoService ;
+ @Resource
+ private Client client ;
+ @Resource
+ private Event event ;
@Override
public void process(String msg) {
try {
- Set onlineUsers = routeRequest.onlineUsers();
+ Set onlineUsers = client.getOnlineUser();
TrieTree trieTree = new TrieTree();
for (CIMUserInfo onlineUser : onlineUsers) {
trieTree.insert(onlineUser.getUserName());
@@ -43,7 +43,7 @@ public void process(String msg) {
for (String res : list) {
res = res.replace(key, "\033[31;4m" + key + "\033[0m");
- echoService.echo(res) ;
+ event.info(res) ;
}
} catch (Exception e) {
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/PrintAllCommand.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/PrintAllCommand.java
index 11807815..f597ba24 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/PrintAllCommand.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/PrintAllCommand.java
@@ -1,12 +1,11 @@
package com.crossoverjie.cim.client.service.impl.command;
-import com.crossoverjie.cim.client.service.EchoService;
+import com.crossoverjie.cim.client.sdk.Event;
import com.crossoverjie.cim.client.service.InnerCommand;
import com.crossoverjie.cim.common.enums.SystemCommandEnum;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
+import jakarta.annotation.Resource;
import java.util.Map;
+import org.springframework.stereotype.Service;
/**
* Function:
@@ -19,18 +18,18 @@
public class PrintAllCommand implements InnerCommand {
- @Autowired
- private EchoService echoService ;
+ @Resource
+ private Event event ;
@Override
public void process(String msg) {
Map allStatusCode = SystemCommandEnum.getAllStatusCode();
- echoService.echo("====================================");
+ event.info("====================================");
for (Map.Entry stringStringEntry : allStatusCode.entrySet()) {
String key = stringStringEntry.getKey();
String value = stringStringEntry.getValue();
- echoService.echo(key + "----->" + value);
+ event.info(key + "----->" + value);
}
- echoService.echo("====================================");
+ event.info("====================================");
}
}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/PrintOnlineUsersCommand.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/PrintOnlineUsersCommand.java
index 4fe9992b..f1cb2300 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/PrintOnlineUsersCommand.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/PrintOnlineUsersCommand.java
@@ -1,12 +1,12 @@
package com.crossoverjie.cim.client.service.impl.command;
-import com.crossoverjie.cim.client.service.EchoService;
+import com.crossoverjie.cim.client.sdk.Client;
+import com.crossoverjie.cim.client.sdk.Event;
import com.crossoverjie.cim.client.service.InnerCommand;
-import com.crossoverjie.cim.client.service.RouteRequest;
import com.crossoverjie.cim.common.pojo.CIMUserInfo;
+import jakarta.annotation.Resource;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
@@ -20,22 +20,22 @@
@Service
public class PrintOnlineUsersCommand implements InnerCommand {
- @Autowired
- private RouteRequest routeRequest ;
+ @Resource
+ private Client client ;
- @Autowired
- private EchoService echoService ;
+ @Resource
+ private Event event ;
@Override
public void process(String msg) {
try {
- Set onlineUsers = routeRequest.onlineUsers();
+ Set onlineUsers = client.getOnlineUser();
- echoService.echo("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
+ event.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
for (CIMUserInfo onlineUser : onlineUsers) {
- echoService.echo("userId={}=====userName={}",onlineUser.getUserId(),onlineUser.getUserName());
+ event.info("userId={}=====userName={}",onlineUser.getUserId(),onlineUser.getUserName());
}
- echoService.echo("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
+ event.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
} catch (Exception e) {
log.error("Exception", e);
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/QueryHistoryCommand.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/QueryHistoryCommand.java
index 61314460..586b0d3f 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/QueryHistoryCommand.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/QueryHistoryCommand.java
@@ -1,10 +1,10 @@
package com.crossoverjie.cim.client.service.impl.command;
-import com.crossoverjie.cim.client.service.EchoService;
+import com.crossoverjie.cim.client.sdk.Event;
import com.crossoverjie.cim.client.service.InnerCommand;
import com.crossoverjie.cim.client.service.MsgLogger;
+import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
@@ -18,11 +18,11 @@
@Service
public class QueryHistoryCommand implements InnerCommand {
- @Autowired
+ @Resource
private MsgLogger msgLogger ;
- @Autowired
- private EchoService echoService ;
+ @Resource
+ private Event event ;
@Override
public void process(String msg) {
@@ -31,6 +31,6 @@ public void process(String msg) {
return;
}
String res = msgLogger.query(split[1]);
- echoService.echo(res);
+ event.info(res);
}
}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/ShutDownCommand.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/ShutDownCommand.java
index 758187ec..608a44b9 100644
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/ShutDownCommand.java
+++ b/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/ShutDownCommand.java
@@ -1,20 +1,17 @@
package com.crossoverjie.cim.client.service.impl.command;
-import com.crossoverjie.cim.client.client.CIMClient;
-import com.crossoverjie.cim.client.service.EchoService;
+import com.crossoverjie.cim.client.sdk.Client;
+import com.crossoverjie.cim.client.sdk.Event;
import com.crossoverjie.cim.client.service.InnerCommand;
import com.crossoverjie.cim.client.service.MsgLogger;
-import com.crossoverjie.cim.client.service.RouteRequest;
-import com.crossoverjie.cim.client.service.ShutDownMsg;
+import com.crossoverjie.cim.client.service.ShutDownSign;
import com.crossoverjie.cim.common.data.construct.RingBufferWheel;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
-import org.springframework.stereotype.Service;
-
import jakarta.annotation.Resource;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.stereotype.Service;
/**
* Function:
@@ -28,45 +25,40 @@
@ConditionalOnWebApplication
public class ShutDownCommand implements InnerCommand {
- @Autowired
- private RouteRequest routeRequest ;
+ @Resource
+ private Client cimClient;
- @Autowired
- private CIMClient cimClient;
-
- @Autowired
+ @Resource
private MsgLogger msgLogger;
@Resource(name = "callBackThreadPool")
private ThreadPoolExecutor callBackExecutor;
- @Autowired
- private EchoService echoService ;
-
+ @Resource
+ private Event event;
- @Autowired
- private ShutDownMsg shutDownMsg ;
+ @Resource
+ private ShutDownSign shutDownSign;
- @Autowired
+ @Resource
private RingBufferWheel ringBufferWheel ;
@Override
public void process(String msg) throws Exception {
- echoService.echo("cim client closing...");
- shutDownMsg.shutdown();
- routeRequest.offLine();
+ event.info("cim client closing...");
+ cimClient.close();
+ shutDownSign.shutdown();
msgLogger.stop();
callBackExecutor.shutdown();
ringBufferWheel.stop(false);
try {
while (!callBackExecutor.awaitTermination(1, TimeUnit.SECONDS)) {
- echoService.echo("thread pool closing");
+ event.info("thread pool closing");
}
- cimClient.close();
} catch (Exception e) {
log.error("exception", e);
}
- echoService.echo("cim close success!");
+ event.info("cim close success!");
System.exit(0);
}
}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/thread/ContextHolder.java b/cim-client/src/main/java/com/crossoverjie/cim/client/thread/ContextHolder.java
deleted file mode 100644
index 5867e9b7..00000000
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/thread/ContextHolder.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.crossoverjie.cim.client.thread;
-
-/**
- * Function: Something about of client runtime sign.
- *
- * @author crossoverJie
- * Date: 2020-04-13 02:10
- * @since JDK 1.8
- */
-public class ContextHolder {
- private static final ThreadLocal IS_RECONNECT = new ThreadLocal<>() ;
-
- public static void setReconnect(boolean reconnect){
- IS_RECONNECT.set(reconnect);
- }
-
- public static Boolean getReconnect(){
- return IS_RECONNECT.get() ;
- }
-
- public static void clear(){
- IS_RECONNECT.remove();
- }
-}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/thread/ReConnectJob.java b/cim-client/src/main/java/com/crossoverjie/cim/client/thread/ReConnectJob.java
deleted file mode 100644
index 3e148e7f..00000000
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/thread/ReConnectJob.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.crossoverjie.cim.client.thread;
-
-import com.crossoverjie.cim.client.service.impl.ClientHeartBeatHandlerImpl;
-import com.crossoverjie.cim.client.util.SpringBeanFactory;
-import com.crossoverjie.cim.common.kit.HeartBeatHandler;
-import io.netty.channel.ChannelHandlerContext;
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * Function:
- *
- * @author crossoverJie
- * Date: 2019-01-20 21:35
- * @since JDK 1.8
- */
-@Slf4j
-public class ReConnectJob implements Runnable {
-
-
- private ChannelHandlerContext context ;
-
- private HeartBeatHandler heartBeatHandler ;
-
- public ReConnectJob(ChannelHandlerContext context) {
- this.context = context;
- this.heartBeatHandler = SpringBeanFactory.getBean(ClientHeartBeatHandlerImpl.class) ;
- }
-
- @Override
- public void run() {
- try {
- heartBeatHandler.process(context);
- } catch (Exception e) {
- log.error("Exception",e);
- }
- }
-}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/vo/req/GoogleProtocolVO.java b/cim-client/src/main/java/com/crossoverjie/cim/client/vo/req/GoogleProtocolVO.java
deleted file mode 100644
index a25a0afb..00000000
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/vo/req/GoogleProtocolVO.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.crossoverjie.cim.client.vo.req;
-
-import com.crossoverjie.cim.common.req.BaseRequest;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-
-/**
- * Function: Google Protocol 编解码发送
- *
- * @author crossoverJie
- * Date: 2018/05/21 15:56
- * @since JDK 1.8
- */
-public class GoogleProtocolVO extends BaseRequest {
- @NotNull(message = "requestId 不能为空")
- @Schema(requiredMode = Schema.RequiredMode.REQUIRED , description = "requestId", example = "123")
- private Integer requestId ;
-
- @NotNull(message = "msg 不能为空")
- @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "msg", example = "hello")
- private String msg ;
-
- public String getMsg() {
- return msg;
- }
-
- public void setMsg(String msg) {
- this.msg = msg;
- }
-
- public Integer getRequestId() {
- return requestId;
- }
-
- public void setRequestId(Integer requestId) {
- this.requestId = requestId;
- }
-
- @Override
- public String toString() {
- return "GoogleProtocolVO{" +
- "requestId=" + requestId +
- ", msg='" + msg + '\'' +
- "} " + super.toString();
- }
-}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/vo/req/StringReqVO.java b/cim-client/src/main/java/com/crossoverjie/cim/client/vo/req/StringReqVO.java
deleted file mode 100644
index 36cc491a..00000000
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/vo/req/StringReqVO.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.crossoverjie.cim.client.vo.req;
-
-import com.crossoverjie.cim.common.req.BaseRequest;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-
-/**
- * Function:
- *
- * @author crossoverJie
- * Date: 2018/05/21 15:56
- * @since JDK 1.8
- */
-public class StringReqVO extends BaseRequest {
-
- @NotNull(message = "msg 不能为空")
- @Schema(requiredMode = Schema.RequiredMode.REQUIRED , description = "msg", example = "hello")
- private String msg ;
-
- public String getMsg() {
- return msg;
- }
-
- public void setMsg(String msg) {
- this.msg = msg;
- }
-}
diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/vo/res/SendMsgResVO.java b/cim-client/src/main/java/com/crossoverjie/cim/client/vo/res/SendMsgResVO.java
deleted file mode 100644
index 69160275..00000000
--- a/cim-client/src/main/java/com/crossoverjie/cim/client/vo/res/SendMsgResVO.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.crossoverjie.cim.client.vo.res;
-
-/**
- * Function:
- *
- * @author crossoverJie
- * Date: 2017/6/26 15:43
- * @since JDK 1.8
- */
-public class SendMsgResVO {
- private String msg ;
-
- public String getMsg() {
- return msg;
- }
-
- public void setMsg(String msg) {
- this.msg = msg;
- }
-}
diff --git a/cim-client/src/test/java/com/crossoverjie/cim/client/service/impl/EchoServiceImplTest.java b/cim-client/src/test/java/com/crossoverjie/cim/client/service/impl/EchoServiceImplTest.java
deleted file mode 100644
index 0aa19f7c..00000000
--- a/cim-client/src/test/java/com/crossoverjie/cim/client/service/impl/EchoServiceImplTest.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.crossoverjie.cim.client.service.impl;
-
-import com.crossoverjie.cim.client.CIMClientApplication;
-import com.crossoverjie.cim.client.service.EchoService;
-import jakarta.annotation.Resource;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.junit4.SpringRunner;
-
-
-@SpringBootTest(classes = CIMClientApplication.class)
-@RunWith(SpringRunner.class)
-public class EchoServiceImplTest {
-
- @Resource
- private EchoService echoService;
-
- @Test
- public void echo(){
- echoService.echo("test");
- }
-}
\ No newline at end of file
diff --git a/cim-client/src/test/java/com/crossoverjie/cim/server/test/RouteTest.java b/cim-client/src/test/java/com/crossoverjie/cim/server/test/RouteTest.java
deleted file mode 100644
index 16760c4b..00000000
--- a/cim-client/src/test/java/com/crossoverjie/cim/server/test/RouteTest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.crossoverjie.cim.server.test;
-
-import com.crossoverjie.cim.client.service.RouteRequest;
-import com.crossoverjie.cim.route.api.vo.req.LoginReqVO;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Value;
-
-/**
- * Function:
- *
- * @author crossoverJie
- * Date: 2018/12/23 22:39
- * @since JDK 1.8
- */
-//@SpringBootTest(classes = CIMClientApplication.class)
-//@RunWith(SpringRunner.class)
-@Slf4j
-public class RouteTest {
-
- @Value("${cim.user.id}")
- private long userId;
-
- @Value("${cim.user.userName}")
- private String userName;
-
-// @Autowired
- private RouteRequest routeRequest ;
-
- // TODO: 2024/8/31 Integration test
-// @Test
- public void test() throws Exception {
- LoginReqVO vo = new LoginReqVO(userId,userName) ;
- com.crossoverjie.cim.route.api.vo.res.CIMServerResVO cimServer = routeRequest.getCIMServer(vo);
- log.info("cimServer=[{}]",cimServer.toString());
- }
-}
diff --git a/cim-client/src/test/resources/application.yaml b/cim-client/src/test/resources/application.yaml
index 65f0430f..6ea8c51e 100644
--- a/cim-client/src/test/resources/application.yaml
+++ b/cim-client/src/test/resources/application.yaml
@@ -31,7 +31,7 @@ cim:
callback:
thread:
queue:
- size: 2
+ size: 1000
pool:
size: 2
heartbeat:
diff --git a/cim-common/pom.xml b/cim-common/pom.xml
index cb9420ad..b03b38ba 100644
--- a/cim-common/pom.xml
+++ b/cim-common/pom.xml
@@ -64,6 +64,12 @@
junit
+
+ org.junit.vintage
+ junit-vintage-engine
+ test
+
+
org.junit.jupiter
junit-jupiter
diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/data/construct/SortArrayMap.java b/cim-common/src/main/java/com/crossoverjie/cim/common/data/construct/SortArrayMap.java
index a46aaa6b..5e5e48a7 100644
--- a/cim-common/src/main/java/com/crossoverjie/cim/common/data/construct/SortArrayMap.java
+++ b/cim-common/src/main/java/com/crossoverjie/cim/common/data/construct/SortArrayMap.java
@@ -1,7 +1,13 @@
package com.crossoverjie.cim.common.data.construct;
+import java.util.AbstractMap;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import org.apache.curator.shaded.com.google.common.collect.Sets;
/**
* Function:根据 key 排序的 Map
@@ -10,7 +16,7 @@
* Date: 2019-02-25 18:17
* @since JDK 1.8
*/
-public class SortArrayMap {
+public class SortArrayMap extends AbstractMap {
/**
* 核心数组
@@ -39,6 +45,13 @@ public void add(Long key, String value) {
buckets[size++] = node;
}
+ public SortArrayMap remove(String value){
+ List list = new ArrayList<>(Arrays.asList(buckets));
+ list.removeIf(next -> next != null && next.value.equals(value));
+ buckets = list.toArray(new Node[0]);
+ return this;
+ }
+
/**
* 校验是否需要扩容
* @param size
@@ -105,6 +118,27 @@ public void clear(){
size = 0 ;
}
+ @Override
+ public Set> entrySet() {
+ Set> set = Sets.newHashSet();
+ for (Node bucket : buckets) {
+ set.add(new SimpleEntry<>(String.valueOf(bucket.key), bucket.value));
+ }
+ return set;
+ }
+
+ @Override
+ public Set keySet() {
+ Set set = Sets.newHashSet();
+ for (Node bucket : buckets) {
+ if (bucket == null){
+ continue;
+ }
+ set.add(bucket.value);
+ }
+ return set;
+ }
+
/**
* 数据节点
*/
diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/enums/StatusEnum.java b/cim-common/src/main/java/com/crossoverjie/cim/common/enums/StatusEnum.java
index bff3eaa6..9f93622f 100644
--- a/cim-common/src/main/java/com/crossoverjie/cim/common/enums/StatusEnum.java
+++ b/cim-common/src/main/java/com/crossoverjie/cim/common/enums/StatusEnum.java
@@ -25,7 +25,7 @@ public enum StatusEnum {
REQUEST_LIMIT("6000", "请求限流"),
/** 账号不在线 */
- OFF_LINE("7000", "你选择的账号不在线,请重新选择!"),
+ OFF_LINE("7000", "You selected user is offline!, please try again later!"),
SERVER_NOT_AVAILABLE("7100", "cim server is not available, please try again later!"),
diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/metastore/ZkMetaStoreImpl.java b/cim-common/src/main/java/com/crossoverjie/cim/common/metastore/ZkMetaStoreImpl.java
index d5420cb4..91f141f9 100644
--- a/cim-common/src/main/java/com/crossoverjie/cim/common/metastore/ZkMetaStoreImpl.java
+++ b/cim-common/src/main/java/com/crossoverjie/cim/common/metastore/ZkMetaStoreImpl.java
@@ -74,7 +74,7 @@ public void addServer(String ip, int cimServerPort, int httpPort) throws Excepti
@Override
public void listenServerList(ChildListener childListener) throws Exception {
client.subscribeChildChanges(ROOT, (parentPath, currentChildren) -> {
- log.info("Clear and update local cache parentPath=[{}],currentChildren=[{}]", parentPath, currentChildren.toString());
+ log.info("Clear and update local cache parentPath=[{}],current server list=[{}]", parentPath, currentChildren.toString());
childListener.childChanged(parentPath, currentChildren);
// TODO: 2024/8/19 maybe can reuse currentChildren.
diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/req/BaseRequest.java b/cim-common/src/main/java/com/crossoverjie/cim/common/req/BaseRequest.java
index 1d1a5aea..2300b10a 100644
--- a/cim-common/src/main/java/com/crossoverjie/cim/common/req/BaseRequest.java
+++ b/cim-common/src/main/java/com/crossoverjie/cim/common/req/BaseRequest.java
@@ -12,10 +12,10 @@
public class BaseRequest {
- @Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED, description = "唯一请求号", example = "1234567890")
+ @Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED, description = "reqNo", example = "1234567890")
private String reqNo;
- @Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED, description = "当前请求的时间戳", example = "0")
+ @Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED, description = "timestamp", example = "0")
private int timeStamp;
diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/RouteHandle.java b/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/RouteHandle.java
index 74a8a0d8..a68113ef 100644
--- a/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/RouteHandle.java
+++ b/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/RouteHandle.java
@@ -1,5 +1,6 @@
package com.crossoverjie.cim.common.route.algorithm;
+import com.crossoverjie.cim.common.pojo.RouteInfo;
import java.util.List;
/**
@@ -17,5 +18,8 @@ public interface RouteHandle {
* @param key
* @return
*/
+ // TODO: 2024/9/13 Use List instead of List to make the code more type-safe
String routeServer(List values,String key) ;
+
+ List removeExpireServer(RouteInfo routeInfo);
}
diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/AbstractConsistentHash.java b/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/AbstractConsistentHash.java
index 34e92b6d..015993aa 100644
--- a/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/AbstractConsistentHash.java
+++ b/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/AbstractConsistentHash.java
@@ -4,6 +4,7 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
+import java.util.Map;
/**
* Function:一致性 hash 算法抽象类
@@ -21,6 +22,13 @@ public abstract class AbstractConsistentHash {
*/
protected abstract void add(long key,String value);
+ /**
+ * remove node
+ * @param value node
+ * @return current data
+ */
+ protected abstract Map remove(String value);
+
/**
* Clear old data in the structure
*/
diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/ConsistentHashHandle.java b/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/ConsistentHashHandle.java
index b0eb4621..9a58a2d5 100644
--- a/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/ConsistentHashHandle.java
+++ b/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/ConsistentHashHandle.java
@@ -1,8 +1,12 @@
package com.crossoverjie.cim.common.route.algorithm.consistenthash;
+import com.crossoverjie.cim.common.pojo.RouteInfo;
import com.crossoverjie.cim.common.route.algorithm.RouteHandle;
+import com.crossoverjie.cim.common.util.RouteInfoParseUtil;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* Function:
@@ -22,4 +26,10 @@ public void setHash(AbstractConsistentHash hash) {
public String routeServer(List values, String key) {
return hash.process(values, key);
}
+
+ @Override
+ public List removeExpireServer(RouteInfo routeInfo) {
+ Map remove = hash.remove(RouteInfoParseUtil.parse(routeInfo));
+ return new ArrayList<>(remove.keySet());
+ }
}
diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/SortArrayMapConsistentHash.java b/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/SortArrayMapConsistentHash.java
index 73c6f011..a328a9f8 100644
--- a/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/SortArrayMapConsistentHash.java
+++ b/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/SortArrayMapConsistentHash.java
@@ -3,6 +3,9 @@
import com.crossoverjie.cim.common.data.construct.SortArrayMap;
import com.google.common.annotations.VisibleForTesting;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.TreeMap;
/**
@@ -25,11 +28,17 @@ public class SortArrayMapConsistentHash extends AbstractConsistentHash {
public void add(long key, String value) {
for (int i = 0; i < VIRTUAL_NODE_SIZE; i++) {
Long hash = super.hash("vir" + key + i);
- sortArrayMap.add(hash,value);
+ sortArrayMap.add(hash, value);
}
sortArrayMap.add(key, value);
}
+ @Override
+ protected Map remove(String value) {
+ sortArrayMap = sortArrayMap.remove(value);
+ return sortArrayMap;
+ }
+
@Override
public void sort() {
sortArrayMap.sort();
@@ -52,7 +61,6 @@ protected void clear() {
@Override
public String getFirstNodeValue(String value) {
long hash = super.hash(value);
- System.out.println("value=" + value + " hash = " + hash);
return sortArrayMap.firstNodeValue(hash);
}
diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/TreeMapConsistentHash.java b/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/TreeMapConsistentHash.java
index 650ba766..ac330a3e 100644
--- a/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/TreeMapConsistentHash.java
+++ b/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/TreeMapConsistentHash.java
@@ -4,6 +4,10 @@
import com.crossoverjie.cim.common.exception.CIMException;
import com.google.common.annotations.VisibleForTesting;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
@@ -15,7 +19,7 @@
* @since JDK 1.8
*/
public class TreeMapConsistentHash extends AbstractConsistentHash {
- private TreeMap treeMap = new TreeMap() ;
+ private final TreeMap treeMap = new TreeMap() ;
/**
* 虚拟节点数量
@@ -31,6 +35,16 @@ public void add(long key, String value) {
treeMap.put(key, value);
}
+ @Override
+ protected Map remove(String value) {
+ treeMap.entrySet().removeIf(next -> next.getValue().equals(value));
+ Map result = new HashMap<>(treeMap.entrySet().size());
+ for (Map.Entry longStringEntry : treeMap.entrySet()) {
+ result.put(longStringEntry.getValue(),"");
+ }
+ return result;
+ }
+
@Override
protected void clear() {
treeMap.clear();
@@ -48,7 +62,6 @@ public TreeMap getTreeMap() {
@Override
public String getFirstNodeValue(String value) {
long hash = super.hash(value);
- System.out.println("value=" + value + " hash = " + hash);
SortedMap last = treeMap.tailMap(hash);
if (!last.isEmpty()) {
return last.get(last.firstKey());
diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/loop/LoopHandle.java b/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/loop/LoopHandle.java
index 1d0f4859..eabca47c 100644
--- a/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/loop/LoopHandle.java
+++ b/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/loop/LoopHandle.java
@@ -2,8 +2,11 @@
import com.crossoverjie.cim.common.enums.StatusEnum;
import com.crossoverjie.cim.common.exception.CIMException;
+import com.crossoverjie.cim.common.pojo.RouteInfo;
import com.crossoverjie.cim.common.route.algorithm.RouteHandle;
+import com.crossoverjie.cim.common.util.RouteInfoParseUtil;
+import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
@@ -15,13 +18,16 @@
* @since JDK 1.8
*/
public class LoopHandle implements RouteHandle {
- private AtomicLong index = new AtomicLong();
+ private final AtomicLong index = new AtomicLong();
+
+ private List values;
@Override
public String routeServer(List values,String key) {
if (values.size() == 0) {
throw new CIMException(StatusEnum.SERVER_NOT_AVAILABLE) ;
}
+ this.values = values;
Long position = index.incrementAndGet() % values.size();
if (position < 0) {
position = 0L;
@@ -29,4 +35,11 @@ public String routeServer(List values,String key) {
return values.get(position.intValue());
}
+
+ @Override
+ public List removeExpireServer(RouteInfo routeInfo) {
+ String parse = RouteInfoParseUtil.parse(routeInfo);
+ values.removeIf(next -> next.equals(parse));
+ return values;
+ }
}
diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/random/RandomHandle.java b/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/random/RandomHandle.java
index 55e742fe..6775410c 100644
--- a/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/random/RandomHandle.java
+++ b/cim-common/src/main/java/com/crossoverjie/cim/common/route/algorithm/random/RandomHandle.java
@@ -2,8 +2,10 @@
import com.crossoverjie.cim.common.enums.StatusEnum;
import com.crossoverjie.cim.common.exception.CIMException;
+import com.crossoverjie.cim.common.pojo.RouteInfo;
import com.crossoverjie.cim.common.route.algorithm.RouteHandle;
+import com.crossoverjie.cim.common.util.RouteInfoParseUtil;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
@@ -16,14 +18,23 @@
*/
public class RandomHandle implements RouteHandle {
+ private List values;
@Override
public String routeServer(List values, String key) {
int size = values.size();
if (size == 0) {
throw new CIMException(StatusEnum.SERVER_NOT_AVAILABLE) ;
}
+ this.values = values;
int offset = ThreadLocalRandom.current().nextInt(size);
return values.get(offset);
}
+
+ @Override
+ public List removeExpireServer(RouteInfo routeInfo) {
+ String parse = RouteInfoParseUtil.parse(routeInfo);
+ values.removeIf(next -> next.equals(parse));
+ return values;
+ }
}
diff --git a/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/ConsistentHashHandleTest.java b/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/ConsistentHashHandleTest.java
new file mode 100644
index 00000000..60190610
--- /dev/null
+++ b/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/ConsistentHashHandleTest.java
@@ -0,0 +1,52 @@
+package com.crossoverjie.cim.common.route.algorithm.consistenthash;
+
+import static org.junit.jupiter.api.Assertions.*;
+import com.crossoverjie.cim.common.pojo.RouteInfo;
+import com.crossoverjie.cim.common.route.algorithm.RouteHandle;
+import com.crossoverjie.cim.common.util.RouteInfoParseUtil;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class ConsistentHashHandleTest {
+
+ @Test
+ void removeSortMapExpireServer() {
+ ConsistentHashHandle routeHandle = new ConsistentHashHandle();
+ routeHandle.setHash(new SortArrayMapConsistentHash());
+ List strings = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ var routeInfo = new RouteInfo("127.0.0." + i, 1000, 2000);
+ strings.add(RouteInfoParseUtil.parse(routeInfo));
+ }
+ RouteInfo routeInfo = new RouteInfo("127.0.0.9", 1000, 2000);
+ String parse = RouteInfoParseUtil.parse(routeInfo);
+ String r1 = routeHandle.routeServer(strings, parse);
+ String r2 = routeHandle.routeServer(strings, parse);
+ assertEquals(r1, r2);
+
+ List list = routeHandle.removeExpireServer(routeInfo);
+ boolean contains = list.contains(parse);
+ assertFalse(contains);
+ }
+ @Test
+ void removeTreeMapExpireServer() {
+ ConsistentHashHandle routeHandle = new ConsistentHashHandle();
+ routeHandle.setHash(new TreeMapConsistentHash());
+ List strings = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ var routeInfo = new RouteInfo("127.0.0." + i, 1000, 2000);
+ strings.add(RouteInfoParseUtil.parse(routeInfo));
+ }
+ RouteInfo routeInfo = new RouteInfo("127.0.0.9", 1000, 2000);
+ String parse = RouteInfoParseUtil.parse(routeInfo);
+ String r1 = routeHandle.routeServer(strings, parse);
+ String r2 = routeHandle.routeServer(strings, parse);
+ assertEquals(r1, r2);
+
+ List list = routeHandle.removeExpireServer(routeInfo);
+ boolean contains = list.contains(parse);
+ assertFalse(contains);
+ }
+}
\ No newline at end of file
diff --git a/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/loop/LoopHandleTest.java b/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/loop/LoopHandleTest.java
new file mode 100644
index 00000000..273f15ba
--- /dev/null
+++ b/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/loop/LoopHandleTest.java
@@ -0,0 +1,30 @@
+package com.crossoverjie.cim.common.route.algorithm.loop;
+
+import static org.junit.jupiter.api.Assertions.*;
+import com.crossoverjie.cim.common.pojo.RouteInfo;
+import com.crossoverjie.cim.common.route.algorithm.RouteHandle;
+import com.crossoverjie.cim.common.util.RouteInfoParseUtil;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+class LoopHandleTest {
+
+ @Test
+ void removeExpireServer() {
+ RouteHandle routeHandle = new LoopHandle();
+ List strings = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ var routeInfo = new RouteInfo("127.0.0." + i, 1000, 2000);
+ strings.add(RouteInfoParseUtil.parse(routeInfo));
+ }
+ String zs = routeHandle.routeServer(strings, "zs");
+ String zs2 = routeHandle.routeServer(strings, "zs");
+ assertNotEquals(zs, zs2);
+
+ RouteInfo remove = new RouteInfo("127.0.0.0", 1000, 2000);
+ List list = routeHandle.removeExpireServer(remove);
+ boolean contains = list.contains(RouteInfoParseUtil.parse(remove));
+ assertFalse(contains);
+ }
+}
\ No newline at end of file
diff --git a/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/random/RandomHandleTest.java b/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/random/RandomHandleTest.java
new file mode 100644
index 00000000..2d3af96c
--- /dev/null
+++ b/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/random/RandomHandleTest.java
@@ -0,0 +1,29 @@
+package com.crossoverjie.cim.common.route.algorithm.random;
+
+import static org.junit.jupiter.api.Assertions.*;
+import com.crossoverjie.cim.common.pojo.RouteInfo;
+import com.crossoverjie.cim.common.route.algorithm.RouteHandle;
+import com.crossoverjie.cim.common.route.algorithm.loop.LoopHandle;
+import com.crossoverjie.cim.common.util.RouteInfoParseUtil;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+class RandomHandleTest {
+ @Test
+ void removeExpireServer() {
+ RouteHandle routeHandle = new RandomHandle();
+ List strings = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ var routeInfo = new RouteInfo("127.0.0." + i, 1000, 2000);
+ strings.add(RouteInfoParseUtil.parse(routeInfo));
+ }
+ routeHandle.routeServer(strings, "zs");
+
+ RouteInfo remove = new RouteInfo("127.0.0.0", 1000, 2000);
+ List list = routeHandle.removeExpireServer(remove);
+ boolean contains = list.contains(RouteInfoParseUtil.parse(remove));
+ assertFalse(contains);
+ }
+
+}
\ No newline at end of file
diff --git a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/config/BeanConfig.java b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/config/BeanConfig.java
index 02f7c0a3..81b05c90 100644
--- a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/config/BeanConfig.java
+++ b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/config/BeanConfig.java
@@ -49,7 +49,7 @@ public MetaStore metaStore() throws Exception {
.retryPolicy(retryPolicy)
.build());
metaStore.listenServerList((root, currentChildren) -> {
- log.info("Server list change, root=[{}], currentChildren=[{}]", root, currentChildren);
+ log.info("Server list change, root=[{}], current server list=[{}]", root, currentChildren);
});
return metaStore;
}
diff --git a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/controller/RouteController.java b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/controller/RouteController.java
index bdcaa8e0..e655473e 100644
--- a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/controller/RouteController.java
+++ b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/controller/RouteController.java
@@ -36,7 +36,7 @@
* Function:
*
* @author crossoverJie
- * Date: 22/05/2018 14:46
+ * Date: 22/05/2018 14:46
* @since JDK 1.8
*/
@Slf4j
@@ -51,13 +51,13 @@ public class RouteController implements RouteApi {
private AccountService accountService;
@Autowired
- private UserInfoCacheService userInfoCacheService ;
+ private UserInfoCacheService userInfoCacheService;
@Autowired
- private CommonBizService commonBizService ;
+ private CommonBizService commonBizService;
- @Autowired
- private RouteHandle routeHandle ;
+ @Resource
+ private RouteHandle routeHandle;
@Operation(summary = "群聊 API")
@RequestMapping(value = "groupRoute", method = RequestMethod.POST)
@@ -73,16 +73,16 @@ public BaseResponse groupRoute(@RequestBody ChatReqVO groupReqVO) thro
for (Map.Entry cimServerResVOEntry : serverResVOMap.entrySet()) {
Long userId = cimServerResVOEntry.getKey();
CIMServerResVO cimServerResVO = cimServerResVOEntry.getValue();
- if (userId.equals(groupReqVO.getUserId())){
+ if (userId.equals(groupReqVO.getUserId())) {
//过滤掉自己
CIMUserInfo cimUserInfo = userInfoCacheService.loadUserInfoByUserId(groupReqVO.getUserId());
- log.warn("过滤掉了发送者 userId={}",cimUserInfo.toString());
+ log.warn("过滤掉了发送者 userId={}", cimUserInfo.toString());
continue;
}
//推送消息
- ChatReqVO chatVO = new ChatReqVO(userId,groupReqVO.getMsg()) ;
- accountService.pushMsg(cimServerResVO ,groupReqVO.getUserId(),chatVO);
+ ChatReqVO chatVO = new ChatReqVO(userId, groupReqVO.getMsg());
+ accountService.pushMsg(cimServerResVO, groupReqVO.getUserId(), chatVO);
}
@@ -98,7 +98,7 @@ public BaseResponse groupRoute(@RequestBody ChatReqVO groupReqVO) thro
* @param p2pRequest
* @return
*/
- @Operation(summary = "私聊 API")
+ @Operation(summary = "私聊 API")
@RequestMapping(value = "p2pRoute", method = RequestMethod.POST)
@ResponseBody()
@Override
@@ -110,13 +110,13 @@ public BaseResponse p2pRoute(@RequestBody P2PReqVO p2pRequest) throws
CIMServerResVO cimServerResVO = accountService.loadRouteRelatedByUserId(p2pRequest.getReceiveUserId());
//p2pRequest.getReceiveUserId()==>消息接收者的 userID
- ChatReqVO chatVO = new ChatReqVO(p2pRequest.getReceiveUserId(),p2pRequest.getMsg()) ;
- accountService.pushMsg(cimServerResVO ,p2pRequest.getUserId(),chatVO);
+ ChatReqVO chatVO = new ChatReqVO(p2pRequest.getReceiveUserId(), p2pRequest.getMsg());
+ accountService.pushMsg(cimServerResVO, p2pRequest.getUserId(), chatVO);
res.setCode(StatusEnum.SUCCESS.getCode());
res.setMessage(StatusEnum.SUCCESS.getMessage());
- }catch (CIMException e){
+ } catch (CIMException e) {
res.setCode(e.getErrorCode());
res.setMessage(e.getErrorMessage());
}
@@ -128,7 +128,7 @@ public BaseResponse p2pRoute(@RequestBody P2PReqVO p2pRequest) throws
@RequestMapping(value = "offLine", method = RequestMethod.POST)
@ResponseBody()
@Override
- public BaseResponse offLine(@RequestBody ChatReqVO groupReqVO) throws Exception {
+ public BaseResponse offLine(@RequestBody ChatReqVO groupReqVO) {
BaseResponse res = new BaseResponse();
CIMUserInfo cimUserInfo = userInfoCacheService.loadUserInfoByUserId(groupReqVO.getUserId());
@@ -152,29 +152,30 @@ public BaseResponse offLine(@RequestBody ChatReqVO groupReqVO) throws
@Override
public BaseResponse login(@RequestBody LoginReqVO loginReqVO) throws Exception {
BaseResponse res = new BaseResponse();
+ //登录校验
+ StatusEnum status = accountService.login(loginReqVO);
+ res.setCode(status.getCode());
+ res.setMessage(status.getMessage());
+ if (status != StatusEnum.SUCCESS) {
+ return res;
+ }
// check server available
Set availableServerList = metaStore.getAvailableServerList();
- String server = routeHandle.routeServer(List.copyOf(availableServerList),String.valueOf(loginReqVO.getUserId()));
- log.info("userName=[{}] route server info=[{}]", loginReqVO.getUserName(), server);
+ String key = String.valueOf(loginReqVO.getUserId());
+ String server =
+ routeHandle.routeServer(List.copyOf(availableServerList), key);
+ log.info("userInfo=[{}] route server info=[{}]", loginReqVO, server);
RouteInfo routeInfo = RouteInfoParseUtil.parse(server);
- commonBizService.checkServerAvailable(routeInfo);
+ routeInfo = commonBizService.checkServerAvailable(routeInfo, key);
- //登录校验
- StatusEnum status = accountService.login(loginReqVO);
- if (status == StatusEnum.SUCCESS) {
-
- //保存路由信息
- accountService.saveRouteInfo(loginReqVO,server);
-
- CIMServerResVO vo = new CIMServerResVO(routeInfo.getIp(), routeInfo.getCimServerPort(), routeInfo.getHttpPort());
- res.setDataBody(vo);
-
- }
- res.setCode(status.getCode());
- res.setMessage(status.getMessage());
+ //保存路由信息
+ accountService.saveRouteInfo(loginReqVO, server);
+ CIMServerResVO vo =
+ new CIMServerResVO(routeInfo.getIp(), routeInfo.getCimServerPort(), routeInfo.getHttpPort());
+ res.setDataBody(vo);
return res;
}
@@ -187,7 +188,8 @@ public BaseResponse login(@RequestBody LoginReqVO loginReqVO) th
@RequestMapping(value = "registerAccount", method = RequestMethod.POST)
@ResponseBody()
@Override
- public BaseResponse registerAccount(@RequestBody RegisterInfoReqVO registerInfoReqVO) throws Exception {
+ public BaseResponse registerAccount(@RequestBody RegisterInfoReqVO registerInfoReqVO)
+ throws Exception {
BaseResponse res = new BaseResponse();
long userId = System.currentTimeMillis();
@@ -213,7 +215,7 @@ public BaseResponse> onlineUser() throws Exception {
BaseResponse> res = new BaseResponse();
Set cimUserInfos = userInfoCacheService.onlineUser();
- res.setDataBody(cimUserInfos) ;
+ res.setDataBody(cimUserInfos);
res.setCode(StatusEnum.SUCCESS.getCode());
res.setMessage(StatusEnum.SUCCESS.getMessage());
return res;
diff --git a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/kit/NetAddressIsReachable.java b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/kit/NetAddressIsReachable.java
index aba715d2..df0458c0 100644
--- a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/kit/NetAddressIsReachable.java
+++ b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/kit/NetAddressIsReachable.java
@@ -3,6 +3,7 @@
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
+import lombok.extern.slf4j.Slf4j;
/**
* Function:
@@ -11,6 +12,7 @@
* Date: 2020-04-12 20:32
* @since JDK 1.8
*/
+@Slf4j
public class NetAddressIsReachable {
/**
@@ -32,7 +34,7 @@ public static boolean checkAddressReachable(String address, int port, int timeou
try {
socket.close();
} catch (IOException e) {
- return false ;
+ log.warn("close socket error", e);
}
}
}
diff --git a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/AccountService.java b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/AccountService.java
index dd78b419..227d37f0 100644
--- a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/AccountService.java
+++ b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/AccountService.java
@@ -69,5 +69,5 @@ public interface AccountService {
* @param userId 下线用户ID
* @throws Exception
*/
- void offLine(Long userId) throws Exception;
+ void offLine(Long userId);
}
diff --git a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/CommonBizService.java b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/CommonBizService.java
index 9cde9adc..f63d0992 100644
--- a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/CommonBizService.java
+++ b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/CommonBizService.java
@@ -4,8 +4,11 @@
import com.crossoverjie.cim.common.exception.CIMException;
import com.crossoverjie.cim.common.metastore.MetaStore;
import com.crossoverjie.cim.common.pojo.RouteInfo;
+import com.crossoverjie.cim.common.route.algorithm.RouteHandle;
+import com.crossoverjie.cim.common.util.RouteInfoParseUtil;
import com.crossoverjie.cim.route.kit.NetAddressIsReachable;
import jakarta.annotation.Resource;
+import java.util.List;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@@ -23,20 +26,22 @@ public class CommonBizService {
@Resource
- private MetaStore metaStore ;
+ private RouteHandle routeHandle;
/**
- * check ip and port
+ * check ip and port, and return a new server if the current server is not available
* @param routeInfo
*/
@SneakyThrows
- public void checkServerAvailable(RouteInfo routeInfo){
+ public RouteInfo checkServerAvailable(RouteInfo routeInfo, String userId){
boolean reachable = NetAddressIsReachable.checkAddressReachable(routeInfo.getIp(), routeInfo.getCimServerPort(), 1000);
if (!reachable) {
- log.error("ip={}, port={} are not available", routeInfo.getIp(), routeInfo.getCimServerPort());
- metaStore.rebuildCache();
- throw new CIMException(StatusEnum.SERVER_NOT_AVAILABLE) ;
+ log.error("ip={}, port={} are not available, remove it.", routeInfo.getIp(), routeInfo.getCimServerPort());
+ List list = routeHandle.removeExpireServer(routeInfo);
+ String routeServer = routeHandle.routeServer(list, userId);
+ log.info("Reselect new server:[{}]", routeServer);
+ return RouteInfoParseUtil.parse(routeServer);
}
-
+ return routeInfo;
}
}
diff --git a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/UserInfoCacheService.java b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/UserInfoCacheService.java
index 1e11619d..a76e7da0 100644
--- a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/UserInfoCacheService.java
+++ b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/UserInfoCacheService.java
@@ -32,9 +32,8 @@ public interface UserInfoCacheService {
/**
* 清除用户的登录状态
* @param userId
- * @throws Exception
*/
- void removeLoginStatus(Long userId) throws Exception ;
+ void removeLoginStatus(Long userId) ;
/**
diff --git a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/impl/AccountServiceRedisImpl.java b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/impl/AccountServiceRedisImpl.java
index e05299a6..4b10699c 100644
--- a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/impl/AccountServiceRedisImpl.java
+++ b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/impl/AccountServiceRedisImpl.java
@@ -85,7 +85,7 @@ public StatusEnum login(LoginReqVO loginReqVO) throws Exception {
//登录成功,保存登录状态
boolean status = userInfoCacheService.saveAndCheckUserLoginStatus(loginReqVO.getUserId());
- if (status == false) {
+ if (!status) {
//重复登录
return StatusEnum.REPEAT_LOGIN;
}
@@ -156,7 +156,7 @@ public void pushMsg(CIMServerResVO cimServerResVO, long sendUserId, ChatReqVO gr
}
@Override
- public void offLine(Long userId) throws Exception {
+ public void offLine(Long userId) {
// TODO: 2019-01-21 改为一个原子命令,以防数据一致性
diff --git a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/impl/UserInfoCacheServiceImpl.java b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/impl/UserInfoCacheServiceImpl.java
index 87025534..cfbe4b97 100644
--- a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/impl/UserInfoCacheServiceImpl.java
+++ b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/impl/UserInfoCacheServiceImpl.java
@@ -64,7 +64,7 @@ public boolean saveAndCheckUserLoginStatus(Long userId) throws Exception {
}
@Override
- public void removeLoginStatus(Long userId) throws Exception {
+ public void removeLoginStatus(Long userId) {
redisTemplate.opsForSet().remove(LOGIN_STATUS_PREFIX,userId.toString()) ;
}
diff --git a/cim-integration-test/pom.xml b/cim-integration-test/pom.xml
index 7017b58e..cdd5f4f9 100644
--- a/cim-integration-test/pom.xml
+++ b/cim-integration-test/pom.xml
@@ -32,13 +32,6 @@
compile
-
- com.crossoverjie.netty
- cim-client
- ${project.version}
- compile
-
-
org.junit.vintage
junit-vintage-engine
@@ -55,6 +48,7 @@
org.testcontainers
testcontainers
+ compile
org.testcontainers
@@ -64,29 +58,21 @@
org.springframework.boot
spring-boot-starter-test
- test
+ compile
com.clever-cloud
testcontainers-zookeeper
+ compile
com.redis
testcontainers-redis
- test
+ compile
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
-
-
\ No newline at end of file
diff --git a/cim-integration-test/src/test/java/com/crossoverjie/cim/route/AbstractRouteBaseTest.java b/cim-integration-test/src/main/java/com/crossoverjie/cim/client/sdk/route/AbstractRouteBaseTest.java
similarity index 78%
rename from cim-integration-test/src/test/java/com/crossoverjie/cim/route/AbstractRouteBaseTest.java
rename to cim-integration-test/src/main/java/com/crossoverjie/cim/client/sdk/route/AbstractRouteBaseTest.java
index ee5a9564..8f025507 100644
--- a/cim-integration-test/src/test/java/com/crossoverjie/cim/route/AbstractRouteBaseTest.java
+++ b/cim-integration-test/src/main/java/com/crossoverjie/cim/client/sdk/route/AbstractRouteBaseTest.java
@@ -1,15 +1,14 @@
-package com.crossoverjie.cim.route;
+package com.crossoverjie.cim.client.sdk.route;
-import com.clevercloud.testcontainers.zookeeper.ZooKeeperContainer;
import com.crossoverjie.cim.common.res.BaseResponse;
+import com.crossoverjie.cim.client.sdk.server.AbstractServerBaseTest;
+import com.crossoverjie.cim.route.RouteApplication;
import com.crossoverjie.cim.route.api.RouteApi;
import com.crossoverjie.cim.route.api.vo.req.RegisterInfoReqVO;
import com.crossoverjie.cim.route.api.vo.res.RegisterInfoResVO;
-import com.crossoverjie.cim.server.AbstractServerBaseTest;
-import com.crossoverjie.cim.server.CIMServerApplication;
import com.redis.testcontainers.RedisContainer;
-import org.junit.Before;
import org.springframework.boot.SpringApplication;
+import org.springframework.context.ConfigurableApplicationContext;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.utility.DockerImageName;
@@ -18,6 +17,7 @@ public abstract class AbstractRouteBaseTest extends AbstractServerBaseTest {
@Container
RedisContainer redis = new RedisContainer(DockerImageName.parse("redis:7.4.0"));
+ private ConfigurableApplicationContext run;
public void startRoute() {
redis.start();
SpringApplication route = new SpringApplication(RouteApplication.class);
@@ -27,7 +27,13 @@ public void startRoute() {
"--app.zk.addr=" + super.getZookeeperAddr(),
};
route.setAdditionalProfiles("route");
- route.run(args);
+ run = route.run(args);
+ }
+
+ public void close(){
+ super.close();
+ redis.close();
+ run.close();
}
public Long registerAccount(String userName) throws Exception {
@@ -39,4 +45,4 @@ public Long registerAccount(String userName) throws Exception {
routeApi.registerAccount(reqVO);
return account.getDataBody().getUserId();
}
-}
+}
\ No newline at end of file
diff --git a/cim-integration-test/src/main/java/com/crossoverjie/cim/client/sdk/server/AbstractServerBaseTest.java b/cim-integration-test/src/main/java/com/crossoverjie/cim/client/sdk/server/AbstractServerBaseTest.java
new file mode 100644
index 00000000..d274d498
--- /dev/null
+++ b/cim-integration-test/src/main/java/com/crossoverjie/cim/client/sdk/server/AbstractServerBaseTest.java
@@ -0,0 +1,77 @@
+package com.crossoverjie.cim.client.sdk.server;
+
+import com.clevercloud.testcontainers.zookeeper.ZooKeeperContainer;
+import com.crossoverjie.cim.server.CIMServerApplication;
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+import lombok.Getter;
+import org.springframework.boot.SpringApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.utility.DockerImageName;
+
+public abstract class AbstractServerBaseTest {
+ private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName
+ .parse("zookeeper")
+ .withTag("3.9.2");
+
+ private static final Duration DEFAULT_STARTUP_TIMEOUT = Duration.ofSeconds(60);
+
+ @Container
+ public final ZooKeeperContainer
+ zooKeeperContainer = new ZooKeeperContainer(DEFAULT_IMAGE_NAME, DEFAULT_STARTUP_TIMEOUT);
+
+ @Getter
+ private String zookeeperAddr;
+
+ private ConfigurableApplicationContext singleRun;
+ public void starSingleServer() {
+ zooKeeperContainer.start();
+ zookeeperAddr = String.format("%s:%d", zooKeeperContainer.getHost(), zooKeeperContainer.getMappedPort(ZooKeeperContainer.DEFAULT_CLIENT_PORT));
+ SpringApplication server = new SpringApplication(CIMServerApplication.class);
+ singleRun = server.run("--app.zk.addr=" + zookeeperAddr);
+ }
+ public void stopSingle(){
+ singleRun.close();
+ }
+
+ private final Map runMap = new HashMap<>(2);
+ public void startTwoServer() {
+ if (!zooKeeperContainer.isRunning()){
+ zooKeeperContainer.start();
+ }
+ zookeeperAddr = String.format("%s:%d", zooKeeperContainer.getHost(), zooKeeperContainer.getMappedPort(ZooKeeperContainer.DEFAULT_CLIENT_PORT));
+ SpringApplication server = new SpringApplication(CIMServerApplication.class);
+ String[] args1 = new String[]{
+ "--cim.server.port=11211",
+ "--server.port=8081",
+ "--app.zk.addr=" + zookeeperAddr,
+ };
+ ConfigurableApplicationContext run1 = server.run(args1);
+ runMap.put(Integer.parseInt("11211"), run1);
+
+
+ SpringApplication server2 = new SpringApplication(CIMServerApplication.class);
+ String[] args2 = new String[]{
+ "--cim.server.port=11212",
+ "--server.port=8082",
+ "--app.zk.addr=" + zookeeperAddr,
+ };
+ ConfigurableApplicationContext run2 = server2.run(args2);
+ runMap.put(Integer.parseInt("11212"), run2);
+ }
+
+ public void stopServer(Integer port) {
+ runMap.get(port).close();
+ runMap.remove(port);
+ }
+ public void stopTwoServer() {
+ runMap.forEach((k,v) -> v.close());
+ }
+
+ public void close(){
+ zooKeeperContainer.close();
+ }
+
+}
diff --git a/cim-integration-test/src/test/java/com/crossoverjie/cim/client/ClientTest.java b/cim-integration-test/src/test/java/com/crossoverjie/cim/client/ClientTest.java
deleted file mode 100644
index 6b183fb0..00000000
--- a/cim-integration-test/src/test/java/com/crossoverjie/cim/client/ClientTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.crossoverjie.cim.client;
-
-import com.crossoverjie.cim.client.service.MsgHandle;
-import com.crossoverjie.cim.client.util.SpringBeanFactory;
-import com.crossoverjie.cim.common.pojo.CIMUserInfo;
-import com.crossoverjie.cim.route.AbstractRouteBaseTest;
-import java.util.concurrent.TimeUnit;
-import lombok.extern.slf4j.Slf4j;
-import org.junit.Test;
-import org.springframework.boot.SpringApplication;
-
-@Slf4j
-public class ClientTest extends AbstractRouteBaseTest {
-
-
- private void login(String userName, int port) throws Exception {
- Long userId = super.registerAccount(userName);
- SpringApplication client = new SpringApplication(CIMClientApplication.class);
- client.setAdditionalProfiles("client");
- String[] args = new String[]{
- "--server.port=" + port,
- "--cim.user.id=" + userId,
- "--cim.user.userName=" + userName
- };
- client.run(args);
- }
-
- @Test
- public void olu() throws Exception {
- super.startServer();
- super.startRoute();
- this.login("crossoverJie", 8082);
- this.login("cj", 8182);
- MsgHandle msgHandle = SpringBeanFactory.getBean(MsgHandle.class);
- msgHandle.innerCommand(":olu");
- msgHandle.sendMsg("hello");
- TimeUnit.SECONDS.sleep(1);
- }
-
-
-}
diff --git a/cim-integration-test/src/test/java/com/crossoverjie/cim/server/AbstractServerBaseTest.java b/cim-integration-test/src/test/java/com/crossoverjie/cim/server/AbstractServerBaseTest.java
deleted file mode 100644
index efd7a73a..00000000
--- a/cim-integration-test/src/test/java/com/crossoverjie/cim/server/AbstractServerBaseTest.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.crossoverjie.cim.server;
-
-import com.clevercloud.testcontainers.zookeeper.ZooKeeperContainer;
-import java.time.Duration;
-import lombok.Getter;
-import org.junit.Before;
-import org.springframework.boot.SpringApplication;
-import org.testcontainers.junit.jupiter.Container;
-import org.testcontainers.utility.DockerImageName;
-
-public abstract class AbstractServerBaseTest {
-
- private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName
- .parse("zookeeper")
- .withTag("3.9.2");
-
- private static final Duration DEFAULT_STARTUP_TIMEOUT = Duration.ofSeconds(60);
-
- @Container
- public final ZooKeeperContainer
- zooKeeperContainer = new ZooKeeperContainer(DEFAULT_IMAGE_NAME, DEFAULT_STARTUP_TIMEOUT);
-
- @Getter
- private String zookeeperAddr;
-
- public void startServer() {
- zooKeeperContainer.start();
- zookeeperAddr = String.format("%s:%d", zooKeeperContainer.getHost(), zooKeeperContainer.getMappedPort(ZooKeeperContainer.DEFAULT_CLIENT_PORT));
- SpringApplication server = new SpringApplication(CIMServerApplication.class);
- server.run("--app.zk.addr=" + zookeeperAddr);
- }
-
-}
diff --git a/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/RouteApi.java b/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/RouteApi.java
index 665e2276..12bf8a06 100644
--- a/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/RouteApi.java
+++ b/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/RouteApi.java
@@ -46,7 +46,7 @@ public interface RouteApi {
* @return
* @throws Exception
*/
- BaseResponse offLine(ChatReqVO groupReqVO) throws Exception;
+ BaseResponse offLine(ChatReqVO groupReqVO);
/**
* Login account
diff --git a/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/P2PReqVO.java b/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/P2PReqVO.java
index d0d0c62c..98a615fd 100644
--- a/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/P2PReqVO.java
+++ b/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/P2PReqVO.java
@@ -4,29 +4,31 @@
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
+import lombok.Builder;
/**
- * Function: 单聊请求
+ * Function: P2P request
*
* @author crossoverJie
* Date: 2018/05/21 15:56
* @since JDK 1.8
*/
+@Builder
public class P2PReqVO extends BaseRequest {
- @NotNull(message = "userId 不能为空")
- @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "消息发送者的 userId", example = "1545574049323")
+ @NotNull(message = "userId can't be null")
+ @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "current send userId", example = "1545574049323")
private Long userId ;
- @NotNull(message = "userId 不能为空")
- @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "消息接收者的 userId", example = "1545574049323")
+ @NotNull(message = "userId can't be null")
+ @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "message received userId", example = "1545574049323")
private Long receiveUserId ;
- @NotNull(message = "msg 不能为空")
+ @NotNull(message = "msg can't be null")
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "msg", example = "hello")
private String msg ;
diff --git a/cim-server/src/main/java/com/crossoverjie/cim/server/handle/CIMServerHandle.java b/cim-server/src/main/java/com/crossoverjie/cim/server/handle/CIMServerHandle.java
index e7e616f1..0b01708e 100644
--- a/cim-server/src/main/java/com/crossoverjie/cim/server/handle/CIMServerHandle.java
+++ b/cim-server/src/main/java/com/crossoverjie/cim/server/handle/CIMServerHandle.java
@@ -59,7 +59,7 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc
IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
if (idleStateEvent.state() == IdleState.READER_IDLE) {
- log.info("定时检测客户端端是否存活");
+ log.info("!!READER_IDLE!!");
HeartBeatHandler heartBeatHandler = SpringBeanFactory.getBean(ServerHeartBeatHandlerImpl.class) ;
heartBeatHandler.process(ctx) ;
diff --git a/cim-server/src/main/java/com/crossoverjie/cim/server/kit/RouteHandler.java b/cim-server/src/main/java/com/crossoverjie/cim/server/kit/RouteHandler.java
index c959293a..3ca18a4b 100644
--- a/cim-server/src/main/java/com/crossoverjie/cim/server/kit/RouteHandler.java
+++ b/cim-server/src/main/java/com/crossoverjie/cim/server/kit/RouteHandler.java
@@ -37,9 +37,8 @@ public class RouteHandler {
*
* @param userInfo
* @param channel
- * @throws IOException
*/
- public void userOffLine(CIMUserInfo userInfo, NioSocketChannel channel) throws IOException {
+ public void userOffLine(CIMUserInfo userInfo, NioSocketChannel channel) {
if (userInfo != null) {
log.info("Account [{}] offline", userInfo.getUserName());
SessionSocketHolder.removeSession(userInfo.getUserId());
@@ -60,11 +59,7 @@ public void userOffLine(CIMUserInfo userInfo, NioSocketChannel channel) throws I
public void clearRouteInfo(CIMUserInfo userInfo) {
RouteApi routeApi = RpcProxyManager.create(RouteApi.class, configuration.getRouteUrl(), okHttpClient);
ChatReqVO vo = new ChatReqVO(userInfo.getUserId(), userInfo.getUserName());
- try {
- routeApi.offLine(vo);
- } catch (Exception e){
- log.error("Exception",e);
- }
+ routeApi.offLine(vo);
}
}
diff --git a/pom.xml b/pom.xml
index 2978d98f..68898199 100644
--- a/pom.xml
+++ b/pom.xml
@@ -36,6 +36,7 @@
cim-rout-api
cim-server-api
cim-integration-test
+ cim-client-sdk
@@ -117,19 +118,24 @@
com.crossoverjie.netty
cim-common
- 1.0.0-SNAPSHOT
+ ${project.version}
+
+
+ com.crossoverjie.netty
+ cim-client-sdk
+ ${project.version}
com.crossoverjie.netty
cim-rout-api
- 1.0.0-SNAPSHOT
+ ${project.version}
com.crossoverjie.netty
cim-server-api
- 1.0.0-SNAPSHOT
+ ${project.version}
diff --git a/protocol/BaseRequestProto.proto b/protocol/BaseRequestProto.proto
index dde24203..56e1a568 100644
--- a/protocol/BaseRequestProto.proto
+++ b/protocol/BaseRequestProto.proto
@@ -6,6 +6,7 @@ option java_package = "com.crossoverjie.cim.common.protocol";
option java_outer_classname = "CIMRequestProto";
message CIMReqProtocol {
+ // todo source user info
required int64 requestId = 2;
required string reqMsg = 1;
required int32 type = 3;