Quellcode durchsuchen

channel, session management update, and db initialize not use

shjung vor 1 Woche
Ursprung
Commit
ac14998e3e
17 geänderte Dateien mit 233 neuen und 113 gelöschten Zeilen
  1. 35 1
      tsi-comm-server/src/main/java/com/tsi/comm/server/controller/TsiCommServerRestController.java
  2. 15 0
      tsi-comm-server/src/main/java/com/tsi/comm/server/repository/TsiAlarmManager.java
  3. 6 0
      tsi-comm-server/src/main/java/com/tsi/comm/server/repository/TsiNodeManager.java
  4. 2 4
      tsi-comm-server/src/main/java/com/tsi/comm/server/repository/TsiReportManager.java
  5. 22 4
      tsi-comm-server/src/main/java/com/tsi/comm/server/repository/TsiSessionManager.java
  6. 1 1
      tsi-comm-server/src/main/java/com/tsi/comm/server/repository/TsiTpmsManager.java
  7. 21 21
      tsi-comm-server/src/main/java/com/tsi/comm/server/scheduler/ApplicationScheduler.java
  8. 3 3
      tsi-comm-server/src/main/java/com/tsi/comm/server/service/TsiCommServerService.java
  9. 4 3
      tsi-comm-server/src/main/java/com/tsi/comm/server/tcp/TsiCvimServer.java
  10. 16 16
      tsi-comm-server/src/main/java/com/tsi/comm/server/tcp/codec/CvimServerByteBufMessageDecoder.java
  11. 38 19
      tsi-comm-server/src/main/java/com/tsi/comm/server/tcp/handler/CvimServerInboundMessageHandler.java
  12. 0 30
      tsi-comm-server/src/main/java/com/tsi/comm/server/tcp/handler/CvimServerRegisterHandler.java
  13. 14 2
      tsi-comm-server/src/main/java/com/tsi/comm/server/tcp/initializer/CvimServerInitializer.java
  14. 5 5
      tsi-comm-server/src/main/java/com/tsi/comm/server/xnet/NettyTcpServer.java
  15. 24 0
      tsi-comm-server/src/main/java/com/tsi/comm/server/xnet/NettyUtils.java
  16. 23 0
      tsi-comm-server/src/main/resources/mybatis/mapper/TsiCommServerMapper.xml
  17. 4 4
      tsi-common/src/main/java/com/tsi/common/utils/Counter.java

+ 35 - 1
tsi-comm-server/src/main/java/com/tsi/comm/server/controller/TsiCommServerRestController.java

@@ -5,9 +5,11 @@ import com.tsi.comm.server.config.DatabaseConfig;
 import com.tsi.comm.server.config.TsiCvimServerConfig;
 import com.tsi.comm.server.repository.ApplicationRepository;
 import com.tsi.comm.server.repository.TsiNodeManager;
+import com.tsi.comm.server.repository.TsiSessionManager;
 import com.tsi.comm.server.vo.TsiNodeVo;
 import com.tsi.comm.server.xnet.NettyUtils;
 import com.tsi.common.utils.TimeUtils;
+import io.netty.channel.Channel;
 import lombok.RequiredArgsConstructor;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -15,6 +17,7 @@ import org.springframework.web.bind.annotation.RestController;
 
 import java.text.SimpleDateFormat;
 import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
 
 @RestController
 @RequiredArgsConstructor
