diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index e849a6f4..9d55a6c6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,28 +1,28 @@ --- name: Bug report about: Create a report to help us improve -title: '提交之前请查看是否有相关 issue,谢谢。' +title: 'Please check if there are similar issues before submitting' labels: '' assignees: '' --- -**Bug 描述** +**Bug Description** A clear and concise description of what the bug is. -**复现** -复现步骤: +**Reproduce** +step: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error -**预期结果** +**Expected results** A clear and concise description of what you expected to happen. -**截图** +**Screenshot** If applicable, add screenshots to help explain your problem. -**附加信息** +**Additional Information** Add any other context about the problem here. diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 00000000..26ac135c --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,21 @@ +# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Java CI with Maven + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + uses: ./.github/workflows/reusable_run_tests.yml + secrets: + codecov_token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/reusable_run_tests.yml b/.github/workflows/reusable_run_tests.yml new file mode 100644 index 00000000..42ce8c34 --- /dev/null +++ b/.github/workflows/reusable_run_tests.yml @@ -0,0 +1,29 @@ +name: A reusable workflow to build and run the unit test suite + +on: + workflow_call: + secrets: + codecov_token: + required: true + workflow_dispatch: + +jobs: + build_and_test: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + - name: Build with Maven + run: mvn -B package --file pom.xml + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + verbose: true \ No newline at end of file diff --git a/README.md b/README.md index fc4a8938..c069c84e 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@
+[![codecov](https://codecov.io/gh/crossoverJie/cim/graph/badge.svg?token=oW5Gd1oKmf)](https://codecov.io/gh/crossoverJie/cim) [![Build Status](https://img.shields.io/badge/cim-cross--im-brightgreen.svg)](https://github.com/crossoverJie/cim) [![](https://badge.juejin.im/entry/5c2c000e6fb9a049f5713e26/likes.svg?style=flat-square)](https://juejin.im/post/5c2bffdc51882509181395d7) @@ -16,27 +17,26 @@ # V2.0 - [x] Upgrade to JDK17 & springboot3.0 +- [x] Client SDK - [ ] Client use [picocli](https://picocli.info/) instead of springboot. -- [ ] Supports binary client(build with golang). -- [ ] Support integration testing. +- [x] Support integration testing. - [ ] Integrate OpenTelemetry . - [ ] Support single node startup(Contains no components). - [ ] Third-party components support replacement(Redis/Zookeeper, etc.). - [ ] Support web client(websocket). - [ ] Support docker container. - [ ] Support kubernetes operation. +- [ ] Supports binary client(build with golang). -## 介绍 +## Introduction -`CIM(CROSS-IM)` 一款面向开发者的 `IM(即时通讯)`系统;同时提供了一些组件帮助开发者构建一款属于自己可水平扩展的 `IM` 。 +`CIM(CROSS-IM)` is an `IM (instant messaging)` system for developers; it also provides some components to help developers build their own scalable `IM`. +Using `CIM`, you can achieve the following requirements: +- `IM` instant messaging system. +- Message push middleware for `APP`. +- Message middleware for `IOT` massive connection scenarios. -借助 `CIM` 你可以实现以下需求: - -- `IM` 即时通讯系统。 -- 适用于 `APP` 的消息推送中间件。 -- `IOT` 海量连接场景中的消息透传中间件。 - -> 在使用或开发过程中有任何疑问都可[联系我](#联系作者)。 +> If you have any questions during use or development, you can [contact me](#联系作者). ## 视频演示 @@ -47,6 +47,7 @@ | [群聊](https://youtu.be/_9a4lIkQ5_o) [私聊](https://youtu.be/kfEfQFPLBTQ) | [群聊](https://www.bilibili.com/video/av39405501) [私聊](https://www.bilibili.com/video/av39405821) | | | +![demo.gif](pic/demo.gif) ## TODO LIST @@ -60,45 +61,46 @@ * [x] 服务端自动剔除离线客户端 * [x] 客户端自动重连 * [x] [延时消息](#延时消息) +* [x] SDK 开发包 * [ ] 分组群聊 -* [ ] SDK 开发包 * [ ] 离线消息 -* [ ] 协议支持消息加密 +* [ ] 消息加密 -## 系统架构 +## Architecture -![](https://i.loli.net/2019/05/08/5cd1d45a156f1.jpg) +![](pic/architecture.png) -- `CIM` 中的各个组件均采用 `SpringBoot` 构建。 -- 采用 `Netty` 构建底层通信。 -- `Redis` 存放各个客户端的路由信息、账号信息、在线状态等。 -- `Zookeeper` 用于 `IM-server` 服务的注册与发现。 +- Each component in `CIM` is built using `SpringBoot` + - Client build with [cim-client-sdk](https://github.com/crossoverJie/cim/tree/master/cim-client-sdk) +- Use `Netty` to build the underlying communication. +- `MetaStore` is used for registration and discovery of `IM-server` services. ### cim-server +IM server is used to receive client connections, message forwarding, message push, etc. +Support cluster deployment. -`IM` 服务端;用于接收 `client` 连接、消息透传、消息推送等功能。 - -**支持集群部署。** +### cim-route -### cim-forward-route - -消息路由服务器;用于处理消息路由、消息转发、用户登录、用户下线以及一些运营工具(获取在线用户数等)。 +Route server; used to process message routing, message forwarding, user login, user offline, and some operation tools (get the number of online users, etc.). ### cim-client +IM client terminal, a command can be started and initiated to communicate with others (group chat, private chat). -`IM` 客户端;给用户使用的消息终端,一个命令即可启动并向其他人发起通讯(群聊、私聊)。 - -## 流程图 +## Flow Chart -![](https://i.loli.net/2019/05/08/5cd1d45b982b3.jpg) +![](https://s2.loli.net/2024/10/13/8teMn7BSa5VWuvi.png) -- 客户端向 `route` 发起登录。 -- 登录成功从 `Zookeeper` 中选择可用 `IM-server` 返回给客户端,并保存登录、路由信息到 `Redis`。 -- 客户端向 `IM-server` 发起长连接,成功后保持心跳。 -- 客户端下线时通过 `route` 清除状态信息。 +- Server register to `MetaStore` +- Route subscribe `MetaStore` +- Client login to Route + - Route get Server info from `MetaStore` +- Client open connection to Server +- Client1 send message to Route +- Route select Server and forward message to Server +- Server push message to Client2 ## 快速启动 @@ -106,14 +108,16 @@ 首先需要安装 `Zookeeper、Redis` 并保证网络通畅。 ```shell -docker run --name zookeeper -d -p 2181:2181 zookeeper -docker run --rm --name redis -d -p 6379:6379 redis +docker run --rm --name zookeeper -d -p 2181:2181 zookeeper:3.9.2 +docker run --rm --name redis -d -p 6379:6379 redis:7.4.0 ``` ```shell git clone https://github.com/crossoverJie/cim.git cd cim -mvn -Dmaven.test.skip=true clean package +mvn clean package -DskipTests=true +cd cim-server && cim-client && cim-forward-route +mvn clean package spring-boot:repackage -DskipTests=true ``` ### 部署 IM-server(cim-server) @@ -272,7 +276,7 @@ java -jar cim-client-1.0.0-SNAPSHOT.jar --server.port=8084 --cim.user.id=上方 :delay delayMsg 10 ``` -![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7brppmokqg30gn07gafj.gif) +![](pic/delay.gif) ## 联系作者 diff --git a/cim-client-sdk/README.md b/cim-client-sdk/README.md new file mode 100644 index 00000000..bac1d54c --- /dev/null +++ b/cim-client-sdk/README.md @@ -0,0 +1,25 @@ + +```java + var auth1 = ClientConfigurationData.Auth.builder() + .userId(id) + .userName(cj) + .build(); + + Client client1 = Client.builder() + .auth(auth1) + .routeUrl(routeUrl) + .build(); + + 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()); + + // send msg + String msg = "hello"; + client1.sendGroup(msg); + + // get oline user + Set onlineUser = client1.getOnlineUser(); +``` \ No newline at end of file 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..783b3b3c --- /dev/null +++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/ClientBuilder.java @@ -0,0 +1,26 @@ +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 com.crossoverjie.cim.client.sdk.io.backoff.BackoffStrategy; +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); + ClientBuilder backoffStrategy(BackoffStrategy backoffStrategy); +} 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..c86c0419 --- /dev/null +++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/impl/ClientBuilderImpl.java @@ -0,0 +1,89 @@ +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.client.sdk.io.backoff.BackoffStrategy; +import com.crossoverjie.cim.common.util.StringUtil; +import java.util.concurrent.ThreadPoolExecutor; +import okhttp3.OkHttpClient; + +public class ClientBuilderImpl implements ClientBuilder { + + + private final 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; + } + + @Override + public ClientBuilder backoffStrategy(BackoffStrategy backoffStrategy) { + this.conf.setBackoffStrategy(backoffStrategy); + 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..b76883c9 --- /dev/null +++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/impl/ClientConfigurationData.java @@ -0,0 +1,54 @@ +package com.crossoverjie.cim.client.sdk.impl; + +import com.crossoverjie.cim.client.sdk.Event; +import com.crossoverjie.cim.client.sdk.io.backoff.BackoffStrategy; +import com.crossoverjie.cim.client.sdk.io.MessageListener; +import com.crossoverjie.cim.client.sdk.io.backoff.RandomBackoff; +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, properties, 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; + + @JsonIgnore + private BackoffStrategy backoffStrategy = new RandomBackoff(); +} 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..5071f627 --- /dev/null +++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/impl/ClientImpl.java @@ -0,0 +1,256 @@ +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.exception.CIMException; +import com.crossoverjie.cim.common.kit.HeartBeatHandler; +import com.crossoverjie.cim.common.pojo.CIMUserInfo; +import com.crossoverjie.cim.common.protocol.BaseCommand; +import com.crossoverjie.cim.common.protocol.Request; +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 Request 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 = Request.newBuilder() + .setRequestId(this.conf.getAuth().getUserId()) + .setReqMsg("ping") + .setCmd(BaseCommand.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() { + Request login = Request.newBuilder() + .setRequestId(this.conf.getAuth().getUserId()) + .setReqMsg(this.conf.getAuth().getUserName()) + .setCmd(BaseCommand.LOGIN_REQUEST) + .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...."); + + this.conf.getBackoffStrategy().runBackoff(); + + // 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..f49de355 --- /dev/null +++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/CIMClientHandle.java @@ -0,0 +1,82 @@ +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.protocol.BaseCommand; +import com.crossoverjie.cim.common.protocol.Response; +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, Response msg) { + + + if (msg.getCmd() == BaseCommand.PING) { + ClientImpl.getClient().getConf().getEvent().debug("received ping from server"); + NettyAttrUtil.updateReaderTime(ctx.channel(), System.currentTimeMillis()); + } + + if (msg.getCmd() != BaseCommand.PING) { + // callback + ClientImpl.getClient().getConf().getCallbackThreadPool().execute(() -> { + ClientImpl.getClient().getConf().getMessageListener().received(ClientImpl.getClient(), msg.getPropertiesMap(), 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 54% 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..fb50e4dc 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,7 +1,6 @@ -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 com.crossoverjie.cim.common.protocol.Response; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.handler.codec.protobuf.ProtobufDecoder; @@ -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 ProtobufDecoder(Response.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..51375dee --- /dev/null +++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/MessageListener.java @@ -0,0 +1,14 @@ +package com.crossoverjie.cim.client.sdk.io; + +import com.crossoverjie.cim.client.sdk.Client; +import java.util.Map; + +public interface MessageListener { + + /** + * @param client client + * @param properties meta data + * @param msg msgs + */ + void received(Client client, Map properties, 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/main/java/com/crossoverjie/cim/client/sdk/io/backoff/BackoffStrategy.java b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/backoff/BackoffStrategy.java new file mode 100644 index 00000000..eed72b3a --- /dev/null +++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/backoff/BackoffStrategy.java @@ -0,0 +1,24 @@ +package com.crossoverjie.cim.client.sdk.io.backoff; + +import java.util.concurrent.TimeUnit; + +/** + * @author:qjj + * @create: 2024-09-21 12:16 + * @Description: backoff strategy interface + */ + +public interface BackoffStrategy { + /** + * @return the backoff time in milliseconds + */ + long nextBackoff(); + + /** + * Run the backoff strategy + * @throws InterruptedException + */ + default void runBackoff() throws InterruptedException { + TimeUnit.SECONDS.sleep(nextBackoff()); + } +} diff --git a/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/backoff/RandomBackoff.java b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/backoff/RandomBackoff.java new file mode 100644 index 00000000..7de4ca5f --- /dev/null +++ b/cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/backoff/RandomBackoff.java @@ -0,0 +1,16 @@ +package com.crossoverjie.cim.client.sdk.io.backoff; + +/** + * @author:qjj + * @create: 2024-09-21 12:22 + * @Description: random backoff strategy + */ + +public class RandomBackoff implements BackoffStrategy { + + @Override + public long nextBackoff() { + return (int) (Math.random() * 7 + 3); + } + +} 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..e97a9ed9 --- /dev/null +++ b/cim-client-sdk/src/test/java/com/crossoverjie/cim/client/sdk/ClientTest.java @@ -0,0 +1,429 @@ +package com.crossoverjie.cim.client.sdk; + +import com.crossoverjie.cim.client.sdk.impl.ClientConfigurationData; +import com.crossoverjie.cim.client.sdk.io.backoff.RandomBackoff; +import com.crossoverjie.cim.client.sdk.route.AbstractRouteBaseTest; +import com.crossoverjie.cim.common.constant.Constants; +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, properties, message) -> { + client2Receive.set(message); + Assertions.assertEquals(properties.get(Constants.MetaKey.USER_ID), String.valueOf(auth1.getUserId())); + Assertions.assertEquals(properties.get(Constants.MetaKey.USER_NAME), auth1.getUserName()); + }) + .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(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, properties, 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, properties, 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(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(); + var backoffStrategy = new RandomBackoff(); + + @Cleanup + Client client1 = Client.builder() + .auth(auth1) + .routeUrl(routeUrl) + .backoffStrategy(backoffStrategy) + .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, properties, message) -> client2Receive.set(message)) + .backoffStrategy(backoffStrategy) + .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(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(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, properties, 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(msg, client2Receive.get())); + client2Receive.set(""); + + // Manually offline + client2.close(); + TimeUnit.SECONDS.sleep(10); + client2 = Client.builder() + .auth(auth2) + .routeUrl(routeUrl) + .messageListener((client, properties, 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(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(); + } + + @Test + public void testIncorrectUser() throws Exception { + super.starSingleServer(); + super.startRoute(); + String routeUrl = "http://localhost:8083"; + String cj = "xx"; + long id = 100L; + var auth1 = ClientConfigurationData.Auth.builder() + .userId(id) + .userName(cj) + .build(); + + Client client1 = Client.builder() + .auth(auth1) + .routeUrl(routeUrl) + .build(); + TimeUnit.SECONDS.sleep(3); + + Assertions.assertDoesNotThrow(client1::close); + + 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 47d7d931..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 @@ -42,6 +47,12 @@ test + + org.junit.vintage + junit-vintage-engine + test + + org.springframework.boot spring-boot-configuration-processor @@ -99,4 +110,5 @@ + \ 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 e0dbe90d..00000000 --- a/cim-client/src/main/java/com/crossoverjie/cim/client/client/CIMClient.java +++ /dev/null @@ -1,232 +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.client.vo.req.LoginReqVO; -import com.crossoverjie.cim.client.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 lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import jakarta.annotation.PostConstruct; - -/** - * Function: - * - * @author crossoverJie - * Date: 22/05/2018 14:19 - * @since JDK 1.8 - */ -@Component -@Slf4j -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.ServerInfo cimServer = userLogin(); - - //启动客户端 - startClient(cimServer); - - //向服务端注册 - loginCIMServer(); - - - } - - /** - * 启动客户端 - * - * @param cimServer - * @throws Exception - */ - private void startClient(CIMServerResVO.ServerInfo 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.ServerInfo userLogin() { - LoginReqVO loginReqVO = new LoginReqVO(userId, userName); - CIMServerResVO.ServerInfo cimServer = null; - try { - cimServer = routeRequest.getCIMServer(loginReqVO); - - //保存系统信息 - clientInfo.saveServiceInfo(cimServer.getIp() + ":" + cimServer.getCimServerPort()) - .saveUserInfo(userId, userName); - - log.info("cimServer=[{}]", cimServer.toString()); - } 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 ef1d488b..30ea454f 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,110 +1,108 @@ 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.sdk.io.backoff.RandomBackoff; +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, event)) + .callbackThreadPool(callbackThreadPool) + .backoffStrategy(new RandomBackoff()) .build(); - return heart; } - /** * http client + * * @return okHttp */ @Bean public OkHttpClient okHttpClient() { OkHttpClient.Builder builder = new OkHttpClient.Builder(); - builder.connectTimeout(30, TimeUnit.SECONDS) - .readTimeout(10, TimeUnit.SECONDS) - .writeTimeout(10,TimeUnit.SECONDS) + builder.connectTimeout(3, TimeUnit.SECONDS) + .readTimeout(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 a1c8c2fb..00000000 --- a/cim-client/src/main/java/com/crossoverjie/cim/client/controller/IndexController.java +++ /dev/null @@ -1,112 +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.GroupReqVO; -import com.crossoverjie.cim.client.vo.req.SendMsgReqVO; -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 io.swagger.v3.oas.annotations.Operation; -import org.springframework.beans.factory.annotation.Autowired; -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("/") -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 sendMsgReqVO - * @return - */ - @Operation(summary = "群发消息") - @RequestMapping(value = "sendGroupMsg",method = RequestMethod.POST) - @ResponseBody - public BaseResponse sendGroupMsg(@RequestBody SendMsgReqVO sendMsgReqVO) throws Exception { - BaseResponse res = new BaseResponse(); - - GroupReqVO groupReqVO = new GroupReqVO(sendMsgReqVO.getUserId(),sendMsgReqVO.getMsg()) ; - routeRequest.sendGroupMsg(groupReqVO) ; - - // 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 824bee0d..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,13 +1,11 @@ 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; /** * Function: @@ -19,47 +17,39 @@ 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 @Override public void run() { Scanner sc = new Scanner(System.in); 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/InnerCommand.java b/cim-client/src/main/java/com/crossoverjie/cim/client/service/InnerCommand.java index 77b704d9..3ea54802 100644 --- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/InnerCommand.java +++ b/cim-client/src/main/java/com/crossoverjie/cim/client/service/InnerCommand.java @@ -13,5 +13,5 @@ public interface InnerCommand { * 执行 * @param msg */ - void process(String msg) ; + void process(String msg) throws Exception; } 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 8af1e805..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 @@ -1,67 +1,50 @@ package com.crossoverjie.cim.client.service; -import com.crossoverjie.cim.client.vo.req.GroupReqVO; -import com.crossoverjie.cim.client.vo.req.P2PReqVO; +import com.crossoverjie.cim.route.api.vo.req.ChatReqVO; +import com.crossoverjie.cim.route.api.vo.req.P2PReqVO; /** * 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(GroupReqVO 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) ; - + boolean innerCommand(String msg) throws Exception; - /** - * 关闭系统 - */ - void shutdown() ; /** * 开启 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 33286f4a..00000000 --- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/RouteRequest.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.crossoverjie.cim.client.service; - -import com.crossoverjie.cim.client.vo.req.GroupReqVO; -import com.crossoverjie.cim.client.vo.req.LoginReqVO; -import com.crossoverjie.cim.client.vo.req.P2PReqVO; -import com.crossoverjie.cim.client.vo.res.CIMServerResVO; -import com.crossoverjie.cim.client.vo.res.OnlineUsersResVO; - -import java.util.List; - -/** - * Function: - * - * @author crossoverJie - * Date: 2018/12/22 22:26 - * @since JDK 1.8 - */ -public interface RouteRequest { - - /** - * 群发消息 - * @param groupReqVO 消息 - * @throws Exception - */ - void sendGroupMsg(GroupReqVO groupReqVO) throws Exception; - - - /** - * 私聊 - * @param p2PReqVO - * @throws Exception - */ - void sendP2PMsg(P2PReqVO p2PReqVO)throws Exception; - - /** - * 获取服务器 - * @return 服务ip+port - * @param loginReqVO - * @throws Exception - */ - CIMServerResVO.ServerInfo getCIMServer(LoginReqVO loginReqVO) throws Exception; - - /** - * 获取所有在线用户 - * @return - * @throws Exception - */ - List onlineUsers()throws Exception ; - - - void offLine() ; - -} 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 ed5b21bd..00000000 --- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/ClientHeartBeatHandlerImpl.java +++ /dev/null @@ -1,34 +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.stereotype.Service; - -/** - * Function: - * - * @author crossoverJie - * Date: 2019-01-20 17:16 - * @since JDK 1.8 - */ -@Service -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..2dce9ebb 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,14 @@ 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.crossoverjie.cim.client.service.MsgLogger; +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 +18,26 @@ * @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; + @Resource + private MsgLogger msgLogger; + @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) { + msgLogger.log(String.format("Debug[%s]", msg)); + } + + @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 +46,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..040ef0fd 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,35 @@ 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.Event; +import com.crossoverjie.cim.client.sdk.io.MessageListener; import com.crossoverjie.cim.client.service.MsgLogger; -import com.crossoverjie.cim.client.util.SpringBeanFactory; +import com.crossoverjie.cim.common.constant.Constants; +import java.util.Map; /** * 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; + private final Event event; - public MsgCallBackListener() { - this.msgLogger = SpringBeanFactory.getBean(MsgLogger.class) ; + public MsgCallBackListener(MsgLogger msgLogger, Event event) { + this.msgLogger = msgLogger; + this.event = event; } + @Override - public void handle(String msg) { - msgLogger.log(msg) ; + public void received(Client client, Map properties, String msg) { + String sendUserName = properties.getOrDefault(Constants.MetaKey.USER_NAME, "nobody"); + this.msgLogger.log(sendUserName + ":" + msg); + this.event.info(sendUserName + ":" + 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 a71ec1da..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,22 +1,15 @@ 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.client.vo.req.GroupReqVO; -import com.crossoverjie.cim.client.vo.req.P2PReqVO; import com.crossoverjie.cim.common.util.StringUtil; +import com.crossoverjie.cim.route.api.vo.req.P2PReqVO; +import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import jakarta.annotation.Resource; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - /** * Function: * @@ -27,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 { @@ -57,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 { - //群聊 - GroupReqVO groupReqVO = new GroupReqVO(configuration.getUserId(), msg); - try { - groupChat(groupReqVO); - } catch (Exception e) { - log.error("Exception", e); - } + client.sendGroup(msg); } } @@ -101,18 +66,6 @@ private void aiChat(String msg) { System.out.println("AI:\033[31;4m" + msg + "\033[0m"); } - @Override - public void groupChat(GroupReqVO 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)) { @@ -123,7 +76,7 @@ public boolean checkMsg(String msg) { } @Override - public boolean innerCommand(String msg) { + public boolean innerCommand(String msg) throws Exception { if (msg.startsWith(":")) { @@ -135,28 +88,6 @@ public boolean innerCommand(String msg) { } else { return false; } - - - } - - /** - * 关闭系统 - */ - @Override - public void shutdown() { - 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 0cfe2462..00000000 --- a/cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/RouteRequestImpl.java +++ /dev/null @@ -1,162 +0,0 @@ -package com.crossoverjie.cim.client.service.impl; - -import com.alibaba.fastjson.JSON; -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.client.vo.req.GroupReqVO; -import com.crossoverjie.cim.client.vo.req.LoginReqVO; -import com.crossoverjie.cim.client.vo.req.P2PReqVO; -import com.crossoverjie.cim.client.vo.res.CIMServerResVO; -import com.crossoverjie.cim.client.vo.res.OnlineUsersResVO; -import com.crossoverjie.cim.common.core.proxy.ProxyManager; -import com.crossoverjie.cim.common.enums.StatusEnum; -import com.crossoverjie.cim.common.exception.CIMException; -import com.crossoverjie.cim.common.res.BaseResponse; -import com.crossoverjie.cim.route.api.RouteApi; -import com.crossoverjie.cim.route.api.vo.req.ChatReqVO; -import lombok.extern.slf4j.Slf4j; -import okhttp3.OkHttpClient; -import okhttp3.Response; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * 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(GroupReqVO groupReqVO) throws Exception { - RouteApi routeApi = new ProxyManager<>(RouteApi.class, routeUrl, okHttpClient).getInstance(); - ChatReqVO chatReqVO = new ChatReqVO(groupReqVO.getUserId(), groupReqVO.getMsg()) ; - Response response = null; - try { - response = (Response)routeApi.groupRoute(chatReqVO); - }catch (Exception e){ - log.error("exception",e); - }finally { - response.body().close(); - } - } - - @Override - public void sendP2PMsg(P2PReqVO p2PReqVO) throws Exception { - RouteApi routeApi = new ProxyManager<>(RouteApi.class, routeUrl, okHttpClient).getInstance(); - com.crossoverjie.cim.route.api.vo.req.P2PReqVO vo = new com.crossoverjie.cim.route.api.vo.req.P2PReqVO() ; - vo.setMsg(p2PReqVO.getMsg()); - vo.setReceiveUserId(p2PReqVO.getReceiveUserId()); - vo.setUserId(p2PReqVO.getUserId()); - - Response response = null; - try { - response = (Response) routeApi.p2pRoute(vo); - String json = response.body().string() ; - BaseResponse baseResponse = JSON.parseObject(json, BaseResponse.class); - - // account offline. - if (baseResponse.getCode().equals(StatusEnum.OFF_LINE.getCode())){ - log.error(p2PReqVO.getReceiveUserId() + ":" + StatusEnum.OFF_LINE.getMessage()); - } - - }catch (Exception e){ - log.error("exception",e); - }finally { - response.body().close(); - } - } - - @Override - public CIMServerResVO.ServerInfo getCIMServer(LoginReqVO loginReqVO) throws Exception { - - RouteApi routeApi = new ProxyManager<>(RouteApi.class, routeUrl, okHttpClient).getInstance(); - com.crossoverjie.cim.route.api.vo.req.LoginReqVO vo = new com.crossoverjie.cim.route.api.vo.req.LoginReqVO() ; - vo.setUserId(loginReqVO.getUserId()); - vo.setUserName(loginReqVO.getUserName()); - - Response response = null; - CIMServerResVO cimServerResVO = null; - try { - response = (Response) routeApi.login(vo); - String json = response.body().string(); - cimServerResVO = JSON.parseObject(json, CIMServerResVO.class); - - //重复失败 - 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); - } - - }catch (Exception e){ - log.error("exception",e); - }finally { - response.body().close(); - } - - return cimServerResVO.getDataBody(); - } - - @Override - public List onlineUsers() throws Exception{ - RouteApi routeApi = new ProxyManager<>(RouteApi.class, routeUrl, okHttpClient).getInstance(); - - Response response = null; - OnlineUsersResVO onlineUsersResVO = null; - try { - response = (Response) routeApi.onlineUser(); - String json = response.body().string() ; - onlineUsersResVO = JSON.parseObject(json, OnlineUsersResVO.class); - - }catch (Exception e){ - log.error("exception",e); - }finally { - response.body().close(); - } - - return onlineUsersResVO.getDataBody(); - } - - @Override - public void offLine() { - RouteApi routeApi = new ProxyManager<>(RouteApi.class, routeUrl, okHttpClient).getInstance(); - ChatReqVO vo = new ChatReqVO(appConfiguration.getUserId(), "offLine") ; - Response response = null; - try { - response = (Response) routeApi.offLine(vo); - } catch (Exception e) { - log.error("exception",e); - } finally { - response.body().close(); - } - } -} 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 e3270a9b..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,16 +1,16 @@ 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.client.vo.res.OnlineUsersResVO; 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; -import java.util.List; - /** * Function: * @@ -23,17 +23,17 @@ 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 { - List onlineUsers = routeRequest.onlineUsers(); + Set onlineUsers = client.getOnlineUser(); TrieTree trieTree = new TrieTree(); - for (OnlineUsersResVO.DataBodyBean onlineUser : onlineUsers) { + 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 b015f0c7..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,15 +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.client.vo.res.OnlineUsersResVO; +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; -import java.util.List; - /** * Function: * @@ -21,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 { - List onlineUsers = routeRequest.onlineUsers(); + Set onlineUsers = client.getOnlineUser(); - echoService.echo("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); - for (OnlineUsersResVO.DataBodyBean onlineUser : onlineUsers) { - echoService.echo("userId={}=====userName={}",onlineUser.getUserId(),onlineUser.getUserName()); + event.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + for (CIMUserInfo onlineUser : onlineUsers) { + 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 f461add8..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,19 +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.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: @@ -24,47 +22,43 @@ */ @Slf4j @Service +@ConditionalOnWebApplication public class ShutDownCommand implements InnerCommand { - @Autowired - private RouteRequest routeRequest ; - - @Autowired - private CIMClient cimClient; + @Resource + private Client 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) { - echoService.echo("cim client closing..."); - shutDownMsg.shutdown(); - routeRequest.offLine(); + public void process(String msg) throws Exception { + 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 (InterruptedException e) { - log.error("InterruptedException", e); + } 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/GroupReqVO.java b/cim-client/src/main/java/com/crossoverjie/cim/client/vo/req/GroupReqVO.java deleted file mode 100644 index c9d2145e..00000000 --- a/cim-client/src/main/java/com/crossoverjie/cim/client/vo/req/GroupReqVO.java +++ /dev/null @@ -1,54 +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 GroupReqVO extends BaseRequest { - - @NotNull(message = "userId 不能为空") - @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "消息发送者的 userId", example = "1545574049323") - private Long userId ; - - - @NotNull(message = "msg 不能为空") - @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "msg", example = "hello") - private String msg ; - - public GroupReqVO(Long userId, String msg) { - this.userId = userId; - this.msg = msg; - } - - public String getMsg() { - return msg; - } - - public void setMsg(String msg) { - this.msg = msg; - } - - public Long getUserId() { - return userId; - } - - public void setUserId(Long userId) { - this.userId = userId; - } - - @Override - public String toString() { - return "GroupReqVO{" + - "userId=" + userId + - ", msg='" + msg + '\'' + - "} " + super.toString(); - } -} diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/vo/req/LoginReqVO.java b/cim-client/src/main/java/com/crossoverjie/cim/client/vo/req/LoginReqVO.java deleted file mode 100644 index 66de629e..00000000 --- a/cim-client/src/main/java/com/crossoverjie/cim/client/vo/req/LoginReqVO.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.crossoverjie.cim.client.vo.req; - -import com.crossoverjie.cim.common.req.BaseRequest; - -/** - * Function: - * - * @author crossoverJie - * Date: 2018/12/23 22:30 - * @since JDK 1.8 - */ -public class LoginReqVO extends BaseRequest{ - private Long userId ; - private String userName ; - - public LoginReqVO(Long userId, String userName) { - this.userId = userId; - this.userName = userName; - } - - 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; - } - - @Override - public String toString() { - return "LoginReqVO{" + - "userId=" + userId + - ", userName='" + userName + '\'' + - "} " + super.toString(); - } -} diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/vo/req/P2PReqVO.java b/cim-client/src/main/java/com/crossoverjie/cim/client/vo/req/P2PReqVO.java deleted file mode 100644 index 7b66dc41..00000000 --- a/cim-client/src/main/java/com/crossoverjie/cim/client/vo/req/P2PReqVO.java +++ /dev/null @@ -1,73 +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 P2PReqVO extends BaseRequest { - - @NotNull(message = "userId 不能为空") - @Schema(requiredMode = Schema.RequiredMode.REQUIRED , description = "消息发送者的 userId", example = "1545574049323") - private Long userId ; - - - @NotNull(message = "userId 不能为空") - @Schema(requiredMode = Schema.RequiredMode.REQUIRED , description = "消息接收者的 userId", example = "1545574049323") - private Long receiveUserId ; - - - - - @NotNull(message = "msg 不能为空") - @Schema(requiredMode = Schema.RequiredMode.REQUIRED , description = "msg", example = "hello") - private String msg ; - - public P2PReqVO() { - } - - public P2PReqVO(Long userId, Long receiveUserId, String msg) { - this.userId = userId; - this.receiveUserId = receiveUserId; - this.msg = msg; - } - - public Long getReceiveUserId() { - return receiveUserId; - } - - public void setReceiveUserId(Long receiveUserId) { - this.receiveUserId = receiveUserId; - } - - public String getMsg() { - return msg; - } - - public void setMsg(String msg) { - this.msg = msg; - } - - public Long getUserId() { - return userId; - } - - public void setUserId(Long userId) { - this.userId = userId; - } - - @Override - public String toString() { - return "GroupReqVO{" + - "userId=" + userId + - ", msg='" + msg + '\'' + - "} " + super.toString(); - } -} diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/vo/req/SendMsgReqVO.java b/cim-client/src/main/java/com/crossoverjie/cim/client/vo/req/SendMsgReqVO.java deleted file mode 100644 index 8fe4c8e1..00000000 --- a/cim-client/src/main/java/com/crossoverjie/cim/client/vo/req/SendMsgReqVO.java +++ /dev/null @@ -1,40 +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 SendMsgReqVO extends BaseRequest { - - @NotNull(message = "msg 不能为空") - @Schema(requiredMode = Schema.RequiredMode.REQUIRED , description = "msg", example = "hello") - private String msg ; - - @NotNull(message = "userId 不能为空") - @Schema(requiredMode = Schema.RequiredMode.REQUIRED , description = "userId", example = "11") - private Long userId ; - - public String getMsg() { - return msg; - } - - public void setMsg(String msg) { - this.msg = msg; - } - - public long getUserId() { - return userId; - } - - public void setUserId(long userId) { - this.userId = userId; - } -} 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/CIMServerResVO.java b/cim-client/src/main/java/com/crossoverjie/cim/client/vo/res/CIMServerResVO.java deleted file mode 100644 index 5382ea26..00000000 --- a/cim-client/src/main/java/com/crossoverjie/cim/client/vo/res/CIMServerResVO.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.crossoverjie.cim.client.vo.res; - -import java.io.Serializable; - -/** - * Function: - * - * @author crossoverJie - * Date: 2018/12/23 00:43 - * @since JDK 1.8 - */ -public class CIMServerResVO implements Serializable { - - /** - * code : 9000 - * message : 成功 - * reqNo : null - * dataBody : {"ip":"127.0.0.1","port":8081} - */ - - private String code; - private String message; - private Object reqNo; - private ServerInfo dataBody; - - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - public Object getReqNo() { - return reqNo; - } - - public void setReqNo(Object reqNo) { - this.reqNo = reqNo; - } - - public ServerInfo getDataBody() { - return dataBody; - } - - public void setDataBody(ServerInfo dataBody) { - this.dataBody = dataBody; - } - - public static class ServerInfo { - /** - * ip : 127.0.0.1 - * port : 8081 - */ - private String ip ; - private Integer cimServerPort; - private Integer httpPort; - - public String getIp() { - return ip; - } - - public void setIp(String ip) { - this.ip = ip; - } - - public Integer getCimServerPort() { - return cimServerPort; - } - - public void setCimServerPort(Integer cimServerPort) { - this.cimServerPort = cimServerPort; - } - - public Integer getHttpPort() { - return httpPort; - } - - public void setHttpPort(Integer httpPort) { - this.httpPort = httpPort; - } - - @Override - public String toString() { - return "ServerInfo{" + - "ip='" + ip + '\'' + - ", cimServerPort=" + cimServerPort + - ", httpPort=" + httpPort + - '}'; - } - } - - - @Override - public String toString() { - return "CIMServerResVO{" + - "code='" + code + '\'' + - ", message='" + message + '\'' + - ", reqNo=" + reqNo + - ", dataBody=" + dataBody + - '}'; - } -} diff --git a/cim-client/src/main/java/com/crossoverjie/cim/client/vo/res/OnlineUsersResVO.java b/cim-client/src/main/java/com/crossoverjie/cim/client/vo/res/OnlineUsersResVO.java deleted file mode 100644 index 19658168..00000000 --- a/cim-client/src/main/java/com/crossoverjie/cim/client/vo/res/OnlineUsersResVO.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.crossoverjie.cim.client.vo.res; - -import java.util.List; - -/** - * Function: - * - * @author crossoverJie - * Date: 2018/12/26 23:17 - * @since JDK 1.8 - */ -public class OnlineUsersResVO { - - - /** - * code : 9000 - * message : 成功 - * reqNo : null - * dataBody : [{"userId":1545574841528,"userName":"zhangsan"},{"userId":1545574871143,"userName":"crossoverJie"}] - */ - - private String code; - private String message; - private Object reqNo; - private List dataBody; - - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - public Object getReqNo() { - return reqNo; - } - - public void setReqNo(Object reqNo) { - this.reqNo = reqNo; - } - - public List getDataBody() { - return dataBody; - } - - public void setDataBody(List dataBody) { - this.dataBody = dataBody; - } - - public static class DataBodyBean { - /** - * userId : 1545574841528 - * userName : zhangsan - */ - - private long userId; - private String userName; - - 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; - } - } -} 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/main/resources/application.yaml b/cim-client/src/main/resources/application.yaml index f1b92859..862f1f59 100644 --- a/cim-client/src/main/resources/application.yaml +++ b/cim-client/src/main/resources/application.yaml @@ -1,37 +1,37 @@ -spring: - application: - name: cim-client - -# web port -server: - port: 8082 - -logging: - level: - root: error - -# enable swagger -springdoc: - swagger-ui: - enabled: true - -# log path -cim: - msg: - logger: - path: /opt/logs/cim/ - route: - url: http://localhost:8083 # route url suggested that this is Nginx address - user: # cim userId and userName - id: 1722343979085 - userName: zhangsan - callback: - thread: - queue: - size: 2 - pool: - size: 2 - heartbeat: - time: 60 # cim heartbeat time (seconds) - reconnect: - count: 3 \ No newline at end of file +spring: + application: + name: cim-client + +# web port +server: + port: 8082 + +logging: + level: + root: error + +# enable swagger +springdoc: + swagger-ui: + enabled: true + +# log path +cim: + msg: + logger: + path: /opt/logs/cim/ + route: + url: http://localhost:8083 # route url suggested that this is Nginx address + user: # cim userId and userName + id: 1725714450795 + userName: cj4 + callback: + thread: + queue: + size: 2 + pool: + size: 2 + heartbeat: + time: 60 # cim heartbeat time (seconds) + reconnect: + count: 3 diff --git a/cim-client/src/main/resources/banner.txt b/cim-client/src/main/resources/banner.txt index 97b15b91..c7e303f1 100644 --- a/cim-client/src/main/resources/banner.txt +++ b/cim-client/src/main/resources/banner.txt @@ -1,14 +1,7 @@ - - ▄████▄ ██▓ ███▄ ▄███▓ -▒██▀ ▀█ ▓██▒▓██▒▀█▀ ██▒ -▒▓█ ▄ ▒██▒▓██ ▓██░ -▒▓▓▄ ▄██▒░██░▒██ ▒██ -▒ ▓███▀ ░░██░▒██▒ ░██▒ -░ ░▒ ▒ ░░▓ ░ ▒░ ░ ░ - ░ ▒ ▒ ░░ ░ ░ -░ ▒ ░░ ░ -░ ░ ░ ░ -░ + _ ___ __ + ____(_)_ _ ____/ (_)__ ___ / /_ +/ __/ / ' \ / __/ / / -_) _ \/ __/ +\__/_/_/_/_/ \__/_/_/\__/_//_/\__/ Power by @crossoverJie diff --git a/cim-client/src/test/com/crossoverjie/cim/server/test/CommonTest.java b/cim-client/src/test/com/crossoverjie/cim/server/test/CommonTest.java deleted file mode 100644 index 207c7b3a..00000000 --- a/cim-client/src/test/com/crossoverjie/cim/server/test/CommonTest.java +++ /dev/null @@ -1,217 +0,0 @@ -package com.crossoverjie.cim.server.test; - - -import com.alibaba.fastjson.JSON; -import com.crossoverjie.cim.client.vo.res.CIMServerResVO; -import com.crossoverjie.cim.client.vo.res.OnlineUsersResVO; -import com.vdurmont.emoji.EmojiParser; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; - - -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Function: - * - * @author crossoverJie - * Date: 22/05/2018 18:44 - * @since JDK 1.8 - */ -@Slf4j -public class CommonTest { - - @Test - public void test() { - - String json = "{\"code\":\"9000\",\"message\":\"成功\",\"reqNo\":null,\"dataBody\":{\"ip\":\"127.0.0.1\",\"port\":8081}}" ; - - CIMServerResVO cimServerResVO = JSON.parseObject(json, CIMServerResVO.class); - - System.out.println(cimServerResVO.toString()); - - String text = "nihaoaaa" ; - String[] split = text.split(" "); - System.out.println(split.length); - } - - @Test - public void onlineUser(){ - List onlineUsers = new ArrayList<>(64) ; - - OnlineUsersResVO.DataBodyBean bodyBean = new OnlineUsersResVO.DataBodyBean() ; - - bodyBean.setUserId(100L); - bodyBean.setUserName("zhangsan"); - onlineUsers.add(bodyBean) ; - - bodyBean = new OnlineUsersResVO.DataBodyBean(); - bodyBean.setUserId(200L); - bodyBean.setUserName("crossoverJie"); - onlineUsers.add(bodyBean) ; - - log.info("list={}",JSON.toJSONString(onlineUsers)); - - log.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); - - for (OnlineUsersResVO.DataBodyBean onlineUser : onlineUsers) { - - log.info("userId={}=====userName={}",onlineUser.getUserId(),onlineUser.getUserName()); - } - log.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); - } - - - @Test - public void searchMsg(){ - StringBuilder sb = new StringBuilder() ; - String allMsg = "于是在之前的基础上我完善了一些内容,先来看看这个项目的介绍吧:\n" + - "\n" + - "CIM(CROSS-IM) 一款面向开发者的 IM(即时通讯)系统;同时提供了一些组件帮助开发者构建一款属于自己可水平扩展的 IM 。\n" + - "\n" + - "借助 CIM 你可以实现以下需求:" ; - - String key = "IM" ; - - String[] split = allMsg.split("\n"); - for (String msg : split) { - if (msg.trim().contains(key)){ - sb.append(msg).append("\n") ; - } - } - int pos = 0; - - String result = sb.toString(); - - int count = 1 ; - int multiple = 2 ; - while((pos = result.indexOf(key, pos)) >= 0) { - - log.info("{},{}",pos, pos + key.length()); - - if (count == 1){ - sb.insert(pos,"**"); - }else { - Double pow = Math.pow(multiple, count); - sb.insert(pos +pow.intValue(),"**"); - } - - pos += key.length(); - - if (count == 1){ - sb.insert(pos +2,"**"); - }else { - Double pow = Math.pow(multiple, count); - sb.insert((pos +2) + pow.intValue(),"**"); - - } - - - count ++ ; - } - - System.out.println(sb); - } - @Test - public void searchMsg2(){ - StringBuilder sb = new StringBuilder() ; - String allMsg = "于是在之前的基础上我完善了一些内容,先来看看这个项目的介绍吧:\n" + - "\n" + - "CIM(CROSS-IM) 一款面向开发者的 IM(即时通讯)系统;同时提供了一些组件帮助开发者构建一款属于自己可水平扩展的 IM 。\n" + - "\n" + - "借助 CIM 你可以实现以下需求:" ; - - String key = "CIM" ; - - String[] split = allMsg.split("\n"); - for (String msg : split) { - if (msg.trim().contains(key)){ - sb.append(msg).append("\n") ; - } - } - int pos = 0; - - String result = sb.toString(); - - int count = 1 ; - int multiple = 2 ; - while((pos = result.indexOf(key, pos)) >= 0) { - - log.info("{},{}",pos, pos + key.length()); - - pos += key.length(); - - - count ++ ; - } - - System.out.println(sb.toString()); - System.out.println(sb.toString().replace(key,"\033[31;4m" + key+"\033[0m")); - } - - @Test - public void log(){ - String msg = "hahahdsadsd" ; - LocalDate today = LocalDate.now(); - int year = today.getYear(); - int month = today.getMonthValue(); - int day = today.getDayOfMonth(); - - String dir = "/opt/logs/cim/zhangsan" + "/"; - String fileName = dir + year + month + day + ".log"; - log.info("fileName={}", fileName); - - Path file = Paths.get(fileName); - boolean exists = Files.exists(Paths.get(dir), LinkOption.NOFOLLOW_LINKS); - try { - if (!exists) { - Files.createDirectories(Paths.get(dir)); - } - - List lines = Arrays.asList(msg); - - Files.write(file, lines, Charset.forName("UTF-8"), StandardOpenOption.CREATE, StandardOpenOption.APPEND); - } catch (IOException e) { - log.info("IOException", e); - } - - } - - @Test - public void emoji() throws Exception{ - String str = "An :grinning:awesome :smiley:string 😄with a few :wink:emojis!"; - String result = EmojiParser.parseToUnicode(str); - System.out.println(result); - - - result = EmojiParser.parseToAliases(str); - System.out.println(result); -// -// Collection all = EmojiManager.getAll(); -// for (Emoji emoji : all) { -// System.out.println(EmojiParser.parseToAliases(emoji.getUnicode()) + "--->" + emoji.getUnicode() ); -// } - - } - - @Test - public void emoji2(){ - String emostring ="😂"; - - String face_with_tears_of_joy = emostring.replaceAll("\uD83D\uDE02", "face with tears of joy"); - System.out.println(face_with_tears_of_joy); - - System.out.println("======" + face_with_tears_of_joy.replaceAll("face with tears of joy","\uD83D\uDE02")); - } - -} diff --git a/cim-client/src/test/com/crossoverjie/cim/server/test/RouteTest.java b/cim-client/src/test/com/crossoverjie/cim/server/test/RouteTest.java deleted file mode 100644 index 51a97205..00000000 --- a/cim-client/src/test/com/crossoverjie/cim/server/test/RouteTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.crossoverjie.cim.server.test; - -import com.crossoverjie.cim.client.CIMClientApplication; -import com.crossoverjie.cim.client.service.RouteRequest; -import com.crossoverjie.cim.client.vo.req.LoginReqVO; -import com.crossoverjie.cim.client.vo.res.CIMServerResVO; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * 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 ; - - @Test - public void test() throws Exception { - LoginReqVO vo = new LoginReqVO(userId,userName) ; - CIMServerResVO.ServerInfo cimServer = routeRequest.getCIMServer(vo); - log.info("cimServer=[{}]",cimServer.toString()); - } -} diff --git a/cim-client/src/test/com/crossoverjie/cim/client/service/InnerCommandContextTest.java b/cim-client/src/test/java/com/crossoverjie/cim/client/service/InnerCommandContextTest.java similarity index 66% rename from cim-client/src/test/com/crossoverjie/cim/client/service/InnerCommandContextTest.java rename to cim-client/src/test/java/com/crossoverjie/cim/client/service/InnerCommandContextTest.java index f878fafa..6bfc1463 100644 --- a/cim-client/src/test/com/crossoverjie/cim/client/service/InnerCommandContextTest.java +++ b/cim-client/src/test/java/com/crossoverjie/cim/client/service/InnerCommandContextTest.java @@ -1,7 +1,16 @@ package com.crossoverjie.cim.client.service; +import com.alibaba.fastjson.JSONObject; import com.crossoverjie.cim.client.CIMClientApplication; +import com.crossoverjie.cim.common.core.proxy.RpcProxyManager; import com.crossoverjie.cim.common.enums.SystemCommandEnum; +import com.crossoverjie.cim.common.res.BaseResponse; +import com.crossoverjie.cim.route.api.RouteApi; +import com.crossoverjie.cim.route.api.vo.req.LoginReqVO; +import com.crossoverjie.cim.route.api.vo.res.CIMServerResVO; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import okhttp3.OkHttpClient; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -16,63 +25,65 @@ public class InnerCommandContextTest { private InnerCommandContext context; @Test - public void execute() { + public void execute() throws Exception { String msg = ":all"; InnerCommand execute = context.getInstance(msg); execute.process(msg) ; } - @Test - public void execute3() { +// @Test + public void execute3() throws Exception { + // TODO: 2024/8/31 Integration test String msg = SystemCommandEnum.ONLINE_USER.getCommandType(); InnerCommand execute = context.getInstance(msg); execute.process(msg) ; } @Test - public void execute4() { + public void execute4() throws Exception { String msg = ":q 天气"; InnerCommand execute = context.getInstance(msg); execute.process(msg) ; } @Test - public void execute5() { + public void execute5() throws Exception { String msg = ":q crossoverJie"; InnerCommand execute = context.getInstance(msg); execute.process(msg) ; } @Test - public void execute6() { + public void execute6() throws Exception { String msg = SystemCommandEnum.AI.getCommandType(); InnerCommand execute = context.getInstance(msg); execute.process(msg) ; } @Test - public void execute7() { + public void execute7() throws Exception { String msg = SystemCommandEnum.QAI.getCommandType(); InnerCommand execute = context.getInstance(msg); execute.process(msg) ; } - @Test - public void execute8() { +// @Test + public void execute8() throws Exception { + // TODO: 2024/8/31 Integration test String msg = ":pu cross"; InnerCommand execute = context.getInstance(msg); execute.process(msg) ; } @Test - public void execute9() { + public void execute9() throws Exception { String msg = SystemCommandEnum.INFO.getCommandType(); InnerCommand execute = context.getInstance(msg); execute.process(msg) ; } @Test - public void execute10() { + public void execute10() throws Exception { String msg = "dsds"; InnerCommand execute = context.getInstance(msg); execute.process(msg) ; @@ -81,7 +92,7 @@ public void execute10() { // @Test - public void quit() { + public void quit() throws Exception { String msg = ":q!"; InnerCommand execute = context.getInstance(msg); execute.process(msg) ; diff --git a/cim-client/src/test/com/crossoverjie/cim/client/service/impl/AsyncMsgLoggerTest.java b/cim-client/src/test/java/com/crossoverjie/cim/client/service/impl/AsyncMsgLoggerTest.java similarity index 100% rename from cim-client/src/test/com/crossoverjie/cim/client/service/impl/AsyncMsgLoggerTest.java rename to cim-client/src/test/java/com/crossoverjie/cim/client/service/impl/AsyncMsgLoggerTest.java diff --git a/cim-client/src/test/java/com/crossoverjie/cim/server/test/CommonTest.java b/cim-client/src/test/java/com/crossoverjie/cim/server/test/CommonTest.java new file mode 100644 index 00000000..7233389b --- /dev/null +++ b/cim-client/src/test/java/com/crossoverjie/cim/server/test/CommonTest.java @@ -0,0 +1,222 @@ +package com.crossoverjie.cim.server.test; + + +import com.crossoverjie.cim.common.core.proxy.RpcProxyManager; +import com.crossoverjie.cim.common.pojo.CIMUserInfo; +import com.crossoverjie.cim.common.res.BaseResponse; +import com.crossoverjie.cim.route.api.RouteApi; +import com.crossoverjie.cim.route.api.vo.req.LoginReqVO; +import com.crossoverjie.cim.route.api.vo.req.P2PReqVO; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.vdurmont.emoji.EmojiParser; +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.time.LocalDate; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import lombok.extern.slf4j.Slf4j; +import okhttp3.OkHttpClient; +import org.junit.Test; + +/** + * Function: + * + * @author crossoverJie + * Date: 22/05/2018 18:44 + * @since JDK 1.8 + */ +@Slf4j +public class CommonTest { + + + + + @Test + public void searchMsg2(){ + StringBuilder sb = new StringBuilder() ; + String allMsg = "于是在之前的基础上我完善了一些内容,先来看看这个项目的介绍吧:\n" + + "\n" + + "CIM(CROSS-IM) 一款面向开发者的 IM(即时通讯)系统;同时提供了一些组件帮助开发者构建一款属于自己可水平扩展的 IM 。\n" + + "\n" + + "借助 CIM 你可以实现以下需求:" ; + + String key = "CIM" ; + + String[] split = allMsg.split("\n"); + for (String msg : split) { + if (msg.trim().contains(key)){ + sb.append(msg).append("\n") ; + } + } + int pos = 0; + + String result = sb.toString(); + + int count = 1 ; + int multiple = 2 ; + while((pos = result.indexOf(key, pos)) >= 0) { + + log.info("{},{}",pos, pos + key.length()); + + pos += key.length(); + + + count ++ ; + } + + System.out.println(sb.toString()); + System.out.println(sb.toString().replace(key,"\033[31;4m" + key+"\033[0m")); + } + + @Test + public void log(){ + String msg = "hahahdsadsd" ; + LocalDate today = LocalDate.now(); + int year = today.getYear(); + int month = today.getMonthValue(); + int day = today.getDayOfMonth(); + + String dir = "/opt/logs/cim/zhangsan" + "/"; + String fileName = dir + year + month + day + ".log"; + log.info("fileName={}", fileName); + + Path file = Paths.get(fileName); + boolean exists = Files.exists(Paths.get(dir), LinkOption.NOFOLLOW_LINKS); + try { + if (!exists) { + Files.createDirectories(Paths.get(dir)); + } + + List lines = Arrays.asList(msg); + + Files.write(file, lines, Charset.forName("UTF-8"), StandardOpenOption.CREATE, StandardOpenOption.APPEND); + } catch (IOException e) { + log.info("IOException", e); + } + + } + + @Test + public void emoji() throws Exception{ + String str = "An :grinning:awesome :smiley:string 😄with a few :wink:emojis!"; + String result = EmojiParser.parseToUnicode(str); + System.out.println(result); + + + result = EmojiParser.parseToAliases(str); + System.out.println(result); +// +// Collection all = EmojiManager.getAll(); +// for (Emoji emoji : all) { +// System.out.println(EmojiParser.parseToAliases(emoji.getUnicode()) + "--->" + emoji.getUnicode() ); +// } + + } + + @Test + public void emoji2(){ + String emostring ="😂"; + + String face_with_tears_of_joy = emostring.replaceAll("\uD83D\uDE02", "face with tears of joy"); + System.out.println(face_with_tears_of_joy); + + System.out.println("======" + face_with_tears_of_joy.replaceAll("face with tears of joy","\uD83D\uDE02")); + } + +// @Test + public void deSerialize() throws Exception { + RouteApi routeApi = RpcProxyManager.create(RouteApi.class, "http://localhost:8083", new OkHttpClient()); + + BaseResponse login = + routeApi.login(new LoginReqVO(1725722966520L, "cj")); + System.out.println(login.getDataBody()); + + BaseResponse> setBaseResponse = routeApi.onlineUser(); + log.info("setBaseResponse={}",setBaseResponse.getDataBody()); + } + + @Test + public void json() throws JsonProcessingException, ClassNotFoundException { + String json = "{\"code\":\"9000\",\"message\":\"成功\",\"reqNo\":null,\"dataBody\":{\"ip\":\"127.0.0.1\",\"cimServerPort\":11211,\"httpPort\":8081}}"; + + ObjectMapper objectMapper = new ObjectMapper(); + Class generic = null; + for (Method declaredMethod : RouteApi.class.getDeclaredMethods()) { + if (declaredMethod.getName().equals("login")){ + Type returnType = declaredMethod.getGenericReturnType(); + + // check if the return type is a parameterized type + if (returnType instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) returnType; + + Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); + + for (Type typeArgument : actualTypeArguments) { + System.out.println("generic: " + typeArgument.getTypeName()); + generic = Class.forName(typeArgument.getTypeName()); + break; + } + } else { + System.out.println("not a generic type"); + } + } + } + BaseResponse response = objectMapper.readValue(json, + objectMapper.getTypeFactory().constructParametricType(BaseResponse.class, generic)); + System.out.println(response.getDataBody().getIp()); + } + + + private static class Gen{ + private T t; + private R r; + } + + interface TestInterface{ + Gen login(); + } + + + @Test + public void test1() throws JsonProcessingException { + String json = "{\"code\":\"200\",\"message\":\"Success\",\"reqNo\":null,\"dataBody\":[{\"userId\":\"123\",\"userName\":\"Alice\"}, {\"userId\":\"456\",\"userName\":\"Bob\"}]}"; + + ObjectMapper objectMapper = new ObjectMapper(); + + // 获取 BaseResponse> 的泛型参数 + Type setType = getGenericTypeOfBaseResponse(); + + // 将泛型类型传递给 ObjectMapper 进行反序列化 + BaseResponse> response = objectMapper.readValue(json, + objectMapper.getTypeFactory().constructParametricType(BaseResponse.class, objectMapper.getTypeFactory().constructType(setType))); + + System.out.println("Response Code: " + response.getCode()); + System.out.println("Online Users: "); + for (CIMUserInfo user : response.getDataBody()) { + System.out.println("User ID: " + user.getUserId() + ", User Name: " + user.getUserName()); + } + } + + // 通过反射获取 BaseResponse> 中的泛型类型 + public static Type getGenericTypeOfBaseResponse() { + // 这里模拟你需要处理的 BaseResponse> + ParameterizedType baseResponseType = (ParameterizedType) new TypeReference>>() {}.getType(); + + // 获取 BaseResponse 的泛型参数,即 Set + Type[] actualTypeArguments = baseResponseType.getActualTypeArguments(); + + // 返回第一个泛型参数 (Set) + return actualTypeArguments[0]; + } +} diff --git a/cim-client/src/test/com/crossoverjie/cim/server/test/EchoTest.java b/cim-client/src/test/java/com/crossoverjie/cim/server/test/EchoTest.java similarity index 100% rename from cim-client/src/test/com/crossoverjie/cim/server/test/EchoTest.java rename to cim-client/src/test/java/com/crossoverjie/cim/server/test/EchoTest.java diff --git a/cim-client/src/test/resources/application.yaml b/cim-client/src/test/resources/application.yaml new file mode 100644 index 00000000..6ea8c51e --- /dev/null +++ b/cim-client/src/test/resources/application.yaml @@ -0,0 +1,40 @@ +spring: + application: + name: cim-client + main: + # this will not be used to create real spring context, because don't need this context in test case. + web-application-type: none + +# web port +server: + port: 8082 + +logging: + level: + root: error + +# enable swagger +springdoc: + swagger-ui: + enabled: true + +# log path +cim: + msg: + logger: + path: /opt/logs/cim/ + route: + url: http://localhost:8083 # route url suggested that this is Nginx address + user: # cim userId and userName + id: 1722343979085 + userName: zhangsan + callback: + thread: + queue: + size: 1000 + pool: + size: 2 + heartbeat: + time: 60 # cim heartbeat time (seconds) + reconnect: + count: 3 \ No newline at end of file diff --git a/cim-common/pom.xml b/cim-common/pom.xml index e878860a..c83720ea 100644 --- a/cim-common/pom.xml +++ b/cim-common/pom.xml @@ -8,7 +8,7 @@ 1.0.0-SNAPSHOT 4.0.0 - 1.0.1-SNAPSHOT + 1.0.0-SNAPSHOT cim-common @@ -42,11 +42,40 @@ guava + - com.github.sgroschupf + com.101tec zkclient + + + org.apache.curator + curator-recipes + + + + org.apache.zookeeper + zookeeper + + + + junit + junit + + + + org.junit.vintage + junit-vintage-engine + test + + + + org.junit.jupiter + junit-jupiter + test + + io.netty netty-all @@ -58,4 +87,34 @@ fastjson + + + + + kr.motd.maven + os-maven-plugin + 1.5.0.Final + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.5.1 + + com.google.protobuf:protoc:${protobuf-java.version}:exe:${os.detected.classifier} + grpc-java + io.grpc:protoc-gen-grpc-java:1.19.0:exe:${os.detected.classifier} + + + + + compile + compile-custom + + + + + + \ No newline at end of file diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/constant/Constants.java b/cim-common/src/main/java/com/crossoverjie/cim/common/constant/Constants.java index e4191eda..f128b46c 100644 --- a/cim-common/src/main/java/com/crossoverjie/cim/common/constant/Constants.java +++ b/cim-common/src/main/java/com/crossoverjie/cim/common/constant/Constants.java @@ -21,25 +21,9 @@ public class Constants { */ public static final String COUNTER_CLIENT_PUSH_COUNT = "counter.client.push.count" ; - - /** - * 自定义报文类型 - */ - public static class CommandType{ - /** - * 登录 - */ - public static final int LOGIN = 1 ; - /** - * 业务消息 - */ - public static final int MSG = 2 ; - - /** - * ping - */ - public static final int PING = 3 ; + public static class MetaKey { + public static final String USER_ID = "userId" ; + public static final String USER_NAME = "userName" ; } - } diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/core/proxy/DynamicUrl.java b/cim-common/src/main/java/com/crossoverjie/cim/common/core/proxy/DynamicUrl.java new file mode 100644 index 00000000..22e5f768 --- /dev/null +++ b/cim-common/src/main/java/com/crossoverjie/cim/common/core/proxy/DynamicUrl.java @@ -0,0 +1,15 @@ +package com.crossoverjie.cim.common.core.proxy; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author crossoverJie + */ +@Target({ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface DynamicUrl { + boolean useMethodEndpoint() default true; +} diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/core/proxy/ProxyManager.java b/cim-common/src/main/java/com/crossoverjie/cim/common/core/proxy/ProxyManager.java deleted file mode 100644 index 92726f5a..00000000 --- a/cim-common/src/main/java/com/crossoverjie/cim/common/core/proxy/ProxyManager.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.crossoverjie.cim.common.core.proxy; - -import com.alibaba.fastjson.JSONObject; -import com.crossoverjie.cim.common.exception.CIMException; -import com.crossoverjie.cim.common.util.HttpClient; -import okhttp3.OkHttpClient; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.net.URI; - -import static com.crossoverjie.cim.common.enums.StatusEnum.VALIDATION_FAIL; - -/** - * Function: - * - * @author crossoverJie - * Date: 2020-04-25 00:18 - * @since JDK 1.8 - */ -public final class ProxyManager { - - - private Class clazz; - - private String url; - - private OkHttpClient okHttpClient; - - /** - * - * @param clazz Proxied interface - * @param url server provider url - * @param okHttpClient http client - */ - public ProxyManager(Class clazz, String url, OkHttpClient okHttpClient) { - this.clazz = clazz; - this.url = url; - this.okHttpClient = okHttpClient; - } - - /** - * Get proxy instance of api. - * @return - */ - public T getInstance() { - return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{clazz}, new ProxyInvocation()); - } - - - private class ProxyInvocation implements InvocationHandler { - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - JSONObject jsonObject = new JSONObject(); - String serverUrl = url + "/" + method.getName() ; - - URI serverUri = new URI(serverUrl); - serverUrl = serverUri.normalize().toString(); - - if (args != null && args.length > 1) { - throw new CIMException(VALIDATION_FAIL); - } - - if (method.getParameterTypes().length > 0){ - Object para = args[0]; - Class parameterType = method.getParameterTypes()[0]; - for (Field field : parameterType.getDeclaredFields()) { - field.setAccessible(true); - jsonObject.put(field.getName(), field.get(para)); - } - } - return HttpClient.call(okHttpClient, jsonObject.toString(), serverUrl); - } - } -} diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/core/proxy/Request.java b/cim-common/src/main/java/com/crossoverjie/cim/common/core/proxy/Request.java new file mode 100644 index 00000000..e5d797e8 --- /dev/null +++ b/cim-common/src/main/java/com/crossoverjie/cim/common/core/proxy/Request.java @@ -0,0 +1,16 @@ +package com.crossoverjie.cim.common.core.proxy; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * @author crossoverJie + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface Request { + String method() default POST; + String url() default ""; + + String GET = "GET"; + String POST = "POST"; +} diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/core/proxy/RpcProxyManager.java b/cim-common/src/main/java/com/crossoverjie/cim/common/core/proxy/RpcProxyManager.java new file mode 100644 index 00000000..ce9323a8 --- /dev/null +++ b/cim-common/src/main/java/com/crossoverjie/cim/common/core/proxy/RpcProxyManager.java @@ -0,0 +1,229 @@ +package com.crossoverjie.cim.common.core.proxy; + +import static com.crossoverjie.cim.common.enums.StatusEnum.VALIDATION_FAIL; +import com.alibaba.fastjson.JSONObject; +import com.crossoverjie.cim.common.exception.CIMException; +import com.crossoverjie.cim.common.util.HttpClient; +import com.crossoverjie.cim.common.util.StringUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; +import java.net.URI; +import lombok.extern.slf4j.Slf4j; +import okhttp3.OkHttpClient; +import okhttp3.Response; + +/** + * RpcProxyManager is a proxy manager for creating dynamic proxy instances of interfaces. + * It handles HTTP requests and responses using OkHttpClient. + * + * @param the type of the proxied interface + */ +@Slf4j +public final class RpcProxyManager { + + private Class clazz; + private String url; + private OkHttpClient okHttpClient; + private final ObjectMapper objectMapper = new ObjectMapper(); + + /** + * Private constructor to initialize RpcProxyManager. + * + * @param clazz Proxied interface + * @param url Server provider URL + * @param okHttpClient HTTP client + */ + private RpcProxyManager(Class clazz, String url, OkHttpClient okHttpClient) { + this.clazz = clazz; + this.url = url; + this.okHttpClient = okHttpClient; + } + + private RpcProxyManager(Class clazz, OkHttpClient okHttpClient) { + this(clazz, "", okHttpClient); + } + + /** + * Default private constructor. + */ + private RpcProxyManager() { + } + + /** + * Creates a proxy instance of the specified interface. + * + * @param clazz Proxied interface + * @param url Server provider URL + * @param okHttpClient HTTP client + * @param Type of the proxied interface + * @return Proxy instance of the specified interface + */ + public static T create(Class clazz, String url, OkHttpClient okHttpClient) { + return new RpcProxyManager<>(clazz, url, okHttpClient).getInstance(); + } + + public static T create(Class clazz, OkHttpClient okHttpClient) { + return new RpcProxyManager<>(clazz, okHttpClient).getInstance(); + } + + /** + * Gets the proxy instance of the API. + * + * @return Proxy instance of the API + */ + @SuppressWarnings("unchecked") + public T getInstance() { + return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{clazz}, + new ProxyInvocation()); + } + + /** + * ProxyInvocation is an invocation handler for handling method calls on proxy instances. + */ + private class ProxyInvocation implements InvocationHandler { + + /** + * Handles method calls on proxy instances. + * + * @param proxy The proxy instance + * @param method The method being called + * @param args The arguments of the method call + * @return The result of the method call + * @throws Throwable if an error occurs during method invocation + */ + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + + Response result = null; + String serverUrl = url + "/" + method.getName(); + Request annotation = method.getAnnotation(Request.class); + if (annotation != null && StringUtil.isNotEmpty(annotation.url())) { + serverUrl = url + "/" + annotation.url(); + } + URI serverUri = new URI(serverUrl); + serverUrl = serverUri.normalize().toString(); + + Object para = null; + Class parameterType = null; + for (int i = 0; i < method.getParameterAnnotations().length; i++) { + Annotation[] annotations = method.getParameterAnnotations()[i]; + if (annotations.length == 0) { + para = args[i]; + parameterType = method.getParameterTypes()[i]; + } + + for (Annotation ann : annotations) { + if (ann instanceof DynamicUrl) { + if (args[i] instanceof String) { + serverUrl = (String) args[i]; + if (((DynamicUrl) ann).useMethodEndpoint()) { + serverUrl = serverUrl + "/" + method.getName(); + } + break; + } else { + throw new CIMException("DynamicUrl must be String type"); + } + } + } + } + + try { + if (annotation != null && annotation.method().equals(Request.GET)) { + result = HttpClient.get(okHttpClient, serverUrl); + } else { + + if (args == null || args.length > 2 || para == null || parameterType == null) { + throw new IllegalArgumentException(VALIDATION_FAIL.message()); + } + JSONObject jsonObject = new JSONObject(); + for (Field field : parameterType.getDeclaredFields()) { + field.setAccessible(true); + jsonObject.put(field.getName(), field.get(para)); + } + + result = HttpClient.post(okHttpClient, jsonObject.toString(), serverUrl); + } + if (method.getReturnType() == void.class) { + return null; + } + + String json = result.body().string(); + Type genericTypeOfBaseResponse = getGenericTypeOfBaseResponse(method); + if (genericTypeOfBaseResponse == null) { + return objectMapper.readValue(json, method.getReturnType()); + } else { + return objectMapper.readValue(json, objectMapper.getTypeFactory() + .constructParametricType(method.getReturnType(), + objectMapper.getTypeFactory().constructType(genericTypeOfBaseResponse))); + } + } finally { + if (result != null) { + result.body().close(); + } + } + } + } + + private Type getGenericTypeOfBaseResponse(Method declaredMethod) { + Type returnType = declaredMethod.getGenericReturnType(); + + // check if the return type is a parameterized type + if (returnType instanceof ParameterizedType parameterizedType) { + + Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); + + for (Type typeArgument : actualTypeArguments) { + return typeArgument; + } + } + + return null; + + } + + /** + * Gets the generic type of the BaseResponse. + * + * @param declaredMethod The method whose return type is being checked + * @return The generic type of the BaseResponse, or null if not found + * @throws ClassNotFoundException if the class of the generic type is not found + private Class getBaseResponseGeneric(Method declaredMethod) throws ClassNotFoundException { + + Type returnType = declaredMethod.getGenericReturnType(); + + // check if the return type is a parameterized type + if (returnType instanceof ParameterizedType parameterizedType) { + + Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); + + for (Type typeArgument : actualTypeArguments) { + // BaseResponse only has one generic type + return getClass(typeArgument); + } + } + + return null; + } + + public static Class getClass(Type type) throws ClassNotFoundException { + if (type instanceof Class) { + // 普通类型,直接返回 + return (Class) type; + } else if (type instanceof ParameterizedType) { + // 参数化类型,返回原始类型 + return getClass(((ParameterizedType) type).getRawType()); + } else if (type instanceof TypeVariable) { + // 类型变量,无法在运行时获取具体类型 + return Object.class; + } else { + throw new ClassNotFoundException("无法处理的类型: " + type.toString()); + } + }*/ + +} \ No newline at end of file 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 e296d8b5..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 @@ -78,14 +91,11 @@ public String firstNodeValue(long key) { * 排序 */ public void sort() { - Arrays.sort(buckets, 0, size, new Comparator() { - @Override - public int compare(Node o1, Node o2) { - if (o1.key > o2.key) { - return 1; - } else { - return -1; - } + Arrays.sort(buckets, 0, size, (o1, o2) -> { + if (o1.key > o2.key) { + return 1; + } else { + return 0; } }); } @@ -108,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 d19afbd7..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 @@ -10,7 +10,7 @@ public enum StatusEnum { /** 成功 */ - SUCCESS("9000", "成功"), + SUCCESS("9000", "Success"), /** 成功 */ FALLBACK("8000", "FALL_BACK"), /** 参数校验失败**/ @@ -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/AbstractConfiguration.java b/cim-common/src/main/java/com/crossoverjie/cim/common/metastore/AbstractConfiguration.java new file mode 100644 index 00000000..e028ea26 --- /dev/null +++ b/cim-common/src/main/java/com/crossoverjie/cim/common/metastore/AbstractConfiguration.java @@ -0,0 +1,16 @@ +package com.crossoverjie.cim.common.metastore; + +import lombok.Builder; +import lombok.Data; + +/** + * @author crossverJie + */ +@Data +@Builder +public class AbstractConfiguration { + + private String metaServiceUri; + private int timeoutMs; + private RETRY retryPolicy; +} diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/metastore/MetaStore.java b/cim-common/src/main/java/com/crossoverjie/cim/common/metastore/MetaStore.java new file mode 100644 index 00000000..b2a09a94 --- /dev/null +++ b/cim-common/src/main/java/com/crossoverjie/cim/common/metastore/MetaStore.java @@ -0,0 +1,49 @@ +package com.crossoverjie.cim.common.metastore; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +/** + * @author crossoverJie + */ +public interface MetaStore { + + void initialize(AbstractConfiguration configuration) throws Exception; + + /** + * Get available server list + * @return available server list + * @throws Exception exception + */ + Set getAvailableServerList() throws Exception; + + /** + * Add server to meta store + * @throws Exception exception + */ + void addServer(String ip, int cimServerPort, int httpPort) throws Exception; + + /** + * Subscribe server list + * @param childListener child listener + * @throws Exception exception + */ + void listenServerList(ChildListener childListener) throws Exception; + + + /** + * @throws Exception + */ + void rebuildCache() throws Exception; + + interface ChildListener { + /** + * Child changed + * @param parentPath parent path(eg. for zookeeper: [/cim]) + * @param currentChildren current children + * @throws Exception exception + */ + void childChanged(String parentPath, List currentChildren) throws Exception; + } +} diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/metastore/ZkConfiguration.java b/cim-common/src/main/java/com/crossoverjie/cim/common/metastore/ZkConfiguration.java new file mode 100644 index 00000000..4b95ee72 --- /dev/null +++ b/cim-common/src/main/java/com/crossoverjie/cim/common/metastore/ZkConfiguration.java @@ -0,0 +1,12 @@ +package com.crossoverjie.cim.common.metastore; + +import org.apache.curator.RetryPolicy; + +/** + * @author crossoverJie + */ +public class ZkConfiguration extends AbstractConfiguration { + ZkConfiguration(String metaServiceUri, int timeout, RetryPolicy retryPolicy) { + super(metaServiceUri, timeout, retryPolicy); + } +} 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 new file mode 100644 index 00000000..91f141f9 --- /dev/null +++ b/cim-common/src/main/java/com/crossoverjie/cim/common/metastore/ZkMetaStoreImpl.java @@ -0,0 +1,119 @@ +package com.crossoverjie.cim.common.metastore; + +import com.crossoverjie.cim.common.pojo.RouteInfo; +import com.crossoverjie.cim.common.util.RouteInfoParseUtil; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.I0Itec.zkclient.ZkClient; +import org.apache.curator.framework.CuratorFramework; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.Watcher; + +/** + * @author crossovreJie + */ +@Slf4j +public class ZkMetaStoreImpl implements MetaStore { + public static final String ROOT = "/cim"; + + private ZkClient client; + + LoadingCache cache; + + @Override + public void initialize(AbstractConfiguration configuration) throws Exception { + // TODO: 2024/8/19 Change to set or caffeine? + cache = CacheBuilder.newBuilder() + .concurrencyLevel(3) + .build(new CacheLoader<>() { + @Override + public String load(String s) { + return null; + } + }); + client = new ZkClient(configuration.getMetaServiceUri(), configuration.getTimeoutMs()); + } + + @Override + public Set getAvailableServerList() throws Exception { + if (cache.size() > 0) { + return cache.asMap().keySet(); + } + List coll = client.getChildren(ROOT); + Map voidMap = coll.stream().collect(Collectors.toMap( + Function.identity(), + Function.identity() + )); + cache.putAll(voidMap); + return voidMap.keySet(); + } + + @Override + public void addServer(String ip, int cimServerPort, int httpPort) throws Exception { + boolean exists = client.exists(ROOT); + if (!exists) { + client.createPersistent(ROOT); + } + String zkParse = RouteInfoParseUtil.parse(RouteInfo.builder() + .ip(ip) + .cimServerPort(cimServerPort) + .httpPort(httpPort) + .build()); + String serverPath = String.format("%s/%s", ROOT, zkParse); + client.createEphemeral(serverPath); + log.info("Add server to zk [{}]", serverPath); + } + + @Override + public void listenServerList(ChildListener childListener) throws Exception { + client.subscribeChildChanges(ROOT, (parentPath, currentChildren) -> { + 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. + // Because rebuildCache() will re-fetch the server list from zk. + rebuildCache(); + }); + } + + @Override + public synchronized void rebuildCache() throws Exception { + cache.invalidateAll(); + + // Because of calling invalidateAll, this method will re-fetch the server list from zk. + this.getAvailableServerList(); + + } + + + private List watchedGetChildren(CuratorFramework client, String path) throws Exception { + /** + * Get children and set a watcher on the node. The watcher notification will come through the + * CuratorListener (see setDataAsync() above). + */ + return client.getChildren().watched().forPath(path); + } + + private void createEphemeral(CuratorFramework client, String path, byte[] payload) throws Exception { + // this will create the given EPHEMERAL ZNode with the given data + client.create().withMode(CreateMode.EPHEMERAL).forPath(path, payload); + } + + private void create(CuratorFramework client, String path, byte[] payload) throws Exception { + // this will create the given ZNode with the given data + client.create().forPath(path, payload); + } + + private void watchedGetChildren(CuratorFramework client, String path, Watcher watcher) + throws Exception { + // Get children and set the given watcher on the node. + client.getChildren().usingWatcher(watcher).forPath(path); + } +} diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/pojo/CIMUserInfo.java b/cim-common/src/main/java/com/crossoverjie/cim/common/pojo/CIMUserInfo.java index 2e50f3fa..5c6ed5b7 100644 --- a/cim-common/src/main/java/com/crossoverjie/cim/common/pojo/CIMUserInfo.java +++ b/cim-common/src/main/java/com/crossoverjie/cim/common/pojo/CIMUserInfo.java @@ -1,5 +1,9 @@ package com.crossoverjie.cim.common.pojo; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + /** * Function: 用户信息 * @@ -7,36 +11,11 @@ * Date: 2018/12/24 02:33 * @since JDK 1.8 */ +@Data +@AllArgsConstructor +@NoArgsConstructor public class CIMUserInfo { private Long userId ; private String userName ; - public CIMUserInfo(Long userId, String userName) { - this.userId = userId; - this.userName = userName; - } - - 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; - } - - @Override - public String toString() { - return "CIMUserInfo{" + - "userId=" + userId + - ", userName='" + userName + '\'' + - '}'; - } } diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/pojo/RouteInfo.java b/cim-common/src/main/java/com/crossoverjie/cim/common/pojo/RouteInfo.java index eec73743..0e72592f 100644 --- a/cim-common/src/main/java/com/crossoverjie/cim/common/pojo/RouteInfo.java +++ b/cim-common/src/main/java/com/crossoverjie/cim/common/pojo/RouteInfo.java @@ -1,5 +1,9 @@ package com.crossoverjie.cim.common.pojo; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + /** * Function: * @@ -7,39 +11,12 @@ * Date: 2020-04-12 20:48 * @since JDK 1.8 */ +@Data +@AllArgsConstructor +@Builder public final class RouteInfo { private String ip ; private Integer cimServerPort; private Integer httpPort; - - public RouteInfo(String ip, Integer cimServerPort, Integer httpPort) { - this.ip = ip; - this.cimServerPort = cimServerPort; - this.httpPort = httpPort; - } - - public String getIp() { - return ip; - } - - public void setIp(String ip) { - this.ip = ip; - } - - public Integer getCimServerPort() { - return cimServerPort; - } - - public void setCimServerPort(Integer cimServerPort) { - this.cimServerPort = cimServerPort; - } - - public Integer getHttpPort() { - return httpPort; - } - - public void setHttpPort(Integer httpPort) { - this.httpPort = httpPort; - } } diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/protocol/CIMRequestProto.java b/cim-common/src/main/java/com/crossoverjie/cim/common/protocol/CIMRequestProto.java deleted file mode 100644 index 2b61c77a..00000000 --- a/cim-common/src/main/java/com/crossoverjie/cim/common/protocol/CIMRequestProto.java +++ /dev/null @@ -1,811 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: BaseRequestProto.proto - -package com.crossoverjie.cim.common.protocol; - -public final class CIMRequestProto { - private CIMRequestProto() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistryLite registry) { - } - - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - registerAllExtensions( - (com.google.protobuf.ExtensionRegistryLite) registry); - } - public interface CIMReqProtocolOrBuilder extends - // @@protoc_insertion_point(interface_extends:protocol.CIMReqProtocol) - com.google.protobuf.MessageOrBuilder { - - /** - * required int64 requestId = 2; - */ - boolean hasRequestId(); - /** - * required int64 requestId = 2; - */ - long getRequestId(); - - /** - * required string reqMsg = 1; - */ - boolean hasReqMsg(); - /** - * required string reqMsg = 1; - */ - String getReqMsg(); - /** - * required string reqMsg = 1; - */ - com.google.protobuf.ByteString - getReqMsgBytes(); - - /** - * required int32 type = 3; - */ - boolean hasType(); - /** - * required int32 type = 3; - */ - int getType(); - } - /** - * Protobuf type {@code protocol.CIMReqProtocol} - */ - public static final class CIMReqProtocol extends - com.google.protobuf.GeneratedMessageV3 implements - // @@protoc_insertion_point(message_implements:protocol.CIMReqProtocol) - CIMReqProtocolOrBuilder { - private static final long serialVersionUID = 0L; - // Use CIMReqProtocol.newBuilder() to construct. - private CIMReqProtocol(com.google.protobuf.GeneratedMessageV3.Builder builder) { - super(builder); - } - private CIMReqProtocol() { - requestId_ = 0L; - reqMsg_ = ""; - type_ = 0; - } - - @Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private CIMReqProtocol( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - this(); - if (extensionRegistry == null) { - throw new NullPointerException(); - } - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField( - input, unknownFields, extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - com.google.protobuf.ByteString bs = input.readBytes(); - bitField0_ |= 0x00000002; - reqMsg_ = bs; - break; - } - case 16: { - bitField0_ |= 0x00000001; - requestId_ = input.readInt64(); - break; - } - case 24: { - bitField0_ |= 0x00000004; - type_ = input.readInt32(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return CIMRequestProto.internal_static_protocol_CIMReqProtocol_descriptor; - } - - protected FieldAccessorTable - internalGetFieldAccessorTable() { - return CIMRequestProto.internal_static_protocol_CIMReqProtocol_fieldAccessorTable - .ensureFieldAccessorsInitialized( - CIMReqProtocol.class, Builder.class); - } - - private int bitField0_; - public static final int REQUESTID_FIELD_NUMBER = 2; - private long requestId_; - /** - * required int64 requestId = 2; - */ - public boolean hasRequestId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * required int64 requestId = 2; - */ - public long getRequestId() { - return requestId_; - } - - public static final int REQMSG_FIELD_NUMBER = 1; - private volatile Object reqMsg_; - /** - * required string reqMsg = 1; - */ - public boolean hasReqMsg() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * required string reqMsg = 1; - */ - public String getReqMsg() { - Object ref = reqMsg_; - if (ref instanceof String) { - return (String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - reqMsg_ = s; - } - return s; - } - } - /** - * required string reqMsg = 1; - */ - public com.google.protobuf.ByteString - getReqMsgBytes() { - Object ref = reqMsg_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (String) ref); - reqMsg_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - public static final int TYPE_FIELD_NUMBER = 3; - private int type_; - /** - * required int32 type = 3; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * required int32 type = 3; - */ - public int getType() { - return type_; - } - - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized == 1) return true; - if (isInitialized == 0) return false; - - if (!hasRequestId()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasReqMsg()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasType()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - if (((bitField0_ & 0x00000002) == 0x00000002)) { - com.google.protobuf.GeneratedMessageV3.writeString(output, 1, reqMsg_); - } - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeInt64(2, requestId_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeInt32(3, type_); - } - unknownFields.writeTo(output); - } - - public int getSerializedSize() { - int size = memoizedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, reqMsg_); - } - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeInt64Size(2, requestId_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeInt32Size(3, type_); - } - size += unknownFields.getSerializedSize(); - memoizedSize = size; - return size; - } - - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof CIMReqProtocol)) { - return super.equals(obj); - } - CIMReqProtocol other = (CIMReqProtocol) obj; - - boolean result = true; - result = result && (hasRequestId() == other.hasRequestId()); - if (hasRequestId()) { - result = result && (getRequestId() - == other.getRequestId()); - } - result = result && (hasReqMsg() == other.hasReqMsg()); - if (hasReqMsg()) { - result = result && getReqMsg() - .equals(other.getReqMsg()); - } - result = result && (hasType() == other.hasType()); - if (hasType()) { - result = result && (getType() - == other.getType()); - } - result = result && unknownFields.equals(other.unknownFields); - return result; - } - - @Override - public int hashCode() { - if (memoizedHashCode != 0) { - return memoizedHashCode; - } - int hash = 41; - hash = (19 * hash) + getDescriptor().hashCode(); - if (hasRequestId()) { - hash = (37 * hash) + REQUESTID_FIELD_NUMBER; - hash = (53 * hash) + com.google.protobuf.Internal.hashLong( - getRequestId()); - } - if (hasReqMsg()) { - hash = (37 * hash) + REQMSG_FIELD_NUMBER; - hash = (53 * hash) + getReqMsg().hashCode(); - } - if (hasType()) { - hash = (37 * hash) + TYPE_FIELD_NUMBER; - hash = (53 * hash) + getType(); - } - hash = (29 * hash) + unknownFields.hashCode(); - memoizedHashCode = hash; - return hash; - } - - public static CIMReqProtocol parseFrom( - java.nio.ByteBuffer data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static CIMReqProtocol parseFrom( - java.nio.ByteBuffer data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static CIMReqProtocol parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static CIMReqProtocol parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static CIMReqProtocol parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static CIMReqProtocol parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static CIMReqProtocol parseFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - public static CIMReqProtocol parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - public static CIMReqProtocol parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input); - } - public static CIMReqProtocol parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input, extensionRegistry); - } - public static CIMReqProtocol parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - public static CIMReqProtocol parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder() { - return DEFAULT_INSTANCE.toBuilder(); - } - public static Builder newBuilder(CIMReqProtocol prototype) { - return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { - return this == DEFAULT_INSTANCE - ? new Builder() : new Builder().mergeFrom(this); - } - - @Override - protected Builder newBuilderForType( - BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code protocol.CIMReqProtocol} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessageV3.Builder implements - // @@protoc_insertion_point(builder_implements:protocol.CIMReqProtocol) - CIMReqProtocolOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return CIMRequestProto.internal_static_protocol_CIMReqProtocol_descriptor; - } - - protected FieldAccessorTable - internalGetFieldAccessorTable() { - return CIMRequestProto.internal_static_protocol_CIMReqProtocol_fieldAccessorTable - .ensureFieldAccessorsInitialized( - CIMReqProtocol.class, Builder.class); - } - - // Construct using com.crossoverjie.cim.common.protocol.CIMRequestProto.CIMReqProtocol.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessageV3 - .alwaysUseFieldBuilders) { - } - } - public Builder clear() { - super.clear(); - requestId_ = 0L; - bitField0_ = (bitField0_ & ~0x00000001); - reqMsg_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - type_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return CIMRequestProto.internal_static_protocol_CIMReqProtocol_descriptor; - } - - public CIMReqProtocol getDefaultInstanceForType() { - return CIMReqProtocol.getDefaultInstance(); - } - - public CIMReqProtocol build() { - CIMReqProtocol result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public CIMReqProtocol buildPartial() { - CIMReqProtocol result = new CIMReqProtocol(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.requestId_ = requestId_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.reqMsg_ = reqMsg_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.type_ = type_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder clone() { - return (Builder) super.clone(); - } - public Builder setField( - com.google.protobuf.Descriptors.FieldDescriptor field, - Object value) { - return (Builder) super.setField(field, value); - } - public Builder clearField( - com.google.protobuf.Descriptors.FieldDescriptor field) { - return (Builder) super.clearField(field); - } - public Builder clearOneof( - com.google.protobuf.Descriptors.OneofDescriptor oneof) { - return (Builder) super.clearOneof(oneof); - } - public Builder setRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - int index, Object value) { - return (Builder) super.setRepeatedField(field, index, value); - } - public Builder addRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - Object value) { - return (Builder) super.addRepeatedField(field, value); - } - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof CIMReqProtocol) { - return mergeFrom((CIMReqProtocol)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(CIMReqProtocol other) { - if (other == CIMReqProtocol.getDefaultInstance()) return this; - if (other.hasRequestId()) { - setRequestId(other.getRequestId()); - } - if (other.hasReqMsg()) { - bitField0_ |= 0x00000002; - reqMsg_ = other.reqMsg_; - onChanged(); - } - if (other.hasType()) { - setType(other.getType()); - } - this.mergeUnknownFields(other.unknownFields); - onChanged(); - return this; - } - - public final boolean isInitialized() { - if (!hasRequestId()) { - return false; - } - if (!hasReqMsg()) { - return false; - } - if (!hasType()) { - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - CIMReqProtocol parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (CIMReqProtocol) e.getUnfinishedMessage(); - throw e.unwrapIOException(); - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - private long requestId_ ; - /** - * required int64 requestId = 2; - */ - public boolean hasRequestId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * required int64 requestId = 2; - */ - public long getRequestId() { - return requestId_; - } - /** - * required int64 requestId = 2; - */ - public Builder setRequestId(long value) { - bitField0_ |= 0x00000001; - requestId_ = value; - onChanged(); - return this; - } - /** - * required int64 requestId = 2; - */ - public Builder clearRequestId() { - bitField0_ = (bitField0_ & ~0x00000001); - requestId_ = 0L; - onChanged(); - return this; - } - - private Object reqMsg_ = ""; - /** - * required string reqMsg = 1; - */ - public boolean hasReqMsg() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * required string reqMsg = 1; - */ - public String getReqMsg() { - Object ref = reqMsg_; - if (!(ref instanceof String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - reqMsg_ = s; - } - return s; - } else { - return (String) ref; - } - } - /** - * required string reqMsg = 1; - */ - public com.google.protobuf.ByteString - getReqMsgBytes() { - Object ref = reqMsg_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (String) ref); - reqMsg_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * required string reqMsg = 1; - */ - public Builder setReqMsg( - String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - reqMsg_ = value; - onChanged(); - return this; - } - /** - * required string reqMsg = 1; - */ - public Builder clearReqMsg() { - bitField0_ = (bitField0_ & ~0x00000002); - reqMsg_ = getDefaultInstance().getReqMsg(); - onChanged(); - return this; - } - /** - * required string reqMsg = 1; - */ - public Builder setReqMsgBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - reqMsg_ = value; - onChanged(); - return this; - } - - private int type_ ; - /** - * required int32 type = 3; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * required int32 type = 3; - */ - public int getType() { - return type_; - } - /** - * required int32 type = 3; - */ - public Builder setType(int value) { - bitField0_ |= 0x00000004; - type_ = value; - onChanged(); - return this; - } - /** - * required int32 type = 3; - */ - public Builder clearType() { - bitField0_ = (bitField0_ & ~0x00000004); - type_ = 0; - onChanged(); - return this; - } - public final Builder setUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return super.setUnknownFields(unknownFields); - } - - public final Builder mergeUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return super.mergeUnknownFields(unknownFields); - } - - - // @@protoc_insertion_point(builder_scope:protocol.CIMReqProtocol) - } - - // @@protoc_insertion_point(class_scope:protocol.CIMReqProtocol) - private static final CIMReqProtocol DEFAULT_INSTANCE; - static { - DEFAULT_INSTANCE = new CIMReqProtocol(); - } - - public static CIMReqProtocol getDefaultInstance() { - return DEFAULT_INSTANCE; - } - - @Deprecated public static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { - public CIMReqProtocol parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new CIMReqProtocol(input, extensionRegistry); - } - }; - - public static com.google.protobuf.Parser parser() { - return PARSER; - } - - @Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public CIMReqProtocol getDefaultInstanceForType() { - return DEFAULT_INSTANCE; - } - - } - - private static final com.google.protobuf.Descriptors.Descriptor - internal_static_protocol_CIMReqProtocol_descriptor; - private static final - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internal_static_protocol_CIMReqProtocol_fieldAccessorTable; - - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - String[] descriptorData = { - "\n\026BaseRequestProto.proto\022\010protocol\"A\n\016CI" + - "MReqProtocol\022\021\n\trequestId\030\002 \002(\003\022\016\n\006reqMs" + - "g\030\001 \002(\t\022\014\n\004type\030\003 \002(\005B7\n$com.crossoverji" + - "e.cim.common.protocolB\017CIMRequestProto" - }; - com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = - new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() { - public com.google.protobuf.ExtensionRegistry assignDescriptors( - com.google.protobuf.Descriptors.FileDescriptor root) { - descriptor = root; - return null; - } - }; - com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }, assigner); - internal_static_protocol_CIMReqProtocol_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_protocol_CIMReqProtocol_fieldAccessorTable = new - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( - internal_static_protocol_CIMReqProtocol_descriptor, - new String[] { "RequestId", "ReqMsg", "Type", }); - } - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/protocol/CIMResponseProto.java b/cim-common/src/main/java/com/crossoverjie/cim/common/protocol/CIMResponseProto.java deleted file mode 100644 index 31bb4668..00000000 --- a/cim-common/src/main/java/com/crossoverjie/cim/common/protocol/CIMResponseProto.java +++ /dev/null @@ -1,812 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: BaseResponseProto.proto - -package com.crossoverjie.cim.common.protocol; - -public final class CIMResponseProto { - private CIMResponseProto() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistryLite registry) { - } - - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - registerAllExtensions( - (com.google.protobuf.ExtensionRegistryLite) registry); - } - public interface CIMResProtocolOrBuilder extends - // @@protoc_insertion_point(interface_extends:protocol.CIMResProtocol) - com.google.protobuf.MessageOrBuilder { - - /** - * required int64 responseId = 2; - */ - boolean hasResponseId(); - /** - * required int64 responseId = 2; - */ - long getResponseId(); - - /** - * required string resMsg = 1; - */ - boolean hasResMsg(); - /** - * required string resMsg = 1; - */ - String getResMsg(); - /** - * required string resMsg = 1; - */ - com.google.protobuf.ByteString - getResMsgBytes(); - - /** - * required int32 type = 3; - */ - boolean hasType(); - /** - * required int32 type = 3; - */ - int getType(); - } - /** - * Protobuf type {@code protocol.CIMResProtocol} - */ - public static final class CIMResProtocol extends - com.google.protobuf.GeneratedMessageV3 implements - // @@protoc_insertion_point(message_implements:protocol.CIMResProtocol) - CIMResProtocolOrBuilder { - private static final long serialVersionUID = 0L; - // Use CIMResProtocol.newBuilder() to construct. - private CIMResProtocol(com.google.protobuf.GeneratedMessageV3.Builder builder) { - super(builder); - } - private CIMResProtocol() { - responseId_ = 0L; - resMsg_ = ""; - type_ = 0; - } - - @Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private CIMResProtocol( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - this(); - if (extensionRegistry == null) { - throw new NullPointerException(); - } - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField( - input, unknownFields, extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - com.google.protobuf.ByteString bs = input.readBytes(); - bitField0_ |= 0x00000002; - resMsg_ = bs; - break; - } - case 16: { - bitField0_ |= 0x00000001; - responseId_ = input.readInt64(); - break; - } - case 24: { - bitField0_ |= 0x00000004; - type_ = input.readInt32(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return CIMResponseProto.internal_static_protocol_CIMResProtocol_descriptor; - } - - protected FieldAccessorTable - internalGetFieldAccessorTable() { - return CIMResponseProto.internal_static_protocol_CIMResProtocol_fieldAccessorTable - .ensureFieldAccessorsInitialized( - CIMResProtocol.class, Builder.class); - } - - private int bitField0_; - public static final int RESPONSEID_FIELD_NUMBER = 2; - private long responseId_; - /** - * required int64 responseId = 2; - */ - public boolean hasResponseId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * required int64 responseId = 2; - */ - public long getResponseId() { - return responseId_; - } - - public static final int RESMSG_FIELD_NUMBER = 1; - private volatile Object resMsg_; - /** - * required string resMsg = 1; - */ - public boolean hasResMsg() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * required string resMsg = 1; - */ - public String getResMsg() { - Object ref = resMsg_; - if (ref instanceof String) { - return (String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - resMsg_ = s; - } - return s; - } - } - /** - * required string resMsg = 1; - */ - public com.google.protobuf.ByteString - getResMsgBytes() { - Object ref = resMsg_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (String) ref); - resMsg_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - public static final int TYPE_FIELD_NUMBER = 3; - private int type_; - /** - * required int32 type = 3; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * required int32 type = 3; - */ - public int getType() { - return type_; - } - - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized == 1) return true; - if (isInitialized == 0) return false; - - if (!hasResponseId()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasResMsg()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasType()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - if (((bitField0_ & 0x00000002) == 0x00000002)) { - com.google.protobuf.GeneratedMessageV3.writeString(output, 1, resMsg_); - } - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeInt64(2, responseId_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeInt32(3, type_); - } - unknownFields.writeTo(output); - } - - public int getSerializedSize() { - int size = memoizedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, resMsg_); - } - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeInt64Size(2, responseId_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeInt32Size(3, type_); - } - size += unknownFields.getSerializedSize(); - memoizedSize = size; - return size; - } - - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof CIMResProtocol)) { - return super.equals(obj); - } - CIMResProtocol other = (CIMResProtocol) obj; - - boolean result = true; - result = result && (hasResponseId() == other.hasResponseId()); - if (hasResponseId()) { - result = result && (getResponseId() - == other.getResponseId()); - } - result = result && (hasResMsg() == other.hasResMsg()); - if (hasResMsg()) { - result = result && getResMsg() - .equals(other.getResMsg()); - } - result = result && (hasType() == other.hasType()); - if (hasType()) { - result = result && (getType() - == other.getType()); - } - result = result && unknownFields.equals(other.unknownFields); - return result; - } - - @Override - public int hashCode() { - if (memoizedHashCode != 0) { - return memoizedHashCode; - } - int hash = 41; - hash = (19 * hash) + getDescriptor().hashCode(); - if (hasResponseId()) { - hash = (37 * hash) + RESPONSEID_FIELD_NUMBER; - hash = (53 * hash) + com.google.protobuf.Internal.hashLong( - getResponseId()); - } - if (hasResMsg()) { - hash = (37 * hash) + RESMSG_FIELD_NUMBER; - hash = (53 * hash) + getResMsg().hashCode(); - } - if (hasType()) { - hash = (37 * hash) + TYPE_FIELD_NUMBER; - hash = (53 * hash) + getType(); - } - hash = (29 * hash) + unknownFields.hashCode(); - memoizedHashCode = hash; - return hash; - } - - public static CIMResProtocol parseFrom( - java.nio.ByteBuffer data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static CIMResProtocol parseFrom( - java.nio.ByteBuffer data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static CIMResProtocol parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static CIMResProtocol parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static CIMResProtocol parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static CIMResProtocol parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static CIMResProtocol parseFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - public static CIMResProtocol parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - public static CIMResProtocol parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input); - } - public static CIMResProtocol parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input, extensionRegistry); - } - public static CIMResProtocol parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - public static CIMResProtocol parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder() { - return DEFAULT_INSTANCE.toBuilder(); - } - public static Builder newBuilder(CIMResProtocol prototype) { - return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { - return this == DEFAULT_INSTANCE - ? new Builder() : new Builder().mergeFrom(this); - } - - @Override - protected Builder newBuilderForType( - BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code protocol.CIMResProtocol} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessageV3.Builder implements - // @@protoc_insertion_point(builder_implements:protocol.CIMResProtocol) - CIMResProtocolOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return CIMResponseProto.internal_static_protocol_CIMResProtocol_descriptor; - } - - protected FieldAccessorTable - internalGetFieldAccessorTable() { - return CIMResponseProto.internal_static_protocol_CIMResProtocol_fieldAccessorTable - .ensureFieldAccessorsInitialized( - CIMResProtocol.class, Builder.class); - } - - // Construct using com.crossoverjie.cim.common.protocol.CIMResponseProto.CIMResProtocol.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessageV3 - .alwaysUseFieldBuilders) { - } - } - public Builder clear() { - super.clear(); - responseId_ = 0L; - bitField0_ = (bitField0_ & ~0x00000001); - resMsg_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - type_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return CIMResponseProto.internal_static_protocol_CIMResProtocol_descriptor; - } - - public CIMResProtocol getDefaultInstanceForType() { - return CIMResProtocol.getDefaultInstance(); - } - - public CIMResProtocol build() { - CIMResProtocol result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public CIMResProtocol buildPartial() { - CIMResProtocol result = new CIMResProtocol(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.responseId_ = responseId_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.resMsg_ = resMsg_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.type_ = type_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder clone() { - return (Builder) super.clone(); - } - public Builder setField( - com.google.protobuf.Descriptors.FieldDescriptor field, - Object value) { - return (Builder) super.setField(field, value); - } - public Builder clearField( - com.google.protobuf.Descriptors.FieldDescriptor field) { - return (Builder) super.clearField(field); - } - public Builder clearOneof( - com.google.protobuf.Descriptors.OneofDescriptor oneof) { - return (Builder) super.clearOneof(oneof); - } - public Builder setRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - int index, Object value) { - return (Builder) super.setRepeatedField(field, index, value); - } - public Builder addRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - Object value) { - return (Builder) super.addRepeatedField(field, value); - } - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof CIMResProtocol) { - return mergeFrom((CIMResProtocol)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(CIMResProtocol other) { - if (other == CIMResProtocol.getDefaultInstance()) return this; - if (other.hasResponseId()) { - setResponseId(other.getResponseId()); - } - if (other.hasResMsg()) { - bitField0_ |= 0x00000002; - resMsg_ = other.resMsg_; - onChanged(); - } - if (other.hasType()) { - setType(other.getType()); - } - this.mergeUnknownFields(other.unknownFields); - onChanged(); - return this; - } - - public final boolean isInitialized() { - if (!hasResponseId()) { - return false; - } - if (!hasResMsg()) { - return false; - } - if (!hasType()) { - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - CIMResProtocol parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (CIMResProtocol) e.getUnfinishedMessage(); - throw e.unwrapIOException(); - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - private long responseId_ ; - /** - * required int64 responseId = 2; - */ - public boolean hasResponseId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * required int64 responseId = 2; - */ - public long getResponseId() { - return responseId_; - } - /** - * required int64 responseId = 2; - */ - public Builder setResponseId(long value) { - bitField0_ |= 0x00000001; - responseId_ = value; - onChanged(); - return this; - } - /** - * required int64 responseId = 2; - */ - public Builder clearResponseId() { - bitField0_ = (bitField0_ & ~0x00000001); - responseId_ = 0L; - onChanged(); - return this; - } - - private Object resMsg_ = ""; - /** - * required string resMsg = 1; - */ - public boolean hasResMsg() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * required string resMsg = 1; - */ - public String getResMsg() { - Object ref = resMsg_; - if (!(ref instanceof String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - resMsg_ = s; - } - return s; - } else { - return (String) ref; - } - } - /** - * required string resMsg = 1; - */ - public com.google.protobuf.ByteString - getResMsgBytes() { - Object ref = resMsg_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (String) ref); - resMsg_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * required string resMsg = 1; - */ - public Builder setResMsg( - String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - resMsg_ = value; - onChanged(); - return this; - } - /** - * required string resMsg = 1; - */ - public Builder clearResMsg() { - bitField0_ = (bitField0_ & ~0x00000002); - resMsg_ = getDefaultInstance().getResMsg(); - onChanged(); - return this; - } - /** - * required string resMsg = 1; - */ - public Builder setResMsgBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - resMsg_ = value; - onChanged(); - return this; - } - - private int type_ ; - /** - * required int32 type = 3; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * required int32 type = 3; - */ - public int getType() { - return type_; - } - /** - * required int32 type = 3; - */ - public Builder setType(int value) { - bitField0_ |= 0x00000004; - type_ = value; - onChanged(); - return this; - } - /** - * required int32 type = 3; - */ - public Builder clearType() { - bitField0_ = (bitField0_ & ~0x00000004); - type_ = 0; - onChanged(); - return this; - } - public final Builder setUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return super.setUnknownFields(unknownFields); - } - - public final Builder mergeUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return super.mergeUnknownFields(unknownFields); - } - - - // @@protoc_insertion_point(builder_scope:protocol.CIMResProtocol) - } - - // @@protoc_insertion_point(class_scope:protocol.CIMResProtocol) - private static final CIMResProtocol DEFAULT_INSTANCE; - static { - DEFAULT_INSTANCE = new CIMResProtocol(); - } - - public static CIMResProtocol getDefaultInstance() { - return DEFAULT_INSTANCE; - } - - @Deprecated public static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { - public CIMResProtocol parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new CIMResProtocol(input, extensionRegistry); - } - }; - - public static com.google.protobuf.Parser parser() { - return PARSER; - } - - @Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public CIMResProtocol getDefaultInstanceForType() { - return DEFAULT_INSTANCE; - } - - } - - private static final com.google.protobuf.Descriptors.Descriptor - internal_static_protocol_CIMResProtocol_descriptor; - private static final - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internal_static_protocol_CIMResProtocol_fieldAccessorTable; - - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - String[] descriptorData = { - "\n\027BaseResponseProto.proto\022\010protocol\"B\n\016C" + - "IMResProtocol\022\022\n\nresponseId\030\002 \002(\003\022\016\n\006res" + - "Msg\030\001 \002(\t\022\014\n\004type\030\003 \002(\005B8\n$com.crossover" + - "jie.cim.common.protocolB\020CIMResponseProt" + - "o" - }; - com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = - new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() { - public com.google.protobuf.ExtensionRegistry assignDescriptors( - com.google.protobuf.Descriptors.FileDescriptor root) { - descriptor = root; - return null; - } - }; - com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }, assigner); - internal_static_protocol_CIMResProtocol_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_protocol_CIMResProtocol_fieldAccessorTable = new - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( - internal_static_protocol_CIMResProtocol_descriptor, - new String[] { "ResponseId", "ResMsg", "Type", }); - } - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/protocol/ProtocolUtil.java b/cim-common/src/main/java/com/crossoverjie/cim/common/protocol/ProtocolUtil.java deleted file mode 100644 index 7e811a92..00000000 --- a/cim-common/src/main/java/com/crossoverjie/cim/common/protocol/ProtocolUtil.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.crossoverjie.cim.common.protocol; - -import com.google.protobuf.InvalidProtocolBufferException; - -/** - * Function: - * - * @author crossoverJie - * Date: 2018/8/1 12:24 - * @since JDK 1.8 - */ -public class ProtocolUtil { - - public static void main(String[] args) throws InvalidProtocolBufferException { - CIMRequestProto.CIMReqProtocol protocol = CIMRequestProto.CIMReqProtocol.newBuilder() - .setRequestId(123L) - .setReqMsg("你好啊") - .build(); - - byte[] encode = encode(protocol); - - CIMRequestProto.CIMReqProtocol parseFrom = decode(encode); - - System.out.println(protocol.toString()); - System.out.println(protocol.toString().equals(parseFrom.toString())); - } - - /** - * 编码 - * @param protocol - * @return - */ - public static byte[] encode(CIMRequestProto.CIMReqProtocol protocol){ - return protocol.toByteArray() ; - } - - /** - * 解码 - * @param bytes - * @return - * @throws InvalidProtocolBufferException - */ - public static CIMRequestProto.CIMReqProtocol decode(byte[] bytes) throws InvalidProtocolBufferException { - return CIMRequestProto.CIMReqProtocol.parseFrom(bytes); - } -} 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 73307b36..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,18 @@ 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 + */ + protected abstract void clear(); + /** * 排序节点,数据结构自身支持排序可以不用重写 */ @@ -40,7 +53,8 @@ protected void sort(){} * @return */ public String process(List values,String key){ - + // fix https://github.com/crossoverJie/cim/issues/79 + clear(); for (String value : values) { add(hash(value), value); } 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 ed7f17ee..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 @@ -1,6 +1,12 @@ package com.crossoverjie.cim.common.route.algorithm.consistenthash; 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; /** * Function:自定义排序 Map 实现 @@ -20,24 +26,41 @@ public class SortArrayMapConsistentHash extends AbstractConsistentHash { @Override public void add(long key, String value) { - // fix https://github.com/crossoverJie/cim/issues/79 - sortArrayMap.clear(); 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(); } + /** + * Used only in test. + * @return Return the data structure of the current algorithm. + */ + @VisibleForTesting + public SortArrayMap getSortArrayMap() { + return sortArrayMap; + } + + @Override + protected void clear() { + sortArrayMap.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 73f9869d..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 @@ -2,7 +2,12 @@ import com.crossoverjie.cim.common.enums.StatusEnum; 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; @@ -14,7 +19,7 @@ * @since JDK 1.8 */ public class TreeMapConsistentHash extends AbstractConsistentHash { - private TreeMap treeMap = new TreeMap() ; + private final TreeMap treeMap = new TreeMap() ; /** * 虚拟节点数量 @@ -23,9 +28,6 @@ public class TreeMapConsistentHash extends AbstractConsistentHash { @Override public void add(long key, String value) { - - // fix https://github.com/crossoverJie/cim/issues/79 - treeMap.clear(); for (int i = 0; i < VIRTUAL_NODE_SIZE; i++) { Long hash = super.hash("vir" + key + i); treeMap.put(hash,value); @@ -33,10 +35,33 @@ 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(); + } + + /** + * Used only in test. + * @return Return the data structure of the current algorithm. + */ + @VisibleForTesting + public TreeMap getTreeMap() { + return treeMap; + } + @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()); @@ -46,4 +71,5 @@ public String getFirstNodeValue(String value) { } return treeMap.firstEntry().getValue(); } + } 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/main/java/com/crossoverjie/cim/common/util/HttpClient.java b/cim-common/src/main/java/com/crossoverjie/cim/common/util/HttpClient.java index 3a80a1d7..a1e4156e 100644 --- a/cim-common/src/main/java/com/crossoverjie/cim/common/util/HttpClient.java +++ b/cim-common/src/main/java/com/crossoverjie/cim/common/util/HttpClient.java @@ -15,11 +15,11 @@ * Date: 2020-04-25 00:39 * @since JDK 1.8 */ -public final class HttpClient { +public final class HttpClient{ private static MediaType mediaType = MediaType.parse("application/json"); - public static Response call(OkHttpClient okHttpClient, String params, String url) throws IOException { + public static Response post(OkHttpClient okHttpClient, String params, String url) throws IOException { RequestBody requestBody = RequestBody.create(mediaType, params); Request request = new Request.Builder() @@ -27,6 +27,20 @@ public static Response call(OkHttpClient okHttpClient, String params, String url .post(requestBody) .build(); + Response response = okHttpClient.newCall(request).execute(); + if (!response.isSuccessful()) { + throw new IOException("RPC failed unexpected code " + response); + } + + return response; + } + + public static Response get(OkHttpClient okHttpClient, String url) throws IOException { + Request request = new Request.Builder() + .url(url) + .get() + .build(); + Response response = okHttpClient.newCall(request).execute(); if (!response.isSuccessful()) { throw new IOException("Unexpected code " + response); @@ -34,4 +48,5 @@ public static Response call(OkHttpClient okHttpClient, String params, String url return response; } + } diff --git a/cim-common/src/main/java/com/crossoverjie/cim/common/util/RouteInfoParseUtil.java b/cim-common/src/main/java/com/crossoverjie/cim/common/util/RouteInfoParseUtil.java index 22bb7345..045b1237 100644 --- a/cim-common/src/main/java/com/crossoverjie/cim/common/util/RouteInfoParseUtil.java +++ b/cim-common/src/main/java/com/crossoverjie/cim/common/util/RouteInfoParseUtil.java @@ -17,10 +17,16 @@ public class RouteInfoParseUtil { public static RouteInfo parse(String info){ try { String[] serverInfo = info.split(":"); - RouteInfo routeInfo = new RouteInfo(serverInfo[0], Integer.parseInt(serverInfo[1]),Integer.parseInt(serverInfo[2])) ; - return routeInfo ; + return new RouteInfo(serverInfo[0], Integer.parseInt(serverInfo[1]),Integer.parseInt(serverInfo[2])); }catch (Exception e){ throw new CIMException(VALIDATION_FAIL) ; } } + + public static String parse(RouteInfo routeInfo){ + return routeInfo.getIp() + ":" + routeInfo.getCimServerPort() + ":" + routeInfo.getHttpPort(); + } + + private RouteInfoParseUtil() { + } } diff --git a/cim-common/src/main/proto/cim.proto b/cim-common/src/main/proto/cim.proto new file mode 100644 index 00000000..3b38289b --- /dev/null +++ b/cim-common/src/main/proto/cim.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; +package com.crossoverjie.cim.common.protocol; +option java_package = "com.crossoverjie.cim.common.protocol"; +option java_multiple_files = true; + +message Request{ + int64 requestId = 2; + string reqMsg = 1; + BaseCommand cmd = 3; + map properties = 4; +} + +message Response{ + int64 responseId = 2; + string resMsg = 1; + BaseCommand cmd = 3; + map properties = 4; +} + +enum BaseCommand{ + LOGIN_REQUEST = 0; + MESSAGE = 1; + PING = 2; +} \ No newline at end of file diff --git a/cim-common/src/test/java/com/crossoverjie/cim/common/CommonTest.java b/cim-common/src/test/java/com/crossoverjie/cim/common/CommonTest.java index 45c24fcf..c35b9af8 100644 --- a/cim-common/src/test/java/com/crossoverjie/cim/common/CommonTest.java +++ b/cim-common/src/test/java/com/crossoverjie/cim/common/CommonTest.java @@ -1,5 +1,11 @@ package com.crossoverjie.cim.common; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; import org.junit.Test; import java.time.LocalDate; @@ -39,7 +45,6 @@ public void test() throws InterruptedException { System.out.println(cycleNum(256,64)) ; - cycle(); } @@ -79,4 +84,6 @@ private void cycle() throws InterruptedException { } } + + } diff --git a/cim-common/src/test/java/com/crossoverjie/cim/common/core/proxy/RpcProxyManagerTest.java b/cim-common/src/test/java/com/crossoverjie/cim/common/core/proxy/RpcProxyManagerTest.java new file mode 100644 index 00000000..c1c43c02 --- /dev/null +++ b/cim-common/src/test/java/com/crossoverjie/cim/common/core/proxy/RpcProxyManagerTest.java @@ -0,0 +1,210 @@ +package com.crossoverjie.cim.common.core.proxy; + +import com.crossoverjie.cim.common.exception.CIMException; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import okhttp3.OkHttpClient; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +class RpcProxyManagerTest { + + @Test + public void testGet() { + OkHttpClient client = new OkHttpClient(); + String url = "https://api.github.com/users"; + Github github = RpcProxyManager.create(Github.class, url, client); + GithubResponse githubResponse = github.crossoverjie(); + Assertions.assertEquals(githubResponse.getName(), "crossoverJie"); + github.torvalds(); + } + + @Test + public void testPost() { + OkHttpClient client = new OkHttpClient(); + String url = "http://echo.free.beeceptor.com"; + Echo echo = RpcProxyManager.create(Echo.class, url, client); + EchoRequest request = new EchoRequest(); + request.setName("crossoverJie"); + request.setAge(18); + request.setCity("shenzhen"); + EchoResponse response = echo.echo(request); + Assertions.assertEquals(response.getParsedBody().getName(), "crossoverJie"); + Assertions.assertEquals(response.getParsedBody().getAge(), 18); + Assertions.assertEquals(response.getParsedBody().getCity(), "shenzhen"); + } + + @Test + public void testUrl() { + OkHttpClient client = new OkHttpClient(); + String url = "http://echo.free.beeceptor.com/sample-request?author=beeceptor"; + Echo echo = RpcProxyManager.create(Echo.class, client); + EchoRequest request = new EchoRequest(); + request.setName("crossoverJie"); + request.setAge(18); + request.setCity("shenzhen"); + EchoResponse response = echo.echoTarget(url,request); + Assertions.assertEquals(response.getParsedBody().getName(), "crossoverJie"); + Assertions.assertEquals(response.getParsedBody().getAge(), 18); + Assertions.assertEquals(response.getParsedBody().getCity(), "shenzhen"); + response = echo.echoTarget(request, url); + Assertions.assertEquals(response.getParsedBody().getName(), "crossoverJie"); + + String req = "/request"; + response = echo.request("http://echo.free.beeceptor.com", request); + Assertions.assertEquals(response.getPath(), req); + Assertions.assertEquals(response.getParsedBody().getAge(), 18); + + Assertions.assertThrows(CIMException.class, () -> echo.echoTarget(request)); + } + + @Test + public void testFail() { + OkHttpClient client = new OkHttpClient(); + String url = "http://echo.free.beeceptor.com"; + Echo echo = RpcProxyManager.create(Echo.class, url, client); + EchoRequest request = new EchoRequest(); + request.setName("crossoverJie"); + request.setAge(18); + request.setCity("shenzhen"); + Assertions.assertThrows(IllegalArgumentException.class, () -> echo.fail(request, "test","")); + } + + + @Test + public void testGeneric() { + OkHttpClient client = new OkHttpClient(); + String url = "http://echo.free.beeceptor.com"; + Echo echo = RpcProxyManager.create(Echo.class, url, client); + EchoRequest request = new EchoRequest(); + request.setName("crossoverJie"); + request.setAge(18); + request.setCity("shenzhen"); + EchoGeneric response = echo.echoGeneric(request); + Assertions.assertEquals(response.getHeaders().getHost(), "echo.free.beeceptor.com"); + } + + interface Echo { + @Request(url = "sample-request?author=beeceptor") + EchoResponse echo(EchoRequest message); + + @Request(url = "sample-request?author=beeceptor") + EchoResponse echoTarget(@DynamicUrl(useMethodEndpoint = false) String url, EchoRequest message); + EchoResponse echoTarget(EchoRequest message, @DynamicUrl(useMethodEndpoint = false) String url); + @Request(url = "sample-request?author=beeceptor") + EchoResponse echoTarget(@DynamicUrl EchoRequest message); + EchoResponse request(@DynamicUrl() String url, EchoRequest message); + @Request(url = "sample-request?author=beeceptor") + EchoResponse fail(EchoRequest message, String s, String s1); + + @Request(url = "sample-request?author=beeceptor") + EchoGeneric echoGeneric(EchoRequest message); + } + + @Data + public static class EchoRequest{ + private String name; + private int age; + private String city; + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class CIMServerResVO implements Serializable { + + private String ip ; + private Integer cimServerPort; + private Integer httpPort; + + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + @AllArgsConstructor + @NoArgsConstructor + public static class EchoGeneric { + private String method; + private String protocol; + private String host; + + private T headers; + } + + @NoArgsConstructor + @Data + public static class EchoResponse{ + + @JsonProperty("method") + private String method; + @JsonProperty("protocol") + private String protocol; + @JsonProperty("host") + private String host; + @JsonProperty("path") + private String path; + @JsonProperty("ip") + private String ip; + @JsonProperty("headers") + private HeadersDTO headers; + @JsonProperty("parsedQueryParams") + private ParsedQueryParamsDTO parsedQueryParams; + @JsonProperty("parsedBody") + private ParsedBodyDTO parsedBody; + + @NoArgsConstructor + @Data + public static class HeadersDTO { + @JsonProperty("Host") + private String host; + @JsonProperty("User-Agent") + private String userAgent; + @JsonProperty("Content-Length") + private String contentLength; + @JsonProperty("Accept") + private String accept; + @JsonProperty("Content-Type") + private String contentType; + @JsonProperty("Accept-Encoding") + private String acceptEncoding; + } + + @NoArgsConstructor + @Data + public static class ParsedQueryParamsDTO { + @JsonProperty("author") + private String author; + } + + @NoArgsConstructor + @Data + public static class ParsedBodyDTO { + @JsonProperty("name") + private String name; + @JsonProperty("age") + private Integer age; + @JsonProperty("city") + private String city; + } + } + + interface Github { + @Request(method = Request.GET) + GithubResponse crossoverjie(); + + @Request(method = Request.GET) + void torvalds(); + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class GithubResponse { + @JsonProperty("name") + private String name; + } +} \ No newline at end of file diff --git a/cim-common/src/test/java/com/crossoverjie/cim/common/metastore/MetaStoreTest.java b/cim-common/src/test/java/com/crossoverjie/cim/common/metastore/MetaStoreTest.java new file mode 100644 index 00000000..8f415bb9 --- /dev/null +++ b/cim-common/src/test/java/com/crossoverjie/cim/common/metastore/MetaStoreTest.java @@ -0,0 +1,105 @@ +package com.crossoverjie.cim.common.metastore; + + +import java.util.List; +import java.util.concurrent.TimeUnit; +import lombok.SneakyThrows; +import org.I0Itec.zkclient.ZkClient; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.framework.api.CuratorWatcher; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.Watcher; +import org.apache.zookeeper.data.Stat; +import org.junit.Test; + +public class MetaStoreTest { + + private static final String connectionString = "127.0.0.1:2181"; + + // TODO: 2024/8/30 integration test + @SneakyThrows +// @Test + public void testZk() { + ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3); + CuratorFramework client = CuratorFrameworkFactory.builder() + .connectString(connectionString) + .retryPolicy(retryPolicy) + .connectionTimeoutMs(5000) + .sessionTimeoutMs(5000) + .build(); + client.start(); + + Stat stat = client.checkExists().forPath("/cim"); + if (stat == null) { + create(client, "/cim", null); + } + + + List list = null; + list = curatorWatcherGetChildren(client, "/cim", watchedEvent -> { + String name = Thread.currentThread().getName(); + System.out.println("watchedEvent = " + watchedEvent + " name = " + name); +// try { +// List children = watchedGetChildren(client, "/cim"); +// System.out.println("children = " + children); +// } catch (Exception e) { +// throw new RuntimeException(e); +// } + }); + + System.out.println(list); + +// createEphemeral(client, "/cim/route1", null); +// createEphemeral(client, "/cim/route2", null); + TimeUnit.SECONDS.sleep(1000); + } + + public static void createEphemeral(CuratorFramework client, String path, byte[] payload) throws Exception { + // this will create the given EPHEMERAL ZNode with the given data + client.create().withMode(CreateMode.EPHEMERAL).forPath(path, payload); + } + + public static void create(CuratorFramework client, String path, byte[] payload) throws Exception { + // this will create the given ZNode with the given data + client.create().forPath(path, payload); + } + + + public static List watchedGetChildren(CuratorFramework client, String path) throws Exception { + /** + * Get children and set a watcher on the node. The watcher notification will come through the + * CuratorListener (see setDataAsync() above). + */ + return client.getChildren().watched().forPath(path); + } + + public static List watchedGetChildren(CuratorFramework client, String path, Watcher watcher) + throws Exception { + /** + * Get children and set the given watcher on the node. + */ + return client.getChildren().usingWatcher(watcher).forPath(path); + } + + public static List curatorWatcherGetChildren(CuratorFramework client, String path, CuratorWatcher watcher) + throws Exception { + /** + * Get children and set the given watcher on the node. + */ + return client.getChildren().usingWatcher(watcher).forPath(path); + } + + + @SneakyThrows +// @Test + public void zkClientTest(){ + ZkClient zkClient = new ZkClient(connectionString, 5000); + zkClient.subscribeChildChanges("/cim", (parentPath, currentChildren) -> { + System.out.println("parentPath = " + parentPath); + System.out.println("currentChildren = " + currentChildren); + }); + TimeUnit.SECONDS.sleep(1000); + } +} \ No newline at end of file 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/consistenthash/RangeCheckTestUtil.java b/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/RangeCheckTestUtil.java new file mode 100644 index 00000000..b36ebee0 --- /dev/null +++ b/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/RangeCheckTestUtil.java @@ -0,0 +1,15 @@ +package com.crossoverjie.cim.common.route.algorithm.consistenthash; + +import org.junit.Assert; + +/** + * @description: TODO + * @author: zhangguoa + * @date: 2024/9/12 9:58 + * @project: cim + */ +public class RangeCheckTestUtil { + public static void assertInRange (int value, int l, int r) { + Assert.assertTrue(value >= l && value <= r); + } +} diff --git a/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/SortArrayMapConsistentHashTest.java b/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/SortArrayMapConsistentHashTest.java index eede2235..97df099e 100644 --- a/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/SortArrayMapConsistentHashTest.java +++ b/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/SortArrayMapConsistentHashTest.java @@ -1,10 +1,11 @@ package com.crossoverjie.cim.common.route.algorithm.consistenthash; +import com.crossoverjie.cim.common.data.construct.SortArrayMap; import org.junit.Assert; import org.junit.Test; -import java.util.ArrayList; -import java.util.List; +import java.lang.reflect.Field; +import java.util.*; public class SortArrayMapConsistentHashTest { @@ -12,42 +13,46 @@ public class SortArrayMapConsistentHashTest { public void getFirstNodeValue() { AbstractConsistentHash map = new SortArrayMapConsistentHash() ; - List strings = new ArrayList(); + List strings = new ArrayList<>(); for (int i = 0; i < 10; i++) { strings.add("127.0.0." + i) ; } - String process = map.process(strings,"zhangsan"); - System.out.println(process); - Assert.assertEquals("127.0.0.2",process); - + String PROCESS = map.process(strings, "zhangsan"); + for (int i = 0; i < 100; i++) { + String process = map.process(strings, "zhangsan"); + Assert.assertEquals(PROCESS, process); + } } @Test public void getFirstNodeValue2() { AbstractConsistentHash map = new SortArrayMapConsistentHash() ; - List strings = new ArrayList(); + List strings = new ArrayList<>(); for (int i = 0; i < 10; i++) { strings.add("127.0.0." + i) ; } - String process = map.process(strings,"zhangsan2"); - System.out.println(process); - Assert.assertEquals("127.0.0.3",process); + String PROCESS = map.process(strings,"zhangsan2"); + for (int i = 0; i < 100; i++) { + String process = map.process(strings, "zhangsan2"); + Assert.assertEquals(PROCESS, process); + } } @Test public void getFirstNodeValue3() { AbstractConsistentHash map = new SortArrayMapConsistentHash() ; - List strings = new ArrayList(); + List strings = new ArrayList<>(); for (int i = 0; i < 10; i++) { strings.add("127.0.0." + i) ; } - String process = map.process(strings,"1551253899106"); - - System.out.println(process); - Assert.assertEquals("127.0.0.6",process); + String PROCESS = map.process(strings,"1551253899106"); + for (int i = 0; i < 100; i++) { + String process = map.process(strings, "1551253899106"); + Assert.assertEquals(PROCESS, process); + } } @@ -55,60 +60,97 @@ public void getFirstNodeValue3() { public void getFirstNodeValue4() { AbstractConsistentHash map = new SortArrayMapConsistentHash() ; - List strings = new ArrayList(); + List strings = new ArrayList<>(); strings.add("45.78.28.220:9000:8081") ; strings.add("45.78.28.220:9100:9081") ; - String process = map.process(strings,"1551253899106"); - System.out.println(process); - Assert.assertEquals("45.78.28.220:9000:8081",process); + String PROCESS = map.process(strings,"1551253899106"); + for (int i = 0; i < 100; i++) { + String process = map.process(strings, "1551253899106"); + Assert.assertEquals(PROCESS, process); + } } @Test public void getFirstNodeValue5() { AbstractConsistentHash map = new SortArrayMapConsistentHash() ; - List strings = new ArrayList(); + List strings = new ArrayList<>(); strings.add("45.78.28.220:9000:8081") ; strings.add("45.78.28.220:9100:9081") ; strings.add("45.78.28.220:9100:10081") ; - String process = map.process(strings,"1551253899106"); - - System.out.println(process); - Assert.assertEquals("45.78.28.220:9000:8081",process); + String PROCESS = map.process(strings,"1551253899106"); + for (int i = 0; i < 100; i++) { + String process = map.process(strings, "1551253899106"); + Assert.assertEquals(PROCESS, process); + } } @Test public void getFirstNodeValue6() { AbstractConsistentHash map = new SortArrayMapConsistentHash() ; - List strings = new ArrayList(); + List strings = new ArrayList<>(); strings.add("45.78.28.220:9000:8081") ; strings.add("45.78.28.220:9100:9081") ; strings.add("45.78.28.220:9100:10081") ; - String process = map.process(strings,"1551253899106"); - - System.out.println(process); - Assert.assertEquals("45.78.28.220:9000:8081",process); + String PROCESS = map.process(strings,"1551253899106"); + for (int i = 0; i < 100; i++) { + String process = map.process(strings, "1551253899106"); + Assert.assertEquals(PROCESS, process); + } } @Test public void getFirstNodeValue7() { AbstractConsistentHash map = new SortArrayMapConsistentHash() ; - List strings = new ArrayList(); + List strings = new ArrayList<>(); strings.add("45.78.28.220:9000:8081") ; strings.add("45.78.28.220:9100:9081") ; strings.add("45.78.28.220:9100:10081") ; strings.add("45.78.28.220:9100:00081") ; - String process = map.process(strings,"1551253899106"); + String PROCESS = map.process(strings,"1551253899106"); + for (int i = 0; i < 100; i++) { + String process = map.process(strings, "1551253899106"); + Assert.assertEquals(PROCESS, process); + } + } + + @Test + public void getFirstNodeValue8() { + AbstractConsistentHash map = new SortArrayMapConsistentHash() ; - System.out.println(process); - Assert.assertEquals("45.78.28.220:9000:8081",process); + List strings = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + strings.add("127.0.0." + i); + } + Set processes = new HashSet<>(); + for (int i = 0; i < 10; i++) { + String process = map.process(strings,"zhangsan" + i); + processes.add(process); + } + RangeCheckTestUtil.assertInRange(processes.size(), 2, 10); } + @Test + public void testVirtualNode() throws NoSuchFieldException, IllegalAccessException { + SortArrayMapConsistentHash map = new SortArrayMapConsistentHash(); + + List strings = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + strings.add("127.0.0." + i); + } + String process = map.process(strings,"zhangsan"); + + SortArrayMap sortArrayMap = map.getSortArrayMap(); + int virtualNodeSize = 2; + + System.out.println("sortArrayMapSize = " + sortArrayMap.size() + "\n" + "virtualNodeSize = " + virtualNodeSize); + Assert.assertEquals(sortArrayMap.size(), (virtualNodeSize + 1) * 10); + } } \ No newline at end of file diff --git a/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/TreeMapConsistentHashTest.java b/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/TreeMapConsistentHashTest.java index b4f9ee3f..01060894 100644 --- a/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/TreeMapConsistentHashTest.java +++ b/cim-common/src/test/java/com/crossoverjie/cim/common/route/algorithm/consistenthash/TreeMapConsistentHashTest.java @@ -3,22 +3,26 @@ import org.junit.Assert; import org.junit.Test; -import java.util.ArrayList; -import java.util.List; +import java.lang.reflect.Field; +import java.util.*; public class TreeMapConsistentHashTest { + + @Test public void getFirstNodeValue() { AbstractConsistentHash map = new TreeMapConsistentHash() ; - List strings = new ArrayList(); + List strings = new ArrayList<>(); for (int i = 0; i < 10; i++) { strings.add("127.0.0." + i) ; } - String process = map.process(strings,"zhangsan"); - System.out.println(process); - Assert.assertEquals("127.0.0.2",process); + String PROCESS = map.process(strings, "zhangsan"); + for (int i = 0; i < 100; i++) { + String process = map.process(strings, "zhangsan"); + Assert.assertEquals(PROCESS, process); + } } @@ -27,14 +31,15 @@ public void getFirstNodeValue() { public void getFirstNodeValue2() { AbstractConsistentHash map = new TreeMapConsistentHash() ; - List strings = new ArrayList(); + List strings = new ArrayList<>(); for (int i = 0; i < 10; i++) { strings.add("127.0.0." + i) ; } - String process = map.process(strings,"zhangsan2"); - System.out.println(process); - - Assert.assertEquals("127.0.0.3",process); + String PROCESS = map.process(strings,"zhangsan2"); + for (int i = 0; i < 100; i++) { + String process = map.process(strings, "zhangsan2"); + Assert.assertEquals(PROCESS, process); + } } @@ -42,13 +47,48 @@ public void getFirstNodeValue2() { public void getFirstNodeValue3() { AbstractConsistentHash map = new TreeMapConsistentHash() ; - List strings = new ArrayList(); + List strings = new ArrayList<>(); for (int i = 0; i < 10; i++) { strings.add("127.0.0." + i) ; } - String process = map.process(strings,"1551253899106"); + String PROCESS = map.process(strings,"1551253899106"); + for (int i = 0; i < 100; i++) { + String process = map.process(strings, "1551253899106"); + Assert.assertEquals(PROCESS, process); + } + } + + @Test + public void getFirstNodeValue4() { + AbstractConsistentHash map = new TreeMapConsistentHash() ; + + List strings = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + strings.add("127.0.0." + i); + } + Set processes = new HashSet<>(); + for (int i = 0; i < 10; i++) { + String process = map.process(strings,"zhangsan" + i); + processes.add(process); + } + RangeCheckTestUtil.assertInRange(processes.size(), 2, 10); + } + + @Test + public void testVirtualNode() throws NoSuchFieldException, IllegalAccessException { + TreeMapConsistentHash map = new TreeMapConsistentHash(); + + List strings = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + strings.add("127.0.0." + i); + } + + String process = map.process(strings,"zhangsan"); + + TreeMap treeMap = map.getTreeMap(); + int virtualNodeSize = 2; - System.out.println(process); - Assert.assertEquals("127.0.0.6",process); + System.out.println("treeMapSize = " + treeMap.size() + "\n" + "virtualNodeSize = " + virtualNodeSize); + Assert.assertEquals(treeMap.size(), (virtualNodeSize + 1) * 10); } } \ 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-common/src/test/java/com/crossoverjie/cim/common/util/HttpClientTest.java b/cim-common/src/test/java/com/crossoverjie/cim/common/util/HttpClientTest.java index a809eab9..0ef260c9 100644 --- a/cim-common/src/test/java/com/crossoverjie/cim/common/util/HttpClientTest.java +++ b/cim-common/src/test/java/com/crossoverjie/cim/common/util/HttpClientTest.java @@ -28,6 +28,7 @@ public void call() throws IOException { jsonObject.put("msg", "hello"); jsonObject.put("userId", 1586617710861L); - HttpClient.call(okHttpClient,jsonObject.toString(),"http://127.0.0.1:8081/sendMsg") ; + // TODO: 2024/8/30 Integration test +// HttpClient.call(okHttpClient,jsonObject.toString(),"http://127.0.0.1:8081/sendMsg") ; } } \ No newline at end of file diff --git a/cim-common/src/test/java/com/crossoverjie/cim/common/util/ProtocolTest.java b/cim-common/src/test/java/com/crossoverjie/cim/common/util/ProtocolTest.java new file mode 100644 index 00000000..bcdd9194 --- /dev/null +++ b/cim-common/src/test/java/com/crossoverjie/cim/common/util/ProtocolTest.java @@ -0,0 +1,44 @@ +package com.crossoverjie.cim.common.util; + +import com.crossoverjie.cim.common.protocol.BaseCommand; +import com.crossoverjie.cim.common.protocol.Request; +import com.google.protobuf.InvalidProtocolBufferException; +import org.junit.Test; + +public class ProtocolTest { + + @Test + public void testProtocol() throws InvalidProtocolBufferException { + Request protocol = Request.newBuilder() + .setRequestId(123L) + .setReqMsg("你好啊") + .setCmd(BaseCommand.LOGIN_REQUEST) + .build(); + + byte[] encode = encode(protocol); + + Request parseFrom = decode(encode); + + System.out.println(protocol); + System.out.println(protocol.toString().equals(parseFrom.toString())); + } + + /** + * 编码 + * @param protocol + * @return + */ + public static byte[] encode(Request protocol){ + return protocol.toByteArray() ; + } + + /** + * 解码 + * @param bytes + * @return + * @throws InvalidProtocolBufferException + */ + public static Request decode(byte[] bytes) throws InvalidProtocolBufferException { + return Request.parseFrom(bytes); + } +} diff --git a/cim-forward-route/pom.xml b/cim-forward-route/pom.xml index fa6c4089..f894b29d 100644 --- a/cim-forward-route/pom.xml +++ b/cim-forward-route/pom.xml @@ -54,6 +54,38 @@ test + + org.junit.vintage + junit-vintage-engine + test + + + + org.junit.jupiter + junit-jupiter + test + + + + com.clever-cloud + testcontainers-zookeeper + + + + com.redis + testcontainers-redis + test + + + + org.testcontainers + testcontainers + + + org.testcontainers + junit-jupiter + + org.springframework.boot spring-boot-configuration-processor @@ -88,21 +120,5 @@ - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - repackage - - - - - - \ No newline at end of file diff --git a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/RouteApplication.java b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/RouteApplication.java index f584d073..22849826 100644 --- a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/RouteApplication.java +++ b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/RouteApplication.java @@ -1,6 +1,5 @@ package com.crossoverjie.cim.route; -import com.crossoverjie.cim.route.kit.ServerListListener; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; @@ -20,10 +19,5 @@ public static void main(String[] args) { @Override public void run(String... args) throws Exception { - - //监听服务 - Thread thread = new Thread(new ServerListListener()); - thread.setName("zk-listener"); - thread.start() ; } } \ No newline at end of file diff --git a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/cache/ServerCache.java b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/cache/ServerCache.java deleted file mode 100644 index 2cf65163..00000000 --- a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/cache/ServerCache.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.crossoverjie.cim.route.cache; - -import com.crossoverjie.cim.route.kit.ZKit; -import com.google.common.cache.LoadingCache; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * Function: 服务器节点缓存 - * - * @author crossoverJie - * Date: 2018/8/19 01:31 - * @since JDK 1.8 - */ -@Component -@Slf4j -public class ServerCache { - - - @Autowired - private LoadingCache cache; - - @Autowired - private ZKit zkUtil; - - public void addCache(String key) { - cache.put(key, key); - } - - - /** - * 更新所有缓存/先删除 再新增 - * - * @param currentChildren - */ - public void updateCache(List currentChildren) { - cache.invalidateAll(); - for (String currentChild : currentChildren) { - // currentChildren=ip-127.0.0.1:11212:9082 or 127.0.0.1:11212:9082 - String key ; - if (currentChild.split("-").length == 2){ - key = currentChild.split("-")[1]; - }else { - key = currentChild ; - } - addCache(key); - } - } - - - /** - * 获取所有的服务列表 - * - * @return - */ - public List getServerList() { - - List list = new ArrayList<>(); - - if (cache.size() == 0) { - List allNode = zkUtil.getAllNode(); - for (String node : allNode) { - String key = node.split("-")[1]; - addCache(key); - } - } - for (Map.Entry entry : cache.asMap().entrySet()) { - list.add(entry.getKey()); - } - return list; - - } - - /** - * rebuild cache list - */ - public void rebuildCacheList(){ - updateCache(getServerList()) ; - } - -} 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 1a515e6f..d43ba8ea 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 @@ -1,13 +1,22 @@ package com.crossoverjie.cim.route.config; +import com.crossoverjie.cim.common.core.proxy.RpcProxyManager; +import com.crossoverjie.cim.common.metastore.MetaStore; +import com.crossoverjie.cim.common.metastore.ZkConfiguration; +import com.crossoverjie.cim.common.metastore.ZkMetaStoreImpl; +import com.crossoverjie.cim.common.pojo.CIMUserInfo; import com.crossoverjie.cim.common.route.algorithm.RouteHandle; import com.crossoverjie.cim.common.route.algorithm.consistenthash.AbstractConsistentHash; +import com.crossoverjie.cim.server.api.ServerApi; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import com.google.common.cache.Weigher; +import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; import org.I0Itec.zkclient.ZkClient; +import org.apache.curator.retry.ExponentialBackoffRetry; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -17,8 +26,12 @@ import org.springframework.data.redis.serializer.StringRedisSerializer; import java.lang.reflect.Method; +import java.time.Duration; +import java.util.Optional; import java.util.concurrent.TimeUnit; +import static com.crossoverjie.cim.route.constant.Constant.ACCOUNT_PREFIX; + /** * Function: * @@ -31,23 +44,23 @@ public class BeanConfig { - @Autowired + @Resource private AppConfiguration appConfiguration; - @Bean - public ZkClient buildZKClient() { - return new ZkClient(appConfiguration.getZkAddr(), appConfiguration.getZkConnectTimeout()); - } @Bean - public LoadingCache buildCache() { - return CacheBuilder.newBuilder() - .build(new CacheLoader() { - @Override - public String load(String s) throws Exception { - return null; - } - }); + public MetaStore metaStore() throws Exception { + MetaStore metaStore = new ZkMetaStoreImpl(); + ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3); + metaStore.initialize(ZkConfiguration.builder() + .metaServiceUri(appConfiguration.getZkAddr()) + .timeoutMs(appConfiguration.getZkConnectTimeout()) + .retryPolicy(retryPolicy) + .build()); + metaStore.listenServerList((root, currentChildren) -> { + log.info("Server list change, root=[{}], current server list=[{}]", root, currentChildren); + }); + return metaStore; } @@ -92,8 +105,8 @@ public RouteHandle buildRouteHandle() throws Exception { Method method = Class.forName(routeWay).getMethod("setHash", AbstractConsistentHash.class); AbstractConsistentHash consistentHash = (AbstractConsistentHash) Class.forName(appConfiguration.getConsistentHashWay()).newInstance(); - method.invoke(routeHandle,consistentHash) ; - return routeHandle ; + method.invoke(routeHandle, consistentHash); + return routeHandle; } else { return routeHandle; @@ -101,4 +114,29 @@ public RouteHandle buildRouteHandle() throws Exception { } } + + @Bean("userInfoCache") + public LoadingCache> userInfoCache(RedisTemplate redisTemplate) { + return CacheBuilder.newBuilder() + .initialCapacity(64) + .maximumSize(1024) + .concurrencyLevel(Runtime.getRuntime().availableProcessors()) + .expireAfterWrite(10, TimeUnit.MINUTES) + .build(new CacheLoader<>() { + @Override + public Optional load(Long userId) throws Exception { + String sendUserName = redisTemplate.opsForValue().get(ACCOUNT_PREFIX + userId); + if (sendUserName == null) { + return Optional.empty(); + } + CIMUserInfo cimUserInfo = new CIMUserInfo(userId, sendUserName); + return Optional.of(cimUserInfo); + } + }); + } + + @Bean + public ServerApi serverApi(OkHttpClient okHttpClient) { + return RpcProxyManager.create(ServerApi.class, okHttpClient); + } } 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 c428a413..6ff84924 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 @@ -2,6 +2,7 @@ import com.crossoverjie.cim.common.enums.StatusEnum; import com.crossoverjie.cim.common.exception.CIMException; +import com.crossoverjie.cim.common.metastore.MetaStore; import com.crossoverjie.cim.common.pojo.CIMUserInfo; import com.crossoverjie.cim.common.pojo.RouteInfo; import com.crossoverjie.cim.common.res.BaseResponse; @@ -15,11 +16,15 @@ import com.crossoverjie.cim.route.api.vo.req.RegisterInfoReqVO; import com.crossoverjie.cim.route.api.vo.res.CIMServerResVO; import com.crossoverjie.cim.route.api.vo.res.RegisterInfoResVO; -import com.crossoverjie.cim.route.cache.ServerCache; import com.crossoverjie.cim.route.service.AccountService; import com.crossoverjie.cim.route.service.CommonBizService; import com.crossoverjie.cim.route.service.UserInfoCacheService; import io.swagger.v3.oas.annotations.Operation; +import jakarta.annotation.Resource; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @@ -28,14 +33,11 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; -import java.util.Map; -import java.util.Set; - /** * Function: * * @author crossoverJie - * Date: 22/05/2018 14:46 + * Date: 22/05/2018 14:46 * @since JDK 1.8 */ @Slf4j @@ -43,45 +45,44 @@ @RequestMapping("/") public class RouteController implements RouteApi { - @Autowired - private ServerCache serverCache; + @Resource + private MetaStore metaStore; - @Autowired + @Resource private AccountService accountService; - @Autowired - private UserInfoCacheService userInfoCacheService ; + @Resource + private UserInfoCacheService userInfoCacheService; - @Autowired - private CommonBizService commonBizService ; + @Resource + private CommonBizService commonBizService; - @Autowired - private RouteHandle routeHandle ; + @Resource + private RouteHandle routeHandle; @Operation(summary = "群聊 API") @RequestMapping(value = "groupRoute", method = RequestMethod.POST) @ResponseBody() @Override - public BaseResponse groupRoute(@RequestBody ChatReqVO groupReqVO) throws Exception { + public BaseResponse groupRoute(@RequestBody ChatReqVO groupReqVO) { BaseResponse res = new BaseResponse(); log.info("msg=[{}]", groupReqVO.toString()); - //获取所有的推送列表 - Map serverResVOMap = accountService.loadRouteRelated(); - for (Map.Entry cimServerResVOEntry : serverResVOMap.entrySet()) { - Long userId = cimServerResVOEntry.getKey(); - CIMServerResVO cimServerResVO = cimServerResVOEntry.getValue(); - if (userId.equals(groupReqVO.getUserId())){ - //过滤掉自己 - CIMUserInfo cimUserInfo = userInfoCacheService.loadUserInfoByUserId(groupReqVO.getUserId()); - log.warn("过滤掉了发送者 userId={}",cimUserInfo.toString()); + Map serverResVoMap = accountService.loadRouteRelated(); + for (Map.Entry cimServerResVoEntry : serverResVoMap.entrySet()) { + Long userId = cimServerResVoEntry.getKey(); + CIMServerResVO cimServerResVO = cimServerResVoEntry.getValue(); + if (userId.equals(groupReqVO.getUserId())) { + // Skip the sender + Optional cimUserInfo = userInfoCacheService.loadUserInfoByUserId(groupReqVO.getUserId()); + cimUserInfo.ifPresent(userInfo -> log.warn("skip send user userId={}", userInfo)); continue; } - //推送消息 - ChatReqVO chatVO = new ChatReqVO(userId,groupReqVO.getMsg()) ; - accountService.pushMsg(cimServerResVO ,groupReqVO.getUserId(),chatVO); + // Push message + ChatReqVO chatVO = new ChatReqVO(userId, groupReqVO.getMsg()); + accountService.pushMsg(cimServerResVO, groupReqVO.getUserId(), chatVO); } @@ -97,11 +98,11 @@ public BaseResponse groupRoute(@RequestBody ChatReqVO groupReqVO) thro * @param p2pRequest * @return */ - @Operation(summary = "私聊 API") + @Operation(summary = "私聊 API") @RequestMapping(value = "p2pRoute", method = RequestMethod.POST) @ResponseBody() @Override - public BaseResponse p2pRoute(@RequestBody P2PReqVO p2pRequest) throws Exception { + public BaseResponse p2pRoute(@RequestBody P2PReqVO p2pRequest) { BaseResponse res = new BaseResponse(); try { @@ -109,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()); } @@ -127,13 +128,15 @@ 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()); + Optional cimUserInfo = userInfoCacheService.loadUserInfoByUserId(groupReqVO.getUserId()); - log.info("user [{}] offline!", cimUserInfo.toString()); - accountService.offLine(groupReqVO.getUserId()); + cimUserInfo.ifPresent(userInfo -> { + log.info("user [{}] offline!", userInfo.toString()); + accountService.offLine(groupReqVO.getUserId()); + }); res.setCode(StatusEnum.SUCCESS.getCode()); res.setMessage(StatusEnum.SUCCESS.getMessage()); @@ -151,28 +154,30 @@ public BaseResponse offLine(@RequestBody ChatReqVO groupReqVO) throws @Override public BaseResponse login(@RequestBody LoginReqVO loginReqVO) throws Exception { BaseResponse res = new BaseResponse(); - - // check server available - String server = routeHandle.routeServer(serverCache.getServerList(),String.valueOf(loginReqVO.getUserId())); - log.info("userName=[{}] route server info=[{}]", loginReqVO.getUserName(), server); - - RouteInfo routeInfo = RouteInfoParseUtil.parse(server); - commonBizService.checkServerAvailable(routeInfo); - //登录校验 StatusEnum status = accountService.login(loginReqVO); - if (status == StatusEnum.SUCCESS) { + res.setCode(status.getCode()); + res.setMessage(status.getMessage()); + if (status != StatusEnum.SUCCESS) { + return res; + } - //保存路由信息 - accountService.saveRouteInfo(loginReqVO,server); + // check server available + Set availableServerList = metaStore.getAvailableServerList(); + String key = String.valueOf(loginReqVO.getUserId()); + String server = + routeHandle.routeServer(List.copyOf(availableServerList), key); + log.info("userInfo=[{}] route server info=[{}]", loginReqVO, server); - CIMServerResVO vo = new CIMServerResVO(routeInfo); - res.setDataBody(vo); + RouteInfo routeInfo = RouteInfoParseUtil.parse(server); + routeInfo = commonBizService.checkServerAvailable(routeInfo, key); - } - 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; } @@ -185,7 +190,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(); @@ -204,14 +210,14 @@ public BaseResponse registerAccount(@RequestBody RegisterInfo * @return */ @Operation(summary = "获取所有在线用户") - @RequestMapping(value = "onlineUser", method = RequestMethod.POST) + @RequestMapping(value = "onlineUser", method = RequestMethod.GET) @ResponseBody() @Override 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 24836557..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,8 +34,10 @@ public static boolean checkAddressReachable(String address, int port, int timeou try { socket.close(); } catch (IOException e) { - return false ; + log.warn("close socket error", e); } } } + + private NetAddressIsReachable(){} } diff --git a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/kit/ServerListListener.java b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/kit/ServerListListener.java deleted file mode 100644 index a699cc0d..00000000 --- a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/kit/ServerListListener.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.crossoverjie.cim.route.kit; - -import com.crossoverjie.cim.route.cache.ServerCache; -import com.crossoverjie.cim.route.config.AppConfiguration; -import com.crossoverjie.cim.route.util.SpringBeanFactory; -import java.util.List; -import lombok.extern.slf4j.Slf4j; -import org.I0Itec.zkclient.IZkChildListener; -import org.I0Itec.zkclient.ZkClient; - -/** - * Function: - * - * @author crossoverJie - * Date: 2018/12/23 00:35 - * @since JDK 1.8 - */ -@Slf4j -public class ServerListListener implements Runnable{ - - - private ZKit zkUtil; - - private AppConfiguration appConfiguration ; - - private ZkClient zkClient; - - private ServerCache serverCache ; - - - public ServerListListener() { - zkUtil = SpringBeanFactory.getBean(ZKit.class) ; - appConfiguration = SpringBeanFactory.getBean(AppConfiguration.class) ; - zkClient = SpringBeanFactory.getBean(ZkClient.class) ; - serverCache = SpringBeanFactory.getBean(ServerCache.class) ; - } - - @Override - public void run() { - //注册监听服务 - subscribeEvent(appConfiguration.getZkRoot()); - - } - - /** - * 监听事件 - * - * @param path - */ - public void subscribeEvent(String path) { - zkClient.subscribeChildChanges(path, new IZkChildListener() { - @Override - public void handleChildChange(String parentPath, List currentChildren) throws Exception { - log.info("Clear and update local cache parentPath=[{}],currentChildren=[{}]", parentPath,currentChildren.toString()); - - //update local cache, delete and save. - serverCache.updateCache(currentChildren) ; - } - }); - - - } -} diff --git a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/kit/ZKit.java b/cim-forward-route/src/main/java/com/crossoverjie/cim/route/kit/ZKit.java deleted file mode 100644 index c7b7ed22..00000000 --- a/cim-forward-route/src/main/java/com/crossoverjie/cim/route/kit/ZKit.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.crossoverjie.cim.route.kit; - -import com.alibaba.fastjson.JSON; -import com.crossoverjie.cim.route.config.AppConfiguration; -import java.util.List; -import lombok.extern.slf4j.Slf4j; -import org.I0Itec.zkclient.ZkClient; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * Function: Zookeeper kit - * - * @author crossoverJie - * Date: 2018/8/19 00:33 - * @since JDK 1.8 - */ -@Component -@Slf4j -public class ZKit { - - - - @Autowired - private ZkClient zkClient; - - @Autowired - private AppConfiguration appConfiguration; - - - - - /** - * get all server node from zookeeper - * @return - */ - public List getAllNode(){ - List children = zkClient.getChildren(appConfiguration.getZkRoot()); - log.info("Query all node =[{}] success.", JSON.toJSONString(children)); - return children; - } - - -} 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..45c2738b 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 @@ -62,12 +62,12 @@ public interface AccountService { * @param sendUserId 发送者的ID * @throws Exception */ - void pushMsg(CIMServerResVO cimServerResVO, long sendUserId , ChatReqVO groupReqVO) throws Exception; + void pushMsg(CIMServerResVO cimServerResVO, long sendUserId , ChatReqVO groupReqVO); /** * 用户下线 * @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 e5a9bb19..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 @@ -2,11 +2,15 @@ import com.crossoverjie.cim.common.enums.StatusEnum; 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.route.cache.ServerCache; +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.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** @@ -21,23 +25,23 @@ public class CommonBizService { - @Autowired - private ServerCache serverCache ; + @Resource + 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 */ - public void checkServerAvailable(RouteInfo routeInfo){ + @SneakyThrows + 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()); - - // rebuild cache - serverCache.rebuildCacheList(); - - 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..a0c57a42 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 @@ -2,6 +2,7 @@ import com.crossoverjie.cim.common.pojo.CIMUserInfo; +import java.util.Optional; import java.util.Set; /** @@ -19,7 +20,7 @@ public interface UserInfoCacheService { * @return * @throws Exception */ - CIMUserInfo loadUserInfoByUserId(Long userId) ; + Optional loadUserInfoByUserId(Long userId) ; /** * 保存和检查用户登录情况 @@ -29,14 +30,6 @@ public interface UserInfoCacheService { */ boolean saveAndCheckUserLoginStatus(Long userId) throws Exception ; - /** - * 清除用户的登录状态 - * @param userId - * @throws Exception - */ - void removeLoginStatus(Long userId) throws Exception ; - - /** * query all online user * @return online user 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 ac23d2fb..d62548d3 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 @@ -1,35 +1,43 @@ package com.crossoverjie.cim.route.service.impl; -import com.crossoverjie.cim.common.core.proxy.ProxyManager; +import com.crossoverjie.cim.common.constant.Constants; +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.pojo.RouteInfo; import com.crossoverjie.cim.common.util.RouteInfoParseUtil; 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.res.CIMServerResVO; import com.crossoverjie.cim.route.api.vo.res.RegisterInfoResVO; +import com.crossoverjie.cim.route.constant.Constant; import com.crossoverjie.cim.route.service.AccountService; import com.crossoverjie.cim.route.service.UserInfoCacheService; import com.crossoverjie.cim.server.api.ServerApi; import com.crossoverjie.cim.server.api.vo.req.SendMsgReqVO; +import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; import okhttp3.Response; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ClassPathResource; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ScanOptions; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.scripting.support.ResourceScriptSource; import org.springframework.stereotype.Service; import java.nio.charset.StandardCharsets; +import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import static com.crossoverjie.cim.common.enums.StatusEnum.OFF_LINE; -import static com.crossoverjie.cim.route.constant.Constant.ACCOUNT_PREFIX; -import static com.crossoverjie.cim.route.constant.Constant.ROUTE_PREFIX; +import static com.crossoverjie.cim.route.constant.Constant.*; /** * Function: @@ -42,14 +50,14 @@ @Service public class AccountServiceRedisImpl implements AccountService { - @Autowired + @Resource private RedisTemplate redisTemplate; - @Autowired + @Resource private UserInfoCacheService userInfoCacheService; - @Autowired - private OkHttpClient okHttpClient; + @Resource + private ServerApi serverApi; @Override public RegisterInfoResVO register(RegisterInfoResVO info) { @@ -84,7 +92,7 @@ public StatusEnum login(LoginReqVO loginReqVO) throws Exception { //登录成功,保存登录状态 boolean status = userInfoCacheService.saveAndCheckUserLoginStatus(loginReqVO.getUserId()); - if (status == false) { + if (!status) { //重复登录 return StatusEnum.REPEAT_LOGIN; } @@ -130,44 +138,48 @@ public CIMServerResVO loadRouteRelatedByUserId(Long userId) { throw new CIMException(OFF_LINE); } - CIMServerResVO cimServerResVO = new CIMServerResVO(RouteInfoParseUtil.parse(value)); + RouteInfo parse = RouteInfoParseUtil.parse(value); + CIMServerResVO cimServerResVO = + new CIMServerResVO(parse.getIp(), parse.getCimServerPort(), parse.getHttpPort()); return cimServerResVO; } private void parseServerInfo(Map routes, String key) { long userId = Long.valueOf(key.split(":")[1]); String value = redisTemplate.opsForValue().get(key); - CIMServerResVO cimServerResVO = new CIMServerResVO(RouteInfoParseUtil.parse(value)); + RouteInfo parse = RouteInfoParseUtil.parse(value); + CIMServerResVO cimServerResVO = + new CIMServerResVO(parse.getIp(), parse.getCimServerPort(), parse.getHttpPort()); routes.put(userId, cimServerResVO); } @Override - public void pushMsg(CIMServerResVO cimServerResVO, long sendUserId, ChatReqVO groupReqVO) throws Exception { - CIMUserInfo cimUserInfo = userInfoCacheService.loadUserInfoByUserId(sendUserId); - - String url = "http://" + cimServerResVO.getIp() + ":" + cimServerResVO.getHttpPort(); - ServerApi serverApi = new ProxyManager<>(ServerApi.class, url, okHttpClient).getInstance(); - SendMsgReqVO vo = new SendMsgReqVO(cimUserInfo.getUserName() + ":" + groupReqVO.getMsg(), groupReqVO.getUserId()); - Response response = null; - try { - response = (Response) serverApi.sendMsg(vo); - } catch (Exception e) { - log.error("Exception", e); - } finally { - response.body().close(); - } + public void pushMsg(CIMServerResVO cimServerResVO, long sendUserId, ChatReqVO groupReqVO) { + Optional cimUserInfo = userInfoCacheService.loadUserInfoByUserId(sendUserId); + + cimUserInfo.ifPresent(userInfo -> { + String url = "http://" + cimServerResVO.getIp() + ":" + cimServerResVO.getHttpPort(); + SendMsgReqVO vo = + new SendMsgReqVO(groupReqVO.getMsg(), groupReqVO.getUserId()); + vo.setProperties(Map.of( + Constants.MetaKey.USER_ID, String.valueOf(sendUserId), + Constants.MetaKey.USER_NAME, userInfo.getUserName()) + ); + serverApi.sendMsg(vo, url); + + }); } @Override - public void offLine(Long userId) throws Exception { - - // TODO: 2019-01-21 改为一个原子命令,以防数据一致性 + public void offLine(Long userId) { - //删除路由 - redisTemplate.delete(ROUTE_PREFIX + userId); + DefaultRedisScript redisScript = new DefaultRedisScript(); + redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/offLine.lua"))); - //删除登录状态 - userInfoCacheService.removeLoginStatus(userId); + redisTemplate.execute(redisScript, + Collections.singletonList(ROUTE_PREFIX + userId), + LOGIN_STATUS_PREFIX, + userId.toString()); } } 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..336afb32 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 @@ -2,14 +2,21 @@ import com.crossoverjie.cim.common.pojo.CIMUserInfo; import com.crossoverjie.cim.route.service.UserInfoCacheService; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import jakarta.annotation.Resource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.HashSet; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; import static com.crossoverjie.cim.route.constant.Constant.ACCOUNT_PREFIX; import static com.crossoverjie.cim.route.constant.Constant.LOGIN_STATUS_PREFIX; @@ -25,31 +32,16 @@ @Service public class UserInfoCacheServiceImpl implements UserInfoCacheService { - /** - * todo 本地缓存,为了防止内存撑爆,后期可换为 LRU。 - */ - private final static Map USER_INFO_MAP = new ConcurrentHashMap<>(64) ; - @Autowired private RedisTemplate redisTemplate ; - @Override - public CIMUserInfo loadUserInfoByUserId(Long userId) { - - //优先从本地缓存获取 - CIMUserInfo cimUserInfo = USER_INFO_MAP.get(userId); - if (cimUserInfo != null){ - return cimUserInfo ; - } - - //load redis - String sendUserName = redisTemplate.opsForValue().get(ACCOUNT_PREFIX + userId); - if (sendUserName != null){ - cimUserInfo = new CIMUserInfo(userId,sendUserName) ; - USER_INFO_MAP.put(userId,cimUserInfo) ; - } + @Resource(name = "userInfoCache") + private LoadingCache> userInfoMap; - return cimUserInfo; + @Override + public Optional loadUserInfoByUserId(Long userId) { + //Retrieve user information using a second-level cache. + return userInfoMap.getUnchecked(userId); } @Override @@ -63,11 +55,6 @@ public boolean saveAndCheckUserLoginStatus(Long userId) throws Exception { } } - @Override - public void removeLoginStatus(Long userId) throws Exception { - redisTemplate.opsForSet().remove(LOGIN_STATUS_PREFIX,userId.toString()) ; - } - @Override public Set onlineUser() { Set set = null ; @@ -76,8 +63,9 @@ public Set onlineUser() { if (set == null){ set = new HashSet<>(64) ; } - CIMUserInfo cimUserInfo = loadUserInfoByUserId(Long.valueOf(member)) ; - set.add(cimUserInfo) ; + Optional cimUserInfo = loadUserInfoByUserId(Long.valueOf(member)) ; + + cimUserInfo.ifPresent(set::add); } return set; diff --git a/cim-forward-route/src/main/resources/banner.txt b/cim-forward-route/src/main/resources/banner.txt index 97b15b91..165abd2e 100644 --- a/cim-forward-route/src/main/resources/banner.txt +++ b/cim-forward-route/src/main/resources/banner.txt @@ -1,14 +1,7 @@ - - ▄████▄ ██▓ ███▄ ▄███▓ -▒██▀ ▀█ ▓██▒▓██▒▀█▀ ██▒ -▒▓█ ▄ ▒██▒▓██ ▓██░ -▒▓▓▄ ▄██▒░██░▒██ ▒██ -▒ ▓███▀ ░░██░▒██▒ ░██▒ -░ ░▒ ▒ ░░▓ ░ ▒░ ░ ░ - ░ ▒ ▒ ░░ ░ ░ -░ ▒ ░░ ░ -░ ░ ░ ░ -░ + _ __ + ____(_)_ _ _______ __ __/ /____ +/ __/ / ' \ / __/ _ \/ // / __/ -_) +\__/_/_/_/_/ /_/ \___/\_,_/\__/\__/ Power by @crossoverJie diff --git a/cim-forward-route/src/main/resources/lua/offLine.lua b/cim-forward-route/src/main/resources/lua/offLine.lua new file mode 100644 index 00000000..7fc5d82a --- /dev/null +++ b/cim-forward-route/src/main/resources/lua/offLine.lua @@ -0,0 +1,5 @@ + +redis.call('DEL', KEYS[1]) + +redis.call('SREM', ARGV[1], ARGV[2]) + diff --git a/cim-forward-route/src/test/java/com/crossoverjie/cim/route/service/impl/AbstractBaseTest.java b/cim-forward-route/src/test/java/com/crossoverjie/cim/route/service/impl/AbstractBaseTest.java new file mode 100644 index 00000000..350cd589 --- /dev/null +++ b/cim-forward-route/src/test/java/com/crossoverjie/cim/route/service/impl/AbstractBaseTest.java @@ -0,0 +1,46 @@ +package com.crossoverjie.cim.route.service.impl; + +import com.clevercloud.testcontainers.zookeeper.ZooKeeperContainer; +import com.redis.testcontainers.RedisContainer; +import java.time.Duration; +import java.util.List; +import org.junit.After; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.utility.DockerImageName; + +public class AbstractBaseTest { + + @Container + static RedisContainer redis = new RedisContainer(DockerImageName.parse("redis:7.4.0")); + + 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 + static final ZooKeeperContainer + zooKeeperContainer = new ZooKeeperContainer(DEFAULT_IMAGE_NAME, DEFAULT_STARTUP_TIMEOUT); + + + @BeforeAll + public static void before(){ + redis.setExposedPorts(List.of(6379)); + redis.setPortBindings(List.of("6379:6379")); + redis.start(); + + zooKeeperContainer.setExposedPorts(List.of(2181)); + zooKeeperContainer.setPortBindings(List.of("2181:2181")); + zooKeeperContainer.start(); + } + + @AfterAll + public static void after(){ + redis.stop(); + zooKeeperContainer.stop(); + } + +} diff --git a/cim-forward-route/src/test/java/com/crossoverjie/cim/route/service/impl/AccountServiceRedisImplTest.java b/cim-forward-route/src/test/java/com/crossoverjie/cim/route/service/impl/AccountServiceRedisImplTest.java index ef43b38e..5046d8df 100644 --- a/cim-forward-route/src/test/java/com/crossoverjie/cim/route/service/impl/AccountServiceRedisImplTest.java +++ b/cim-forward-route/src/test/java/com/crossoverjie/cim/route/service/impl/AccountServiceRedisImplTest.java @@ -4,20 +4,15 @@ import com.crossoverjie.cim.route.RouteApplication; import com.crossoverjie.cim.route.api.vo.res.CIMServerResVO; import com.crossoverjie.cim.route.service.AccountService; +import java.util.Map; import lombok.extern.slf4j.Slf4j; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -import java.util.Map; -import java.util.concurrent.TimeUnit; @Slf4j @SpringBootTest(classes = RouteApplication.class) -@RunWith(SpringRunner.class) -public class AccountServiceRedisImplTest { +public class AccountServiceRedisImplTest extends AbstractBaseTest{ @Autowired private AccountService accountService ; @@ -29,7 +24,6 @@ public void loadRouteRelated() throws Exception { Map longCIMServerResVOMap = accountService.loadRouteRelated(); log.info("longCIMServerResVOMap={},cun={}" , JSON.toJSONString(longCIMServerResVOMap),i); } - TimeUnit.SECONDS.sleep(10); } } \ No newline at end of file diff --git a/cim-forward-route/src/test/java/RedisTest.java b/cim-forward-route/src/test/java/com/crossoverjie/cim/route/service/impl/RedisTest.java similarity index 62% rename from cim-forward-route/src/test/java/RedisTest.java rename to cim-forward-route/src/test/java/com/crossoverjie/cim/route/service/impl/RedisTest.java index 63b1b05b..2b437438 100644 --- a/cim-forward-route/src/test/java/RedisTest.java +++ b/cim-forward-route/src/test/java/com/crossoverjie/cim/route/service/impl/RedisTest.java @@ -1,21 +1,15 @@ +package com.crossoverjie.cim.route.service.impl; + import com.crossoverjie.cim.route.RouteApplication; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.test.context.junit4.SpringRunner; -/** - * Function: - * - * @author crossoverJie - * Date: 2018/12/23 21:40 - * @since JDK 1.8 - */ + @SpringBootTest(classes = RouteApplication.class) -@RunWith(SpringRunner.class) -public class RedisTest { +public class RedisTest extends AbstractBaseTest { @Autowired private RedisTemplate redisTemplate ; @@ -24,6 +18,6 @@ public class RedisTest { public void test(){ redisTemplate.opsForValue().set("test","test") ; String test = redisTemplate.opsForValue().get("test"); - System.out.println("====" + test); + Assertions.assertEquals("test",test); } } diff --git a/cim-forward-route/src/test/java/com/crossoverjie/cim/route/service/impl/UserInfoCacheServiceImplTest.java b/cim-forward-route/src/test/java/com/crossoverjie/cim/route/service/impl/UserInfoCacheServiceImplTest.java index b71ea2ef..51865314 100644 --- a/cim-forward-route/src/test/java/com/crossoverjie/cim/route/service/impl/UserInfoCacheServiceImplTest.java +++ b/cim-forward-route/src/test/java/com/crossoverjie/cim/route/service/impl/UserInfoCacheServiceImplTest.java @@ -4,19 +4,15 @@ import com.crossoverjie.cim.common.pojo.CIMUserInfo; import com.crossoverjie.cim.route.RouteApplication; import com.crossoverjie.cim.route.service.UserInfoCacheService; +import java.util.Set; import lombok.extern.slf4j.Slf4j; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -import java.util.Set; @Slf4j @SpringBootTest(classes = RouteApplication.class) -@RunWith(SpringRunner.class) -public class UserInfoCacheServiceImplTest { +public class UserInfoCacheServiceImplTest extends AbstractBaseTest{ @Autowired private UserInfoCacheService userInfoCacheService; @@ -27,11 +23,6 @@ public void checkUserLoginStatus() throws Exception { log.info("status={}", status); } - @Test - public void removeLoginStatus() throws Exception { - userInfoCacheService.removeLoginStatus(2000L); - } - @Test public void onlineUser(){ Set cimUserInfos = userInfoCacheService.onlineUser(); diff --git a/cim-forward-route/src/test/resources/application.yaml b/cim-forward-route/src/test/resources/application.yaml new file mode 100644 index 00000000..f2007e1d --- /dev/null +++ b/cim-forward-route/src/test/resources/application.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: + addr: 127.0.0.1:2181 + 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.consistenthash.ConsistentHashHandle + + #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-integration-test/pom.xml b/cim-integration-test/pom.xml new file mode 100644 index 00000000..cdd5f4f9 --- /dev/null +++ b/cim-integration-test/pom.xml @@ -0,0 +1,78 @@ + + + 4.0.0 + + com.crossoverjie.netty + cim + 1.0.0-SNAPSHOT + + + cim-integration-test + + + 17 + 17 + UTF-8 + + + + + com.crossoverjie.netty + cim-server + ${project.version} + compile + + + + com.crossoverjie.netty + cim-forward-route + ${project.version} + compile + + + + org.junit.vintage + junit-vintage-engine + test + + + + + org.junit.jupiter + junit-jupiter + test + + + + org.testcontainers + testcontainers + compile + + + org.testcontainers + junit-jupiter + + + + org.springframework.boot + spring-boot-starter-test + compile + + + + com.clever-cloud + testcontainers-zookeeper + compile + + + + com.redis + testcontainers-redis + compile + + + + + \ No newline at end of file diff --git a/cim-integration-test/src/main/java/com/crossoverjie/cim/client/sdk/route/AbstractRouteBaseTest.java b/cim-integration-test/src/main/java/com/crossoverjie/cim/client/sdk/route/AbstractRouteBaseTest.java new file mode 100644 index 00000000..8f025507 --- /dev/null +++ b/cim-integration-test/src/main/java/com/crossoverjie/cim/client/sdk/route/AbstractRouteBaseTest.java @@ -0,0 +1,48 @@ +package com.crossoverjie.cim.client.sdk.route; + +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.redis.testcontainers.RedisContainer; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.utility.DockerImageName; + +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); + String[] args = new String[]{ + "--spring.data.redis.host=" + redis.getHost(), + "--spring.data.redis.port=" + redis.getMappedPort(6379), + "--app.zk.addr=" + super.getZookeeperAddr(), + }; + route.setAdditionalProfiles("route"); + run = route.run(args); + } + + public void close(){ + super.close(); + redis.close(); + run.close(); + } + + public Long registerAccount(String userName) throws Exception { + // register user + RouteApi routeApi = com.crossoverjie.cim.route.util.SpringBeanFactory.getBean(RouteApi.class); + RegisterInfoReqVO reqVO = new RegisterInfoReqVO(); + reqVO.setUserName(userName); + BaseResponse account = + 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/resources/application-client.yaml b/cim-integration-test/src/test/resources/application-client.yaml new file mode 100644 index 00000000..8131eef9 --- /dev/null +++ b/cim-integration-test/src/test/resources/application-client.yaml @@ -0,0 +1,37 @@ +spring: + application: + name: cim-client + +# web port +server: + port: 8082 + +logging: + level: + root: error + +# enable swagger +springdoc: + swagger-ui: + enabled: true + +# log path +cim: + msg: + logger: + path: /opt/logs/cim/ + route: + url: http://localhost:8083 # route url suggested that this is Nginx address + user: # cim userId and userName + id: 1722343979085 + userName: zhangsan + callback: + thread: + queue: + size: 2 + pool: + size: 2 + heartbeat: + time: 60 # cim heartbeat time (seconds) + reconnect: + count: 3 \ No newline at end of file diff --git a/cim-integration-test/src/test/resources/application-route.yaml b/cim-integration-test/src/test/resources/application-route.yaml new file mode 100644 index 00000000..bfd1a5f0 --- /dev/null +++ b/cim-integration-test/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.consistenthash.ConsistentHashHandle + + #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-rout-api/pom.xml b/cim-rout-api/pom.xml index a1164fed..efe9f7b2 100644 --- a/cim-rout-api/pom.xml +++ b/cim-rout-api/pom.xml @@ -10,7 +10,7 @@ 4.0.0 cim-rout-api - 1.0.1-SNAPSHOT + 1.0.0-SNAPSHOT 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 90b65c3d..7c5fd05b 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 @@ -1,11 +1,16 @@ package com.crossoverjie.cim.route.api; +import com.crossoverjie.cim.common.core.proxy.Request; +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.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.req.RegisterInfoReqVO; +import com.crossoverjie.cim.route.api.vo.res.CIMServerResVO; import com.crossoverjie.cim.route.api.vo.res.RegisterInfoResVO; +import java.util.Set; /** * Function: Route Api @@ -23,7 +28,7 @@ public interface RouteApi { * @return * @throws Exception */ - Object groupRoute(ChatReqVO groupReqVO) throws Exception; + BaseResponse groupRoute(ChatReqVO groupReqVO); /** * Point to point chat @@ -31,7 +36,7 @@ public interface RouteApi { * @return * @throws Exception */ - Object p2pRoute(P2PReqVO p2pRequest) throws Exception; + BaseResponse p2pRoute(P2PReqVO p2pRequest); /** @@ -41,7 +46,7 @@ public interface RouteApi { * @return * @throws Exception */ - Object offLine(ChatReqVO groupReqVO) throws Exception; + BaseResponse offLine(ChatReqVO groupReqVO); /** * Login account @@ -49,7 +54,7 @@ public interface RouteApi { * @return * @throws Exception */ - Object login(LoginReqVO loginReqVO) throws Exception; + BaseResponse login(LoginReqVO loginReqVO) throws Exception; /** * Register account @@ -66,5 +71,8 @@ public interface RouteApi { * @return * @throws Exception */ - Object onlineUser() throws Exception; + @Request(method = Request.GET) + BaseResponse> onlineUser() throws Exception; + + // TODO: 2024/8/19 Get cache server & metastore server } diff --git a/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/ChatReqVO.java b/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/ChatReqVO.java index 10c6a682..53b2a01b 100644 --- a/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/ChatReqVO.java +++ b/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/ChatReqVO.java @@ -4,6 +4,10 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; /** * Function: Google Protocol 编解码发送 @@ -12,6 +16,10 @@ * Date: 2018/05/21 15:56 * @since JDK 1.8 */ +@EqualsAndHashCode(callSuper = true) +@Data +@AllArgsConstructor +@NoArgsConstructor public class ChatReqVO extends BaseRequest { @NotNull(message = "userId 不能为空") @@ -23,29 +31,6 @@ public class ChatReqVO extends BaseRequest { @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "msg", example = "hello") private String msg ; - public ChatReqVO() { - } - - public ChatReqVO(Long userId, String msg) { - this.userId = userId; - this.msg = msg; - } - - public String getMsg() { - return msg; - } - - public void setMsg(String msg) { - this.msg = msg; - } - - public Long getUserId() { - return userId; - } - - public void setUserId(Long userId) { - this.userId = userId; - } @Override public String toString() { diff --git a/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/LoginReqVO.java b/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/LoginReqVO.java index fcd97d8f..8e810d80 100644 --- a/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/LoginReqVO.java +++ b/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/LoginReqVO.java @@ -1,6 +1,7 @@ package com.crossoverjie.cim.route.api.vo.req; import com.crossoverjie.cim.common.req.BaseRequest; +import lombok.AllArgsConstructor; /** * Function: @@ -9,6 +10,7 @@ * Date: 2018/12/23 22:30 * @since JDK 1.8 */ +@AllArgsConstructor public class LoginReqVO extends BaseRequest{ private Long userId ; private String userName ; 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-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/RegisterInfoReqVO.java b/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/RegisterInfoReqVO.java index 6417e3ff..712a5e25 100644 --- a/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/RegisterInfoReqVO.java +++ b/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/RegisterInfoReqVO.java @@ -4,6 +4,8 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; +import lombok.Builder; +import lombok.Data; /** * Function: diff --git a/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/res/CIMServerResVO.java b/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/res/CIMServerResVO.java index cec52bfb..07911f84 100644 --- a/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/res/CIMServerResVO.java +++ b/cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/res/CIMServerResVO.java @@ -3,6 +3,9 @@ import com.crossoverjie.cim.common.pojo.RouteInfo; import java.io.Serializable; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; /** * Function: @@ -11,39 +14,13 @@ * Date: 2018/12/23 00:43 * @since JDK 1.8 */ +@Data +@AllArgsConstructor +@NoArgsConstructor public class CIMServerResVO implements Serializable { private String ip ; private Integer cimServerPort; private Integer httpPort; - public CIMServerResVO(RouteInfo routeInfo) { - this.ip = routeInfo.getIp(); - this.cimServerPort = routeInfo.getCimServerPort(); - this.httpPort = routeInfo.getHttpPort(); - } - - public String getIp() { - return ip; - } - - public void setIp(String ip) { - this.ip = ip; - } - - public Integer getCimServerPort() { - return cimServerPort; - } - - public void setCimServerPort(Integer cimServerPort) { - this.cimServerPort = cimServerPort; - } - - public Integer getHttpPort() { - return httpPort; - } - - public void setHttpPort(Integer httpPort) { - this.httpPort = httpPort; - } } diff --git a/cim-server-api/pom.xml b/cim-server-api/pom.xml index 57897840..634775a9 100644 --- a/cim-server-api/pom.xml +++ b/cim-server-api/pom.xml @@ -10,7 +10,7 @@ 4.0.0 cim-server-api - 1.0.1-SNAPSHOT + 1.0.0-SNAPSHOT diff --git a/cim-server-api/src/main/java/com/crossoverjie/cim/server/api/ServerApi.java b/cim-server-api/src/main/java/com/crossoverjie/cim/server/api/ServerApi.java index 9e1be26e..15e83ecc 100644 --- a/cim-server-api/src/main/java/com/crossoverjie/cim/server/api/ServerApi.java +++ b/cim-server-api/src/main/java/com/crossoverjie/cim/server/api/ServerApi.java @@ -1,6 +1,9 @@ package com.crossoverjie.cim.server.api; +import com.crossoverjie.cim.common.core.proxy.DynamicUrl; +import com.crossoverjie.cim.common.res.BaseResponse; import com.crossoverjie.cim.server.api.vo.req.SendMsgReqVO; +import com.crossoverjie.cim.server.api.vo.res.SendMsgResVO; /** * Function: @@ -17,5 +20,5 @@ public interface ServerApi { * @return * @throws Exception */ - Object sendMsg(SendMsgReqVO sendMsgReqVO) throws Exception; + BaseResponse sendMsg(SendMsgReqVO sendMsgReqVO, @DynamicUrl String url); } diff --git a/cim-server-api/src/main/java/com/crossoverjie/cim/server/api/vo/req/SendMsgReqVO.java b/cim-server-api/src/main/java/com/crossoverjie/cim/server/api/vo/req/SendMsgReqVO.java index 247d49f4..730588b2 100644 --- a/cim-server-api/src/main/java/com/crossoverjie/cim/server/api/vo/req/SendMsgReqVO.java +++ b/cim-server-api/src/main/java/com/crossoverjie/cim/server/api/vo/req/SendMsgReqVO.java @@ -4,6 +4,9 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; +import java.util.Map; +import lombok.Getter; +import lombok.Setter; /** * Function: @@ -22,6 +25,10 @@ public class SendMsgReqVO extends BaseRequest { @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "userId", example = "11") private Long userId ; + @Setter + @Getter + private Map properties; + public SendMsgReqVO() { } diff --git a/cim-server/pom.xml b/cim-server/pom.xml index de73ab52..43e5cab5 100644 --- a/cim-server/pom.xml +++ b/cim-server/pom.xml @@ -88,21 +88,5 @@ - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - repackage - - - - - - \ No newline at end of file diff --git a/cim-server/src/main/java/com/crossoverjie/cim/server/CIMServerApplication.java b/cim-server/src/main/java/com/crossoverjie/cim/server/CIMServerApplication.java index cce3513f..a5f99505 100644 --- a/cim-server/src/main/java/com/crossoverjie/cim/server/CIMServerApplication.java +++ b/cim-server/src/main/java/com/crossoverjie/cim/server/CIMServerApplication.java @@ -1,9 +1,10 @@ package com.crossoverjie.cim.server; +import com.crossoverjie.cim.common.metastore.MetaStore; import com.crossoverjie.cim.server.config.AppConfiguration; -import com.crossoverjie.cim.server.kit.RegistryZK; +import com.crossoverjie.cim.server.kit.RegistryMetaStore; +import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; @@ -19,9 +20,12 @@ public class CIMServerApplication implements CommandLineRunner{ - @Autowired + @Resource private AppConfiguration appConfiguration ; + @Resource + private MetaStore metaStore; + @Value("${server.port}") private int httpPort ; @@ -32,9 +36,8 @@ public static void main(String[] args) { @Override public void run(String... args) throws Exception { - //获得本机IP String addr = InetAddress.getLocalHost().getHostAddress(); - Thread thread = new Thread(new RegistryZK(addr, appConfiguration.getCimServerPort(),httpPort)); + Thread thread = new Thread(new RegistryMetaStore(metaStore, addr, appConfiguration.getCimServerPort(),httpPort)); thread.setName("registry-zk"); thread.start() ; } diff --git a/cim-server/src/main/java/com/crossoverjie/cim/server/config/BeanConfig.java b/cim-server/src/main/java/com/crossoverjie/cim/server/config/BeanConfig.java index 81c6f11b..f73bc49f 100644 --- a/cim-server/src/main/java/com/crossoverjie/cim/server/config/BeanConfig.java +++ b/cim-server/src/main/java/com/crossoverjie/cim/server/config/BeanConfig.java @@ -1,15 +1,17 @@ package com.crossoverjie.cim.server.config; -import com.crossoverjie.cim.common.constant.Constants; -import com.crossoverjie.cim.common.protocol.CIMRequestProto; +import com.crossoverjie.cim.common.core.proxy.RpcProxyManager; +import com.crossoverjie.cim.common.metastore.MetaStore; +import com.crossoverjie.cim.common.metastore.ZkMetaStoreImpl; +import com.crossoverjie.cim.common.protocol.BaseCommand; +import com.crossoverjie.cim.common.protocol.Request; +import com.crossoverjie.cim.route.api.RouteApi; +import jakarta.annotation.Resource; +import java.util.concurrent.TimeUnit; import okhttp3.OkHttpClient; -import org.I0Itec.zkclient.ZkClient; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import java.util.concurrent.TimeUnit; - /** * Function: * @@ -20,13 +22,8 @@ @Configuration public class BeanConfig { - @Autowired - private AppConfiguration appConfiguration ; - - @Bean - public ZkClient buildZKClient(){ - return new ZkClient(appConfiguration.getZkAddr(), appConfiguration.getZkConnectTimeout()); - } + @Resource + private AppConfiguration appConfiguration; /** * http client @@ -42,18 +39,27 @@ public OkHttpClient okHttpClient() { return builder.build(); } + @Bean + public MetaStore metaStore() { + return new ZkMetaStoreImpl(); + } + /** * 创建心跳单例 * @return */ @Bean(value = "heartBeat") - public CIMRequestProto.CIMReqProtocol heartBeat() { - CIMRequestProto.CIMReqProtocol heart = CIMRequestProto.CIMReqProtocol.newBuilder() + public Request heartBeat() { + return Request.newBuilder() .setRequestId(0L) .setReqMsg("pong") - .setType(Constants.CommandType.PING) + .setCmd(BaseCommand.PING) .build(); - return heart; + } + + @Bean + public RouteApi routeApi(OkHttpClient okHttpClient) { + return RpcProxyManager.create(RouteApi.class, appConfiguration.getRouteUrl(), okHttpClient); } } diff --git a/cim-server/src/main/java/com/crossoverjie/cim/server/controller/IndexController.java b/cim-server/src/main/java/com/crossoverjie/cim/server/controller/IndexController.java index a85b8677..748e46fa 100644 --- a/cim-server/src/main/java/com/crossoverjie/cim/server/controller/IndexController.java +++ b/cim-server/src/main/java/com/crossoverjie/cim/server/controller/IndexController.java @@ -1,5 +1,6 @@ package com.crossoverjie.cim.server.controller; +import com.crossoverjie.cim.common.core.proxy.DynamicUrl; import com.crossoverjie.cim.common.enums.StatusEnum; import com.crossoverjie.cim.common.res.BaseResponse; import com.crossoverjie.cim.server.api.ServerApi; @@ -38,7 +39,7 @@ public class IndexController implements ServerApi { @Operation(summary = "Push msg to client") @RequestMapping(value = "sendMsg",method = RequestMethod.POST) @ResponseBody - public BaseResponse sendMsg(@RequestBody SendMsgReqVO sendMsgReqVO){ + public BaseResponse sendMsg(@RequestBody SendMsgReqVO sendMsgReqVO, @DynamicUrl String url){ BaseResponse res = new BaseResponse(); cimServer.sendMsg(sendMsgReqVO) ; 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..13636af0 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 @@ -1,10 +1,10 @@ package com.crossoverjie.cim.server.handle; -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.common.protocol.BaseCommand; +import com.crossoverjie.cim.common.protocol.Request; import com.crossoverjie.cim.common.util.NettyAttrUtil; import com.crossoverjie.cim.server.kit.RouteHandler; import com.crossoverjie.cim.server.kit.ServerHeartBeatHandlerImpl; @@ -28,7 +28,7 @@ */ @ChannelHandler.Sharable @Slf4j -public class CIMServerHandle extends SimpleChannelInboundHandler { +public class CIMServerHandle extends SimpleChannelInboundHandler { @@ -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) ; @@ -71,10 +71,10 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc @Override - protected void channelRead0(ChannelHandlerContext ctx, CIMRequestProto.CIMReqProtocol msg) throws Exception { + protected void channelRead0(ChannelHandlerContext ctx, Request msg) throws Exception { log.info("received msg=[{}]", msg.toString()); - if (msg.getType() == Constants.CommandType.LOGIN) { + if (msg.getCmd() == BaseCommand.LOGIN_REQUEST) { //保存客户端与 Channel 之间的关系 SessionSocketHolder.put(msg.getRequestId(), (NioSocketChannel) ctx.channel()); SessionSocketHolder.saveSession(msg.getRequestId(), msg.getReqMsg()); @@ -82,11 +82,10 @@ protected void channelRead0(ChannelHandlerContext ctx, CIMRequestProto.CIMReqPro } //心跳更新时间 - if (msg.getType() == Constants.CommandType.PING){ + if (msg.getCmd() == BaseCommand.PING){ NettyAttrUtil.updateReaderTime(ctx.channel(),System.currentTimeMillis()); //向客户端响应 pong 消息 - CIMRequestProto.CIMReqProtocol heartBeat = SpringBeanFactory.getBean("heartBeat", - CIMRequestProto.CIMReqProtocol.class); + Request heartBeat = SpringBeanFactory.getBean("heartBeat", Request.class); ctx.writeAndFlush(heartBeat).addListeners((ChannelFutureListener) future -> { if (!future.isSuccess()) { log.error("IO error,close Channel"); diff --git a/cim-server/src/main/java/com/crossoverjie/cim/server/init/CIMServerInitializer.java b/cim-server/src/main/java/com/crossoverjie/cim/server/init/CIMServerInitializer.java index 0ca8d7f1..1d2aacfa 100644 --- a/cim-server/src/main/java/com/crossoverjie/cim/server/init/CIMServerInitializer.java +++ b/cim-server/src/main/java/com/crossoverjie/cim/server/init/CIMServerInitializer.java @@ -1,6 +1,6 @@ package com.crossoverjie.cim.server.init; -import com.crossoverjie.cim.common.protocol.CIMRequestProto; +import com.crossoverjie.cim.common.protocol.Request; import com.crossoverjie.cim.server.handle.CIMServerHandle; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; @@ -29,7 +29,7 @@ protected void initChannel(Channel ch) throws Exception { .addLast(new IdleStateHandler(11, 0, 0)) // google Protobuf 编解码 .addLast(new ProtobufVarint32FrameDecoder()) - .addLast(new ProtobufDecoder(CIMRequestProto.CIMReqProtocol.getDefaultInstance())) + .addLast(new ProtobufDecoder(Request.getDefaultInstance())) .addLast(new ProtobufVarint32LengthFieldPrepender()) .addLast(new ProtobufEncoder()) .addLast(cimServerHandle); diff --git a/cim-server/src/main/java/com/crossoverjie/cim/server/kit/RegistryMetaStore.java b/cim-server/src/main/java/com/crossoverjie/cim/server/kit/RegistryMetaStore.java new file mode 100644 index 00000000..60b8825b --- /dev/null +++ b/cim-server/src/main/java/com/crossoverjie/cim/server/kit/RegistryMetaStore.java @@ -0,0 +1,55 @@ +package com.crossoverjie.cim.server.kit; + +import com.crossoverjie.cim.common.metastore.MetaStore; +import com.crossoverjie.cim.common.metastore.ZkConfiguration; +import com.crossoverjie.cim.server.config.AppConfiguration; +import com.crossoverjie.cim.server.util.SpringBeanFactory; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.curator.retry.ExponentialBackoffRetry; + + +/** + * Function: + * + * @author crossoverJie + * Date: 2018/8/24 01:37 + * @since JDK 1.8 + */ +@Slf4j +public class RegistryMetaStore implements Runnable { + + + private MetaStore metaStore; + + private AppConfiguration appConfiguration ; + + private String ip; + private int cimServerPort; + private int httpPort; + public RegistryMetaStore(MetaStore metaStore, String ip, int cimServerPort, int httpPort) { + this.ip = ip; + this.cimServerPort = cimServerPort; + this.httpPort = httpPort ; + this.metaStore = metaStore; + appConfiguration = SpringBeanFactory.getBean(AppConfiguration.class) ; + } + + @SneakyThrows + @Override + public void run() { + + if (!appConfiguration.isZkSwitch()){ + log.info("Skip registry to metaStore"); + return; + } + + ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3); + metaStore.initialize(ZkConfiguration.builder() + .metaServiceUri(appConfiguration.getZkAddr()) + .timeoutMs(appConfiguration.getZkConnectTimeout()) + .retryPolicy(retryPolicy) + .build()); + metaStore.addServer(ip, cimServerPort, httpPort); + } +} \ No newline at end of file diff --git a/cim-server/src/main/java/com/crossoverjie/cim/server/kit/RegistryZK.java b/cim-server/src/main/java/com/crossoverjie/cim/server/kit/RegistryZK.java deleted file mode 100644 index e68d0617..00000000 --- a/cim-server/src/main/java/com/crossoverjie/cim/server/kit/RegistryZK.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.crossoverjie.cim.server.kit; - -import com.crossoverjie.cim.server.config.AppConfiguration; -import com.crossoverjie.cim.server.util.SpringBeanFactory; -import lombok.extern.slf4j.Slf4j; - - -/** - * Function: - * - * @author crossoverJie - * Date: 2018/8/24 01:37 - * @since JDK 1.8 - */ -@Slf4j -public class RegistryZK implements Runnable { - - - private ZKit zKit; - - private AppConfiguration appConfiguration ; - - private String ip; - private int cimServerPort; - private int httpPort; - - public RegistryZK(String ip, int cimServerPort,int httpPort) { - this.ip = ip; - this.cimServerPort = cimServerPort; - this.httpPort = httpPort ; - zKit = SpringBeanFactory.getBean(ZKit.class) ; - appConfiguration = SpringBeanFactory.getBean(AppConfiguration.class) ; - } - - @Override - public void run() { - - //创建父节点 - zKit.createRootNode(); - - //是否要将自己注册到 ZK - if (appConfiguration.isZkSwitch()){ - String path = appConfiguration.getZkRoot() + "/ip-" + ip + ":" + cimServerPort + ":" + httpPort; - zKit.createNode(path); - log.info("Registry zookeeper success, msg=[{}]", path); - } - - - } -} \ No newline at end of file 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 a70aadb9..58ac16c6 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 @@ -1,12 +1,13 @@ package com.crossoverjie.cim.server.kit; -import com.crossoverjie.cim.common.core.proxy.ProxyManager; +import com.crossoverjie.cim.common.core.proxy.RpcProxyManager; import com.crossoverjie.cim.common.pojo.CIMUserInfo; import com.crossoverjie.cim.route.api.RouteApi; import com.crossoverjie.cim.route.api.vo.req.ChatReqVO; import com.crossoverjie.cim.server.config.AppConfiguration; import com.crossoverjie.cim.server.util.SessionSocketHolder; import io.netty.channel.socket.nio.NioSocketChannel; +import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; import okhttp3.Response; @@ -26,20 +27,22 @@ @Slf4j public class RouteHandler { - @Autowired + @Resource private OkHttpClient okHttpClient; - @Autowired + @Resource private AppConfiguration configuration; + @Resource + private RouteApi routeApi; + /** * 用户下线 * * @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()); @@ -58,16 +61,8 @@ public void userOffLine(CIMUserInfo userInfo, NioSocketChannel channel) throws I * @throws IOException */ public void clearRouteInfo(CIMUserInfo userInfo) { - RouteApi routeApi = new ProxyManager<>(RouteApi.class, configuration.getRouteUrl(), okHttpClient).getInstance(); - Response response = null; ChatReqVO vo = new ChatReqVO(userInfo.getUserId(), userInfo.getUserName()); - try { - response = (Response) routeApi.offLine(vo); - } catch (Exception e){ - log.error("Exception",e); - }finally { - response.body().close(); - } + routeApi.offLine(vo); } } diff --git a/cim-server/src/main/java/com/crossoverjie/cim/server/kit/ZKit.java b/cim-server/src/main/java/com/crossoverjie/cim/server/kit/ZKit.java deleted file mode 100644 index 6a9839c9..00000000 --- a/cim-server/src/main/java/com/crossoverjie/cim/server/kit/ZKit.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.crossoverjie.cim.server.kit; - -import com.crossoverjie.cim.server.config.AppConfiguration; -import lombok.extern.slf4j.Slf4j; -import org.I0Itec.zkclient.ZkClient; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * Function: Zookeeper 工具 - * - * @author crossoverJie - * Date: 2018/8/19 00:33 - * @since JDK 1.8 - */ -@Component -@Slf4j -public class ZKit { - - @Autowired - private ZkClient zkClient; - - @Autowired - private AppConfiguration appConfiguration ; - - /** - * 创建父级节点 - */ - public void createRootNode(){ - boolean exists = zkClient.exists(appConfiguration.getZkRoot()); - if (exists){ - return; - } - - //创建 root - zkClient.createPersistent(appConfiguration.getZkRoot()) ; - } - - /** - * 写入指定节点 临时目录 - * - * @param path - */ - public void createNode(String path) { - zkClient.createEphemeral(path); - } - -} diff --git a/cim-server/src/main/java/com/crossoverjie/cim/server/server/CIMServer.java b/cim-server/src/main/java/com/crossoverjie/cim/server/server/CIMServer.java index f823dac7..89a3e73a 100644 --- a/cim-server/src/main/java/com/crossoverjie/cim/server/server/CIMServer.java +++ b/cim-server/src/main/java/com/crossoverjie/cim/server/server/CIMServer.java @@ -1,7 +1,7 @@ package com.crossoverjie.cim.server.server; -import com.crossoverjie.cim.common.constant.Constants; -import com.crossoverjie.cim.common.protocol.CIMRequestProto; +import com.crossoverjie.cim.common.protocol.BaseCommand; +import com.crossoverjie.cim.common.protocol.Request; import com.crossoverjie.cim.server.api.vo.req.SendMsgReqVO; import com.crossoverjie.cim.server.init.CIMServerInitializer; import com.crossoverjie.cim.server.util.SessionSocketHolder; @@ -86,10 +86,11 @@ public void sendMsg(SendMsgReqVO sendMsgReqVO){ log.error("client {} offline!", sendMsgReqVO.getUserId()); return; } - CIMRequestProto.CIMReqProtocol protocol = CIMRequestProto.CIMReqProtocol.newBuilder() + Request protocol = Request.newBuilder() .setRequestId(sendMsgReqVO.getUserId()) .setReqMsg(sendMsgReqVO.getMsg()) - .setType(Constants.CommandType.MSG) + .putAllProperties(sendMsgReqVO.getProperties()) + .setCmd(BaseCommand.MESSAGE) .build(); ChannelFuture future = socketChannel.writeAndFlush(protocol); diff --git a/cim-server/src/main/resources/banner.txt b/cim-server/src/main/resources/banner.txt index 97b15b91..1fc2e873 100644 --- a/cim-server/src/main/resources/banner.txt +++ b/cim-server/src/main/resources/banner.txt @@ -1,14 +1,8 @@ - ▄████▄ ██▓ ███▄ ▄███▓ -▒██▀ ▀█ ▓██▒▓██▒▀█▀ ██▒ -▒▓█ ▄ ▒██▒▓██ ▓██░ -▒▓▓▄ ▄██▒░██░▒██ ▒██ -▒ ▓███▀ ░░██░▒██▒ ░██▒ -░ ░▒ ▒ ░░▓ ░ ▒░ ░ ░ - ░ ▒ ▒ ░░ ░ ░ -░ ▒ ░░ ░ -░ ░ ░ ░ -░ + _ + ____(_)_ _ ___ ___ _____ _____ ____ +/ __/ / ' \ (_- - 4.0.0 - com.crossoverjie.netty - cim - 1.0.0-SNAPSHOT - pom - - cim - Spring Boot - - - - 4.12 - 4.1.68.Final - UTF-8 - UTF-8 - 2.5.0 - - - - org.springframework.boot - spring-boot-starter-parent - 3.3.0 - - - - - cim-server - cim-client - cim-common - cim-forward-route + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + com.crossoverjie.netty + cim + 1.0.0-SNAPSHOT + pom + + cim + Spring Boot + + + + 4.12 + 4.1.68.Final + UTF-8 + UTF-8 + 2.5.0 + 5.1.0 + 3.8.4 + 0.8.11 + 4.28.3 + 1.19.0 + + + + org.springframework.boot + spring-boot-starter-parent + 3.3.0 + + + + + cim-server + cim-client + cim-common + cim-forward-route cim-rout-api cim-server-api + cim-integration-test + cim-client-sdk - - - - - com.github.xiaoymin - knife4j-openapi3-jakarta-spring-boot-starter - 4.4.0 - - - - com.squareup.okhttp3 - okhttp - 3.3.1 - - - com.github.sgroschupf - zkclient - 0.1 - - - - com.crossoverjie.netty - cim-common - 1.0.1-SNAPSHOT - - - - com.crossoverjie.netty - cim-rout-api - 1.0.1-SNAPSHOT - - - - com.crossoverjie.netty - cim-server-api - 1.0.1-SNAPSHOT - - - - com.google.protobuf - protobuf-java - 3.16.3 - - - - - - io.netty - netty-all - ${netty.version} - - - - junit - junit - ${junit.version} - - - - com.alibaba - fastjson - 1.2.83 - - - - com.google.guava - guava - 19.0 - - - - de.codecentric - spring-boot-admin-starter-client - 1.5.7 - - - - jakarta.validation - jakarta.validation-api - 3.0.0 - - - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 17 - 17 - - - - + + + + + com.github.xiaoymin + knife4j-openapi3-jakarta-spring-boot-starter + 4.4.0 + + + + com.squareup.okhttp3 + okhttp + 3.3.1 + + + + com.101tec + zkclient + 0.11 + + + + + org.apache.curator + curator-recipes + ${curator.version} + + + + org.apache.curator + curator-x-discovery + ${curator.version} + + + + + org.apache.zookeeper + zookeeper + ${zookeeper.version} + + + net.java.dev.javacc + javacc + + + ch.qos.logback + * + + + io.netty + * + + + + + org.apache.zookeeper + zookeeper + ${zookeeper.version} + test-jar + + + org.slf4j + slf4j-api + + + ch.qos.logback + * + + + io.netty + * + + + + + + com.crossoverjie.netty + cim-common + ${project.version} + + + com.crossoverjie.netty + cim-client-sdk + ${project.version} + + + + com.crossoverjie.netty + cim-rout-api + ${project.version} + + + + com.crossoverjie.netty + cim-server-api + ${project.version} + + + + com.google.protobuf + protobuf-java + ${protobuf-java.version} + + + + + io.netty + netty-all + ${netty.version} + + + + junit + junit + ${junit.version} + + + + com.alibaba + fastjson + 1.2.83 + + + + com.google.guava + guava + 19.0 + + + + de.codecentric + spring-boot-admin-starter-client + 1.5.7 + + + + jakarta.validation + jakarta.validation-api + 3.0.0 + + + + com.clever-cloud + testcontainers-zookeeper + 0.1.3 + test + + + + com.redis + testcontainers-redis + 2.2.2 + test + + + + + + + + + x86 + + true + + + + + + + aarch + + + aarch64 + + + + osx-x86_64 + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + 17 + + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + + prepare-agent + + + + report + test + + report + + + + + + \ No newline at end of file diff --git a/protocol/BaseRequestProto.proto b/protocol/BaseRequestProto.proto deleted file mode 100644 index dde24203..00000000 --- a/protocol/BaseRequestProto.proto +++ /dev/null @@ -1,14 +0,0 @@ -syntax = "proto2"; - -package protocol; - -option java_package = "com.crossoverjie.cim.common.protocol"; -option java_outer_classname = "CIMRequestProto"; - -message CIMReqProtocol { - required int64 requestId = 2; - required string reqMsg = 1; - required int32 type = 3; - -} - diff --git a/protocol/BaseResponseProto.proto b/protocol/BaseResponseProto.proto deleted file mode 100644 index b9bc6d16..00000000 --- a/protocol/BaseResponseProto.proto +++ /dev/null @@ -1,13 +0,0 @@ -syntax = "proto2"; - -package protocol; - -option java_package = "com.crossoverjie.cim.common.protocol"; -option java_outer_classname = "CIMResponseProto"; - -message CIMResProtocol { - required int64 responseId = 2; - required string resMsg = 1; - required int32 type = 3; - -} diff --git a/script/build.sh b/script/build.sh new file mode 100644 index 00000000..d463deb5 --- /dev/null +++ b/script/build.sh @@ -0,0 +1 @@ +# todo build \ No newline at end of file