提交者
GitHub
Merge pull request #1 from crossoverJie/master
merge
正在显示
51 个修改的文件
包含
1233 行增加
和
247 行删除
| @@ -49,8 +49,9 @@ | @@ -49,8 +49,9 @@ | ||
| 49 | * [x] 根据实际情况灵活的水平扩容、缩容。 | 49 | * [x] 根据实际情况灵活的水平扩容、缩容。 |
| 50 | * [x] 路由(`cim-forward-route`)服务自身是无状态,可用 `Nginx` 代理支持高可用。 | 50 | * [x] 路由(`cim-forward-route`)服务自身是无状态,可用 `Nginx` 代理支持高可用。 |
| 51 | * [x] 服务端自动剔除离线客户端。 | 51 | * [x] 服务端自动剔除离线客户端。 |
| 52 | -* [ ] 弱网环境下客户端自动重连。 | 52 | +* [x] 客户端自动重连。 |
| 53 | * [ ] 分组群聊。 | 53 | * [ ] 分组群聊。 |
| 54 | +* [ ] Android SDK。 | ||
| 54 | * [ ] 离线消息。 | 55 | * [ ] 离线消息。 |
| 55 | * [ ] 协议支持消息加密。 | 56 | * [ ] 协议支持消息加密。 |
| 56 | * [ ] 更多的客户端路由策略。 | 57 | * [ ] 更多的客户端路由策略。 |
| @@ -179,6 +180,7 @@ java -jar cim-client-1.0.0-SNAPSHOT.jar --server.port=8084 --cim.user.id=上方 | @@ -179,6 +180,7 @@ java -jar cim-client-1.0.0-SNAPSHOT.jar --server.port=8084 --cim.user.id=上方 | ||
| 179 | | `:ai` | 开启 AI 模式 | | 180 | | `:ai` | 开启 AI 模式 | |
| 180 | | `:qai` | 关闭 AI 模式 | | 181 | | `:qai` | 关闭 AI 模式 | |
| 181 | | `:pu` | 模糊匹配用户 | | 182 | | `:pu` | 模糊匹配用户 | |
| 183 | +| `:info` | 获取客户端信息 | | ||
| 182 | | `:` | 更多命令正在开发中。。 | | 184 | | `:` | 更多命令正在开发中。。 | |
| 183 | 185 | ||
| 184 |  | 186 |  |
| @@ -205,7 +207,7 @@ java -jar cim-client-1.0.0-SNAPSHOT.jar --server.port=8084 --cim.user.id=上方 | @@ -205,7 +207,7 @@ java -jar cim-client-1.0.0-SNAPSHOT.jar --server.port=8084 --cim.user.id=上方 | ||
| 205 | 207 | ||
| 206 |  | 208 |  |
| 207 | 209 | ||
| 208 | -使用命令 `:qu prefix` 可以按照前缀的方式重新用户信息。 | 210 | +使用命令 `:qu prefix` 可以按照前缀的方式搜索用户信息。 |
| 209 | 211 | ||
| 210 | > 该功能主要用于在移动端中的输入框中搜索用户。 | 212 | > 该功能主要用于在移动端中的输入框中搜索用户。 |
| 211 | 213 | ||
| @@ -248,6 +250,12 @@ java -jar cim-client-1.0.0-SNAPSHOT.jar --server.port=8084 --cim.user.id=上方 | @@ -248,6 +250,12 @@ java -jar cim-client-1.0.0-SNAPSHOT.jar --server.port=8084 --cim.user.id=上方 | ||
| 248 | 250 | ||
| 249 |  | 251 |  |
| 250 | 252 | ||
| 251 | -# Code Visualization | ||
| 252 | 253 | ||
| 253 | - [](https://youtu.be/NhV_brPIG74) | 254 | +### Code Visualization: |
| 255 | + | ||
| 256 | +Here is a cool visualization of the code evolution | ||
| 257 | + | ||
| 258 | + [](https://www.youtube.com/watch?v=NhV_brPIG74) | ||
| 259 | + | ||
| 260 | + [https://www.youtube.com/watch?v=NhV_brPIG74](https://www.youtube.com/watch?v=NhV_brPIG74) | ||
| 261 | + |
| @@ -73,12 +73,6 @@ | @@ -73,12 +73,6 @@ | ||
| 73 | </dependency> | 73 | </dependency> |
| 74 | 74 | ||
| 75 | <dependency> | 75 | <dependency> |
| 76 | - <groupId>io.netty</groupId> | ||
| 77 | - <artifactId>netty-all</artifactId> | ||
| 78 | - <version>${netty.version}</version> | ||
| 79 | - </dependency> | ||
| 80 | - | ||
| 81 | - <dependency> | ||
| 82 | <groupId>junit</groupId> | 76 | <groupId>junit</groupId> |
| 83 | <artifactId>junit</artifactId> | 77 | <artifactId>junit</artifactId> |
| 84 | </dependency> | 78 | </dependency> |
| 1 | package com.crossoverjie.cim.client; | 1 | package com.crossoverjie.cim.client; |
| 2 | 2 | ||
| 3 | import com.crossoverjie.cim.client.scanner.Scan; | 3 | import com.crossoverjie.cim.client.scanner.Scan; |
| 4 | +import com.crossoverjie.cim.client.service.impl.ClientInfo; | ||
| 4 | import org.slf4j.Logger; | 5 | import org.slf4j.Logger; |
| 5 | import org.slf4j.LoggerFactory; | 6 | import org.slf4j.LoggerFactory; |
| 7 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 6 | import org.springframework.boot.CommandLineRunner; | 8 | import org.springframework.boot.CommandLineRunner; |
| 7 | import org.springframework.boot.SpringApplication; | 9 | import org.springframework.boot.SpringApplication; |
| 8 | import org.springframework.boot.autoconfigure.SpringBootApplication; | 10 | import org.springframework.boot.autoconfigure.SpringBootApplication; |
| @@ -15,6 +17,8 @@ public class CIMClientApplication implements CommandLineRunner{ | @@ -15,6 +17,8 @@ public class CIMClientApplication implements CommandLineRunner{ | ||
| 15 | 17 | ||
| 16 | private final static Logger LOGGER = LoggerFactory.getLogger(CIMClientApplication.class); | 18 | private final static Logger LOGGER = LoggerFactory.getLogger(CIMClientApplication.class); |
| 17 | 19 | ||
| 20 | + @Autowired | ||
| 21 | + private ClientInfo clientInfo ; | ||
| 18 | public static void main(String[] args) { | 22 | public static void main(String[] args) { |
| 19 | SpringApplication.run(CIMClientApplication.class, args); | 23 | SpringApplication.run(CIMClientApplication.class, args); |
| 20 | LOGGER.info("启动 Client 服务成功"); | 24 | LOGGER.info("启动 Client 服务成功"); |
| @@ -26,5 +30,6 @@ public class CIMClientApplication implements CommandLineRunner{ | @@ -26,5 +30,6 @@ public class CIMClientApplication implements CommandLineRunner{ | ||
| 26 | Thread thread = new Thread(scan); | 30 | Thread thread = new Thread(scan); |
| 27 | thread.setName("scan-thread"); | 31 | thread.setName("scan-thread"); |
| 28 | thread.start(); | 32 | thread.start(); |
| 33 | + clientInfo.saveStartDate(); | ||
| 29 | } | 34 | } |
| 30 | } | 35 | } |
| 1 | package com.crossoverjie.cim.client.client; | 1 | package com.crossoverjie.cim.client.client; |
| 2 | 2 | ||
| 3 | +import com.crossoverjie.cim.client.config.AppConfiguration; | ||
| 3 | import com.crossoverjie.cim.client.init.CIMClientHandleInitializer; | 4 | import com.crossoverjie.cim.client.init.CIMClientHandleInitializer; |
| 5 | +import com.crossoverjie.cim.client.service.MsgHandle; | ||
| 4 | import com.crossoverjie.cim.client.service.RouteRequest; | 6 | import com.crossoverjie.cim.client.service.RouteRequest; |
| 7 | +import com.crossoverjie.cim.client.service.impl.ClientInfo; | ||
| 5 | import com.crossoverjie.cim.client.vo.req.GoogleProtocolVO; | 8 | import com.crossoverjie.cim.client.vo.req.GoogleProtocolVO; |
| 6 | import com.crossoverjie.cim.client.vo.req.LoginReqVO; | 9 | import com.crossoverjie.cim.client.vo.req.LoginReqVO; |
| 7 | import com.crossoverjie.cim.client.vo.res.CIMServerResVO; | 10 | import com.crossoverjie.cim.client.vo.res.CIMServerResVO; |
| @@ -16,6 +19,7 @@ import io.netty.channel.EventLoopGroup; | @@ -16,6 +19,7 @@ import io.netty.channel.EventLoopGroup; | ||
| 16 | import io.netty.channel.nio.NioEventLoopGroup; | 19 | import io.netty.channel.nio.NioEventLoopGroup; |
| 17 | import io.netty.channel.socket.SocketChannel; | 20 | import io.netty.channel.socket.SocketChannel; |
| 18 | import io.netty.channel.socket.nio.NioSocketChannel; | 21 | import io.netty.channel.socket.nio.NioSocketChannel; |
| 22 | +import io.netty.util.concurrent.DefaultThreadFactory; | ||
| 19 | import org.slf4j.Logger; | 23 | import org.slf4j.Logger; |
| 20 | import org.slf4j.LoggerFactory; | 24 | import org.slf4j.LoggerFactory; |
| 21 | import org.springframework.beans.factory.annotation.Autowired; | 25 | import org.springframework.beans.factory.annotation.Autowired; |
| @@ -36,7 +40,7 @@ public class CIMClient { | @@ -36,7 +40,7 @@ public class CIMClient { | ||
| 36 | 40 | ||
| 37 | private final static Logger LOGGER = LoggerFactory.getLogger(CIMClient.class); | 41 | private final static Logger LOGGER = LoggerFactory.getLogger(CIMClient.class); |
| 38 | 42 | ||
| 39 | - private EventLoopGroup group = new NioEventLoopGroup(); | 43 | + private EventLoopGroup group = new NioEventLoopGroup(0, new DefaultThreadFactory("cim-work")); |
| 40 | 44 | ||
| 41 | @Value("${cim.user.id}") | 45 | @Value("${cim.user.id}") |
| 42 | private long userId; | 46 | private long userId; |
| @@ -49,6 +53,20 @@ public class CIMClient { | @@ -49,6 +53,20 @@ public class CIMClient { | ||
| 49 | @Autowired | 53 | @Autowired |
| 50 | private RouteRequest routeRequest; | 54 | private RouteRequest routeRequest; |
| 51 | 55 | ||
| 56 | + @Autowired | ||
| 57 | + private AppConfiguration configuration; | ||
| 58 | + | ||
| 59 | + @Autowired | ||
| 60 | + private MsgHandle msgHandle; | ||
| 61 | + | ||
| 62 | + @Autowired | ||
| 63 | + private ClientInfo clientInfo; | ||
| 64 | + | ||
| 65 | + /** | ||
| 66 | + * 重试次数 | ||
| 67 | + */ | ||
| 68 | + private int errorCount; | ||
| 69 | + | ||
| 52 | @PostConstruct | 70 | @PostConstruct |
| 53 | public void start() throws Exception { | 71 | public void start() throws Exception { |
| 54 | 72 | ||
| @@ -70,14 +88,25 @@ public class CIMClient { | @@ -70,14 +88,25 @@ public class CIMClient { | ||
| 70 | * @param cimServer | 88 | * @param cimServer |
| 71 | * @throws InterruptedException | 89 | * @throws InterruptedException |
| 72 | */ | 90 | */ |
| 73 | - private void startClient(CIMServerResVO.ServerInfo cimServer) throws InterruptedException { | 91 | + private void startClient(CIMServerResVO.ServerInfo cimServer) { |
| 74 | Bootstrap bootstrap = new Bootstrap(); | 92 | Bootstrap bootstrap = new Bootstrap(); |
| 75 | bootstrap.group(group) | 93 | bootstrap.group(group) |
| 76 | .channel(NioSocketChannel.class) | 94 | .channel(NioSocketChannel.class) |
| 77 | .handler(new CIMClientHandleInitializer()) | 95 | .handler(new CIMClientHandleInitializer()) |
| 78 | ; | 96 | ; |
| 79 | 97 | ||
| 80 | - ChannelFuture future = bootstrap.connect(cimServer.getIp(), cimServer.getCimServerPort()).sync(); | 98 | + ChannelFuture future = null; |
| 99 | + try { | ||
| 100 | + future = bootstrap.connect(cimServer.getIp(), cimServer.getCimServerPort()).sync(); | ||
| 101 | + } catch (InterruptedException e) { | ||
| 102 | + errorCount++; | ||
| 103 | + | ||
| 104 | + if (errorCount >= configuration.getErrorCount()) { | ||
| 105 | + LOGGER.error("链接失败次数达到上限[{}]次", errorCount); | ||
| 106 | + msgHandle.shutdown(); | ||
| 107 | + } | ||
| 108 | + LOGGER.error("连接失败", e); | ||
| 109 | + } | ||
| 81 | if (future.isSuccess()) { | 110 | if (future.isSuccess()) { |
| 82 | LOGGER.info("启动 cim client 成功"); | 111 | LOGGER.info("启动 cim client 成功"); |
| 83 | } | 112 | } |
| @@ -90,10 +119,26 @@ public class CIMClient { | @@ -90,10 +119,26 @@ public class CIMClient { | ||
| 90 | * @return 路由服务器信息 | 119 | * @return 路由服务器信息 |
| 91 | * @throws Exception | 120 | * @throws Exception |
| 92 | */ | 121 | */ |
| 93 | - private CIMServerResVO.ServerInfo userLogin() throws Exception { | 122 | + private CIMServerResVO.ServerInfo userLogin() { |
| 94 | LoginReqVO loginReqVO = new LoginReqVO(userId, userName); | 123 | LoginReqVO loginReqVO = new LoginReqVO(userId, userName); |
| 95 | - CIMServerResVO.ServerInfo cimServer = routeRequest.getCIMServer(loginReqVO); | 124 | + CIMServerResVO.ServerInfo cimServer = null; |
| 125 | + try { | ||
| 126 | + cimServer = routeRequest.getCIMServer(loginReqVO); | ||
| 127 | + | ||
| 128 | + //保存系统信息 | ||
| 129 | + clientInfo.saveServiceInfo(cimServer.getIp() + ":" + cimServer.getCimServerPort()) | ||
| 130 | + .saveUserInfo(userId, userName); | ||
| 131 | + | ||
| 96 | LOGGER.info("cimServer=[{}]", cimServer.toString()); | 132 | LOGGER.info("cimServer=[{}]", cimServer.toString()); |
| 133 | + } catch (Exception e) { | ||
| 134 | + errorCount++; | ||
| 135 | + | ||
| 136 | + if (errorCount >= configuration.getErrorCount()) { | ||
| 137 | + LOGGER.error("重连次数达到上限[{}]次", errorCount); | ||
| 138 | + msgHandle.shutdown(); | ||
| 139 | + } | ||
| 140 | + LOGGER.error("登录失败", e); | ||
| 141 | + } | ||
| 97 | return cimServer; | 142 | return cimServer; |
| 98 | } | 143 | } |
| 99 | 144 | ||
| @@ -145,11 +190,27 @@ public class CIMClient { | @@ -145,11 +190,27 @@ public class CIMClient { | ||
| 145 | 190 | ||
| 146 | } | 191 | } |
| 147 | 192 | ||
| 193 | + | ||
| 194 | + public void reconnect() throws Exception { | ||
| 195 | + if (channel != null && channel.isActive()) { | ||
| 196 | + return; | ||
| 197 | + } | ||
| 198 | + //首先清除路由信息,下线 | ||
| 199 | + routeRequest.offLine(); | ||
| 200 | + | ||
| 201 | + LOGGER.info("开始重连。。"); | ||
| 202 | + start(); | ||
| 203 | + LOGGER.info("重连成功!!"); | ||
| 204 | + } | ||
| 205 | + | ||
| 148 | /** | 206 | /** |
| 149 | * 关闭 | 207 | * 关闭 |
| 208 | + * | ||
| 150 | * @throws InterruptedException | 209 | * @throws InterruptedException |
| 151 | */ | 210 | */ |
| 152 | public void close() throws InterruptedException { | 211 | public void close() throws InterruptedException { |
| 153 | - channel.close() ; | 212 | + if (channel != null){ |
| 213 | + channel.close(); | ||
| 214 | + } | ||
| 154 | } | 215 | } |
| 155 | } | 216 | } |
| @@ -22,6 +22,15 @@ public class AppConfiguration { | @@ -22,6 +22,15 @@ public class AppConfiguration { | ||
| 22 | @Value("${cim.msg.logger.path}") | 22 | @Value("${cim.msg.logger.path}") |
| 23 | private String msgLoggerPath ; | 23 | private String msgLoggerPath ; |
| 24 | 24 | ||
| 25 | + @Value("${cim.clear.route.request.url}") | ||
| 26 | + private String clearRouteUrl ; | ||
| 27 | + | ||
| 28 | + @Value("${cim.heartbeat.time}") | ||
| 29 | + private long heartBeatTime ; | ||
| 30 | + | ||
| 31 | + @Value("${cim.reconnect.count}") | ||
| 32 | + private int errorCount ; | ||
| 33 | + | ||
| 25 | public Long getUserId() { | 34 | public Long getUserId() { |
| 26 | return userId; | 35 | return userId; |
| 27 | } | 36 | } |
| @@ -45,4 +54,30 @@ public class AppConfiguration { | @@ -45,4 +54,30 @@ public class AppConfiguration { | ||
| 45 | public void setMsgLoggerPath(String msgLoggerPath) { | 54 | public void setMsgLoggerPath(String msgLoggerPath) { |
| 46 | this.msgLoggerPath = msgLoggerPath; | 55 | this.msgLoggerPath = msgLoggerPath; |
| 47 | } | 56 | } |
| 57 | + | ||
| 58 | + | ||
| 59 | + public long getHeartBeatTime() { | ||
| 60 | + return heartBeatTime; | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + public void setHeartBeatTime(long heartBeatTime) { | ||
| 64 | + this.heartBeatTime = heartBeatTime; | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + | ||
| 68 | + public String getClearRouteUrl() { | ||
| 69 | + return clearRouteUrl; | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + public void setClearRouteUrl(String clearRouteUrl) { | ||
| 73 | + this.clearRouteUrl = clearRouteUrl; | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + public int getErrorCount() { | ||
| 77 | + return errorCount; | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + public void setErrorCount(int errorCount) { | ||
| 81 | + this.errorCount = errorCount; | ||
| 82 | + } | ||
| 48 | } | 83 | } |
| @@ -82,6 +82,17 @@ public class BeanConfig { | @@ -82,6 +82,17 @@ public class BeanConfig { | ||
| 82 | return productExecutor ; | 82 | return productExecutor ; |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | + | ||
| 86 | + @Bean("scheduledTask") | ||
| 87 | + public ScheduledExecutorService buildSchedule(){ | ||
| 88 | + ThreadFactory sche = new ThreadFactoryBuilder() | ||
| 89 | + .setNameFormat("scheduled-%d") | ||
| 90 | + .setDaemon(true) | ||
| 91 | + .build(); | ||
| 92 | + ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1,sche) ; | ||
| 93 | + return scheduledExecutorService ; | ||
| 94 | + } | ||
| 95 | + | ||
| 85 | /** | 96 | /** |
| 86 | * 回调 bean | 97 | * 回调 bean |
| 87 | * @return | 98 | * @return |
| 1 | package com.crossoverjie.cim.client.handle; | 1 | package com.crossoverjie.cim.client.handle; |
| 2 | 2 | ||
| 3 | +import com.crossoverjie.cim.client.thread.ReConnectJob; | ||
| 3 | import com.crossoverjie.cim.client.util.SpringBeanFactory; | 4 | import com.crossoverjie.cim.client.util.SpringBeanFactory; |
| 4 | import com.crossoverjie.cim.common.constant.Constants; | 5 | import com.crossoverjie.cim.common.constant.Constants; |
| 5 | import com.crossoverjie.cim.common.protocol.CIMRequestProto; | 6 | import com.crossoverjie.cim.common.protocol.CIMRequestProto; |
| 6 | import com.crossoverjie.cim.common.protocol.CIMResponseProto; | 7 | import com.crossoverjie.cim.common.protocol.CIMResponseProto; |
| 8 | +import com.crossoverjie.cim.common.util.NettyAttrUtil; | ||
| 7 | import io.netty.channel.ChannelFutureListener; | 9 | import io.netty.channel.ChannelFutureListener; |
| 8 | import io.netty.channel.ChannelHandler; | 10 | import io.netty.channel.ChannelHandler; |
| 9 | import io.netty.channel.ChannelHandlerContext; | 11 | import io.netty.channel.ChannelHandlerContext; |
| @@ -13,7 +15,9 @@ import io.netty.handler.timeout.IdleStateEvent; | @@ -13,7 +15,9 @@ import io.netty.handler.timeout.IdleStateEvent; | ||
| 13 | import org.slf4j.Logger; | 15 | import org.slf4j.Logger; |
| 14 | import org.slf4j.LoggerFactory; | 16 | import org.slf4j.LoggerFactory; |
| 15 | 17 | ||
| 18 | +import java.util.concurrent.ScheduledExecutorService; | ||
| 16 | import java.util.concurrent.ThreadPoolExecutor; | 19 | import java.util.concurrent.ThreadPoolExecutor; |
| 20 | +import java.util.concurrent.TimeUnit; | ||
| 17 | 21 | ||
| 18 | /** | 22 | /** |
| 19 | * Function: | 23 | * Function: |
| @@ -31,6 +35,8 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt | @@ -31,6 +35,8 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt | ||
| 31 | 35 | ||
| 32 | private ThreadPoolExecutor threadPoolExecutor ; | 36 | private ThreadPoolExecutor threadPoolExecutor ; |
| 33 | 37 | ||
| 38 | + private ScheduledExecutorService scheduledExecutorService ; | ||
| 39 | + | ||
| 34 | 40 | ||
| 35 | @Override | 41 | @Override |
| 36 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { | 42 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { |
| @@ -38,12 +44,18 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt | @@ -38,12 +44,18 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt | ||
| 38 | if (evt instanceof IdleStateEvent){ | 44 | if (evt instanceof IdleStateEvent){ |
| 39 | IdleStateEvent idleStateEvent = (IdleStateEvent) evt ; | 45 | IdleStateEvent idleStateEvent = (IdleStateEvent) evt ; |
| 40 | 46 | ||
| 47 | + //LOGGER.info("定时检测服务端是否存活"); | ||
| 48 | + | ||
| 41 | if (idleStateEvent.state() == IdleState.WRITER_IDLE){ | 49 | if (idleStateEvent.state() == IdleState.WRITER_IDLE){ |
| 42 | CIMRequestProto.CIMReqProtocol heartBeat = SpringBeanFactory.getBean("heartBeat", | 50 | CIMRequestProto.CIMReqProtocol heartBeat = SpringBeanFactory.getBean("heartBeat", |
| 43 | CIMRequestProto.CIMReqProtocol.class); | 51 | CIMRequestProto.CIMReqProtocol.class); |
| 44 | - ctx.writeAndFlush(heartBeat).addListeners(ChannelFutureListener.CLOSE_ON_FAILURE) ; | 52 | + ctx.writeAndFlush(heartBeat).addListeners((ChannelFutureListener) future -> { |
| 53 | + if (!future.isSuccess()) { | ||
| 54 | + LOGGER.error("IO error,close Channel"); | ||
| 55 | + future.channel().close(); | ||
| 56 | + } | ||
| 57 | + }) ; | ||
| 45 | } | 58 | } |
| 46 | - | ||
| 47 | 59 | ||
| 48 | } | 60 | } |
| 49 | 61 | ||
| @@ -58,10 +70,24 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt | @@ -58,10 +70,24 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt | ||
| 58 | } | 70 | } |
| 59 | 71 | ||
| 60 | @Override | 72 | @Override |
| 61 | - protected void channelRead0(ChannelHandlerContext channelHandlerContext, CIMResponseProto.CIMResProtocol msg) throws Exception { | 73 | + public void channelInactive(ChannelHandlerContext ctx) throws Exception { |
| 74 | + LOGGER.info("客户端断开了,重新连接!"); | ||
| 75 | + | ||
| 76 | + if (scheduledExecutorService == null){ | ||
| 77 | + scheduledExecutorService = SpringBeanFactory.getBean("scheduledTask",ScheduledExecutorService.class) ; | ||
| 78 | + } | ||
| 79 | + // TODO: 2019-01-22 后期可以改为不用定时任务,连上后就关闭任务 节省性能。 | ||
| 80 | + scheduledExecutorService.scheduleAtFixedRate(new ReConnectJob(ctx),0,10, TimeUnit.SECONDS) ; | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + @Override | ||
| 84 | + protected void channelRead0(ChannelHandlerContext ctx, CIMResponseProto.CIMResProtocol msg) throws Exception { | ||
| 62 | 85 | ||
| 63 | - //从服务端收到消息时被调用 | ||
| 64 | - //LOGGER.info("客户端收到消息={}",in.toString(CharsetUtil.UTF_8)) ; | 86 | + //心跳更新时间 |
| 87 | + if (msg.getType() == Constants.CommandType.PING){ | ||
| 88 | + //LOGGER.info("收到服务端心跳!!!"); | ||
| 89 | + NettyAttrUtil.updateReaderTime(ctx.channel(),System.currentTimeMillis()); | ||
| 90 | + } | ||
| 65 | 91 | ||
| 66 | if (msg.getType() != Constants.CommandType.PING) { | 92 | if (msg.getType() != Constants.CommandType.PING) { |
| 67 | //回调消息 | 93 | //回调消息 |
| @@ -70,6 +96,10 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt | @@ -70,6 +96,10 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt | ||
| 70 | LOGGER.info(msg.getResMsg()); | 96 | LOGGER.info(msg.getResMsg()); |
| 71 | } | 97 | } |
| 72 | 98 | ||
| 99 | + | ||
| 100 | + | ||
| 101 | + | ||
| 102 | + | ||
| 73 | } | 103 | } |
| 74 | 104 | ||
| 75 | /** | 105 | /** |
| @@ -24,8 +24,8 @@ public class CIMClientHandleInitializer extends ChannelInitializer<Channel> { | @@ -24,8 +24,8 @@ public class CIMClientHandleInitializer extends ChannelInitializer<Channel> { | ||
| 24 | @Override | 24 | @Override |
| 25 | protected void initChannel(Channel ch) throws Exception { | 25 | protected void initChannel(Channel ch) throws Exception { |
| 26 | ch.pipeline() | 26 | ch.pipeline() |
| 27 | - //30 秒没发送消息 将IdleStateHandler 添加到 ChannelPipeline 中 | ||
| 28 | - .addLast(new IdleStateHandler(0, 30, 0)) | 27 | + //10 秒没发送消息 将IdleStateHandler 添加到 ChannelPipeline 中 |
| 28 | + .addLast(new IdleStateHandler(0, 10, 0)) | ||
| 29 | 29 | ||
| 30 | //心跳解码 | 30 | //心跳解码 |
| 31 | //.addLast(new HeartbeatEncode()) | 31 | //.addLast(new HeartbeatEncode()) |
| 1 | +package com.crossoverjie.cim.client.service; | ||
| 2 | + | ||
| 3 | +import com.crossoverjie.cim.client.service.impl.command.PrintAllCommand; | ||
| 4 | +import com.crossoverjie.cim.client.util.SpringBeanFactory; | ||
| 5 | +import com.crossoverjie.cim.common.enums.SystemCommandEnum; | ||
| 6 | +import com.crossoverjie.cim.common.util.StringUtil; | ||
| 7 | +import org.slf4j.Logger; | ||
| 8 | +import org.slf4j.LoggerFactory; | ||
| 9 | +import org.springframework.stereotype.Component; | ||
| 10 | + | ||
| 11 | +import java.util.Map; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * Function: | ||
| 15 | + * | ||
| 16 | + * @author crossoverJie | ||
| 17 | + * Date: 2019-01-27 19:39 | ||
| 18 | + * @since JDK 1.8 | ||
| 19 | + */ | ||
| 20 | +@Component | ||
| 21 | +public class InnerCommandContext { | ||
| 22 | + private final static Logger LOGGER = LoggerFactory.getLogger(InnerCommandContext.class); | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 获取执行器实例 | ||
| 26 | + * @param command 执行器实例 | ||
| 27 | + * @return | ||
| 28 | + */ | ||
| 29 | + public InnerCommand getInstance(String command) { | ||
| 30 | + | ||
| 31 | + Map<String, String> allClazz = SystemCommandEnum.getAllClazz(); | ||
| 32 | + | ||
| 33 | + //兼容需要命令后接参数的数据 :q cross | ||
| 34 | + String[] trim = command.trim().split(" "); | ||
| 35 | + String clazz = allClazz.get(trim[0]); | ||
| 36 | + InnerCommand innerCommand = null; | ||
| 37 | + try { | ||
| 38 | + if (StringUtil.isEmpty(clazz)){ | ||
| 39 | + clazz = PrintAllCommand.class.getName() ; | ||
| 40 | + } | ||
| 41 | + innerCommand = (InnerCommand) SpringBeanFactory.getBean(Class.forName(clazz)); | ||
| 42 | + } catch (Exception e) { | ||
| 43 | + LOGGER.error("Exception", e); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + return innerCommand; | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | +} |
| @@ -48,4 +48,20 @@ public interface MsgHandle { | @@ -48,4 +48,20 @@ public interface MsgHandle { | ||
| 48 | * @return 是否应当跳过当前消息(包含了":" 就需要跳过) | 48 | * @return 是否应当跳过当前消息(包含了":" 就需要跳过) |
| 49 | */ | 49 | */ |
| 50 | boolean innerCommand(String msg) ; | 50 | boolean innerCommand(String msg) ; |
| 51 | + | ||
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * 关闭系统 | ||
| 55 | + */ | ||
| 56 | + void shutdown() ; | ||
| 57 | + | ||
| 58 | + /** | ||
| 59 | + * 开启 AI 模式 | ||
| 60 | + */ | ||
| 61 | + void openAIModel() ; | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 关闭 AI 模式 | ||
| 65 | + */ | ||
| 66 | + void closeAIModel() ; | ||
| 51 | } | 67 | } |
| @@ -41,10 +41,13 @@ public interface RouteRequest { | @@ -41,10 +41,13 @@ public interface RouteRequest { | ||
| 41 | CIMServerResVO.ServerInfo getCIMServer(LoginReqVO loginReqVO) throws Exception; | 41 | CIMServerResVO.ServerInfo getCIMServer(LoginReqVO loginReqVO) throws Exception; |
| 42 | 42 | ||
| 43 | /** | 43 | /** |
| 44 | - * | ||
| 45 | - * @return 获取所有在线用户 | 44 | + * 获取所有在线用户 |
| 45 | + * @return | ||
| 46 | + * @throws Exception | ||
| 46 | */ | 47 | */ |
| 47 | List<OnlineUsersResVO.DataBodyBean> onlineUsers()throws Exception ; | 48 | List<OnlineUsersResVO.DataBodyBean> onlineUsers()throws Exception ; |
| 48 | 49 | ||
| 49 | 50 | ||
| 51 | + void offLine() ; | ||
| 52 | + | ||
| 50 | } | 53 | } |
cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/ClientHeartBeatHandlerImpl.java
0 → 100644
| 1 | +package com.crossoverjie.cim.client.service.impl; | ||
| 2 | + | ||
| 3 | +import com.crossoverjie.cim.client.client.CIMClient; | ||
| 4 | +import com.crossoverjie.cim.common.kit.HeartBeatHandler; | ||
| 5 | +import io.netty.channel.ChannelHandlerContext; | ||
| 6 | +import org.slf4j.Logger; | ||
| 7 | +import org.slf4j.LoggerFactory; | ||
| 8 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 9 | +import org.springframework.stereotype.Service; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * Function: | ||
| 13 | + * | ||
| 14 | + * @author crossoverJie | ||
| 15 | + * Date: 2019-01-20 17:16 | ||
| 16 | + * @since JDK 1.8 | ||
| 17 | + */ | ||
| 18 | +@Service | ||
| 19 | +public class ClientHeartBeatHandlerImpl implements HeartBeatHandler { | ||
| 20 | + | ||
| 21 | + private final static Logger LOGGER = LoggerFactory.getLogger(ClientHeartBeatHandlerImpl.class); | ||
| 22 | + | ||
| 23 | + @Autowired | ||
| 24 | + private CIMClient cimClient; | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + @Override | ||
| 28 | + public void process(ChannelHandlerContext ctx) throws Exception { | ||
| 29 | + | ||
| 30 | + //重连 | ||
| 31 | + cimClient.reconnect(); | ||
| 32 | + | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + | ||
| 36 | +} |
| 1 | +package com.crossoverjie.cim.client.service.impl; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +import org.springframework.stereotype.Component; | ||
| 5 | + | ||
| 6 | +import java.util.Date; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * Function: | ||
| 10 | + * | ||
| 11 | + * @author crossoverJie | ||
| 12 | + * Date: 2019-01-21 23:35 | ||
| 13 | + * @since JDK 1.8 | ||
| 14 | + */ | ||
| 15 | +@Component | ||
| 16 | +public class ClientInfo { | ||
| 17 | + | ||
| 18 | + private Info info = new Info() ; | ||
| 19 | + | ||
| 20 | + public Info get(){ | ||
| 21 | + return info ; | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + public ClientInfo saveUserInfo(long userId,String userName){ | ||
| 25 | + info.setUserId(userId); | ||
| 26 | + info.setUserName(userName); | ||
| 27 | + return this; | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + public ClientInfo saveServiceInfo(String serviceInfo){ | ||
| 32 | + info.setServiceInfo(serviceInfo); | ||
| 33 | + return this; | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + public ClientInfo saveStartDate(){ | ||
| 37 | + info.setStartDate(new Date()); | ||
| 38 | + return this; | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + private class Info{ | ||
| 42 | + private String userName; | ||
| 43 | + private long userId ; | ||
| 44 | + private String serviceInfo ; | ||
| 45 | + private Date startDate ; | ||
| 46 | + | ||
| 47 | + public Info() { | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + public String getUserName() { | ||
| 51 | + return userName; | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + public void setUserName(String userName) { | ||
| 55 | + this.userName = userName; | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + public long getUserId() { | ||
| 59 | + return userId; | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + public void setUserId(long userId) { | ||
| 63 | + this.userId = userId; | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + public String getServiceInfo() { | ||
| 67 | + return serviceInfo; | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + public void setServiceInfo(String serviceInfo) { | ||
| 71 | + this.serviceInfo = serviceInfo; | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + public Date getStartDate() { | ||
| 75 | + return startDate; | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + public void setStartDate(Date startDate) { | ||
| 79 | + this.startDate = startDate; | ||
| 80 | + } | ||
| 81 | + } | ||
| 82 | +} |
| @@ -2,20 +2,19 @@ package com.crossoverjie.cim.client.service.impl; | @@ -2,20 +2,19 @@ package com.crossoverjie.cim.client.service.impl; | ||
| 2 | 2 | ||
| 3 | import com.crossoverjie.cim.client.client.CIMClient; | 3 | import com.crossoverjie.cim.client.client.CIMClient; |
| 4 | import com.crossoverjie.cim.client.config.AppConfiguration; | 4 | import com.crossoverjie.cim.client.config.AppConfiguration; |
| 5 | -import com.crossoverjie.cim.client.service.MsgHandle; | ||
| 6 | -import com.crossoverjie.cim.client.service.MsgLogger; | ||
| 7 | -import com.crossoverjie.cim.client.service.RouteRequest; | 5 | +import com.crossoverjie.cim.client.service.*; |
| 8 | import com.crossoverjie.cim.client.vo.req.GroupReqVO; | 6 | import com.crossoverjie.cim.client.vo.req.GroupReqVO; |
| 9 | import com.crossoverjie.cim.client.vo.req.P2PReqVO; | 7 | import com.crossoverjie.cim.client.vo.req.P2PReqVO; |
| 10 | import com.crossoverjie.cim.client.vo.res.OnlineUsersResVO; | 8 | import com.crossoverjie.cim.client.vo.res.OnlineUsersResVO; |
| 11 | import com.crossoverjie.cim.common.data.construct.TrieTree; | 9 | import com.crossoverjie.cim.common.data.construct.TrieTree; |
| 12 | -import com.crossoverjie.cim.common.enums.SystemCommandEnumType; | 10 | +import com.crossoverjie.cim.common.enums.SystemCommandEnum; |
| 13 | import com.crossoverjie.cim.common.util.StringUtil; | 11 | import com.crossoverjie.cim.common.util.StringUtil; |
| 14 | import org.slf4j.Logger; | 12 | import org.slf4j.Logger; |
| 15 | import org.slf4j.LoggerFactory; | 13 | import org.slf4j.LoggerFactory; |
| 16 | import org.springframework.beans.factory.annotation.Autowired; | 14 | import org.springframework.beans.factory.annotation.Autowired; |
| 17 | import org.springframework.stereotype.Service; | 15 | import org.springframework.stereotype.Service; |
| 18 | 16 | ||
| 17 | +import javax.annotation.Resource; | ||
| 19 | import java.util.List; | 18 | import java.util.List; |
| 20 | import java.util.Map; | 19 | import java.util.Map; |
| 21 | import java.util.concurrent.ThreadPoolExecutor; | 20 | import java.util.concurrent.ThreadPoolExecutor; |
| @@ -32,33 +31,40 @@ import java.util.concurrent.TimeUnit; | @@ -32,33 +31,40 @@ import java.util.concurrent.TimeUnit; | ||
| 32 | public class MsgHandler implements MsgHandle { | 31 | public class MsgHandler implements MsgHandle { |
| 33 | private final static Logger LOGGER = LoggerFactory.getLogger(MsgHandler.class); | 32 | private final static Logger LOGGER = LoggerFactory.getLogger(MsgHandler.class); |
| 34 | @Autowired | 33 | @Autowired |
| 35 | - private RouteRequest routeRequest ; | 34 | + private RouteRequest routeRequest; |
| 36 | 35 | ||
| 37 | @Autowired | 36 | @Autowired |
| 38 | private AppConfiguration configuration; | 37 | private AppConfiguration configuration; |
| 39 | 38 | ||
| 39 | + @Resource(name = "callBackThreadPool") | ||
| 40 | + private ThreadPoolExecutor executor; | ||
| 41 | + | ||
| 42 | + @Autowired | ||
| 43 | + private CIMClient cimClient; | ||
| 44 | + | ||
| 40 | @Autowired | 45 | @Autowired |
| 41 | - private ThreadPoolExecutor executor ; | 46 | + private MsgLogger msgLogger; |
| 42 | 47 | ||
| 43 | @Autowired | 48 | @Autowired |
| 44 | - private CIMClient cimClient ; | 49 | + private ClientInfo clientInfo; |
| 45 | 50 | ||
| 46 | @Autowired | 51 | @Autowired |
| 47 | - private MsgLogger msgLogger ; | 52 | + private InnerCommandContext innerCommandContext ; |
| 48 | 53 | ||
| 49 | - private boolean aiModel = false ; | 54 | + private boolean aiModel = false; |
| 50 | 55 | ||
| 51 | @Override | 56 | @Override |
| 52 | public void sendMsg(String msg) { | 57 | public void sendMsg(String msg) { |
| 53 | - if (aiModel){ | 58 | + if (aiModel) { |
| 54 | aiChat(msg); | 59 | aiChat(msg); |
| 55 | - }else { | 60 | + } else { |
| 56 | normalChat(msg); | 61 | normalChat(msg); |
| 57 | } | 62 | } |
| 58 | } | 63 | } |
| 59 | 64 | ||
| 60 | /** | 65 | /** |
| 61 | * 正常聊天 | 66 | * 正常聊天 |
| 67 | + * | ||
| 62 | * @param msg | 68 | * @param msg |
| 63 | */ | 69 | */ |
| 64 | private void normalChat(String msg) { | 70 | private void normalChat(String msg) { |
| @@ -72,7 +78,7 @@ public class MsgHandler implements MsgHandle { | @@ -72,7 +78,7 @@ public class MsgHandler implements MsgHandle { | ||
| 72 | try { | 78 | try { |
| 73 | p2pChat(p2PReqVO); | 79 | p2pChat(p2PReqVO); |
| 74 | } catch (Exception e) { | 80 | } catch (Exception e) { |
| 75 | - LOGGER.error("Exception",e); | 81 | + LOGGER.error("Exception", e); |
| 76 | } | 82 | } |
| 77 | 83 | ||
| 78 | } else { | 84 | } else { |
| @@ -81,21 +87,22 @@ public class MsgHandler implements MsgHandle { | @@ -81,21 +87,22 @@ public class MsgHandler implements MsgHandle { | ||
| 81 | try { | 87 | try { |
| 82 | groupChat(groupReqVO); | 88 | groupChat(groupReqVO); |
| 83 | } catch (Exception e) { | 89 | } catch (Exception e) { |
| 84 | - LOGGER.error("Exception",e); | 90 | + LOGGER.error("Exception", e); |
| 85 | } | 91 | } |
| 86 | } | 92 | } |
| 87 | } | 93 | } |
| 88 | 94 | ||
| 89 | /** | 95 | /** |
| 90 | * AI model | 96 | * AI model |
| 97 | + * | ||
| 91 | * @param msg | 98 | * @param msg |
| 92 | */ | 99 | */ |
| 93 | private void aiChat(String msg) { | 100 | private void aiChat(String msg) { |
| 94 | - msg = msg.replace("吗","") ; | ||
| 95 | - msg = msg.replace("嘛","") ; | ||
| 96 | - msg = msg.replace("?","!"); | ||
| 97 | - msg = msg.replace("?","!"); | ||
| 98 | - msg = msg.replace("你","我"); | 101 | + msg = msg.replace("吗", ""); |
| 102 | + msg = msg.replace("嘛", ""); | ||
| 103 | + msg = msg.replace("?", "!"); | ||
| 104 | + msg = msg.replace("?", "!"); | ||
| 105 | + msg = msg.replace("你", "我"); | ||
| 99 | System.out.println("AI:\033[31;4m" + msg + "\033[0m"); | 106 | System.out.println("AI:\033[31;4m" + msg + "\033[0m"); |
| 100 | } | 107 | } |
| 101 | 108 | ||
| @@ -113,7 +120,7 @@ public class MsgHandler implements MsgHandle { | @@ -113,7 +120,7 @@ public class MsgHandler implements MsgHandle { | ||
| 113 | 120 | ||
| 114 | @Override | 121 | @Override |
| 115 | public boolean checkMsg(String msg) { | 122 | public boolean checkMsg(String msg) { |
| 116 | - if (StringUtil.isEmpty(msg)){ | 123 | + if (StringUtil.isEmpty(msg)) { |
| 117 | LOGGER.warn("不能发送空消息!"); | 124 | LOGGER.warn("不能发送空消息!"); |
| 118 | return true; | 125 | return true; |
| 119 | } | 126 | } |
| @@ -123,41 +130,15 @@ public class MsgHandler implements MsgHandle { | @@ -123,41 +130,15 @@ public class MsgHandler implements MsgHandle { | ||
| 123 | @Override | 130 | @Override |
| 124 | public boolean innerCommand(String msg) { | 131 | public boolean innerCommand(String msg) { |
| 125 | 132 | ||
| 126 | - if (msg.startsWith(":")){ | ||
| 127 | - Map<String, String> allStatusCode = SystemCommandEnumType.getAllStatusCode(); | ||
| 128 | - | ||
| 129 | - if (SystemCommandEnumType.QUIT.getCommandType().trim().equals(msg)){ | ||
| 130 | - //关闭系统 | ||
| 131 | - shutdown(); | ||
| 132 | - } else if (SystemCommandEnumType.ALL.getCommandType().trim().equals(msg)){ | ||
| 133 | - printAllCommand(allStatusCode); | ||
| 134 | - | ||
| 135 | - } else if (SystemCommandEnumType.ONLINE_USER.getCommandType().toLowerCase().trim().equals(msg.toLowerCase())){ | ||
| 136 | - //打印在线用户 | ||
| 137 | - printOnlineUsers(); | ||
| 138 | - | ||
| 139 | - } else if (msg.startsWith(SystemCommandEnumType.QUERY.getCommandType().trim() + " ")){ | ||
| 140 | - //查询聊天记录 | ||
| 141 | - queryChatHistory(msg); | ||
| 142 | - }else if (SystemCommandEnumType.AI.getCommandType().trim().equals(msg.toLowerCase())){ | ||
| 143 | - //开启 AI 模式 | ||
| 144 | - aiModel = true ; | ||
| 145 | - System.out.println("\033[31;4m" + "Hello,我是估值两亿的 AI 机器人!" + "\033[0m"); | ||
| 146 | - }else if (SystemCommandEnumType.QAI.getCommandType().trim().equals(msg.toLowerCase())){ | ||
| 147 | - //关闭 AI 模式 | ||
| 148 | - aiModel = false ; | ||
| 149 | - System.out.println("\033[31;4m" + "。゚(゚´ω`゚)゚。 AI 下线了!" + "\033[0m"); | ||
| 150 | - }else if (msg.startsWith(SystemCommandEnumType.PREFIX.getCommandType().trim() + " ")){ | ||
| 151 | - //模糊匹配 | ||
| 152 | - prefixSearch(msg); | ||
| 153 | - }else { | ||
| 154 | - printAllCommand(allStatusCode); | ||
| 155 | - } | 133 | + if (msg.startsWith(":")) { |
| 134 | + | ||
| 135 | + InnerCommand instance = innerCommandContext.getInstance(msg); | ||
| 136 | + instance.process(msg) ; | ||
| 156 | 137 | ||
| 157 | - return true ; | 138 | + return true; |
| 158 | 139 | ||
| 159 | - }else { | ||
| 160 | - return false ; | 140 | + } else { |
| 141 | + return false; | ||
| 161 | } | 142 | } |
| 162 | 143 | ||
| 163 | 144 | ||
| @@ -166,12 +147,13 @@ public class MsgHandler implements MsgHandle { | @@ -166,12 +147,13 @@ public class MsgHandler implements MsgHandle { | ||
| 166 | 147 | ||
| 167 | /** | 148 | /** |
| 168 | * 模糊匹配 | 149 | * 模糊匹配 |
| 150 | + * | ||
| 169 | * @param msg | 151 | * @param msg |
| 170 | */ | 152 | */ |
| 171 | private void prefixSearch(String msg) { | 153 | private void prefixSearch(String msg) { |
| 172 | try { | 154 | try { |
| 173 | List<OnlineUsersResVO.DataBodyBean> onlineUsers = routeRequest.onlineUsers(); | 155 | List<OnlineUsersResVO.DataBodyBean> onlineUsers = routeRequest.onlineUsers(); |
| 174 | - TrieTree trieTree = new TrieTree() ; | 156 | + TrieTree trieTree = new TrieTree(); |
| 175 | for (OnlineUsersResVO.DataBodyBean onlineUser : onlineUsers) { | 157 | for (OnlineUsersResVO.DataBodyBean onlineUser : onlineUsers) { |
| 176 | trieTree.insert(onlineUser.getUserName()); | 158 | trieTree.insert(onlineUser.getUserName()); |
| 177 | } | 159 | } |
| @@ -186,16 +168,17 @@ public class MsgHandler implements MsgHandle { | @@ -186,16 +168,17 @@ public class MsgHandler implements MsgHandle { | ||
| 186 | } | 168 | } |
| 187 | 169 | ||
| 188 | } catch (Exception e) { | 170 | } catch (Exception e) { |
| 189 | - LOGGER.error("Exception" ,e); | 171 | + LOGGER.error("Exception", e); |
| 190 | } | 172 | } |
| 191 | } | 173 | } |
| 192 | 174 | ||
| 193 | /** | 175 | /** |
| 194 | * 查询聊天记录 | 176 | * 查询聊天记录 |
| 177 | + * | ||
| 195 | * @param msg | 178 | * @param msg |
| 196 | */ | 179 | */ |
| 197 | private void queryChatHistory(String msg) { | 180 | private void queryChatHistory(String msg) { |
| 198 | - String[] split = msg.split(" ") ; | 181 | + String[] split = msg.split(" "); |
| 199 | String res = msgLogger.query(split[1]); | 182 | String res = msgLogger.query(split[1]); |
| 200 | System.out.println(res); | 183 | System.out.println(res); |
| 201 | } | 184 | } |
| @@ -209,20 +192,22 @@ public class MsgHandler implements MsgHandle { | @@ -209,20 +192,22 @@ public class MsgHandler implements MsgHandle { | ||
| 209 | 192 | ||
| 210 | LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); | 193 | LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); |
| 211 | for (OnlineUsersResVO.DataBodyBean onlineUser : onlineUsers) { | 194 | for (OnlineUsersResVO.DataBodyBean onlineUser : onlineUsers) { |
| 212 | - LOGGER.info("userId={}=====userName={}",onlineUser.getUserId(),onlineUser.getUserName()); | 195 | + LOGGER.info("userId={}=====userName={}", onlineUser.getUserId(), onlineUser.getUserName()); |
| 213 | } | 196 | } |
| 214 | LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); | 197 | LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); |
| 215 | 198 | ||
| 216 | } catch (Exception e) { | 199 | } catch (Exception e) { |
| 217 | - LOGGER.error("Exception" ,e); | 200 | + LOGGER.error("Exception", e); |
| 218 | } | 201 | } |
| 219 | } | 202 | } |
| 220 | 203 | ||
| 221 | /** | 204 | /** |
| 222 | * 关闭系统 | 205 | * 关闭系统 |
| 223 | */ | 206 | */ |
| 224 | - private void shutdown() { | 207 | + @Override |
| 208 | + public void shutdown() { | ||
| 225 | LOGGER.info("系统关闭中。。。。"); | 209 | LOGGER.info("系统关闭中。。。。"); |
| 210 | + routeRequest.offLine(); | ||
| 226 | msgLogger.stop(); | 211 | msgLogger.stop(); |
| 227 | executor.shutdown(); | 212 | executor.shutdown(); |
| 228 | try { | 213 | try { |
| @@ -231,11 +216,21 @@ public class MsgHandler implements MsgHandle { | @@ -231,11 +216,21 @@ public class MsgHandler implements MsgHandle { | ||
| 231 | } | 216 | } |
| 232 | cimClient.close(); | 217 | cimClient.close(); |
| 233 | } catch (InterruptedException e) { | 218 | } catch (InterruptedException e) { |
| 234 | - LOGGER.error("InterruptedException",e); | 219 | + LOGGER.error("InterruptedException", e); |
| 235 | } | 220 | } |
| 236 | System.exit(0); | 221 | System.exit(0); |
| 237 | } | 222 | } |
| 238 | 223 | ||
| 224 | + @Override | ||
| 225 | + public void openAIModel() { | ||
| 226 | + aiModel = true; | ||
| 227 | + } | ||
| 228 | + | ||
| 229 | + @Override | ||
| 230 | + public void closeAIModel() { | ||
| 231 | + aiModel = false ; | ||
| 232 | + } | ||
| 233 | + | ||
| 239 | private void printAllCommand(Map<String, String> allStatusCode) { | 234 | private void printAllCommand(Map<String, String> allStatusCode) { |
| 240 | LOGGER.warn("===================================="); | 235 | LOGGER.warn("===================================="); |
| 241 | for (Map.Entry<String, String> stringStringEntry : allStatusCode.entrySet()) { | 236 | for (Map.Entry<String, String> stringStringEntry : allStatusCode.entrySet()) { |
| @@ -45,7 +45,7 @@ public class RouteRequestImpl implements RouteRequest { | @@ -45,7 +45,7 @@ public class RouteRequestImpl implements RouteRequest { | ||
| 45 | private String p2pRouteRequestUrl; | 45 | private String p2pRouteRequestUrl; |
| 46 | 46 | ||
| 47 | @Value("${cim.server.route.request.url}") | 47 | @Value("${cim.server.route.request.url}") |
| 48 | - private String serverRouteRequestUrl; | 48 | + private String serverRouteLoginUrl; |
| 49 | 49 | ||
| 50 | @Value("${cim.server.online.user.url}") | 50 | @Value("${cim.server.online.user.url}") |
| 51 | private String onlineUserUrl; | 51 | private String onlineUserUrl; |
| @@ -120,7 +120,7 @@ public class RouteRequestImpl implements RouteRequest { | @@ -120,7 +120,7 @@ public class RouteRequestImpl implements RouteRequest { | ||
| 120 | RequestBody requestBody = RequestBody.create(mediaType,jsonObject.toString()); | 120 | RequestBody requestBody = RequestBody.create(mediaType,jsonObject.toString()); |
| 121 | 121 | ||
| 122 | Request request = new Request.Builder() | 122 | Request request = new Request.Builder() |
| 123 | - .url(serverRouteRequestUrl) | 123 | + .url(serverRouteLoginUrl) |
| 124 | .post(requestBody) | 124 | .post(requestBody) |
| 125 | .build(); | 125 | .build(); |
| 126 | 126 | ||
| @@ -178,4 +178,26 @@ public class RouteRequestImpl implements RouteRequest { | @@ -178,4 +178,26 @@ public class RouteRequestImpl implements RouteRequest { | ||
| 178 | 178 | ||
| 179 | return onlineUsersResVO.getDataBody(); | 179 | return onlineUsersResVO.getDataBody(); |
| 180 | } | 180 | } |
| 181 | + | ||
| 182 | + @Override | ||
| 183 | + public void offLine() { | ||
| 184 | + JSONObject jsonObject = new JSONObject(); | ||
| 185 | + jsonObject.put("userId", appConfiguration.getUserId()); | ||
| 186 | + jsonObject.put("msg", "offLine"); | ||
| 187 | + RequestBody requestBody = RequestBody.create(mediaType, jsonObject.toString()); | ||
| 188 | + | ||
| 189 | + Request request = new Request.Builder() | ||
| 190 | + .url(appConfiguration.getClearRouteUrl()) | ||
| 191 | + .post(requestBody) | ||
| 192 | + .build(); | ||
| 193 | + | ||
| 194 | + Response response = null; | ||
| 195 | + try { | ||
| 196 | + response = okHttpClient.newCall(request).execute(); | ||
| 197 | + } catch (IOException e) { | ||
| 198 | + LOGGER.error("exception",e); | ||
| 199 | + } finally { | ||
| 200 | + response.body().close(); | ||
| 201 | + } | ||
| 202 | + } | ||
| 181 | } | 203 | } |
cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/CloseAIModelCommand.java
0 → 100644
| 1 | +package com.crossoverjie.cim.client.service.impl.command; | ||
| 2 | + | ||
| 3 | +import com.crossoverjie.cim.client.service.InnerCommand; | ||
| 4 | +import com.crossoverjie.cim.client.service.MsgHandle; | ||
| 5 | +import org.slf4j.Logger; | ||
| 6 | +import org.slf4j.LoggerFactory; | ||
| 7 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 8 | +import org.springframework.stereotype.Service; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * Function: | ||
| 12 | + * | ||
| 13 | + * @author crossoverJie | ||
| 14 | + * Date: 2019-01-27 19:37 | ||
| 15 | + * @since JDK 1.8 | ||
| 16 | + */ | ||
| 17 | +@Service | ||
| 18 | +public class CloseAIModelCommand implements InnerCommand { | ||
| 19 | + private final static Logger LOGGER = LoggerFactory.getLogger(CloseAIModelCommand.class); | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + @Autowired | ||
| 23 | + private MsgHandle msgHandle ; | ||
| 24 | + | ||
| 25 | + @Override | ||
| 26 | + public void process(String msg) { | ||
| 27 | + msgHandle.closeAIModel(); | ||
| 28 | + System.out.println("\033[31;4m" + "。゚(゚´ω`゚)゚。 AI 下线了!" + "\033[0m"); | ||
| 29 | + } | ||
| 30 | +} |
cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/EchoInfoCommand.java
0 → 100644
| 1 | +package com.crossoverjie.cim.client.service.impl.command; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSON; | ||
| 4 | +import com.crossoverjie.cim.client.service.InnerCommand; | ||
| 5 | +import com.crossoverjie.cim.client.service.impl.ClientInfo; | ||
| 6 | +import org.slf4j.Logger; | ||
| 7 | +import org.slf4j.LoggerFactory; | ||
| 8 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 9 | +import org.springframework.stereotype.Service; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * Function: | ||
| 13 | + * | ||
| 14 | + * @author crossoverJie | ||
| 15 | + * Date: 2019-01-27 19:37 | ||
| 16 | + * @since JDK 1.8 | ||
| 17 | + */ | ||
| 18 | +@Service | ||
| 19 | +public class EchoInfoCommand implements InnerCommand { | ||
| 20 | + private final static Logger LOGGER = LoggerFactory.getLogger(EchoInfoCommand.class); | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + @Autowired | ||
| 24 | + private ClientInfo clientInfo; | ||
| 25 | + | ||
| 26 | + @Override | ||
| 27 | + public void process(String msg) { | ||
| 28 | + LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); | ||
| 29 | + LOGGER.info("client info=[{}]", JSON.toJSONString(clientInfo.get())); | ||
| 30 | + LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); | ||
| 31 | + } | ||
| 32 | +} |
cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/OpenAIModelCommand.java
0 → 100644
| 1 | +package com.crossoverjie.cim.client.service.impl.command; | ||
| 2 | + | ||
| 3 | +import com.crossoverjie.cim.client.service.InnerCommand; | ||
| 4 | +import com.crossoverjie.cim.client.service.MsgHandle; | ||
| 5 | +import org.slf4j.Logger; | ||
| 6 | +import org.slf4j.LoggerFactory; | ||
| 7 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 8 | +import org.springframework.stereotype.Service; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * Function: | ||
| 12 | + * | ||
| 13 | + * @author crossoverJie | ||
| 14 | + * Date: 2019-01-27 19:37 | ||
| 15 | + * @since JDK 1.8 | ||
| 16 | + */ | ||
| 17 | +@Service | ||
| 18 | +public class OpenAIModelCommand implements InnerCommand { | ||
| 19 | + private final static Logger LOGGER = LoggerFactory.getLogger(OpenAIModelCommand.class); | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + @Autowired | ||
| 23 | + private MsgHandle msgHandle ; | ||
| 24 | + | ||
| 25 | + @Override | ||
| 26 | + public void process(String msg) { | ||
| 27 | + msgHandle.openAIModel(); | ||
| 28 | + System.out.println("\033[31;4m" + "Hello,我是估值两亿的 AI 机器人!" + "\033[0m"); | ||
| 29 | + } | ||
| 30 | +} |
cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/PrefixSearchCommand.java
0 → 100644
| 1 | +package com.crossoverjie.cim.client.service.impl.command; | ||
| 2 | + | ||
| 3 | +import com.crossoverjie.cim.client.service.InnerCommand; | ||
| 4 | +import com.crossoverjie.cim.client.service.RouteRequest; | ||
| 5 | +import com.crossoverjie.cim.client.vo.res.OnlineUsersResVO; | ||
| 6 | +import com.crossoverjie.cim.common.data.construct.TrieTree; | ||
| 7 | +import org.slf4j.Logger; | ||
| 8 | +import org.slf4j.LoggerFactory; | ||
| 9 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 10 | +import org.springframework.stereotype.Service; | ||
| 11 | + | ||
| 12 | +import java.util.List; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * Function: | ||
| 16 | + * | ||
| 17 | + * @author crossoverJie | ||
| 18 | + * Date: 2019-01-27 19:37 | ||
| 19 | + * @since JDK 1.8 | ||
| 20 | + */ | ||
| 21 | +@Service | ||
| 22 | +public class PrefixSearchCommand implements InnerCommand { | ||
| 23 | + private final static Logger LOGGER = LoggerFactory.getLogger(PrefixSearchCommand.class); | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + @Autowired | ||
| 27 | + private RouteRequest routeRequest ; | ||
| 28 | + | ||
| 29 | + @Override | ||
| 30 | + public void process(String msg) { | ||
| 31 | + try { | ||
| 32 | + List<OnlineUsersResVO.DataBodyBean> onlineUsers = routeRequest.onlineUsers(); | ||
| 33 | + TrieTree trieTree = new TrieTree(); | ||
| 34 | + for (OnlineUsersResVO.DataBodyBean onlineUser : onlineUsers) { | ||
| 35 | + trieTree.insert(onlineUser.getUserName()); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + String[] split = msg.split(" "); | ||
| 39 | + String key = split[1]; | ||
| 40 | + List<String> list = trieTree.prefixSearch(key); | ||
| 41 | + | ||
| 42 | + for (String res : list) { | ||
| 43 | + res = res.replace(key, "\033[31;4m" + key + "\033[0m"); | ||
| 44 | + System.out.println(res); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + } catch (Exception e) { | ||
| 48 | + LOGGER.error("Exception", e); | ||
| 49 | + } | ||
| 50 | + } | ||
| 51 | +} |
cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/PrintAllCommand.java
0 → 100644
| 1 | +package com.crossoverjie.cim.client.service.impl.command; | ||
| 2 | + | ||
| 3 | +import com.crossoverjie.cim.client.service.InnerCommand; | ||
| 4 | +import com.crossoverjie.cim.common.enums.SystemCommandEnum; | ||
| 5 | +import org.slf4j.Logger; | ||
| 6 | +import org.slf4j.LoggerFactory; | ||
| 7 | +import org.springframework.stereotype.Service; | ||
| 8 | + | ||
| 9 | +import java.util.Map; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * Function: | ||
| 13 | + * | ||
| 14 | + * @author crossoverJie | ||
| 15 | + * Date: 2019-01-27 19:37 | ||
| 16 | + * @since JDK 1.8 | ||
| 17 | + */ | ||
| 18 | +@Service | ||
| 19 | +public class PrintAllCommand implements InnerCommand { | ||
| 20 | + private final static Logger LOGGER = LoggerFactory.getLogger(PrintAllCommand.class); | ||
| 21 | + | ||
| 22 | + @Override | ||
| 23 | + public void process(String msg) { | ||
| 24 | + Map<String, String> allStatusCode = SystemCommandEnum.getAllStatusCode(); | ||
| 25 | + LOGGER.warn("===================================="); | ||
| 26 | + for (Map.Entry<String, String> stringStringEntry : allStatusCode.entrySet()) { | ||
| 27 | + String key = stringStringEntry.getKey(); | ||
| 28 | + String value = stringStringEntry.getValue(); | ||
| 29 | + LOGGER.warn(key + "----->" + value); | ||
| 30 | + } | ||
| 31 | + LOGGER.warn("===================================="); | ||
| 32 | + } | ||
| 33 | +} |
| 1 | +package com.crossoverjie.cim.client.service.impl.command; | ||
| 2 | + | ||
| 3 | +import com.crossoverjie.cim.client.service.InnerCommand; | ||
| 4 | +import com.crossoverjie.cim.client.service.RouteRequest; | ||
| 5 | +import com.crossoverjie.cim.client.vo.res.OnlineUsersResVO; | ||
| 6 | +import org.slf4j.Logger; | ||
| 7 | +import org.slf4j.LoggerFactory; | ||
| 8 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 9 | +import org.springframework.stereotype.Service; | ||
| 10 | + | ||
| 11 | +import java.util.List; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * Function: | ||
| 15 | + * | ||
| 16 | + * @author crossoverJie | ||
| 17 | + * Date: 2019-01-27 19:37 | ||
| 18 | + * @since JDK 1.8 | ||
| 19 | + */ | ||
| 20 | +@Service | ||
| 21 | +public class PrintOnlineUsersCommand implements InnerCommand { | ||
| 22 | + private final static Logger LOGGER = LoggerFactory.getLogger(PrintOnlineUsersCommand.class); | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + @Autowired | ||
| 26 | + private RouteRequest routeRequest ; | ||
| 27 | + | ||
| 28 | + @Override | ||
| 29 | + public void process(String msg) { | ||
| 30 | + try { | ||
| 31 | + List<OnlineUsersResVO.DataBodyBean> onlineUsers = routeRequest.onlineUsers(); | ||
| 32 | + | ||
| 33 | + LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); | ||
| 34 | + for (OnlineUsersResVO.DataBodyBean onlineUser : onlineUsers) { | ||
| 35 | + LOGGER.info("userId={}=====userName={}", onlineUser.getUserId(), onlineUser.getUserName()); | ||
| 36 | + } | ||
| 37 | + LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); | ||
| 38 | + | ||
| 39 | + } catch (Exception e) { | ||
| 40 | + LOGGER.error("Exception", e); | ||
| 41 | + } | ||
| 42 | + } | ||
| 43 | +} |
cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/QueryHistoryCommand.java
0 → 100644
| 1 | +package com.crossoverjie.cim.client.service.impl.command; | ||
| 2 | + | ||
| 3 | +import com.crossoverjie.cim.client.service.InnerCommand; | ||
| 4 | +import com.crossoverjie.cim.client.service.MsgLogger; | ||
| 5 | +import org.slf4j.Logger; | ||
| 6 | +import org.slf4j.LoggerFactory; | ||
| 7 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 8 | +import org.springframework.stereotype.Service; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * Function: | ||
| 12 | + * | ||
| 13 | + * @author crossoverJie | ||
| 14 | + * Date: 2019-01-27 19:37 | ||
| 15 | + * @since JDK 1.8 | ||
| 16 | + */ | ||
| 17 | +@Service | ||
| 18 | +public class QueryHistoryCommand implements InnerCommand { | ||
| 19 | + private final static Logger LOGGER = LoggerFactory.getLogger(QueryHistoryCommand.class); | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + @Autowired | ||
| 23 | + private MsgLogger msgLogger ; | ||
| 24 | + | ||
| 25 | + @Override | ||
| 26 | + public void process(String msg) { | ||
| 27 | + String[] split = msg.split(" "); | ||
| 28 | + String res = msgLogger.query(split[1]); | ||
| 29 | + System.out.println(res); | ||
| 30 | + } | ||
| 31 | +} |
cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/ShutDownCommand.java
0 → 100644
| 1 | +package com.crossoverjie.cim.client.service.impl.command; | ||
| 2 | + | ||
| 3 | +import com.crossoverjie.cim.client.client.CIMClient; | ||
| 4 | +import com.crossoverjie.cim.client.service.InnerCommand; | ||
| 5 | +import com.crossoverjie.cim.client.service.MsgLogger; | ||
| 6 | +import com.crossoverjie.cim.client.service.RouteRequest; | ||
| 7 | +import org.slf4j.Logger; | ||
| 8 | +import org.slf4j.LoggerFactory; | ||
| 9 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 10 | +import org.springframework.stereotype.Service; | ||
| 11 | + | ||
| 12 | +import javax.annotation.Resource; | ||
| 13 | +import java.util.concurrent.ThreadPoolExecutor; | ||
| 14 | +import java.util.concurrent.TimeUnit; | ||
| 15 | + | ||
| 16 | +/** | ||
| 17 | + * Function: | ||
| 18 | + * | ||
| 19 | + * @author crossoverJie | ||
| 20 | + * Date: 2019-01-27 19:28 | ||
| 21 | + * @since JDK 1.8 | ||
| 22 | + */ | ||
| 23 | +@Service | ||
| 24 | +public class ShutDownCommand implements InnerCommand { | ||
| 25 | + private final static Logger LOGGER = LoggerFactory.getLogger(ShutDownCommand.class); | ||
| 26 | + | ||
| 27 | + @Autowired | ||
| 28 | + private RouteRequest routeRequest ; | ||
| 29 | + | ||
| 30 | + @Autowired | ||
| 31 | + private CIMClient cimClient; | ||
| 32 | + | ||
| 33 | + @Autowired | ||
| 34 | + private MsgLogger msgLogger; | ||
| 35 | + | ||
| 36 | + @Resource(name = "callBackThreadPool") | ||
| 37 | + private ThreadPoolExecutor executor; | ||
| 38 | + | ||
| 39 | + @Override | ||
| 40 | + public void process(String msg) { | ||
| 41 | + LOGGER.info("系统关闭中。。。。"); | ||
| 42 | + routeRequest.offLine(); | ||
| 43 | + msgLogger.stop(); | ||
| 44 | + executor.shutdown(); | ||
| 45 | + try { | ||
| 46 | + while (!executor.awaitTermination(1, TimeUnit.SECONDS)) { | ||
| 47 | + LOGGER.info("线程池关闭中。。。。"); | ||
| 48 | + } | ||
| 49 | + cimClient.close(); | ||
| 50 | + } catch (InterruptedException e) { | ||
| 51 | + LOGGER.error("InterruptedException", e); | ||
| 52 | + } | ||
| 53 | + System.exit(0); | ||
| 54 | + } | ||
| 55 | +} |
| 1 | +package com.crossoverjie.cim.client.thread; | ||
| 2 | + | ||
| 3 | +import com.crossoverjie.cim.client.service.impl.ClientHeartBeatHandlerImpl; | ||
| 4 | +import com.crossoverjie.cim.client.util.SpringBeanFactory; | ||
| 5 | +import com.crossoverjie.cim.common.kit.HeartBeatHandler; | ||
| 6 | +import io.netty.channel.ChannelHandlerContext; | ||
| 7 | +import org.slf4j.Logger; | ||
| 8 | +import org.slf4j.LoggerFactory; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * Function: | ||
| 12 | + * | ||
| 13 | + * @author crossoverJie | ||
| 14 | + * Date: 2019-01-20 21:35 | ||
| 15 | + * @since JDK 1.8 | ||
| 16 | + */ | ||
| 17 | +public class ReConnectJob implements Runnable { | ||
| 18 | + | ||
| 19 | + private final static Logger LOGGER = LoggerFactory.getLogger(ReConnectJob.class); | ||
| 20 | + | ||
| 21 | + private ChannelHandlerContext context ; | ||
| 22 | + | ||
| 23 | + private HeartBeatHandler heartBeatHandler ; | ||
| 24 | + | ||
| 25 | + public ReConnectJob(ChannelHandlerContext context) { | ||
| 26 | + this.context = context; | ||
| 27 | + this.heartBeatHandler = SpringBeanFactory.getBean(ClientHeartBeatHandlerImpl.class) ; | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + @Override | ||
| 31 | + public void run() { | ||
| 32 | + try { | ||
| 33 | + heartBeatHandler.process(context); | ||
| 34 | + } catch (Exception e) { | ||
| 35 | + LOGGER.error("Exception",e); | ||
| 36 | + } | ||
| 37 | + } | ||
| 38 | +} |
| @@ -25,6 +25,8 @@ cim.server.route.request.url=http://45.78.28.220:8083/login | @@ -25,6 +25,8 @@ cim.server.route.request.url=http://45.78.28.220:8083/login | ||
| 25 | # 在线用户 | 25 | # 在线用户 |
| 26 | cim.server.online.user.url=http://45.78.28.220:8083/onlineUser | 26 | cim.server.online.user.url=http://45.78.28.220:8083/onlineUser |
| 27 | 27 | ||
| 28 | +# 清除路由信息 | ||
| 29 | +cim.clear.route.request.url=http://45.78.28.220:8083/offLine | ||
| 28 | 30 | ||
| 29 | ###=======本地模拟======### | 31 | ###=======本地模拟======### |
| 30 | ## 群发消息 | 32 | ## 群发消息 |
| @@ -39,6 +41,9 @@ cim.server.online.user.url=http://45.78.28.220:8083/onlineUser | @@ -39,6 +41,9 @@ cim.server.online.user.url=http://45.78.28.220:8083/onlineUser | ||
| 39 | ## 在线用户 | 41 | ## 在线用户 |
| 40 | #cim.server.online.user.url=http://localhost:8083/onlineUser | 42 | #cim.server.online.user.url=http://localhost:8083/onlineUser |
| 41 | 43 | ||
| 44 | +# 清除路由信息 | ||
| 45 | +#cim.clear.route.request.url=http://localhost:8083/offLine | ||
| 46 | + | ||
| 42 | # 客户端唯一ID | 47 | # 客户端唯一ID |
| 43 | cim.user.id=1545574841528 | 48 | cim.user.id=1545574841528 |
| 44 | cim.user.userName=zhangsan | 49 | cim.user.userName=zhangsan |
| @@ -52,3 +57,9 @@ cim.callback.thread.pool.size = 2 | @@ -52,3 +57,9 @@ cim.callback.thread.pool.size = 2 | ||
| 52 | management.security.enabled=false | 57 | management.security.enabled=false |
| 53 | # SpringAdmin 地址 | 58 | # SpringAdmin 地址 |
| 54 | spring.boot.admin.url=http://127.0.0.1:8888 | 59 | spring.boot.admin.url=http://127.0.0.1:8888 |
| 60 | + | ||
| 61 | +# 检测多少秒没有收到服务端端心跳后重新登录获取连接 | ||
| 62 | +cim.heartbeat.time = 60 | ||
| 63 | + | ||
| 64 | +# 客户端连接失败重连次数 | ||
| 65 | +cim.reconnect.count =3 |
| 1 | +package com.crossoverjie.cim.client.service; | ||
| 2 | + | ||
| 3 | +import com.crossoverjie.cim.client.CIMClientApplication; | ||
| 4 | +import com.crossoverjie.cim.common.enums.SystemCommandEnum; | ||
| 5 | +import org.junit.Test; | ||
| 6 | +import org.junit.runner.RunWith; | ||
| 7 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 8 | +import org.springframework.boot.test.context.SpringBootTest; | ||
| 9 | +import org.springframework.test.context.junit4.SpringRunner; | ||
| 10 | + | ||
| 11 | +@SpringBootTest(classes = CIMClientApplication.class) | ||
| 12 | +@RunWith(SpringRunner.class) | ||
| 13 | +public class InnerCommandContextTest { | ||
| 14 | + | ||
| 15 | + @Autowired | ||
| 16 | + private InnerCommandContext context; | ||
| 17 | + | ||
| 18 | + @Test | ||
| 19 | + public void execute() { | ||
| 20 | + String msg = ":all"; | ||
| 21 | + InnerCommand execute = context.getInstance(msg); | ||
| 22 | + execute.process(msg) ; | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + @Test | ||
| 26 | + public void execute3() { | ||
| 27 | + String msg = SystemCommandEnum.ONLINE_USER.getCommandType(); | ||
| 28 | + InnerCommand execute = context.getInstance(msg); | ||
| 29 | + execute.process(msg) ; | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + @Test | ||
| 33 | + public void execute4() { | ||
| 34 | + String msg = ":q 天气"; | ||
| 35 | + InnerCommand execute = context.getInstance(msg); | ||
| 36 | + execute.process(msg) ; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + @Test | ||
| 40 | + public void execute5() { | ||
| 41 | + String msg = ":q crossoverJie"; | ||
| 42 | + InnerCommand execute = context.getInstance(msg); | ||
| 43 | + execute.process(msg) ; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + @Test | ||
| 47 | + public void execute6() { | ||
| 48 | + String msg = SystemCommandEnum.AI.getCommandType(); | ||
| 49 | + InnerCommand execute = context.getInstance(msg); | ||
| 50 | + execute.process(msg) ; | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + @Test | ||
| 54 | + public void execute7() { | ||
| 55 | + String msg = SystemCommandEnum.QAI.getCommandType(); | ||
| 56 | + InnerCommand execute = context.getInstance(msg); | ||
| 57 | + execute.process(msg) ; | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + @Test | ||
| 61 | + public void execute8() { | ||
| 62 | + String msg = ":pu cross"; | ||
| 63 | + InnerCommand execute = context.getInstance(msg); | ||
| 64 | + execute.process(msg) ; | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + @Test | ||
| 68 | + public void execute9() { | ||
| 69 | + String msg = SystemCommandEnum.INFO.getCommandType(); | ||
| 70 | + InnerCommand execute = context.getInstance(msg); | ||
| 71 | + execute.process(msg) ; | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + @Test | ||
| 75 | + public void execute10() { | ||
| 76 | + String msg = "dsds"; | ||
| 77 | + InnerCommand execute = context.getInstance(msg); | ||
| 78 | + execute.process(msg) ; | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + | ||
| 82 | + | ||
| 83 | + // @Test | ||
| 84 | + public void quit() { | ||
| 85 | + String msg = ":q!"; | ||
| 86 | + InnerCommand execute = context.getInstance(msg); | ||
| 87 | + execute.process(msg) ; | ||
| 88 | + } | ||
| 89 | +} |
| @@ -38,5 +38,11 @@ | @@ -38,5 +38,11 @@ | ||
| 38 | <groupId>com.github.sgroschupf</groupId> | 38 | <groupId>com.github.sgroschupf</groupId> |
| 39 | <artifactId>zkclient</artifactId> | 39 | <artifactId>zkclient</artifactId> |
| 40 | </dependency> | 40 | </dependency> |
| 41 | + | ||
| 42 | + <dependency> | ||
| 43 | + <groupId>io.netty</groupId> | ||
| 44 | + <artifactId>netty-all</artifactId> | ||
| 45 | + <version>${netty.version}</version> | ||
| 46 | + </dependency> | ||
| 41 | </dependencies> | 47 | </dependencies> |
| 42 | </project> | 48 | </project> |
| @@ -18,6 +18,10 @@ public class TrieTree { | @@ -18,6 +18,10 @@ public class TrieTree { | ||
| 18 | * 大小写都可保存 | 18 | * 大小写都可保存 |
| 19 | */ | 19 | */ |
| 20 | private static final int CHILDREN_LENGTH = 26 * 2; | 20 | private static final int CHILDREN_LENGTH = 26 * 2; |
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * 存放的最大字符串长度 | ||
| 24 | + */ | ||
| 21 | private static final int MAX_CHAR_LENGTH = 16; | 25 | private static final int MAX_CHAR_LENGTH = 16; |
| 22 | 26 | ||
| 23 | private static final char UPPERCASE_STAR = 'A'; | 27 | private static final char UPPERCASE_STAR = 'A'; |
| @@ -209,7 +213,7 @@ public class TrieTree { | @@ -209,7 +213,7 @@ public class TrieTree { | ||
| 209 | public boolean isEnd = false; | 213 | public boolean isEnd = false; |
| 210 | 214 | ||
| 211 | /** | 215 | /** |
| 212 | - * 如果支持查询,则不需要存储数据 | 216 | + * 如果只是查询,则不需要存储数据 |
| 213 | */ | 217 | */ |
| 214 | public char data; | 218 | public char data; |
| 215 | 219 |
| 1 | package com.crossoverjie.cim.common.enums; | 1 | package com.crossoverjie.cim.common.enums; |
| 2 | 2 | ||
| 3 | -import java.util.ArrayList; | ||
| 4 | import java.util.HashMap; | 3 | import java.util.HashMap; |
| 5 | -import java.util.List; | ||
| 6 | import java.util.Map; | 4 | import java.util.Map; |
| 7 | 5 | ||
| 8 | /** | 6 | /** |
| @@ -12,15 +10,16 @@ import java.util.Map; | @@ -12,15 +10,16 @@ import java.util.Map; | ||
| 12 | * Date: 2018/12/26 18:38 | 10 | * Date: 2018/12/26 18:38 |
| 13 | * @since JDK 1.8 | 11 | * @since JDK 1.8 |
| 14 | */ | 12 | */ |
| 15 | -public enum SystemCommandEnumType { | 13 | +public enum SystemCommandEnum { |
| 16 | 14 | ||
| 17 | - ALL(":all ","获取所有命令"), | ||
| 18 | - ONLINE_USER(":olu ","获取所有在线用户"), | ||
| 19 | - QUIT(":q! ","退出程序"), | ||
| 20 | - QUERY(":q ","【:q 关键字】查询聊天记录"), | ||
| 21 | - AI(":ai ","开启 AI 模式"), | ||
| 22 | - QAI(":qai ","关闭 AI 模式"), | ||
| 23 | - PREFIX(":pu ","模糊匹配用户") | 15 | + ALL(":all ","获取所有命令","PrintAllCommand"), |
| 16 | + ONLINE_USER(":olu ","获取所有在线用户","PrintOnlineUsersCommand"), | ||
| 17 | + QUIT(":q! ","退出程序","ShutDownCommand"), | ||
| 18 | + QUERY(":q ","【:q 关键字】查询聊天记录","QueryHistoryCommand"), | ||
| 19 | + AI(":ai ","开启 AI 模式","OpenAIModelCommand"), | ||
| 20 | + QAI(":qai ","关闭 AI 模式","CloseAIModelCommand"), | ||
| 21 | + PREFIX(":pu ","模糊匹配用户","PrefixSearchCommand"), | ||
| 22 | + INFO(":info ","获取客户端信息","EchoInfoCommand") | ||
| 24 | 23 | ||
| 25 | ; | 24 | ; |
| 26 | 25 | ||
| @@ -30,15 +29,21 @@ public enum SystemCommandEnumType { | @@ -30,15 +29,21 @@ public enum SystemCommandEnumType { | ||
| 30 | /** 枚举描述 */ | 29 | /** 枚举描述 */ |
| 31 | private final String desc; | 30 | private final String desc; |
| 32 | 31 | ||
| 32 | + /** | ||
| 33 | + * 实现类 | ||
| 34 | + */ | ||
| 35 | + private final String clazz ; | ||
| 36 | + | ||
| 33 | 37 | ||
| 34 | /** | 38 | /** |
| 35 | * 构建一个 。 | 39 | * 构建一个 。 |
| 36 | * @param commandType 枚举值码。 | 40 | * @param commandType 枚举值码。 |
| 37 | * @param desc 枚举描述。 | 41 | * @param desc 枚举描述。 |
| 38 | */ | 42 | */ |
| 39 | - private SystemCommandEnumType(String commandType, String desc) { | 43 | + private SystemCommandEnum(String commandType, String desc, String clazz) { |
| 40 | this.commandType = commandType; | 44 | this.commandType = commandType; |
| 41 | this.desc = desc; | 45 | this.desc = desc; |
| 46 | + this.clazz = clazz ; | ||
| 42 | } | 47 | } |
| 43 | 48 | ||
| 44 | /** | 49 | /** |
| @@ -48,6 +53,13 @@ public enum SystemCommandEnumType { | @@ -48,6 +53,13 @@ public enum SystemCommandEnumType { | ||
| 48 | public String getCommandType() { | 53 | public String getCommandType() { |
| 49 | return commandType; | 54 | return commandType; |
| 50 | } | 55 | } |
| 56 | + /** | ||
| 57 | + * 获取 class。 | ||
| 58 | + * @return class。 | ||
| 59 | + */ | ||
| 60 | + public String getClazz() { | ||
| 61 | + return clazz; | ||
| 62 | + } | ||
| 51 | 63 | ||
| 52 | /** | 64 | /** |
| 53 | * 得到枚举描述。 | 65 | * 得到枚举描述。 |
| @@ -79,13 +91,21 @@ public enum SystemCommandEnumType { | @@ -79,13 +91,21 @@ public enum SystemCommandEnumType { | ||
| 79 | * @return 全部枚举值码。 | 91 | * @return 全部枚举值码。 |
| 80 | */ | 92 | */ |
| 81 | public static Map<String,String> getAllStatusCode() { | 93 | public static Map<String,String> getAllStatusCode() { |
| 82 | - List<String> list = new ArrayList<String>(); | ||
| 83 | Map<String,String> map = new HashMap<String, String>(16) ; | 94 | Map<String,String> map = new HashMap<String, String>(16) ; |
| 84 | - for (SystemCommandEnumType status : values()) { | ||
| 85 | - list.add(status.code()); | 95 | + for (SystemCommandEnum status : values()) { |
| 86 | map.put(status.getCommandType(),status.getDesc()) ; | 96 | map.put(status.getCommandType(),status.getDesc()) ; |
| 87 | } | 97 | } |
| 88 | return map; | 98 | return map; |
| 89 | } | 99 | } |
| 90 | 100 | ||
| 101 | + public static Map<String,String> getAllClazz() { | ||
| 102 | + Map<String,String> map = new HashMap<String, String>(16) ; | ||
| 103 | + for (SystemCommandEnum status : values()) { | ||
| 104 | + map.put(status.getCommandType().trim(),"com.crossoverjie.cim.client.service.impl.command." + status.getClazz()) ; | ||
| 105 | + } | ||
| 106 | + return map; | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + | ||
| 110 | + | ||
| 91 | } | 111 | } |
| 1 | +package com.crossoverjie.cim.common.kit; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.ChannelHandlerContext; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * Function: | ||
| 7 | + * | ||
| 8 | + * @author crossoverJie | ||
| 9 | + * Date: 2019-01-20 17:15 | ||
| 10 | + * @since JDK 1.8 | ||
| 11 | + */ | ||
| 12 | +public interface HeartBeatHandler { | ||
| 13 | + | ||
| 14 | + /** | ||
| 15 | + * 处理心跳 | ||
| 16 | + * @param ctx | ||
| 17 | + * @throws Exception | ||
| 18 | + */ | ||
| 19 | + void process(ChannelHandlerContext ctx) throws Exception ; | ||
| 20 | +} |
| @@ -46,6 +46,102 @@ public class TrieTreeTest { | @@ -46,6 +46,102 @@ public class TrieTreeTest { | ||
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | @Test | 48 | @Test |
| 49 | + public void prefixSea() throws Exception { | ||
| 50 | + TrieTree trieTree = new TrieTree(); | ||
| 51 | + trieTree.insert("java"); | ||
| 52 | + trieTree.insert("jsf"); | ||
| 53 | + trieTree.insert("jsp"); | ||
| 54 | + trieTree.insert("javascript"); | ||
| 55 | + trieTree.insert("php"); | ||
| 56 | + | ||
| 57 | + String result =""; | ||
| 58 | + List<String> ab = trieTree.prefixSearch("jav"); | ||
| 59 | + for (String s : ab) { | ||
| 60 | + result += s+","; | ||
| 61 | + System.out.println(s); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + Assert.assertTrue(result.equals("java,javascript,")); | ||
| 65 | + | ||
| 66 | + } | ||
| 67 | + @Test | ||
| 68 | + public void prefixSea2() throws Exception { | ||
| 69 | + TrieTree trieTree = new TrieTree(); | ||
| 70 | + trieTree.insert("java"); | ||
| 71 | + trieTree.insert("jsf"); | ||
| 72 | + trieTree.insert("jsp"); | ||
| 73 | + trieTree.insert("javascript"); | ||
| 74 | + trieTree.insert("php"); | ||
| 75 | + | ||
| 76 | + String result =""; | ||
| 77 | + List<String> ab = trieTree.prefixSearch("j"); | ||
| 78 | + for (String s : ab) { | ||
| 79 | + result += s+","; | ||
| 80 | + System.out.println(s); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + Assert.assertTrue(result.equals("java,javascript,jsf,jsp,")); | ||
| 84 | + | ||
| 85 | + } | ||
| 86 | + @Test | ||
| 87 | + public void prefixSea3() throws Exception { | ||
| 88 | + TrieTree trieTree = new TrieTree(); | ||
| 89 | + trieTree.insert("java"); | ||
| 90 | + trieTree.insert("jsf"); | ||
| 91 | + trieTree.insert("jsp"); | ||
| 92 | + trieTree.insert("javascript"); | ||
| 93 | + trieTree.insert("php"); | ||
| 94 | + | ||
| 95 | + String result =""; | ||
| 96 | + List<String> ab = trieTree.prefixSearch("js"); | ||
| 97 | + for (String s : ab) { | ||
| 98 | + result += s+","; | ||
| 99 | + System.out.println(s); | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + Assert.assertTrue(result.equals("jsf,jsp,")); | ||
| 103 | + | ||
| 104 | + } | ||
| 105 | + @Test | ||
| 106 | + public void prefixSea4() throws Exception { | ||
| 107 | + TrieTree trieTree = new TrieTree(); | ||
| 108 | + trieTree.insert("java"); | ||
| 109 | + trieTree.insert("jsf"); | ||
| 110 | + trieTree.insert("jsp"); | ||
| 111 | + trieTree.insert("javascript"); | ||
| 112 | + trieTree.insert("php"); | ||
| 113 | + | ||
| 114 | + String result =""; | ||
| 115 | + List<String> ab = trieTree.prefixSearch("jav"); | ||
| 116 | + for (String s : ab) { | ||
| 117 | + result += s+","; | ||
| 118 | + System.out.println(s); | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + Assert.assertTrue(result.equals("java,javascript,")); | ||
| 122 | + | ||
| 123 | + } | ||
| 124 | + @Test | ||
| 125 | + public void prefixSea5() throws Exception { | ||
| 126 | + TrieTree trieTree = new TrieTree(); | ||
| 127 | + trieTree.insert("java"); | ||
| 128 | + trieTree.insert("jsf"); | ||
| 129 | + trieTree.insert("jsp"); | ||
| 130 | + trieTree.insert("javascript"); | ||
| 131 | + trieTree.insert("php"); | ||
| 132 | + | ||
| 133 | + String result =""; | ||
| 134 | + List<String> ab = trieTree.prefixSearch("js"); | ||
| 135 | + for (String s : ab) { | ||
| 136 | + result += s+","; | ||
| 137 | + System.out.println(s); | ||
| 138 | + } | ||
| 139 | + | ||
| 140 | + Assert.assertTrue(result.equals("jsf,jsp,")); | ||
| 141 | + | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + @Test | ||
| 49 | public void prefixSearch() throws Exception { | 145 | public void prefixSearch() throws Exception { |
| 50 | TrieTree trieTree = new TrieTree(); | 146 | TrieTree trieTree = new TrieTree(); |
| 51 | trieTree.insert("abc"); | 147 | trieTree.insert("abc"); |
| @@ -112,6 +208,19 @@ public class TrieTreeTest { | @@ -112,6 +208,19 @@ public class TrieTreeTest { | ||
| 112 | } | 208 | } |
| 113 | Assert.assertTrue(result.equals("Cde,")); | 209 | Assert.assertTrue(result.equals("Cde,")); |
| 114 | } | 210 | } |
| 211 | + @Test | ||
| 212 | + public void prefixSearch44() throws Exception { | ||
| 213 | + TrieTree trieTree = new TrieTree(); | ||
| 214 | + trieTree.insert("a"); | ||
| 215 | + trieTree.insert("b"); | ||
| 216 | + trieTree.insert("c"); | ||
| 217 | + trieTree.insert("d"); | ||
| 218 | + trieTree.insert("e"); | ||
| 219 | + trieTree.insert("f"); | ||
| 220 | + trieTree.insert("g"); | ||
| 221 | + trieTree.insert("h"); | ||
| 222 | + | ||
| 223 | + } | ||
| 115 | 224 | ||
| 116 | @Test | 225 | @Test |
| 117 | public void prefixSearch5() throws Exception { | 226 | public void prefixSearch5() throws Exception { |
| @@ -9,7 +9,7 @@ public class SystemCommandEnumTypeTest { | @@ -9,7 +9,7 @@ public class SystemCommandEnumTypeTest { | ||
| 9 | 9 | ||
| 10 | @Test | 10 | @Test |
| 11 | public void getAllStatusCode() throws Exception { | 11 | public void getAllStatusCode() throws Exception { |
| 12 | - Map<String, String> allStatusCode = SystemCommandEnumType.getAllStatusCode(); | 12 | + Map<String, String> allStatusCode = SystemCommandEnum.getAllStatusCode(); |
| 13 | for (Map.Entry<String, String> stringStringEntry : allStatusCode.entrySet()) { | 13 | for (Map.Entry<String, String> stringStringEntry : allStatusCode.entrySet()) { |
| 14 | String key = stringStringEntry.getKey(); | 14 | String key = stringStringEntry.getKey(); |
| 15 | String value = stringStringEntry.getValue(); | 15 | String value = stringStringEntry.getValue(); |
cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/impl/AccountServiceRedisImpl.java
| @@ -174,6 +174,9 @@ public class AccountServiceRedisImpl implements AccountService { | @@ -174,6 +174,9 @@ public class AccountServiceRedisImpl implements AccountService { | ||
| 174 | 174 | ||
| 175 | @Override | 175 | @Override |
| 176 | public void offLine(Long userId) throws Exception { | 176 | public void offLine(Long userId) throws Exception { |
| 177 | + | ||
| 178 | + // TODO: 2019-01-21 改为一个原子命令,以防数据一致性 | ||
| 179 | + | ||
| 177 | //删除路由 | 180 | //删除路由 |
| 178 | redisTemplate.delete(ROUTE_PREFIX + userId) ; | 181 | redisTemplate.delete(ROUTE_PREFIX + userId) ; |
| 179 | 182 |
| @@ -64,13 +64,6 @@ | @@ -64,13 +64,6 @@ | ||
| 64 | <artifactId>spring-boot-starter-actuator</artifactId> | 64 | <artifactId>spring-boot-starter-actuator</artifactId> |
| 65 | </dependency> | 65 | </dependency> |
| 66 | 66 | ||
| 67 | - | ||
| 68 | - <dependency> | ||
| 69 | - <groupId>io.netty</groupId> | ||
| 70 | - <artifactId>netty-all</artifactId> | ||
| 71 | - <version>${netty.version}</version> | ||
| 72 | - </dependency> | ||
| 73 | - | ||
| 74 | <dependency> | 67 | <dependency> |
| 75 | <groupId>junit</groupId> | 68 | <groupId>junit</groupId> |
| 76 | <artifactId>junit</artifactId> | 69 | <artifactId>junit</artifactId> |
| @@ -51,7 +51,7 @@ public class BeanConfig { | @@ -51,7 +51,7 @@ public class BeanConfig { | ||
| 51 | public CIMRequestProto.CIMReqProtocol heartBeat() { | 51 | public CIMRequestProto.CIMReqProtocol heartBeat() { |
| 52 | CIMRequestProto.CIMReqProtocol heart = CIMRequestProto.CIMReqProtocol.newBuilder() | 52 | CIMRequestProto.CIMReqProtocol heart = CIMRequestProto.CIMReqProtocol.newBuilder() |
| 53 | .setRequestId(0L) | 53 | .setRequestId(0L) |
| 54 | - .setReqMsg("ping") | 54 | + .setReqMsg("pong") |
| 55 | .setType(Constants.CommandType.PING) | 55 | .setType(Constants.CommandType.PING) |
| 56 | .build(); | 56 | .build(); |
| 57 | return heart; | 57 | return heart; |
| @@ -3,13 +3,18 @@ package com.crossoverjie.cim.server.handle; | @@ -3,13 +3,18 @@ package com.crossoverjie.cim.server.handle; | ||
| 3 | import com.alibaba.fastjson.JSONObject; | 3 | import com.alibaba.fastjson.JSONObject; |
| 4 | import com.crossoverjie.cim.common.constant.Constants; | 4 | import com.crossoverjie.cim.common.constant.Constants; |
| 5 | import com.crossoverjie.cim.common.exception.CIMException; | 5 | import com.crossoverjie.cim.common.exception.CIMException; |
| 6 | +import com.crossoverjie.cim.common.kit.HeartBeatHandler; | ||
| 6 | import com.crossoverjie.cim.common.pojo.CIMUserInfo; | 7 | import com.crossoverjie.cim.common.pojo.CIMUserInfo; |
| 7 | import com.crossoverjie.cim.common.protocol.CIMRequestProto; | 8 | import com.crossoverjie.cim.common.protocol.CIMRequestProto; |
| 9 | +import com.crossoverjie.cim.common.util.NettyAttrUtil; | ||
| 8 | import com.crossoverjie.cim.server.config.AppConfiguration; | 10 | import com.crossoverjie.cim.server.config.AppConfiguration; |
| 9 | -import com.crossoverjie.cim.server.util.NettyAttrUtil; | 11 | +import com.crossoverjie.cim.server.kit.ServerHeartBeatHandlerImpl; |
| 10 | import com.crossoverjie.cim.server.util.SessionSocketHolder; | 12 | import com.crossoverjie.cim.server.util.SessionSocketHolder; |
| 11 | import com.crossoverjie.cim.server.util.SpringBeanFactory; | 13 | import com.crossoverjie.cim.server.util.SpringBeanFactory; |
| 12 | -import io.netty.channel.*; | 14 | +import io.netty.channel.ChannelFutureListener; |
| 15 | +import io.netty.channel.ChannelHandler; | ||
| 16 | +import io.netty.channel.ChannelHandlerContext; | ||
| 17 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 13 | import io.netty.channel.socket.nio.NioSocketChannel; | 18 | import io.netty.channel.socket.nio.NioSocketChannel; |
| 14 | import io.netty.handler.timeout.IdleState; | 19 | import io.netty.handler.timeout.IdleState; |
| 15 | import io.netty.handler.timeout.IdleStateEvent; | 20 | import io.netty.handler.timeout.IdleStateEvent; |
| @@ -55,23 +60,11 @@ public class CIMServerHandle extends SimpleChannelInboundHandler<CIMRequestProto | @@ -55,23 +60,11 @@ public class CIMServerHandle extends SimpleChannelInboundHandler<CIMRequestProto | ||
| 55 | if (evt instanceof IdleStateEvent) { | 60 | if (evt instanceof IdleStateEvent) { |
| 56 | IdleStateEvent idleStateEvent = (IdleStateEvent) evt; | 61 | IdleStateEvent idleStateEvent = (IdleStateEvent) evt; |
| 57 | if (idleStateEvent.state() == IdleState.READER_IDLE) { | 62 | if (idleStateEvent.state() == IdleState.READER_IDLE) { |
| 58 | - AppConfiguration configuration = SpringBeanFactory.getBean(AppConfiguration.class); | ||
| 59 | - long heartBeatTime = configuration.getHeartBeatTime() * 1000; | ||
| 60 | - | ||
| 61 | 63 | ||
| 62 | - //向客户端发送消息 | ||
| 63 | - CIMRequestProto.CIMReqProtocol heartBeat = SpringBeanFactory.getBean("heartBeat", | ||
| 64 | - CIMRequestProto.CIMReqProtocol.class); | ||
| 65 | - ctx.writeAndFlush(heartBeat).addListeners(ChannelFutureListener.CLOSE_ON_FAILURE); | 64 | + LOGGER.info("定时检测客户端端是否存活"); |
| 66 | 65 | ||
| 67 | - Long lastReadTime = NettyAttrUtil.getReaderTime(ctx.channel()); | ||
| 68 | - long now = System.currentTimeMillis(); | ||
| 69 | - if (lastReadTime != null && now - lastReadTime > heartBeatTime){ | ||
| 70 | - CIMUserInfo userInfo = SessionSocketHolder.getUserId((NioSocketChannel) ctx.channel()); | ||
| 71 | - LOGGER.warn("客户端[{}]心跳超时[{}]ms,需要关闭连接!",userInfo.getUserName(),now - lastReadTime); | ||
| 72 | - userOffLine(userInfo, (NioSocketChannel) ctx.channel()); | ||
| 73 | - ctx.channel().close(); | ||
| 74 | - } | 66 | + HeartBeatHandler heartBeatHandler = SpringBeanFactory.getBean(ServerHeartBeatHandlerImpl.class) ; |
| 67 | + heartBeatHandler.process(ctx) ; | ||
| 75 | } | 68 | } |
| 76 | } | 69 | } |
| 77 | super.userEventTriggered(ctx, evt); | 70 | super.userEventTriggered(ctx, evt); |
| @@ -93,7 +86,7 @@ public class CIMServerHandle extends SimpleChannelInboundHandler<CIMRequestProto | @@ -93,7 +86,7 @@ public class CIMServerHandle extends SimpleChannelInboundHandler<CIMRequestProto | ||
| 93 | } | 86 | } |
| 94 | 87 | ||
| 95 | /** | 88 | /** |
| 96 | - * 清除路由关系 | 89 | + * 下线,清除路由关系 |
| 97 | * | 90 | * |
| 98 | * @param userInfo | 91 | * @param userInfo |
| 99 | * @throws IOException | 92 | * @throws IOException |
| @@ -137,6 +130,15 @@ public class CIMServerHandle extends SimpleChannelInboundHandler<CIMRequestProto | @@ -137,6 +130,15 @@ public class CIMServerHandle extends SimpleChannelInboundHandler<CIMRequestProto | ||
| 137 | //心跳更新时间 | 130 | //心跳更新时间 |
| 138 | if (msg.getType() == Constants.CommandType.PING){ | 131 | if (msg.getType() == Constants.CommandType.PING){ |
| 139 | NettyAttrUtil.updateReaderTime(ctx.channel(),System.currentTimeMillis()); | 132 | NettyAttrUtil.updateReaderTime(ctx.channel(),System.currentTimeMillis()); |
| 133 | + //向客户端响应 pong 消息 | ||
| 134 | + CIMRequestProto.CIMReqProtocol heartBeat = SpringBeanFactory.getBean("heartBeat", | ||
| 135 | + CIMRequestProto.CIMReqProtocol.class); | ||
| 136 | + ctx.writeAndFlush(heartBeat).addListeners((ChannelFutureListener) future -> { | ||
| 137 | + if (!future.isSuccess()) { | ||
| 138 | + LOGGER.error("IO error,close Channel"); | ||
| 139 | + future.channel().close(); | ||
| 140 | + } | ||
| 141 | + }) ; | ||
| 140 | } | 142 | } |
| 141 | 143 | ||
| 142 | } | 144 | } |
| @@ -25,8 +25,8 @@ public class CIMServerInitializer extends ChannelInitializer<Channel> { | @@ -25,8 +25,8 @@ public class CIMServerInitializer extends ChannelInitializer<Channel> { | ||
| 25 | protected void initChannel(Channel ch) throws Exception { | 25 | protected void initChannel(Channel ch) throws Exception { |
| 26 | 26 | ||
| 27 | ch.pipeline() | 27 | ch.pipeline() |
| 28 | - //30 秒没有向客户端发送消息就发生心跳 | ||
| 29 | - .addLast(new IdleStateHandler(30, 0, 0)) | 28 | + //11 秒没有向客户端发送消息就发生心跳 |
| 29 | + .addLast(new IdleStateHandler(11, 0, 0)) | ||
| 30 | // google Protobuf 编解码 | 30 | // google Protobuf 编解码 |
| 31 | .addLast(new ProtobufVarint32FrameDecoder()) | 31 | .addLast(new ProtobufVarint32FrameDecoder()) |
| 32 | .addLast(new ProtobufDecoder(CIMRequestProto.CIMReqProtocol.getDefaultInstance())) | 32 | .addLast(new ProtobufDecoder(CIMRequestProto.CIMReqProtocol.getDefaultInstance())) |
| 1 | +package com.crossoverjie.cim.server.kit; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSONObject; | ||
| 4 | +import com.crossoverjie.cim.common.pojo.CIMUserInfo; | ||
| 5 | +import com.crossoverjie.cim.server.config.AppConfiguration; | ||
| 6 | +import com.crossoverjie.cim.server.util.SessionSocketHolder; | ||
| 7 | +import com.crossoverjie.cim.server.util.SpringBeanFactory; | ||
| 8 | +import io.netty.channel.socket.nio.NioSocketChannel; | ||
| 9 | +import okhttp3.*; | ||
| 10 | +import org.slf4j.Logger; | ||
| 11 | +import org.slf4j.LoggerFactory; | ||
| 12 | +import org.springframework.stereotype.Component; | ||
| 13 | + | ||
| 14 | +import java.io.IOException; | ||
| 15 | + | ||
| 16 | +/** | ||
| 17 | + * Function: | ||
| 18 | + * | ||
| 19 | + * @author crossoverJie | ||
| 20 | + * Date: 2019-01-20 17:20 | ||
| 21 | + * @since JDK 1.8 | ||
| 22 | + */ | ||
| 23 | +@Component | ||
| 24 | +public class RouteHandler { | ||
| 25 | + private final static Logger LOGGER = LoggerFactory.getLogger(RouteHandler.class); | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + private final MediaType mediaType = MediaType.parse("application/json"); | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * 用户下线 | ||
| 32 | + * @param userInfo | ||
| 33 | + * @param channel | ||
| 34 | + * @throws IOException | ||
| 35 | + */ | ||
| 36 | + public void userOffLine(CIMUserInfo userInfo, NioSocketChannel channel) throws IOException { | ||
| 37 | + if (userInfo != null){ | ||
| 38 | + LOGGER.info("用户[{}]下线", userInfo.getUserName()); | ||
| 39 | + SessionSocketHolder.removeSession(userInfo.getUserId()); | ||
| 40 | + //清除路由关系 | ||
| 41 | + clearRouteInfo(userInfo); | ||
| 42 | + } | ||
| 43 | + SessionSocketHolder.remove(channel); | ||
| 44 | + | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * 清除路由关系 | ||
| 50 | + * | ||
| 51 | + * @param userInfo | ||
| 52 | + * @throws IOException | ||
| 53 | + */ | ||
| 54 | + private void clearRouteInfo(CIMUserInfo userInfo) throws IOException { | ||
| 55 | + OkHttpClient okHttpClient = SpringBeanFactory.getBean(OkHttpClient.class); | ||
| 56 | + AppConfiguration configuration = SpringBeanFactory.getBean(AppConfiguration.class); | ||
| 57 | + JSONObject jsonObject = new JSONObject(); | ||
| 58 | + jsonObject.put("userId", userInfo.getUserId()); | ||
| 59 | + jsonObject.put("msg", "offLine"); | ||
| 60 | + RequestBody requestBody = RequestBody.create(mediaType, jsonObject.toString()); | ||
| 61 | + | ||
| 62 | + Request request = new Request.Builder() | ||
| 63 | + .url(configuration.getClearRouteUrl()) | ||
| 64 | + .post(requestBody) | ||
| 65 | + .build(); | ||
| 66 | + | ||
| 67 | + Response response = null; | ||
| 68 | + try { | ||
| 69 | + response = okHttpClient.newCall(request).execute(); | ||
| 70 | + if (!response.isSuccessful()) { | ||
| 71 | + throw new IOException("Unexpected code " + response); | ||
| 72 | + } | ||
| 73 | + } finally { | ||
| 74 | + response.body().close(); | ||
| 75 | + } | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | +} |
| 1 | +package com.crossoverjie.cim.server.kit; | ||
| 2 | + | ||
| 3 | +import com.crossoverjie.cim.common.kit.HeartBeatHandler; | ||
| 4 | +import com.crossoverjie.cim.common.pojo.CIMUserInfo; | ||
| 5 | +import com.crossoverjie.cim.common.util.NettyAttrUtil; | ||
| 6 | +import com.crossoverjie.cim.server.config.AppConfiguration; | ||
| 7 | +import com.crossoverjie.cim.server.util.SessionSocketHolder; | ||
| 8 | +import io.netty.channel.ChannelHandlerContext; | ||
| 9 | +import io.netty.channel.socket.nio.NioSocketChannel; | ||
| 10 | +import org.slf4j.Logger; | ||
| 11 | +import org.slf4j.LoggerFactory; | ||
| 12 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 13 | +import org.springframework.stereotype.Service; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * Function: | ||
| 17 | + * | ||
| 18 | + * @author crossoverJie | ||
| 19 | + * Date: 2019-01-20 17:16 | ||
| 20 | + * @since JDK 1.8 | ||
| 21 | + */ | ||
| 22 | +@Service | ||
| 23 | +public class ServerHeartBeatHandlerImpl implements HeartBeatHandler { | ||
| 24 | + | ||
| 25 | + private final static Logger LOGGER = LoggerFactory.getLogger(ServerHeartBeatHandlerImpl.class); | ||
| 26 | + | ||
| 27 | + @Autowired | ||
| 28 | + private RouteHandler routeHandler ; | ||
| 29 | + | ||
| 30 | + @Autowired | ||
| 31 | + private AppConfiguration appConfiguration ; | ||
| 32 | + | ||
| 33 | + @Override | ||
| 34 | + public void process(ChannelHandlerContext ctx) throws Exception { | ||
| 35 | + | ||
| 36 | + long heartBeatTime = appConfiguration.getHeartBeatTime() * 1000; | ||
| 37 | + | ||
| 38 | + Long lastReadTime = NettyAttrUtil.getReaderTime(ctx.channel()); | ||
| 39 | + long now = System.currentTimeMillis(); | ||
| 40 | + if (lastReadTime != null && now - lastReadTime > heartBeatTime){ | ||
| 41 | + CIMUserInfo userInfo = SessionSocketHolder.getUserId((NioSocketChannel) ctx.channel()); | ||
| 42 | + if (userInfo != null){ | ||
| 43 | + LOGGER.warn("客户端[{}]心跳超时[{}]ms,需要关闭连接!",userInfo.getUserName(),now - lastReadTime); | ||
| 44 | + } | ||
| 45 | + routeHandler.userOffLine(userInfo, (NioSocketChannel) ctx.channel()); | ||
| 46 | + ctx.channel().close(); | ||
| 47 | + } | ||
| 48 | + } | ||
| 49 | +} |
| @@ -34,4 +34,4 @@ app.zk.root=/route | @@ -34,4 +34,4 @@ app.zk.root=/route | ||
| 34 | cim.clear.route.request.url=http://localhost:8083/offLine | 34 | cim.clear.route.request.url=http://localhost:8083/offLine |
| 35 | 35 | ||
| 36 | # 检测多少秒没有收到客户端心跳后服务端关闭连接 | 36 | # 检测多少秒没有收到客户端心跳后服务端关闭连接 |
| 37 | -cim.heartbeat.time = 40 | ||
| 37 | +cim.heartbeat.time = 30 |
| @@ -34,3 +34,13 @@ spring.redis.host=47.98.194.60 spring.redis.port=6379 | @@ -34,3 +34,13 @@ spring.redis.host=47.98.194.60 spring.redis.port=6379 | ||
| 34 |  | 34 |  |
| 35 | 35 | ||
| 36 | 账号信息会存放在 `Redis`。 | 36 | 账号信息会存放在 `Redis`。 |
| 37 | + | ||
| 38 | + | ||
| 39 | +## 本地如何模拟调试? | ||
| 40 | + | ||
| 41 | +至少需要启动以下服务: | ||
| 42 | + | ||
| 43 | +1. 服务端 | ||
| 44 | +2. 路由 | ||
| 45 | +3. 至少两个客户端 | ||
| 46 | +4. `redis`、`zk` 基础组件 |
pic/show.gif
已删除
100644 → 0
30.0 MB
| @@ -30,7 +30,6 @@ | @@ -30,7 +30,6 @@ | ||
| 30 | <module>cim-server</module> | 30 | <module>cim-server</module> |
| 31 | <module>cim-client</module> | 31 | <module>cim-client</module> |
| 32 | <module>cim-common</module> | 32 | <module>cim-common</module> |
| 33 | - <module>springboot-admin</module> | ||
| 34 | <module>cim-zk</module> | 33 | <module>cim-zk</module> |
| 35 | <module>cim-forward-route</module> | 34 | <module>cim-forward-route</module> |
| 36 | </modules> | 35 | </modules> |
springboot-admin/README.md
已删除
100644 → 0
springboot-admin/pom.xml
已删除
100644 → 0
| 1 | -<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 3 | - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 4 | - <modelVersion>4.0.0</modelVersion> | ||
| 5 | - | ||
| 6 | - <parent> | ||
| 7 | - <groupId>com.crossoverjie.netty</groupId> | ||
| 8 | - <artifactId>cim</artifactId> | ||
| 9 | - <version>1.0.0-SNAPSHOT</version> | ||
| 10 | - </parent> | ||
| 11 | - | ||
| 12 | - <artifactId>springboot-admin</artifactId> | ||
| 13 | - <packaging>jar</packaging> | ||
| 14 | - | ||
| 15 | - <name>admin</name> | ||
| 16 | - <description>springBoot admin</description> | ||
| 17 | - | ||
| 18 | - | ||
| 19 | - <properties> | ||
| 20 | - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| 21 | - <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> | ||
| 22 | - <java.version>1.8</java.version> | ||
| 23 | - <logback.version>1.2.3</logback.version> | ||
| 24 | - </properties> | ||
| 25 | - | ||
| 26 | - <dependencies> | ||
| 27 | - <dependency> | ||
| 28 | - <groupId>org.springframework.boot</groupId> | ||
| 29 | - <artifactId>spring-boot-starter</artifactId> | ||
| 30 | - </dependency> | ||
| 31 | - | ||
| 32 | - <dependency> | ||
| 33 | - <groupId>org.springframework.boot</groupId> | ||
| 34 | - <artifactId>spring-boot-starter-test</artifactId> | ||
| 35 | - <scope>test</scope> | ||
| 36 | - </dependency> | ||
| 37 | - | ||
| 38 | - <dependency> | ||
| 39 | - <groupId>org.springframework.boot</groupId> | ||
| 40 | - <artifactId>spring-boot-starter-mail</artifactId> | ||
| 41 | - </dependency> | ||
| 42 | - | ||
| 43 | - <dependency> | ||
| 44 | - <groupId>de.codecentric</groupId> | ||
| 45 | - <artifactId>spring-boot-admin-starter-server</artifactId> | ||
| 46 | - <version>1.5.7</version> | ||
| 47 | - </dependency> | ||
| 48 | - | ||
| 49 | - <dependency> | ||
| 50 | - <groupId>ch.qos.logback</groupId> | ||
| 51 | - <artifactId>logback-classic</artifactId> | ||
| 52 | - </dependency> | ||
| 53 | - | ||
| 54 | - <dependency> | ||
| 55 | - <groupId>de.codecentric</groupId> | ||
| 56 | - <artifactId>spring-boot-admin-server-ui</artifactId> | ||
| 57 | - <version>1.5.6</version> | ||
| 58 | - </dependency> | ||
| 59 | - </dependencies> | ||
| 60 | - | ||
| 61 | - <build> | ||
| 62 | - <plugins> | ||
| 63 | - <plugin> | ||
| 64 | - <groupId>org.springframework.boot</groupId> | ||
| 65 | - <artifactId>spring-boot-maven-plugin</artifactId> | ||
| 66 | - </plugin> | ||
| 67 | - </plugins> | ||
| 68 | - </build> | ||
| 69 | - | ||
| 70 | - | ||
| 71 | -</project> |
| 1 | -package com.ai.obc.springboot.admin; | ||
| 2 | - | ||
| 3 | -import de.codecentric.boot.admin.config.EnableAdminServer; | ||
| 4 | -import org.springframework.boot.SpringApplication; | ||
| 5 | -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; | ||
| 6 | -import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
| 7 | -import org.springframework.context.annotation.Configuration; | ||
| 8 | - | ||
| 9 | -@SpringBootApplication | ||
| 10 | -@Configuration | ||
| 11 | -@EnableAutoConfiguration | ||
| 12 | -@EnableAdminServer | ||
| 13 | -public class AdminApplication { | ||
| 14 | - | ||
| 15 | - public static void main(String[] args) { | ||
| 16 | - SpringApplication.run(AdminApplication.class, args); | ||
| 17 | - } | ||
| 18 | -} |
springboot-admin/src/test/java/com/ai/obc/springboot/admin/AdminApplicationTests.java
已删除
100644 → 0
| 1 | -package com.ai.obc.springboot.admin; | ||
| 2 | - | ||
| 3 | -import org.junit.Test; | ||
| 4 | -import org.junit.runner.RunWith; | ||
| 5 | -import org.springframework.boot.test.context.SpringBootTest; | ||
| 6 | -import org.springframework.test.context.junit4.SpringRunner; | ||
| 7 | - | ||
| 8 | -@RunWith(SpringRunner.class) | ||
| 9 | -@SpringBootTest | ||
| 10 | -public class AdminApplicationTests { | ||
| 11 | - | ||
| 12 | - @Test | ||
| 13 | - public void contextLoads() { | ||
| 14 | - } | ||
| 15 | - | ||
| 16 | -} |
-
请 注册 或 登录 后发表评论