@@ -22,9 +25,10 @@ import java.util.*;
 public class TsiCommServerRestController {
 
     private final ApplicationConfig config;
-    private final DatabaseConfig databaseConfig;
+//    private final DatabaseConfig databaseConfig;
     private final TsiCvimServerConfig serverConfig;
     private final TsiNodeManager nodeManager;
+    private final TsiSessionManager sessionManager;
 
     @GetMapping(value = "/info", produces = {"application/json; charset=utf8"})
     public String info() {
@@ -91,7 +95,37 @@ public class TsiCommServerRestController {
 
         sb.append(heading).append(sep);
         sb.append(String.format(" Total Nodes: %d EA, Registered: %d EA, Unknown: %d EA, Connected: %d EA", nodeCount, registered, unknown, connected)).append(sep);
+        sb.append(String.format(" Channel: %d EA, Session: %d EA", this.sessionManager.getChannelCount(), this.sessionManager.getCount())).append(sep);
         sb.append(heading).append(sep);
+
+        Set<Long> nodeIdSet = new HashSet<>();
+        ConcurrentHashMap<Channel, TsiNodeVo> channelNodeMap = this.sessionManager.getChannelNodeMap();
+        for (Map.Entry<Channel, TsiNodeVo> entry : channelNodeMap.entrySet()) {
+            Channel channel = entry.getKey();
+            TsiNodeVo nodeVo = entry.getValue();
+            Long nodeId = nodeVo.getNodeId();
+
+            TsiNodeVo dbNode = this.nodeManager.getTsiNodeVoMap().get(nodeId);
+            String ip = NettyUtils.getRemoteIpAddress(channel);
+            String dbIp = dbNode == null ? "" : dbNode.getIpAddr();
+
+            if (dbNode == null) {
+                sb.append(String.format("Not Found Node: %d, %s/%s", nodeId, ip, dbIp)).append(sep);
+            }
+            else {
+                if (!dbNode.isConnect()) {
+                    sb.append(String.format("Network state error: %d, %s/%s", nodeId, ip, dbIp)).append(sep);
+                }
+            }
+            if (nodeIdSet.contains(nodeId)) {
+                 sb.append(String.format("Dup Channel: %d %s/%s", nodeId, ip, dbIp)).append(sep);
+            } else {
+                nodeIdSet.add(nodeId);
+            }
+            if (!ip.equals(dbIp)) {
+                sb.append(String.format("IP Not Match: %d,  %s/%s", nodeId, ip, dbIp)).append(sep);
+            }
+        }
         return sb.toString();
     }
 

+ 15 - 0
tsi-comm-server/src/main/java/com/tsi/comm/server/repository/TsiAlarmManager.java

@@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.slf4j.MDC;
 import org.springframework.stereotype.Component;
 
+import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -20,6 +21,8 @@ public class TsiAlarmManager {
     private final ConcurrentHashMap<String, TsiAlarmConfigVo> tsiAlarmConfigMap = new ConcurrentHashMap<>();
     private final Set<String> UNKNOWN_NODE_SET = ConcurrentHashMap.newKeySet();
     private final Set<String> UNKNOWN_NODE_BUF_SET = ConcurrentHashMap.newKeySet();
+    private final Set<String> UNNKOWN_IPADDR_SET = ConcurrentHashMap.newKeySet();
+
 
     public TsiAlarmConfigVo get(String code) {
         return this.tsiAlarmConfigMap.get(code);
@@ -66,4 +69,16 @@ public class TsiAlarmManager {
         MDC.clear();
     }
 
+    public void reportUnknownIp(String ipAddr) {
+        if (this.UNNKOWN_IPADDR_SET.contains(ipAddr)) {
+            return;
+        }
+        this.UNNKOWN_IPADDR_SET.add(ipAddr);
+
+        final String fileName = "unknown_ip";
+        MDC.put("filename", fileName);
+        log.info("Unknown IP Address: {}", ipAddr);
+        MDC.clear();
+    }
+
 }

+ 6 - 0
tsi-comm-server/src/main/java/com/tsi/comm/server/repository/TsiNodeManager.java

@@ -41,6 +41,12 @@ public class TsiNodeManager {
         }
         this.ipAddrMap.put(ipAddr, vo);
     }
+    public void removeIpAddr(String ipAddr) {
+        if (ipAddr == null || ipAddr.isEmpty()) {
+            return;
+        }
+        this.ipAddrMap.remove(ipAddr);
+    }
 
     public int sizeIpAddr() {
         return this.ipAddrMap.size();

+ 2 - 4
tsi-comm-server/src/main/java/com/tsi/comm/server/repository/TsiReportManager.java

@@ -20,9 +20,7 @@ import org.slf4j.MDC;
 import org.springframework.stereotype.Component;
 
 import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
 
 @Slf4j
@@ -127,7 +125,7 @@ public class TsiReportManager {
                         TsiChannelSession.sessionTimeout(node, NettyUtils.getRemoteIpAddress(node.getChannel()));
 
                         if (node.getChannel() != null) {
-                            node.getChannel().disconnect();
+//                            node.getChannel().disconnect();
                             node.getChannel().close();
                         }
                     }

+ 22 - 4
tsi-comm-server/src/main/java/com/tsi/comm/server/repository/TsiSessionManager.java

@@ -22,7 +22,10 @@ public class TsiSessionManager {
     @Getter
     private volatile boolean serverRun;
     private final Counter sessions = new Counter();
+
+    @Getter
     private final ConcurrentHashMap<Channel, TsiNodeVo> channelNodeMap = new ConcurrentHashMap<>();
+//    private final ConcurrentHashMap<String, TsiNodeVo> channelNodeMap = new ConcurrentHashMap<>();
 
     public int add() {
         return (int) this.sessions.increment();
@@ -30,19 +33,29 @@ public class TsiSessionManager {
     public int remove() {
         return (int) this.sessions.decrement();
     }
-    public int get() {
-        return this.channelNodeMap.size();
-//        return (int) this.sessions.get();
+    public int getCount() {
+        return (int) this.sessions.get();
     }
 
+    public int getChannelCount() {
+        return this.channelNodeMap.size();
+    }
     public void addChannel(Channel channel, TsiNodeVo vo) {
+        if (channel == null) {
+            return;
+        }
         this.channelNodeMap.put(channel, vo);
     }
     public void removeChannel(Channel channel) {
+        if (channel == null) {
+            return;
+        }
         this.channelNodeMap.remove(channel);
     }
-
     public TsiNodeVo getNodeVo(Channel channel) {
+        if (channel == null) {
+            return null;
+        }
         return this.channelNodeMap.get(channel);
     }
 
@@ -63,6 +76,11 @@ public class TsiSessionManager {
             TsiNodeVo node = obj.getValue();
             log.info("{}", String.format("%10s %7s  %s", node.getNodeId(), channel.isActive(), NettyUtils.getRemoteAddress(channel)));
         }
+//        for (Map.Entry<String, TsiNodeVo> obj : this.channelNodeMap.entrySet()) {
+//            String channel = obj.getKey();
+//            TsiNodeVo node = obj.getValue();
+//            log.info("{}", String.format("%10s %s", node.getNodeId(), node.getIpAddr()));
+//        }
         log.info("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
     }
 

+ 1 - 1
tsi-comm-server/src/main/java/com/tsi/comm/server/repository/TsiTpmsManager.java

@@ -62,7 +62,7 @@ public class TsiTpmsManager {
         stat.setId(serverId);
         stat.setTrans(trans);
         stat.setBytes(bytes);
-        stat.setSessions(this.sessionManager.get());
+        stat.setSessions(this.sessionManager.getChannelCount());
         this.dbmsProcess.add(stat, (int)Thread.currentThread().getId());
 
         /*log.info("Second Statistics: {} sessions, {} TRX, {}. {}",

+ 21 - 21
tsi-comm-server/src/main/java/com/tsi/comm/server/scheduler/ApplicationScheduler.java

@@ -66,27 +66,27 @@ public class ApplicationScheduler {
         }
     }
 
-    @Scheduled(cron = "0/5 * * * * *")  //
-    public void checkSessionTimeout() {
-        if (!this.config.isStartup()) {
-            return;
-        }
-        if (!this.alarmManager.checkAlarm(TsiAlarmConfigVo.COMM_02)) {
-            return;
-        }
-        Elapsed elapsed = new Elapsed();
-        long timeout = 0;
-        TsiAlarmConfigVo vo = this.alarmManager.get(TsiAlarmConfigVo.COMM_02);
-        if (vo == null) return; // 이거면 안됌.
-        timeout = vo.getValue() * 1000L;
-//        timeout *= 3; // 3배로 늘려서 체크
-        if (timeout <= 0) {
-            log.error("checkSessionTimeout: timeout value error: {}", timeout);
-            return;
-        }
-        this.reportManager.checkSessionTimeout(timeout);
-//        log.info("{}", String.format("%25s: %s", "checkSessionTimeout", TimeUtils.elapsedTimeStr(elapsed.nanoSeconds())));
-    }
+//    @Scheduled(cron = "0/5 * * * * *")  //
+//    public void checkSessionTimeout() {
+//        if (!this.config.isStartup()) {
+//            return;
+//        }
+//        if (!this.alarmManager.checkAlarm(TsiAlarmConfigVo.COMM_02)) {
+//            return;
+//        }
+//        Elapsed elapsed = new Elapsed();
+//        long timeout = 0;
+//        TsiAlarmConfigVo vo = this.alarmManager.get(TsiAlarmConfigVo.COMM_02);
+//        if (vo == null) return; // 이거면 안됌.
+//        timeout = vo.getValue() * 1000L;
+////        timeout *= 3; // 3배로 늘려서 체크
+//        if (timeout <= 0) {
+//            log.error("checkSessionTimeout: timeout value error: {}", timeout);
+//            return;
+//        }
+//        this.reportManager.checkSessionTimeout(timeout);
+////        log.info("{}", String.format("%25s: %s", "checkSessionTimeout", TimeUtils.elapsedTimeStr(elapsed.nanoSeconds())));
+//    }
 
     @Scheduled(cron = "0/30 * * * * *")  // 30초 주기 작업 실행
     public void reportNodeSessionAlive() {

+ 3 - 3
tsi-comm-server/src/main/java/com/tsi/comm/server/service/TsiCommServerService.java

@@ -63,9 +63,9 @@ public class TsiCommServerService {
     }
 
     public void initDatabase() {
-        int result = this.databaseMapper.insertNodeStatusInitialize();
-        //updateNodeStatusTerm();
-        log.info("init database: {} EA", result);
+        //int result = this.databaseMapper.insertNodeStatusInitialize();
+        ////updateNodeStatusTerm();
+        //log.info("init database: {} EA", result);
     }
 
     public void loadDatabase() {

+ 4 - 3
tsi-comm-server/src/main/java/com/tsi/comm/server/tcp/TsiCvimServer.java

@@ -1,5 +1,6 @@
 package com.tsi.comm.server.tcp;
 
+import com.tsi.comm.server.repository.TsiAlarmManager;
 import com.tsi.comm.server.xnet.NettyTcpServer;
 import com.tsi.comm.server.xnet.NettyUtils;
 import com.tsi.comm.server.config.TsiCvimServerConfig;
@@ -31,14 +32,14 @@ public class TsiCvimServer extends NettyTcpServer {
         super.setChannelInitializer(new CvimServerInitializer(this.config));
         if (!OS.isWindows()) {
             if (!Epoll.isAvailable()) {
-                log.warn("{}", Epoll.unavailabilityCause().toString());
+                log.warn("TsiCvimServer Epoll not support: {}", Epoll.unavailabilityCause().toString());
             }
         }
         if (NettyUtils.isEpollAvailable()) {
-            System.out.println("서버가 리눅스 EPOLL 모드에서 실행됩니다.");
+            log.info("Server is running in Linux EPOLL mode.");
         }
         else {
-            System.out.println("서버가 윈도우 NIO 모드에서 실행됩니다.");
+            log.info("Server is running in Windows NIO mode.");
         }
     }
 }

+ 16 - 16
tsi-comm-server/src/main/java/com/tsi/comm/server/tcp/codec/CvimServerByteBufMessageDecoder.java

@@ -77,37 +77,44 @@ public class CvimServerByteBufMessageDecoder extends MessageToMessageDecoder<Byt
                         byteBuf.readBytes(buf);
                         this.alarmManager.loggingUnknownNodePacket(nodeId, ctx.channel(), buf);
                     }
-                    ctx.disconnect();
+//                    ctx.disconnect();
                     ctx.close();
                     return;
                 }
 
                 // first connection, save node information to channel attribute map
+                // 채널 맵에 채널에 대한 노드정보를 저장한다.
+                // 채널 목록에 접속 정보 등록
+                nodeVo.setConnect(ctx.channel());
+                this.sessionManager.addChannel(ctx.channel(), nodeVo);
+                TsiChannelSession.objectRegistered(nodeVo, remoteIpAddress);
+
                 this.queueDistributorService.setLoadedQueue(nodeVo);    // 작업큐를 할당한다.
                 ctx.channel().attr(TsiSessionManager.TSI_NODE_ATTRIBUTE_KEY).set(nodeVo);
 
-                NodeStatusVo status = new NodeStatusVo(AbstractDbmsVo.DBMS_NODE_STATUS);
-                status.setServerId(this.config.getServerId());
-                status.setNodeId(nodeId);
-                status.setStatus(1);
-                status.setIpAddr(NettyUtils.getRemoteIpAddress(ctx.channel()));
                 if (this.sessionManager.isServerRun()) {
+                    NodeStatusVo status = new NodeStatusVo(AbstractDbmsVo.DBMS_NODE_STATUS);
+                    status.setServerId(this.config.getServerId());
+                    status.setNodeId(nodeId);
+                    status.setStatus(1);
+                    status.setIpAddr(NettyUtils.getRemoteIpAddress(ctx.channel()));
+
                     this.dbmsProcess.add(status, (int)Thread.currentThread().getId());
                 }
-                else {
-                    log.error("Node Login but server not running: {}", status);
-                }
             }
 
             nodeVo.setLastCommTm(System.currentTimeMillis());   // 통신 수신시각 저장
 
             if (!remoteIpAddress.equals(nodeVo.getIpAddr())) {
                 // IP 주소가 변경된 경우
+                final String oldIpAddr = nodeVo.getIpAddr();
+                log.warn("Node IpAddr Changed: {} -> {}", oldIpAddr, remoteIpAddress);
                 nodeVo.setIpAddr(remoteIpAddress);
 
                 NodeIpAddrVo nodeIpAddrVo = new NodeIpAddrVo(AbstractDbmsVo.DBMS_NODE_IP_UPDATE);
                 nodeIpAddrVo.setNodeId(nodeVo.getNodeId());
                 nodeIpAddrVo.setIpAddr(remoteIpAddress);
+
                 this.dbmsProcess.add(nodeIpAddrVo, (int)Thread.currentThread().getId());
                 this.nodeManager.putIpAddr(remoteIpAddress, nodeVo); // IP 주소 갱신
             }
@@ -164,13 +171,6 @@ public class CvimServerByteBufMessageDecoder extends MessageToMessageDecoder<Byt
             }
         }
 
-        // 채널 목록에 접속 정보 등록
-        nodeVo.setConnect(channel);
-
-        // 채널 맵에 채널에 대한 노드정보를 저장한다.
-        this.sessionManager.addChannel(channel, nodeVo);
-        TsiChannelSession.objectRegistered(nodeVo, remoteIpAddress);
-//        log.info("Node: {}, Object Register: {}, {}", nodeId, TimeUtils.elapsedTime(start), Thread.currentThread().getId());
         return nodeVo;
     }
 

+ 38 - 19
tsi-comm-server/src/main/java/com/tsi/comm/server/tcp/handler/CvimServerInboundMessageHandler.java

@@ -1,11 +1,6 @@
 package com.tsi.comm.server.tcp.handler;
 
-import com.tsi.comm.server.service.TsiQueueDistributorService;
-import com.tsi.comm.server.xnet.NettyUtils;
 import com.tsi.comm.server.config.TsiCvimServerConfig;
-import com.tsi.comm.server.vo.mariadb.AbstractDbmsVo;
-import com.tsi.comm.server.vo.mariadb.AlarmOccrVo;
-import com.tsi.comm.server.vo.mariadb.NodeStatusVo;
 import com.tsi.comm.server.process.dbms.TsiCvimDbmsProcess;
 import com.tsi.comm.server.process.packet.TsiChannelSession;
 import com.tsi.comm.server.process.packet.TsiCvimPacketProcess;
@@ -14,8 +9,13 @@ import com.tsi.comm.server.protocol.TsiCpuDisconnected;
 import com.tsi.comm.server.repository.TsiAlarmManager;
 import com.tsi.comm.server.repository.TsiNodeManager;
 import com.tsi.comm.server.repository.TsiSessionManager;
+import com.tsi.comm.server.service.TsiQueueDistributorService;
 import com.tsi.comm.server.vo.TsiAlarmConfigVo;
 import com.tsi.comm.server.vo.TsiNodeVo;
+import com.tsi.comm.server.vo.mariadb.AbstractDbmsVo;
+import com.tsi.comm.server.vo.mariadb.AlarmOccrVo;
+import com.tsi.comm.server.vo.mariadb.NodeStatusVo;
+import com.tsi.comm.server.xnet.NettyUtils;
 import io.netty.channel.ChannelHandler;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
@@ -59,8 +59,18 @@ public class CvimServerInboundMessageHandler extends ChannelInboundHandlerAdapte
         final String remoteIpAddr = NettyUtils.getRemoteIpAddress(ctx.channel());
         log.info("--channelActive: {}, {} Sessions.", remoteIpAddr, sessions);
         TsiNodeVo ipNodeVo = this.nodeManager.getIpAddr(remoteIpAddr);
-        if (ipNodeVo != null) {
+        if (ipNodeVo == null) {
+            this.alarmManager.reportUnknownIp(remoteIpAddr);
+        }
+        else {
             TsiChannelSession.sessionActive(ipNodeVo, remoteIpAddr);
+//            if (ipNodeVo.isConnect()) {
+//                // 이미 연결된 상태라면 새 채널을 종료, 이전 채널이 통신을 수행중일수 있으므로 현재 채널을 종료시킴
+//                log.warn("--channelActive: {}, Duplicate connection detected, closing new channel: {}",
+//                        ipNodeVo.getKey(), remoteIpAddr);
+//                ctx.channel().close();
+//                return;
+//            }
         }
         ctx.fireChannelActive();
     }
@@ -84,19 +94,19 @@ public class CvimServerInboundMessageHandler extends ChannelInboundHandlerAdapte
                 packet.setObj(nodeVo);
                 this.packetProcess.add(packet, nodeVo.getPktQIdx());    // 패킷인덱스큐로 데이터 전송
             }
-            log.info("channelInactive: Node {}, Channel close: {}", nodeVo.getNodeId(), remoteIpAddr);
+            log.info("channelInactive: Node {}, {}, sessions: {}", nodeVo.getNodeId(), remoteIpAddr, sessions);
 
             TsiChannelSession.sessionInactive(nodeVo, remoteIpAddr);
 
-            nodeVo.setConnect(null);
             this.sessionManager.removeChannel(ctx.channel());
             ctx.channel().attr(TsiSessionManager.TSI_NODE_ATTRIBUTE_KEY).set(null);
 
             this.queueDistributorService.releaseQueue(nodeVo);    // 작업큐를 할당을 해제한다.
+            nodeVo.setConnect(null);
         }
-        else {
-            log.info("channelInactive: {}, {} Sessions.", remoteIpAddr, sessions);
-        }
+//        else {
+//            log.info("channelInactive: {}, {} Sessions.", remoteIpAddr, sessions);
+//        }
 
         ctx.fireChannelInactive();
     }
@@ -106,27 +116,36 @@ public class CvimServerInboundMessageHandler extends ChannelInboundHandlerAdapte
         if (e instanceof IdleStateEvent) {
             IdleStateEvent evt = (IdleStateEvent) e;
             TsiNodeVo nodeVo = this.sessionManager.getNodeVo(ctx.channel());
-//            if (nodeVo != null) {
-//                log.error("{}.userEventTriggered: {}, {}, {}", this.getClass().getSimpleName(), nodeVo.getNodeId(), ctx.channel(), evt.state().toString());
-//            }
-
             // 연결이 완료된 후 송수신 데이터가 일정시간 동안 없을 경우 이곳에서 처리
             if (evt.state() == IdleState.READER_IDLE) {
+                String remoteIpAddr = NettyUtils.getRemoteIpAddress(ctx.channel());
                 if (nodeVo == null) {
+                    log.warn("userEventTriggered: Recv Timeout: {}", remoteIpAddr);
                     // 통신 접속 후 수신 데이터가 없이 READ 타임아웃이 발생한 경우임
                     if (this.alarmManager.checkAlarm(TsiAlarmConfigVo.COMM_02)) {
                         AlarmOccrVo alarm = new AlarmOccrVo(AbstractDbmsVo.DBMS_ALARM_OCCR_HS);
                         alarm.setAlarmCode(TsiAlarmConfigVo.COMM_02);
-                        alarm.setAlarmTarget(NettyUtils.getRemoteIpAddress(ctx.channel()));
-                        alarm.setAlarmValue(NettyUtils.getRemoteIpAddress(ctx.channel()));
+                        alarm.setAlarmTarget(remoteIpAddr);
+                        alarm.setAlarmValue(remoteIpAddr);
                         this.dbmsProcess.add(alarm, (int) Thread.currentThread().getId());
-                        log.warn("{}.userEventTriggered: Recv Timeout: {}", this.getClass().getSimpleName(), NettyUtils.getRemoteIpAddress(ctx.channel()));
                     }
-                    ctx.channel().disconnect();
+//                    ctx.channel().disconnect();
                     ctx.channel().close();
                 }
                 else {
+                    log.warn("userEventTriggered: Recv Timeout: {}, {}", remoteIpAddr, nodeVo.getNodeId());
                     // 통신 접속 후 데이터를 한번이라도 수신한 경우에는 스케쥴러에서 처리한다.
+                    if (this.alarmManager.checkAlarm(TsiAlarmConfigVo.COMM_02)) {
+                        AlarmOccrVo alarm = new AlarmOccrVo(AbstractDbmsVo.DBMS_ALARM_OCCR_HS);
+                        alarm.setAlarmCode(TsiAlarmConfigVo.COMM_02);
+                        alarm.setAlarmTarget(nodeVo.getKey());
+                        alarm.setAlarmValue(remoteIpAddr);
+                        this.dbmsProcess.add(alarm, (int) Thread.currentThread().getId());
+                    }
+
+                    TsiChannelSession.sessionTimeout(nodeVo, NettyUtils.getRemoteIpAddress(ctx.channel()));
+
+                    ctx.channel().close();
                 }
             } else if (evt.state() == IdleState.WRITER_IDLE) {
                 log.error("{}.userEventTriggered: WRITER_IDLE: {}", this.getClass().getSimpleName(), NettyUtils.getAddress(ctx.channel()));

+ 0 - 30
tsi-comm-server/src/main/java/com/tsi/comm/server/tcp/handler/CvimServerRegisterHandler.java

@@ -1,30 +0,0 @@
-package com.tsi.comm.server.tcp.handler;
-
-import io.netty.channel.ChannelDuplexHandler;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.timeout.IdleState;
-import io.netty.handler.timeout.IdleStateEvent;
-import lombok.extern.slf4j.Slf4j;
-
-@Slf4j
-//@Component
-//@ChannelHandler.Sharable
-public class CvimServerRegisterHandler extends ChannelDuplexHandler {
-    // 사용자는 userEventTriggered 에서 이벤트의 상태를 읽어 필요한 동작을 지정해주면 된다.
-    public CvimServerRegisterHandler() {
-        super();
-        log.error("CvimServerRegisterHandler");
-    }
-
-    @Override
-    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
-        if (evt instanceof IdleStateEvent) {
-            IdleStateEvent e = (IdleStateEvent) evt;
-            log.error("CvimServerRegisterHandler: {}", e.toString());
-            if (e.state() == IdleState.READER_IDLE) {
-                //ctx.close();
-            }
-        }
-        super.userEventTriggered(ctx, evt);
-    }
-}

+ 14 - 2
tsi-comm-server/src/main/java/com/tsi/comm/server/tcp/initializer/CvimServerInitializer.java

@@ -1,5 +1,7 @@
 package com.tsi.comm.server.tcp.initializer;
 
+import com.tsi.comm.server.repository.TsiAlarmManager;
+import com.tsi.comm.server.vo.TsiAlarmConfigVo;
 import com.tsi.comm.server.xnet.NettyUtils;
 import com.tsi.comm.server.config.TsiCvimServerConfig;
 import com.tsi.comm.server.tcp.codec.CvimServerByteBufMessageDecoder;
@@ -20,12 +22,22 @@ public class CvimServerInitializer extends ChannelInitializer<Channel> {
     private final CvimServerByteBufMessageDecoder cvimServerByteBufMessageDecoder;
     private final CvimServerInboundMessageHandler cvimServerInboundMessageHandler;
     private final CvimServerEncoder cvimServerEncoder;
+    private int readerIdleTimeSeconds;
 
     public CvimServerInitializer(TsiCvimServerConfig config) {
         this.config = config;
         this.cvimServerByteBufMessageDecoder = SpringUtils.getBean(CvimServerByteBufMessageDecoder.class);
         this.cvimServerInboundMessageHandler = SpringUtils.getBean(CvimServerInboundMessageHandler.class);
         this.cvimServerEncoder = SpringUtils.getBean(CvimServerEncoder.class);
+        TsiAlarmManager alarmManager = SpringUtils.getBean(TsiAlarmManager.class);
+        this.readerIdleTimeSeconds = config.getReaderIdleTimeSeconds();
+        TsiAlarmConfigVo vo = alarmManager.get(TsiAlarmConfigVo.COMM_02);
+        if (vo != null) {
+            this.readerIdleTimeSeconds = vo.getValue();
+        }
+        if (this.readerIdleTimeSeconds < 5) {
+            this.readerIdleTimeSeconds = 5;
+        }
     }
 
     @Override
@@ -33,14 +45,14 @@ public class CvimServerInitializer extends ChannelInitializer<Channel> {
         final String remoteIpAddress = NettyUtils.getRemoteIpAddress(channel);
         if (this.config.isInWhitelistIps(remoteIpAddress)) {
             // FOR L4, whitelist ips
-            channel.disconnect();
+//            channel.disconnect();
             channel.close();
             return;
         }
 
         // 교차로 제어기는 통신 연결시 IP로 체크하지 않고 처음 데이터를 수신했을 경우에 노드 ID를 확인 할 수 있다.
         // 공단에서 교차로 제어기의 IP 주소를 관리하지 않는다.
-        IdleStateHandler idleStateHandler = new IdleStateHandler(this.config.getReaderIdleTimeSeconds(), 0, 0);
+        IdleStateHandler idleStateHandler = new IdleStateHandler(this.readerIdleTimeSeconds, 0, 0);
         ChannelPipeline pipeline = channel.pipeline();
         pipeline.addLast("idleStateHandler", idleStateHandler);
         pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(2048, 2, 2, -2, 0));

+ 5 - 5
tsi-comm-server/src/main/java/com/tsi/comm/server/xnet/NettyTcpServer.java

@@ -29,16 +29,16 @@ public abstract class NettyTcpServer {
 
         try {
             if (NettyUtils.isEpollAvailable()) {
-                log.info("서버가 리눅스 EPOLL 모드에서 실행됩니다.");
+                log.info("Server is running in Linux EPOLL mode.");
             }
             else {
-                log.info("서버가 윈도우 NIO 모드에서 실행됩니다.");
+                log.info("Server is running in Windows NIO mode.");
             }
 
             this.serverBootstrap = ServerBootstrapFactory.createBootstrap(this.config, this.channelInitializer);
 
         } catch (Exception e) {
-            e.printStackTrace();
+            log.error("NettyTcpServer createBootstrap exception: {}", e.getMessage());
         }
 
         log.info("  bindAddress: {}", this.config.getBindingAddr());
@@ -56,7 +56,7 @@ public abstract class NettyTcpServer {
             }
         }
         catch (Exception e) {
-            log.error("{}", e.toString());
+            log.error("NettyTcpServer bind exception: {}", e.getMessage());
             this.acceptGroups.shutdownGracefully();
             this.workerGroups.shutdownGracefully();
         }
@@ -69,7 +69,7 @@ public abstract class NettyTcpServer {
             this.channelFuture.channel().closeFuture().sync();
         }
         catch (InterruptedException e) {
-            log.error("{}", e.toString());
+            log.error("NettyTcpServer stop interrupt: {}", e.getMessage());
         }
     }
 }

+ 24 - 0
tsi-comm-server/src/main/java/com/tsi/comm/server/xnet/NettyUtils.java

@@ -24,6 +24,9 @@ public final class NettyUtils {
         String remoteIp = "remote-unknown";
         int localPort = 0;
         int remotePort = 0;
+        if (ch == null) {
+            return "[Local #(" + localIp + ":" + localPort + ") Remote #(" + remoteIp + ":" + remotePort + ")]";
+        }
         InetSocketAddress localAddr = (InetSocketAddress)ch.localAddress();
         if (localAddr != null) {
             localIp = localAddr.getAddress().getHostAddress();
@@ -39,12 +42,18 @@ public final class NettyUtils {
     }
 
     public static String getRemoteAddress(Channel ch) {
+        if (ch == null) {
+            return "[Remote #(255.255.255.255:0)]";
+        }
         String ip = getRemoteIpAddress(ch);
         int port = getRemotePort(ch);
         return "[Remote #(" + ip + ":" + port + ")]";
     }
 
     public static String getLocalAddress(Channel ch) {
+        if (ch == null) {
+            return "[Local #(255.255.255.255:0)]";
+        }
         String ip = getLocalIpAddress(ch);
         int port = getLocalPort(ch);
         return "[Local #(" + ip + ":" + port + ")]";
@@ -52,6 +61,9 @@ public final class NettyUtils {
 
     public static String getRemoteIpAddress(Channel ch) {
         String ip = "255.255.255.255";
+        if (ch == null) {
+            return ip;
+        }
         InetSocketAddress inetAddr = (InetSocketAddress)ch.remoteAddress();
         if (inetAddr != null) {
             ip = inetAddr.getAddress().getHostAddress();
@@ -62,6 +74,9 @@ public final class NettyUtils {
     public static long getRemoteIpAddressToLong(Channel ch) {
         String[] ipAddressInArray = getRemoteIpAddress(ch).split("\\.");
         long result = 0;
+        if (ch == null) {
+            return result;
+        }
         for (int i = 0; i < ipAddressInArray.length; i++) {
             int power = 3 - i;
             int ip = Integer.parseInt(ipAddressInArray[i]);
@@ -72,6 +87,9 @@ public final class NettyUtils {
 
     public static int getRemotePort(Channel ch) {
         int port = 0;
+        if (ch == null) {
+            return port;
+        }
         InetSocketAddress inetAddr = (InetSocketAddress)ch.remoteAddress();
         if (inetAddr != null) {
             port = inetAddr.getPort();
@@ -81,6 +99,9 @@ public final class NettyUtils {
 
     public static String getLocalIpAddress(Channel ch) {
         String ip = "127.0.0.1";
+        if (ch == null) {
+            return ip;
+        }
         InetSocketAddress inetAddr = (InetSocketAddress)ch.localAddress();
         if (inetAddr != null) {
             ip = inetAddr.getAddress().getHostAddress();
@@ -89,6 +110,9 @@ public final class NettyUtils {
     }
     public static int getLocalPort(Channel ch) {
         int port = 0;
+        if (ch == null) {
+            return port;
+        }
         InetSocketAddress inetAddr = (InetSocketAddress)ch.localAddress();
         if (inetAddr != null) {
             port = inetAddr.getPort();

+ 23 - 0
tsi-comm-server/src/main/resources/mybatis/mapper/TsiCommServerMapper.xml

@@ -80,6 +80,29 @@
         ]]>
     </update>
 
+    <update id="updateNodeStatus_MERGE" parameterType="com.tsi.comm.server.vo.mariadb.NodeStatusVo">
+    <![CDATA[
+        INSERT INTO tb_tsc_node_status (
+            nodeid,
+            status,
+            eventdt,
+            ipAddr,
+            serverid
+        ) VALUES (
+             #{stts.nodeId},
+             #{stts.status},
+             NOW(),
+             #{stts.ipAddr},
+             #{stts.serverId}
+         )
+        ON DUPLICATE KEY UPDATE
+             status   = VALUES(status),
+             eventdt  = VALUES(eventdt),
+             ipAddr   = VALUES(ipAddr),
+             serverid = VALUES(serverid);
+        ]]>
+    </update>
+
     <update id="updateNodeStatus" parameterType="com.tsi.comm.server.vo.mariadb.NodeStatusVo">
     <![CDATA[
         update tb_tsc_node_status

+ 4 - 4
tsi-common/src/main/java/com/tsi/common/utils/Counter.java

@@ -4,17 +4,16 @@ import java.util.concurrent.atomic.AtomicLong;
 
 public class Counter {
 
-    private AtomicLong counter;
+    private final AtomicLong counter = new AtomicLong(0);
 
     public Counter() {
-        this.counter = new AtomicLong(0);
     }
 
     public long reset() {
         return this.counter.getAndSet(0);
     }
     public long reset(long value) {
-        return this.counter.getAndSet(0);
+        return this.counter.getAndSet(value);
     }
 
     public long increment() {
@@ -33,6 +32,7 @@ public class Counter {
 
     @Override
     public String toString() {
-        return Converter.getSize(this.counter.doubleValue());
+        return String.valueOf(counter.get());
+        // return Converter.getSize(counter.doubleValue());
     }
 }