提交者
GitHub
Merge pull request #26 from crossoverJie/cim-1.0.2
cim 1.0.2
正在显示
33 个修改的文件
包含
591 行增加
和
204 行删除
| @@ -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,25 @@ public class CIMClient { | @@ -145,11 +190,25 @@ 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 | + channel.close(); |
| 154 | } | 213 | } |
| 155 | } | 214 | } |
| @@ -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()) |
| @@ -48,4 +48,10 @@ public interface MsgHandle { | @@ -48,4 +48,10 @@ 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() ; | ||
| 51 | } | 57 | } |
| @@ -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 | +} |
| 1 | package com.crossoverjie.cim.client.service.impl; | 1 | package com.crossoverjie.cim.client.service.impl; |
| 2 | 2 | ||
| 3 | +import com.alibaba.fastjson.JSON; | ||
| 3 | import com.crossoverjie.cim.client.client.CIMClient; | 4 | import com.crossoverjie.cim.client.client.CIMClient; |
| 4 | import com.crossoverjie.cim.client.config.AppConfiguration; | 5 | import com.crossoverjie.cim.client.config.AppConfiguration; |
| 5 | import com.crossoverjie.cim.client.service.MsgHandle; | 6 | import com.crossoverjie.cim.client.service.MsgHandle; |
| @@ -16,6 +17,7 @@ import org.slf4j.LoggerFactory; | @@ -16,6 +17,7 @@ import org.slf4j.LoggerFactory; | ||
| 16 | import org.springframework.beans.factory.annotation.Autowired; | 17 | import org.springframework.beans.factory.annotation.Autowired; |
| 17 | import org.springframework.stereotype.Service; | 18 | import org.springframework.stereotype.Service; |
| 18 | 19 | ||
| 20 | +import javax.annotation.Resource; | ||
| 19 | import java.util.List; | 21 | import java.util.List; |
| 20 | import java.util.Map; | 22 | import java.util.Map; |
| 21 | import java.util.concurrent.ThreadPoolExecutor; | 23 | import java.util.concurrent.ThreadPoolExecutor; |
| @@ -32,33 +34,37 @@ import java.util.concurrent.TimeUnit; | @@ -32,33 +34,37 @@ import java.util.concurrent.TimeUnit; | ||
| 32 | public class MsgHandler implements MsgHandle { | 34 | public class MsgHandler implements MsgHandle { |
| 33 | private final static Logger LOGGER = LoggerFactory.getLogger(MsgHandler.class); | 35 | private final static Logger LOGGER = LoggerFactory.getLogger(MsgHandler.class); |
| 34 | @Autowired | 36 | @Autowired |
| 35 | - private RouteRequest routeRequest ; | 37 | + private RouteRequest routeRequest; |
| 36 | 38 | ||
| 37 | @Autowired | 39 | @Autowired |
| 38 | private AppConfiguration configuration; | 40 | private AppConfiguration configuration; |
| 39 | 41 | ||
| 42 | + @Resource(name = "callBackThreadPool") | ||
| 43 | + private ThreadPoolExecutor executor; | ||
| 44 | + | ||
| 40 | @Autowired | 45 | @Autowired |
| 41 | - private ThreadPoolExecutor executor ; | 46 | + private CIMClient cimClient; |
| 42 | 47 | ||
| 43 | @Autowired | 48 | @Autowired |
| 44 | - private CIMClient cimClient ; | 49 | + private MsgLogger msgLogger; |
| 45 | 50 | ||
| 46 | @Autowired | 51 | @Autowired |
| 47 | - private MsgLogger msgLogger ; | 52 | + private ClientInfo clientInfo ; |
| 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,47 @@ public class MsgHandler implements MsgHandle { | @@ -123,41 +130,47 @@ 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(":")){ | 133 | + // TODO: 2019-01-22 判断逻辑过多,需要重构。 |
| 134 | + if (msg.startsWith(":")) { | ||
| 127 | Map<String, String> allStatusCode = SystemCommandEnumType.getAllStatusCode(); | 135 | Map<String, String> allStatusCode = SystemCommandEnumType.getAllStatusCode(); |
| 128 | 136 | ||
| 129 | - if (SystemCommandEnumType.QUIT.getCommandType().trim().equals(msg)){ | 137 | + if (SystemCommandEnumType.QUIT.getCommandType().trim().equals(msg)) { |
| 130 | //关闭系统 | 138 | //关闭系统 |
| 131 | shutdown(); | 139 | shutdown(); |
| 132 | - } else if (SystemCommandEnumType.ALL.getCommandType().trim().equals(msg)){ | 140 | + } else if (SystemCommandEnumType.ALL.getCommandType().trim().equals(msg)) { |
| 133 | printAllCommand(allStatusCode); | 141 | printAllCommand(allStatusCode); |
| 134 | 142 | ||
| 135 | - } else if (SystemCommandEnumType.ONLINE_USER.getCommandType().toLowerCase().trim().equals(msg.toLowerCase())){ | 143 | + } else if (SystemCommandEnumType.ONLINE_USER.getCommandType().toLowerCase().trim().equals(msg.toLowerCase())) { |
| 136 | //打印在线用户 | 144 | //打印在线用户 |
| 137 | printOnlineUsers(); | 145 | printOnlineUsers(); |
| 138 | 146 | ||
| 139 | - } else if (msg.startsWith(SystemCommandEnumType.QUERY.getCommandType().trim() + " ")){ | 147 | + } else if (msg.startsWith(SystemCommandEnumType.QUERY.getCommandType().trim() + " ")) { |
| 140 | //查询聊天记录 | 148 | //查询聊天记录 |
| 141 | queryChatHistory(msg); | 149 | queryChatHistory(msg); |
| 142 | - }else if (SystemCommandEnumType.AI.getCommandType().trim().equals(msg.toLowerCase())){ | 150 | + } else if (SystemCommandEnumType.AI.getCommandType().trim().equals(msg.toLowerCase())) { |
| 143 | //开启 AI 模式 | 151 | //开启 AI 模式 |
| 144 | - aiModel = true ; | 152 | + aiModel = true; |
| 145 | System.out.println("\033[31;4m" + "Hello,我是估值两亿的 AI 机器人!" + "\033[0m"); | 153 | System.out.println("\033[31;4m" + "Hello,我是估值两亿的 AI 机器人!" + "\033[0m"); |
| 146 | - }else if (SystemCommandEnumType.QAI.getCommandType().trim().equals(msg.toLowerCase())){ | 154 | + } else if (SystemCommandEnumType.QAI.getCommandType().trim().equals(msg.toLowerCase())) { |
| 147 | //关闭 AI 模式 | 155 | //关闭 AI 模式 |
| 148 | - aiModel = false ; | 156 | + aiModel = false; |
| 149 | System.out.println("\033[31;4m" + "。゚(゚´ω`゚)゚。 AI 下线了!" + "\033[0m"); | 157 | System.out.println("\033[31;4m" + "。゚(゚´ω`゚)゚。 AI 下线了!" + "\033[0m"); |
| 150 | - }else if (msg.startsWith(SystemCommandEnumType.PREFIX.getCommandType().trim() + " ")){ | 158 | + } else if (msg.startsWith(SystemCommandEnumType.PREFIX.getCommandType().trim() + " ")) { |
| 151 | //模糊匹配 | 159 | //模糊匹配 |
| 152 | prefixSearch(msg); | 160 | prefixSearch(msg); |
| 153 | - }else { | 161 | + } else if (SystemCommandEnumType.INFO.getCommandType().trim().equals(msg.toLowerCase())) { |
| 162 | + LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); | ||
| 163 | + LOGGER.info("client info=[{}]", JSON.toJSONString(clientInfo.get())); | ||
| 164 | + LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); | ||
| 165 | + | ||
| 166 | + } else { | ||
| 154 | printAllCommand(allStatusCode); | 167 | printAllCommand(allStatusCode); |
| 155 | } | 168 | } |
| 156 | 169 | ||
| 157 | - return true ; | 170 | + return true; |
| 158 | 171 | ||
| 159 | - }else { | ||
| 160 | - return false ; | 172 | + } else { |
| 173 | + return false; | ||
| 161 | } | 174 | } |
| 162 | 175 | ||
| 163 | 176 | ||
| @@ -166,12 +179,13 @@ public class MsgHandler implements MsgHandle { | @@ -166,12 +179,13 @@ public class MsgHandler implements MsgHandle { | ||
| 166 | 179 | ||
| 167 | /** | 180 | /** |
| 168 | * 模糊匹配 | 181 | * 模糊匹配 |
| 182 | + * | ||
| 169 | * @param msg | 183 | * @param msg |
| 170 | */ | 184 | */ |
| 171 | private void prefixSearch(String msg) { | 185 | private void prefixSearch(String msg) { |
| 172 | try { | 186 | try { |
| 173 | List<OnlineUsersResVO.DataBodyBean> onlineUsers = routeRequest.onlineUsers(); | 187 | List<OnlineUsersResVO.DataBodyBean> onlineUsers = routeRequest.onlineUsers(); |
| 174 | - TrieTree trieTree = new TrieTree() ; | 188 | + TrieTree trieTree = new TrieTree(); |
| 175 | for (OnlineUsersResVO.DataBodyBean onlineUser : onlineUsers) { | 189 | for (OnlineUsersResVO.DataBodyBean onlineUser : onlineUsers) { |
| 176 | trieTree.insert(onlineUser.getUserName()); | 190 | trieTree.insert(onlineUser.getUserName()); |
| 177 | } | 191 | } |
| @@ -186,16 +200,17 @@ public class MsgHandler implements MsgHandle { | @@ -186,16 +200,17 @@ public class MsgHandler implements MsgHandle { | ||
| 186 | } | 200 | } |
| 187 | 201 | ||
| 188 | } catch (Exception e) { | 202 | } catch (Exception e) { |
| 189 | - LOGGER.error("Exception" ,e); | 203 | + LOGGER.error("Exception", e); |
| 190 | } | 204 | } |
| 191 | } | 205 | } |
| 192 | 206 | ||
| 193 | /** | 207 | /** |
| 194 | * 查询聊天记录 | 208 | * 查询聊天记录 |
| 209 | + * | ||
| 195 | * @param msg | 210 | * @param msg |
| 196 | */ | 211 | */ |
| 197 | private void queryChatHistory(String msg) { | 212 | private void queryChatHistory(String msg) { |
| 198 | - String[] split = msg.split(" ") ; | 213 | + String[] split = msg.split(" "); |
| 199 | String res = msgLogger.query(split[1]); | 214 | String res = msgLogger.query(split[1]); |
| 200 | System.out.println(res); | 215 | System.out.println(res); |
| 201 | } | 216 | } |
| @@ -209,20 +224,22 @@ public class MsgHandler implements MsgHandle { | @@ -209,20 +224,22 @@ public class MsgHandler implements MsgHandle { | ||
| 209 | 224 | ||
| 210 | LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); | 225 | LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); |
| 211 | for (OnlineUsersResVO.DataBodyBean onlineUser : onlineUsers) { | 226 | for (OnlineUsersResVO.DataBodyBean onlineUser : onlineUsers) { |
| 212 | - LOGGER.info("userId={}=====userName={}",onlineUser.getUserId(),onlineUser.getUserName()); | 227 | + LOGGER.info("userId={}=====userName={}", onlineUser.getUserId(), onlineUser.getUserName()); |
| 213 | } | 228 | } |
| 214 | LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); | 229 | LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); |
| 215 | 230 | ||
| 216 | } catch (Exception e) { | 231 | } catch (Exception e) { |
| 217 | - LOGGER.error("Exception" ,e); | 232 | + LOGGER.error("Exception", e); |
| 218 | } | 233 | } |
| 219 | } | 234 | } |
| 220 | 235 | ||
| 221 | /** | 236 | /** |
| 222 | * 关闭系统 | 237 | * 关闭系统 |
| 223 | */ | 238 | */ |
| 224 | - private void shutdown() { | 239 | + @Override |
| 240 | + public void shutdown() { | ||
| 225 | LOGGER.info("系统关闭中。。。。"); | 241 | LOGGER.info("系统关闭中。。。。"); |
| 242 | + routeRequest.offLine(); | ||
| 226 | msgLogger.stop(); | 243 | msgLogger.stop(); |
| 227 | executor.shutdown(); | 244 | executor.shutdown(); |
| 228 | try { | 245 | try { |
| @@ -231,7 +248,7 @@ public class MsgHandler implements MsgHandle { | @@ -231,7 +248,7 @@ public class MsgHandler implements MsgHandle { | ||
| 231 | } | 248 | } |
| 232 | cimClient.close(); | 249 | cimClient.close(); |
| 233 | } catch (InterruptedException e) { | 250 | } catch (InterruptedException e) { |
| 234 | - LOGGER.error("InterruptedException",e); | 251 | + LOGGER.error("InterruptedException", e); |
| 235 | } | 252 | } |
| 236 | System.exit(0); | 253 | System.exit(0); |
| 237 | } | 254 | } |
| @@ -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 | } |
| 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 |
| @@ -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> |
| @@ -20,7 +20,8 @@ public enum SystemCommandEnumType { | @@ -20,7 +20,8 @@ public enum SystemCommandEnumType { | ||
| 20 | QUERY(":q ","【:q 关键字】查询聊天记录"), | 20 | QUERY(":q ","【:q 关键字】查询聊天记录"), |
| 21 | AI(":ai ","开启 AI 模式"), | 21 | AI(":ai ","开启 AI 模式"), |
| 22 | QAI(":qai ","关闭 AI 模式"), | 22 | QAI(":qai ","关闭 AI 模式"), |
| 23 | - PREFIX(":pu ","模糊匹配用户") | 23 | + PREFIX(":pu ","模糊匹配用户"), |
| 24 | + INFO(":info ","获取客户端信息") | ||
| 24 | 25 | ||
| 25 | ; | 26 | ; |
| 26 | 27 |
| 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 | +} |
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 |
| @@ -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 | -} |
-
请 注册 或 登录 后发表评论