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;