Ver Fonte

refactoring-2

shjung há 1 semana atrás
pai
commit
c658e19a67
23 ficheiros alterados com 512 adições e 93 exclusões
  1. 11 0
      conf/tsi-comm-server-dev.yml
  2. 138 0
      tsi-comm-server/src/main/java/com/tsi/comm/server/ApplicationLifecycle.java
  3. 13 25
      tsi-comm-server/src/main/java/com/tsi/comm/server/TsiCommServerApplication.java
  4. 8 3
      tsi-comm-server/src/main/java/com/tsi/comm/server/controller/TsiCommServerRestController.java
  5. 3 3
      tsi-comm-server/src/main/java/com/tsi/comm/server/kafka/KafkaProducerService.java
  6. 1 1
      tsi-comm-server/src/main/java/com/tsi/comm/server/kafka/TsiKafkaConsumerWorker.java
  7. 36 5
      tsi-comm-server/src/main/java/com/tsi/comm/server/process/dbms/TsiCvimDbmsProcess.java
  8. 7 7
      tsi-comm-server/src/main/java/com/tsi/comm/server/process/dbms/TsiCvimDbmsWorker.java
  9. 31 4
      tsi-comm-server/src/main/java/com/tsi/comm/server/process/logging/TsiCvimLoggingProcess.java
  10. 8 14
      tsi-comm-server/src/main/java/com/tsi/comm/server/process/logging/TsiCvimLoggingWorker.java
  11. 19 0
      tsi-comm-server/src/main/java/com/tsi/comm/server/process/packet/TsiChannelSession.java
  12. 97 4
      tsi-comm-server/src/main/java/com/tsi/comm/server/process/packet/TsiCvimPacketProcess.java
  13. 20 11
      tsi-comm-server/src/main/java/com/tsi/comm/server/process/packet/TsiCvimPacketWorker.java
  14. 1 0
      tsi-comm-server/src/main/java/com/tsi/comm/server/protocol/AbstractTsiPacket.java
  15. 2 0
      tsi-comm-server/src/main/java/com/tsi/comm/server/protocol/TsiCpuPacket.java
  16. 1 1
      tsi-comm-server/src/main/java/com/tsi/comm/server/repository/TsiReportManager.java
  17. 4 5
      tsi-comm-server/src/main/java/com/tsi/comm/server/repository/TsiTpmsManager.java
  18. 10 7
      tsi-comm-server/src/main/java/com/tsi/comm/server/tcp/TsiCvimServer.java
  19. 7 0
      tsi-comm-server/src/main/java/com/tsi/comm/server/tcp/handler/CvimServerPacketProcessHandler.java
  20. 3 3
      tsi-comm-server/src/main/java/com/tsi/comm/server/tcp/service/ConnectionLifecycleService.java
  21. 74 0
      tsi-comm-server/src/main/java/com/tsi/comm/server/vo/TsiNodeVo.java
  22. 1 0
      tsi-comm-server/src/main/java/com/tsi/comm/server/vo/mariadb/AbstractDbmsVo.java
  23. 17 0
      tsi-comm-server/src/main/java/com/tsi/comm/server/vo/mariadb/ProcessExitVo.java

+ 11 - 0
conf/tsi-comm-server-dev.yml

@@ -0,0 +1,11 @@
+application:
+  process-id: tsi-comm-server-1
+  cvim-server:
+    server-id: 0
+    check-packet: false
+    white-list-ips:
+      -
+
+logging:
+  file:
+    path: ${user.dir}/logs/tsi-comm-server/

+ 138 - 0
tsi-comm-server/src/main/java/com/tsi/comm/server/ApplicationLifecycle.java

@@ -0,0 +1,138 @@
+package com.tsi.comm.server;
+
+import com.tsi.comm.server.config.ApplicationConfig;
+import com.tsi.comm.server.config.TsiCvimServerConfig;
+import com.tsi.comm.server.kafka.KafkaConsumerService;
+import com.tsi.comm.server.kafka.KafkaProducerService;
+import com.tsi.comm.server.process.dbms.TsiCvimDbmsProcess;
+import com.tsi.comm.server.process.logging.TsiCvimLoggingProcess;
+import com.tsi.comm.server.process.packet.TsiCvimPacketProcess;
+import com.tsi.comm.server.repository.ApplicationRepository;
+import com.tsi.comm.server.service.TsiCommServerService;
+import com.tsi.comm.server.tcp.TsiCvimServer;
+import com.tsi.comm.server.vo.TsiAlarmConfigVo;
+import com.tsi.comm.server.vo.mariadb.AbstractDbmsVo;
+import com.tsi.comm.server.vo.mariadb.AlarmOccrVo;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+@Slf4j
+@RequiredArgsConstructor
+//@Component
+public class ApplicationLifecycle {
+    private final ApplicationConfig applicationConfig;
+    private final TsiCvimServerConfig serverConfig;
+    private final TsiCommServerService tsiCommServerService;
+    private final KafkaConsumerService kafkaConsumerService;
+    private final KafkaProducerService kafkaProducerService;
+    private final TsiCvimPacketProcess packetProcess;
+    private final TsiCvimLoggingProcess loggingProcess;
+    private final TsiCvimDbmsProcess dbmsProcess;
+    private final TsiCvimServer cvimServer;
+
+    private String applicationName;
+    private int serverId = 0;
+
+    @PostConstruct
+    public void start() {
+        SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        log.info("");
+        log.info("************************************************************************************");
+        log.info("**                                 TSI CVIM System                                **");
+        log.info("**                      CVIM Communication Server Program.                        **");
+        log.info("**                                                                   [ver.1.0]    **");
+        log.info("** startup: {}", sdfDate.format(new Date()));
+        log.info("************************************************************************************");
+
+        this.applicationConfig.setStartSchedule(false);
+        serverId = this.serverConfig.getServerId();
+
+        this.applicationName = TsiCommServerApplication.APPLICATION_NAME + "-" + this.serverConfig.getServerId();
+        ApplicationRepository.processStateVo.setProcessId(applicationName);
+
+        this.tsiCommServerService.init();
+        this.kafkaConsumerService.start();
+        this.kafkaProducerService.start();
+        this.packetProcess.start();
+        this.loggingProcess.start();
+        this.dbmsProcess.start();
+
+        // Netty 서버 시작은 백그라운드 스레드에서 실행되므로, 이 메서드를 블로킹하지 않습니다.
+        this.cvimServer.run();
+
+        this.serverConfig.setStartup(true);
+        try {
+            // 시스템 시작 알람 이력 저장
+            AlarmOccrVo alarm = new AlarmOccrVo(AbstractDbmsVo.DBMS_ALARM_OCCR_HS);
+            alarm.setAlarmCode(TsiAlarmConfigVo.SYS_00);
+            alarm.setAlarmTarget(applicationName);
+            alarm.setAlarmValue("Running");
+            this.tsiCommServerService.insertAlarmOccrHs(alarm);
+            this.tsiCommServerService.updateProcessState(0);
+        } catch(Exception e) {
+            log.error("Error during startup alarm logging", e);
+        }
+
+        this.applicationConfig.setStartSchedule(true);
+        log.info("Application has been started successfully.");
+    }
+
+    /**
+     * Spring 컨텍스트가 종료될 때, 빈이 소멸되기 직전에 호출됩니다.
+     */
+    @PreDestroy
+    public void stop() {
+        SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        log.error("************************************************************************************");
+        log.error("**    Application Terminating: {}", sdfDate.format(new Date()));
+
+        // 종료는 생성의 역순이 안전합니다.
+
+        // 1. 새로운 연결을 막습니다.
+        if (this.cvimServer != null) {
+            this.cvimServer.stop();
+            log.info("Netty Server stopped.");
+        }
+        if (this.kafkaConsumerService != null) {
+            this.kafkaConsumerService.shutdown();
+            log.info("Kafka Consumer stopped.");
+        }
+
+        // 2. 내부 처리 로직(워커 스레드 풀)을 종료합니다.
+        if (this.packetProcess != null) {
+            this.packetProcess.stop();
+            log.info("Packet Process stopped.");
+        }
+        if (this.loggingProcess != null) {
+            this.loggingProcess.stop();
+            log.info("Logging Process stopped.");
+        }
+        if (this.dbmsProcess != null) {
+            this.dbmsProcess.stop();
+            log.info("DBMS Process stopped.");
+        }
+
+        // 3. 외부로 나가는 통신(Kafka Producer)을 종료합니다.
+        if (this.kafkaProducerService != null) {
+            this.kafkaProducerService.shutdown();
+            log.info("Kafka Producer stopped.");
+        }
+
+        // 4. DB에 최종 상태를 기록합니다.
+        if (this.tsiCommServerService != null) {
+            this.serverConfig.setStartup(false);
+            int serverId = this.serverConfig.getServerId();
+            this.tsiCommServerService.updateNodeStatusTerm(serverId);
+            // ... 시스템 종료 알람 저장 로직 ...
+            this.tsiCommServerService.updateProcessState(2);
+            log.info("Final DB states updated.");
+        }
+
+        log.error("************************************************************************************");
+    }
+}

+ 13 - 25
tsi-comm-server/src/main/java/com/tsi/comm/server/TsiCommServerApplication.java

@@ -40,6 +40,7 @@ public class TsiCommServerApplication implements CommandLineRunner, ApplicationL
     public static final String APPLICATION_NAME = "tsi-comm-server";
 
     private String applicationName = APPLICATION_NAME;
+    private int serverId = 0;
 
     public static void main(String[] args) {
         SpringApplication application = new SpringApplicationBuilder()
@@ -65,15 +66,14 @@ public class TsiCommServerApplication implements CommandLineRunner, ApplicationL
         log.info("** startup: {}", sdfDate.format(new Date()));
         log.info("************************************************************************************");
 
-        ApplicationConfig applicationConfig = SpringUtils.getBean(ApplicationConfig.class);
-        applicationConfig.setStartSchedule(false);
+        ApplicationConfig config = SpringUtils.getBean(ApplicationConfig.class);
+        config.setStartSchedule(false);
 
-        TsiCvimServerConfig config = SpringUtils.getBean(TsiCvimServerConfig.class);
+        TsiCvimServerConfig serverConfig = SpringUtils.getBean(TsiCvimServerConfig.class);
+        serverId = serverConfig.getServerId();
 
-        applicationName = APPLICATION_NAME + "-" + config.getServerId();
+        applicationName = APPLICATION_NAME + "-" + serverId;
         ApplicationRepository.processStateVo.setProcessId(applicationName);
-//        TsiAlarmManager alarmManager = SpringUtils.getBean(TsiAlarmManager.class);
-//        alarmManager.getProcessStateVo().setProcessId(APPLICATION_NAME + "-" + config.getServerId());
 
         TsiCommServerService tsiCommServerService = SpringUtils.getBean(TsiCommServerService.class);
         tsiCommServerService.init();
@@ -93,13 +93,10 @@ public class TsiCommServerApplication implements CommandLineRunner, ApplicationL
         TsiCvimDbmsProcess dbmsProcess = SpringUtils.getBean(TsiCvimDbmsProcess.class);
         dbmsProcess.start();
 
-//        TsiSessionManager sessionManager = SpringUtils.getBean(TsiSessionManager.class);
-//        sessionManager.start();
-
         TsiCvimServer cvimServer = SpringUtils.getBean(TsiCvimServer.class);
         cvimServer.run();
 
-        config.setStartup(true);
+        serverConfig.setStartup(true);
         try {
             // 시스템 시작 알람 이력 저장
             AlarmOccrVo alarm = new AlarmOccrVo(AbstractDbmsVo.DBMS_ALARM_OCCR_HS);
@@ -113,11 +110,11 @@ public class TsiCommServerApplication implements CommandLineRunner, ApplicationL
             // no logging
         }
 
-        applicationConfig.setStartSchedule(true);
+        config.setStartSchedule(true);
 
         Runtime.getRuntime().addShutdownHook(new Thread(() -> {
             log.error("on shutdown hook.");
-            applicationConfig.setStartSchedule(false);
+            //config.setStartSchedule(false);
             terminate();
         }));
     }
@@ -127,24 +124,15 @@ public class TsiCommServerApplication implements CommandLineRunner, ApplicationL
 
         log.error("************************************************************************************");
         log.error("**    Application Terminated: {}", sdfDate.format(new Date()));
-        int serverId = 0;
         try {
-            TsiCvimServerConfig config = SpringUtils.getBean(TsiCvimServerConfig.class);
-            config.setStartup(false);
-            serverId = config.getServerId();
+            TsiCvimServerConfig serverConfig = SpringUtils.getBean(TsiCvimServerConfig.class);
+            if (serverConfig != null) {
+                serverConfig.setStartup(false);
+            }
         } catch (Exception e) {
             log.error("**    Application.terminate:config: {}", e.getMessage());
         }
 
-//        try {
-//            TsiSessionManager sessionManager = SpringUtils.getBean(TsiSessionManager.class);
-//            if (sessionManager != null) {
-//                sessionManager.stop();
-//            }
-//        } catch (Exception e) {
-//            log.error("**    Application.terminate:sessionManager: {}", e.getMessage());
-//        }
-
         try {
             TsiCvimServer cvimServer = SpringUtils.getBean(TsiCvimServer.class);
             if (cvimServer != null) {

+ 8 - 3
tsi-comm-server/src/main/java/com/tsi/comm/server/controller/TsiCommServerRestController.java

@@ -43,7 +43,7 @@ public class TsiCommServerRestController {
     private final TsiCvimDbmsProcess dbmsProcess;
 
     private final String sep = System.lineSeparator();
-    private final String heading = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------";
+    private final String heading = "---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------";
 
     private StringBuilder getCommonHead() {
         StringBuilder sb = new StringBuilder();
@@ -71,7 +71,8 @@ public class TsiCommServerRestController {
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         sdf.setTimeZone(java.util.TimeZone.getTimeZone("GMT+9"));
 
-        sb.append(String.format("   SEQ[U] %10s %4s %7s %19s %19s %9s %6s %19s  IP Address      Remote-Address   Queue(P/L/D)", "Node ID", "INTC", "Connect", "Connect Time", "Disconnect Time", "Connected", "Closed", "Last-Recv-Time")).append(sep);
+        sb.append(String.format("   SEQ[U] %10s %4s %7s %19s %19s %9s %6s %19s  IP Address      Remote-Address   Signal AvgPrcTm(us)  Dropped/Overlapping Queue(P/L/D)",
+                "Node ID", "INTC", "Connect", "Connect Time", "Disconnect Time", "Connected", "Closed", "Last-Recv-Time")).append(sep);
         sb.append(heading).append(sep);
 
         int ii = 1;
@@ -90,6 +91,7 @@ public class TsiCommServerRestController {
                 unknownNode = "***";
             }
 
+            long avgTime = node.getSigCount() == 0 ? 0 : node.getAverageProcessingTimeMicros();
             String connect;
             String info;
             if (node.isConnect()) {
@@ -100,15 +102,18 @@ public class TsiCommServerRestController {
             else {
                 connect = "N";
                 info = "---.---.---.---";
+                avgTime = 0;
             }
 
             String connectTm = sdf.format(new Date(node.getConnectTm()));
             String disconnectTm = sdf.format(new Date(node.getDisconnectTm()));
             String lastCommTm = sdf.format(new Date(node.getLastCommTm()));
 
-            sb.append(String.format(" %5d%3s %10s %4s %7s %19s %19s %9d %6d %19s  %-15.15s %-15.15s  %d/%d/%d",
+            sb.append(String.format(" %5d%3s %10s %4s %7s %19s %19s %9d %6d %19s  %-15.15s %-15.15s  %6d %12d  %-19s %d/%d/%d",
                     ii++, unknownNode, node.getKey(), check, connect, connectTm, disconnectTm, node.getConnectCount().get(),
                     node.getDisconnectCount().get(), lastCommTm, node.getIpAddr(), info,
+                    node.getSigCount(), avgTime,
+                    String.format("%d/%d", node.getDroppedPacketCount(), node.getOverlappingPacketCount()),
                     node.getPktQIdx(), node.getLogQIdx(), node.getDbmsQIdx())).append(sep);
         }
         sb.append(heading).append(sep);

+ 3 - 3
tsi-comm-server/src/main/java/com/tsi/comm/server/kafka/KafkaProducerService.java

@@ -149,7 +149,7 @@ public class KafkaProducerService {
                         alarm.setAlarmCode(TsiAlarmConfigVo.KAFKA_02);
                         alarm.setAlarmTarget(producerConfig.getBootstrapServers());
                         alarm.setAlarmValue(Long.toString(sendTime));
-                        dbmsProcess.add(alarm, (int) Thread.currentThread().getId());
+                        dbmsProcess.add(alarm, 0);
                     }
                 }
             }
@@ -162,7 +162,7 @@ public class KafkaProducerService {
                 stat.setStatus(0);
                 stat.setSendTm(TimeUnit.MICROSECONDS.convert(Math.abs(recvNanoTime - sendNanoTime), TimeUnit.NANOSECONDS));
                 stat.setRecvTm(0);
-                dbmsProcess.add(stat, (int)Thread.currentThread().getId());
+                dbmsProcess.add(stat, 0);
                 log.error("send ping failed: {}, {}, {}", sendNanoTime, TimeUtils.elapsedTimeStr(recvNanoTime - sendNanoTime), ex.getMessage());
 
                 // 카프카 전송 오류 알람 저장
@@ -174,7 +174,7 @@ public class KafkaProducerService {
                 alarm.setAlarmCode(TsiAlarmConfigVo.KAFKA_01);
                 alarm.setAlarmTarget(producerConfig.getBootstrapServers());
                 alarm.setAlarmValue(value);
-                dbmsProcess.add(alarm, (int)Thread.currentThread().getId());
+                dbmsProcess.add(alarm, 0);
             }
         });
     }

+ 1 - 1
tsi-comm-server/src/main/java/com/tsi/comm/server/kafka/TsiKafkaConsumerWorker.java

@@ -38,7 +38,7 @@ public class TsiKafkaConsumerWorker implements MessageListener<String, Long> {
             stat.setSendTm(stat.getRecvTm());
             log.info("recv ping success, sendNanoTime miss match: {}, {}", sendNanoTime, TimeUtils.elapsedTimeStr(recvNanoTime - sendNanoTime));
         }
-        dbmsProcess.add(stat, (int)Thread.currentThread().getId());
+        dbmsProcess.add(stat, 0);
         log.info("recv ping success: {}, {}", sendNanoTime, TimeUtils.elapsedTimeStr(recvNanoTime - sendNanoTime));
     }
 

+ 36 - 5
tsi-comm-server/src/main/java/com/tsi/comm/server/process/dbms/TsiCvimDbmsProcess.java

@@ -3,6 +3,7 @@ package com.tsi.comm.server.process.dbms;
 import com.tsi.comm.server.config.TsiCvimServerConfig;
 import com.tsi.comm.server.process.AbstractTsiCvimProcess;
 import com.tsi.comm.server.process.AbstractTsiCvimWorker;
+import com.tsi.comm.server.process.packet.TsiCvimPacketWorker;
 import com.tsi.comm.server.repository.TsiNodeManager;
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
@@ -24,10 +25,11 @@ public class TsiCvimDbmsProcess extends AbstractTsiCvimProcess {
     private final TsiNodeManager nodeManager;
     private ExecutorService executor = null;
     private int qSize;
+
     public void start() {
         final ThreadGroup workerGroup = new ThreadGroup("dbmsProcess");
         this.workers = this.config.getDbmsWorkers();
-        this.qSize = this.nodeManager.size();
+        this.qSize = Math.max(10, this.nodeManager.size());
         if (this.workers == 1) {
             this.qSize *= 3;
         }
@@ -52,9 +54,12 @@ public class TsiCvimDbmsProcess extends AbstractTsiCvimProcess {
     public boolean add(Object object, int idx) {
         boolean offer = false;
         try {
-            //idx = idx % this.workers;
-            idx = Math.abs(idx % this.workers);
-            offer = this.workerList.get(idx).add(object);
+            if (idx >= 0 && idx < this.workers) {
+                offer = this.workerList.get(idx).add(object);
+            }
+            else {
+                log.error("DbmsProcess: Invalid packet queue index: {}", idx);
+            }
         }
         catch (Exception e) {
             log.error("DbmsProcess: QUEUE_DATA.add: Exception: {}, {}", object.toString(), e.getMessage());
@@ -64,15 +69,41 @@ public class TsiCvimDbmsProcess extends AbstractTsiCvimProcess {
 
     public void stop() {
         log.info("DbmsProcess Stopping...");
+
+        for (AbstractTsiCvimWorker worker : this.workerList) {
+            try {
+                ((TsiCvimDbmsWorker) worker).getDATA_QUEUE().put(TsiCvimDbmsWorker.SHUTDOWN_PACKET);
+            } catch (InterruptedException e) {
+                log.warn("DbmsProcess: Interrupted while sending shutdown signal to worker {}.", worker.getIdx());
+                Thread.currentThread().interrupt();
+            }
+        }
+
         this.executor.shutdown();
+
         try {
             if (!this.executor.awaitTermination(3, TimeUnit.SECONDS)) {
-                this.executor.shutdownNow(); // 실행 중인 작업도 중단 시도
+                this.executor.shutdownNow(); // 실행 중인 작업도 강제 중단 시도
+
+                // 큐에 남아있는 데이터 로깅
+                int remainingTasks = 0;
+                for (AbstractTsiCvimWorker worker : this.workerList) {
+                    remainingTasks += ((TsiCvimPacketWorker) worker).getDATA_QUEUE().size();
+                }
+                if (remainingTasks > 0) {
+                    log.warn("DbmsProcess: queue remain {}, forced shutdown.", remainingTasks);
+                }
+
+                // 강제 종료 후에도 종료될 때까지 조금 더 기다려줌
+                if (!this.executor.awaitTermination(1, TimeUnit.SECONDS)) {
+                    log.error("DbmsProcess: Executor did not terminate even after forced shutdown.");
+                }
             }
         } catch (InterruptedException e) {
             this.executor.shutdownNow();
             Thread.currentThread().interrupt(); // 현재 스레드도 중단
         }
+
         log.info("DbmsProcess Stopped...");
     }
 

+ 7 - 7
tsi-comm-server/src/main/java/com/tsi/comm/server/process/dbms/TsiCvimDbmsWorker.java

@@ -10,13 +10,13 @@ import lombok.Getter;
 import lombok.extern.slf4j.Slf4j;
 
 import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
 
 @Slf4j
 @Getter
 public class TsiCvimDbmsWorker extends AbstractTsiCvimWorker implements Runnable {
 
-    //private ConcurrentLinkedQueue<AbstractTsiPacket> DATA_QUEUE;
+    public static final ProcessExitVo SHUTDOWN_PACKET = new ProcessExitVo(AbstractDbmsVo.DBMS_NONE);
+
     private final LinkedBlockingQueue<Object> DATA_QUEUE;
     private final TsiCommServerMapper tsiCommServerMapper;
 
@@ -33,12 +33,12 @@ public class TsiCvimDbmsWorker extends AbstractTsiCvimWorker implements Runnable
         try {
             while (!Thread.currentThread().isInterrupted()) {
                 try {
-                    Object object = this.DATA_QUEUE.poll(1, TimeUnit.SECONDS); // 인터럽트 감지 가능
-                    if (object != null) {
-                        process(object);
-                    } else {
-                        Thread.yield(); // 대기 중 CPU 양보
+                    Object object = this.DATA_QUEUE.take();
+                    if (object == SHUTDOWN_PACKET) {
+                        log.info("DbmsWorker({}): Shutdown signal received. Exiting...", this.idx);
+                        break;
                     }
+                    process(object);
                 } catch (InterruptedException ie) {
                     log.warn("DbmsWorker({}): {} Interrupted. Exiting...", this.idx, Thread.currentThread().getName());
                     Thread.currentThread().interrupt(); // 상태 복구

+ 31 - 4
tsi-comm-server/src/main/java/com/tsi/comm/server/process/logging/TsiCvimLoggingProcess.java

@@ -3,6 +3,7 @@ package com.tsi.comm.server.process.logging;
 import com.tsi.comm.server.config.TsiCvimServerConfig;
 import com.tsi.comm.server.process.AbstractTsiCvimProcess;
 import com.tsi.comm.server.process.AbstractTsiCvimWorker;
+import com.tsi.comm.server.process.packet.TsiCvimPacketWorker;
 import com.tsi.comm.server.protocol.AbstractTsiPacket;
 import com.tsi.comm.server.repository.TsiNodeManager;
 import com.tsi.common.spring.SpringUtils;
@@ -33,10 +34,12 @@ public class TsiCvimLoggingProcess extends AbstractTsiCvimProcess {
         TsiCvimServerConfig tsiCvimServerConfig = SpringUtils.getBean(TsiCvimServerConfig.class);
 
         this.workers = tsiCvimServerConfig.getLoggingWorkers();
-        this.qSize = this.nodeManager.size();
+        this.qSize = Math.max(10, this.nodeManager.size());
         if (this.workers == 1) {
             this.qSize *= 2;
         }
+//        int nodeSize = Math.max(1, this.nodeManager.size());
+//        int perQ = nodeSize / this.workers;
 
         this.executor = Executors.newFixedThreadPool(this.workers, new ThreadFactory() {
             private int count = 0;
@@ -71,13 +74,11 @@ public class TsiCvimLoggingProcess extends AbstractTsiCvimProcess {
         boolean offer = false;
         AbstractTsiPacket packet = (AbstractTsiPacket)object;
         try {
-            // TODO: 20251117: 통신연결시 할당된 로깅큐 인덱스로 바로 전송
-//            idx = Math.abs(idx % this.workers);
             if (idx >= 0 && idx < this.workers) {
                 offer = this.workerList.get(idx).add(packet);
             }
             else {
-                log.error("Invalid logging queue index: {}", idx);
+                log.error("LoggingProcess: Invalid logging queue index: {}", idx);
             }
         }
         catch (Exception e) {
@@ -90,15 +91,41 @@ public class TsiCvimLoggingProcess extends AbstractTsiCvimProcess {
 
     public void stop() {
         log.info("LoggingProcess Stopping...");
+
+        for (AbstractTsiCvimWorker worker : this.workerList) {
+            try {
+                ((TsiCvimLoggingWorker) worker).getDATA_QUEUE().put(TsiCvimLoggingWorker.SHUTDOWN_PACKET);
+            } catch (InterruptedException e) {
+                log.warn("LoggingProcess: Interrupted while sending shutdown signal to worker {}.", worker.getIdx());
+                Thread.currentThread().interrupt();
+            }
+        }
+
         this.executor.shutdown();
+
         try {
             if (!this.executor.awaitTermination(3, TimeUnit.SECONDS)) {
                 this.executor.shutdownNow(); // 실행 중인 작업도 중단 시도
+
+                // 큐에 남아있는 데이터 로깅
+                int remainingTasks = 0;
+                for (AbstractTsiCvimWorker worker : this.workerList) {
+                    remainingTasks += ((TsiCvimPacketWorker) worker).getDATA_QUEUE().size();
+                }
+                if (remainingTasks > 0) {
+                    log.warn("LoggingProcess: queue remain {}, forced shutdown.", remainingTasks);
+                }
+
+                // 강제 종료 후에도 종료될 때까지 조금 더 기다려줌
+                if (!this.executor.awaitTermination(1, TimeUnit.SECONDS)) {
+                    log.error("LoggingProcess: Executor did not terminate even after forced shutdown.");
+                }
             }
         } catch (InterruptedException e) {
             this.executor.shutdownNow();
             Thread.currentThread().interrupt(); // 현재 스레드도 중단
         }
+
         log.info("LoggingProcess Stopped...");
     }
 

+ 8 - 14
tsi-comm-server/src/main/java/com/tsi/comm/server/process/logging/TsiCvimLoggingWorker.java

@@ -2,6 +2,7 @@ package com.tsi.comm.server.process.logging;
 
 import com.tsi.comm.server.process.AbstractTsiCvimWorker;
 import com.tsi.comm.server.protocol.AbstractTsiPacket;
+import com.tsi.comm.server.protocol.TsiCpuPacket;
 import com.tsi.common.utils.TimeUtils;
 import lombok.Getter;
 import lombok.extern.slf4j.Slf4j;
@@ -10,27 +11,20 @@ import org.slf4j.MDC;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
 
 @Slf4j
 @Getter
 public class TsiCvimLoggingWorker extends AbstractTsiCvimWorker implements Runnable {
 
+    public static final AbstractTsiPacket SHUTDOWN_PACKET = new TsiCpuPacket(-1L, 0, 0, null);
+
     private final LinkedBlockingQueue<AbstractTsiPacket> DATA_QUEUE;
-//    private TsiNodeStatusRepository tsiNodeStatusRepository;
     private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
     public TsiCvimLoggingWorker(int idx, int qSize) {
         this.idx = idx;
         this.qSize = qSize;
         this.DATA_QUEUE = new LinkedBlockingQueue<>(qSize);
-//        try {
-//            this.tsiNodeStatusRepository = SpringUtils.getBean(TsiNodeStatusRepository.class);
-//        }
-//        catch(Exception e) {
-//            this.tsiNodeStatusRepository = null;
-//            log.error("MongoDB: {}", e.getMessage());
-//        }
         this.sdf.setTimeZone(java.util.TimeZone.getTimeZone("GMT+9"));
     }
 
@@ -41,12 +35,12 @@ public class TsiCvimLoggingWorker extends AbstractTsiCvimWorker implements Runna
         try {
             while (!Thread.currentThread().isInterrupted()) {
                 try {
-                    AbstractTsiPacket packet = this.DATA_QUEUE.poll(1, TimeUnit.SECONDS); // 인터럽트 감지 가능
-                    if (packet != null) {
-                        process(packet);
-                    } else {
-                        Thread.yield(); // 대기 중 CPU 양보
+                    AbstractTsiPacket packet = this.DATA_QUEUE.take();
+                    if (packet == SHUTDOWN_PACKET) {
+                        log.info("LoggingWorker({}): Shutdown signal received. Exiting...", this.idx);
+                        break;
                     }
+                    process(packet);
                 } catch (InterruptedException ie) {
                     log.warn("LoggingWorker({}): {} Interrupted. Exiting...", this.idx, Thread.currentThread().getName());
                     Thread.currentThread().interrupt(); // 상태 복구

+ 19 - 0
tsi-comm-server/src/main/java/com/tsi/comm/server/process/packet/TsiChannelSession.java

@@ -3,6 +3,8 @@ package com.tsi.comm.server.process.packet;
 import com.tsi.comm.server.protocol.TsiCpuPacket;
 import com.tsi.comm.server.vo.TsiNodeVo;
 import com.tsi.common.utils.SysUtils;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.slf4j.MDC;
 
@@ -89,4 +91,21 @@ public class TsiChannelSession {
             // Log the exception with a meaningful message
         }
     }
+
+    public static void recvPacketDump(String logKey, ByteBuf byteBuf) {
+        try {
+            MDC.put("id", logKey);
+            if (byteBuf == null || !byteBuf.isReadable()) {
+                log.info("RECV: {}, null or empty buffer.", logKey);
+            }
+            else {
+                log.info("RECV: {}, {} bytes.\n{}", logKey, byteBuf.readableBytes(), ByteBufUtil.prettyHexDump(byteBuf));
+            }
+            MDC.clear();
+        }
+        catch (Exception e) {
+            // Log the exception with a meaningful message
+        }
+    }
+
 }

+ 97 - 4
tsi-comm-server/src/main/java/com/tsi/comm/server/process/packet/TsiCvimPacketProcess.java

@@ -5,7 +5,9 @@ import com.tsi.comm.server.kafka.KafkaProducerService;
 import com.tsi.comm.server.process.AbstractTsiCvimProcess;
 import com.tsi.comm.server.process.AbstractTsiCvimWorker;
 import com.tsi.comm.server.protocol.AbstractTsiPacket;
+import com.tsi.comm.server.protocol.TsiCpuPacket;
 import com.tsi.comm.server.repository.TsiNodeManager;
+import com.tsi.comm.server.vo.TsiNodeVo;
 import com.tsi.common.spring.SpringUtils;
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
@@ -35,7 +37,7 @@ public class TsiCvimPacketProcess extends AbstractTsiCvimProcess {
         TsiCvimServerConfig tsiCvimServerConfig = SpringUtils.getBean(TsiCvimServerConfig.class);
 
         this.workers = tsiCvimServerConfig.getPacketWorkers();
-        this.qSize = this.nodeManager.size();
+        this.qSize = Math.max(10, this.nodeManager.size());
         if (this.workers == 1) {
             this.qSize *= 3;
         }
@@ -55,19 +57,84 @@ public class TsiCvimPacketProcess extends AbstractTsiCvimProcess {
             this.workerList.add(packetWorker);
             this.executor.submit(packetWorker); // 여기서 실행됨!
         }
+
+//        warmUpWorkers();
+    }
+    /**
+     * 모든 워커 스레드의 JIT 컴파일을 유도하기 위해 웜업을 수행합니다.
+     */
+    private void warmUpWorkers() {
+        int warmUpRounds = 10000; // JIT 컴파일을 유도하기에 충분한 반복 횟수 (튜닝 필요)
+        log.info("Starting JIT compiler warm-up for {} packet workers with {} rounds each...", this.workers, warmUpRounds);
+
+        // 가짜 TsiNodeVo 객체 생성 (실제 DB나 Manager를 통하지 않음)
+        TsiNodeVo dummyNodeVo = new TsiNodeVo(1L, "127.0.0.1", false, false, false);
+
+        long startTime = System.currentTimeMillis();
+
+        // 모든 워커에게 웜업 작업을 분배
+        for (int i = 0; i < this.workers; i++) {
+            final int workerIndex = i;
+            // 각 워커를 위한 웜업 작업을 별도의 스레드에서 실행하여 병렬로 웜업
+            new Thread(() -> {
+                for (int j = 0; j < warmUpRounds; j++) {
+                    // 웜업용 가짜 패킷 생성
+                    TsiCpuPacket warmUpPacket = createWarmUpPacket(dummyNodeVo);
+                    // 해당 워커의 큐에 직접 추가
+                    this.workerList.get(workerIndex).add(warmUpPacket);
+                }
+            }).start();
+        }
+
+        // 간단한 웜업에서는 별도의 완료 대기 없이, 백그라운드에서 웜업이 진행되도록 둡니다.
+        // 실제 트래픽이 들어오기 시작할 때쯤이면 대부분의 웜업이 완료될 것입니다.
+        log.info("Warm-up tasks submitted. Main process continues...");
+    }
+
+    /**
+     * 웜업에 사용할 가짜 TsiCpuPacket을 생성.
+     * @param dummyNodeVo 가짜 TsiNodeVo 객체
+     * @return 웜업용 패킷
+     */
+    private TsiCpuPacket createWarmUpPacket(TsiNodeVo dummyNodeVo) {
+        // 실제 패킷과 유사한 데이터를 사용하여 생성
+        long nodeId = dummyNodeVo.getNodeId();
+        long msec = System.currentTimeMillis();
+        long nsec = System.nanoTime();
+
+        int packetSize = 100;
+        byte[] dummyBytes = new byte[packetSize];
+//        ByteBuf dummyBuf = ByteBufAllocator.DEFAULT.buffer(packetSize);
+//        try {
+//            dummyBuf.writeShort(0x7E7E);
+//            dummyBuf.writeShort(packetSize);
+//            dummyBuf.writeByte(1);
+//            dummyBuf.writeByte(0);
+//            dummyBuf.writeInt((int)nodeId);
+//            // 나머지 공간을 0x00으로 채움.
+//            dummyBuf.writeBytes(new byte[packetSize - dummyBuf.writerIndex()]);
+//        } catch (Exception e) {
+//            dummyBuf.release();
+//            throw e;
+//        }
+
+        TsiCpuPacket packet = new TsiCpuPacket(nodeId, msec, nsec, null); // channel은 null
+        packet.setBuf(dummyBytes);
+        packet.setObj(dummyNodeVo);
+
+        //dummyBuf.release();
+        return packet;
     }
 
     public boolean add(Object object, int idx) {
         boolean offer = false;
         AbstractTsiPacket packet = (AbstractTsiPacket)object;
         try {
-            // TODO: 20251117: 통신연결시 할당된 패킷큐 인덱스로 바로 전송
-//            idx = Math.abs(idx % this.workers);
             if (idx >= 0 && idx < this.workers) {
                 offer = this.workerList.get(idx).add(packet);
             }
             else {
-                log.error("Invalid packet queue index: {}", idx);
+                log.error("PacketProcess: Invalid packet queue index: {}", idx);
             }
         }
         catch (Exception e) {
@@ -80,15 +147,41 @@ public class TsiCvimPacketProcess extends AbstractTsiCvimProcess {
 
     public void stop() {
         log.info("PacketProcess Stopping...");
+
+        for (AbstractTsiCvimWorker worker : this.workerList) {
+            try {
+                ((TsiCvimPacketWorker) worker).getDATA_QUEUE().put(TsiCvimPacketWorker.SHUTDOWN_PACKET);
+            } catch (InterruptedException e) {
+                log.warn("PacketProcess: Interrupted while sending shutdown signal to worker {}.", worker.getIdx());
+                Thread.currentThread().interrupt();
+            }
+        }
+
         this.executor.shutdown();
+
         try {
             if (!this.executor.awaitTermination(3, TimeUnit.SECONDS)) {
                 this.executor.shutdownNow(); // 실행 중인 작업도 중단 시도
+
+                // 큐에 남아있는 데이터 로깅
+                int remainingTasks = 0;
+                for (AbstractTsiCvimWorker worker : this.workerList) {
+                    remainingTasks += ((TsiCvimPacketWorker) worker).getDATA_QUEUE().size();
+                }
+                if (remainingTasks > 0) {
+                    log.warn("PacketProcess: queue remain {}, forced shutdown.", remainingTasks);
+                }
+
+                // 강제 종료 후에도 종료될 때까지 조금 더 기다려줌
+                if (!this.executor.awaitTermination(1, TimeUnit.SECONDS)) {
+                    log.error("PacketProcess: Executor did not terminate even after forced shutdown.");
+                }
             }
         } catch (InterruptedException e) {
             this.executor.shutdownNow();
             Thread.currentThread().interrupt(); // 현재 스레드도 중단
         }
+
         log.info("PacketProcess Stopped...");
     }
 

+ 20 - 11
tsi-comm-server/src/main/java/com/tsi/comm/server/process/packet/TsiCvimPacketWorker.java

@@ -24,10 +24,12 @@ import java.util.concurrent.TimeUnit;
 @Getter
 public class TsiCvimPacketWorker extends AbstractTsiCvimWorker implements Runnable {
 
+    public static final AbstractTsiPacket SHUTDOWN_PACKET = new TsiCpuPacket(-1L, 0, 0, null);
+
     private final LinkedBlockingQueue<AbstractTsiPacket> DATA_QUEUE;
 
     private final KafkaProducerService kafkaProducer;
-    private final TsiCvimLoggingProcess loggingService;
+    private final TsiCvimLoggingProcess loggingProcess;
     private final TraceConfig traceConfig;
     private final TsiCvimServerConfig cvimServerConfig;
 
@@ -36,7 +38,7 @@ public class TsiCvimPacketWorker extends AbstractTsiCvimWorker implements Runnab
         this.qSize = qSize;
         this.kafkaProducer = kafkaProducer;
         this.DATA_QUEUE = new LinkedBlockingQueue<>(qSize);
-        this.loggingService = SpringUtils.getBean(TsiCvimLoggingProcess.class);
+        this.loggingProcess = SpringUtils.getBean(TsiCvimLoggingProcess.class);
         this.traceConfig = SpringUtils.getBean(TraceConfig.class);
         this.cvimServerConfig = SpringUtils.getBean(TsiCvimServerConfig.class);
     }
@@ -49,13 +51,11 @@ public class TsiCvimPacketWorker extends AbstractTsiCvimWorker implements Runnab
                 Object packet = null;
                 try {
                     packet = this.DATA_QUEUE.take();
+                    if (packet == SHUTDOWN_PACKET) {
+                        log.info("PacketWorker({}): Shutdown signal received. Exiting...", this.idx);
+                        break;
+                    }
                     process(packet);
-//                    packet = this.DATA_QUEUE.poll(1, TimeUnit.SECONDS); // 인터럽트 감지 가능
-//                    if (packet != null) {
-//                        process(packet);
-//                    } else {
-//                        Thread.yield(); // 대기 중 CPU 양보
-//                    }
                 } catch (InterruptedException ie) {
                     log.warn("PacketWorker({}): {} Interrupted. Exiting...", this.idx, Thread.currentThread().getName());
                     Thread.currentThread().interrupt(); // 상태 복구
@@ -71,6 +71,15 @@ public class TsiCvimPacketWorker extends AbstractTsiCvimWorker implements Runnab
                     else {
                         log.error("PacketWorker({}): {} Exception: {}", this.idx, Thread.currentThread().getName(), e.getMessage());
                     }
+                } finally {
+                    if (packet != null) {
+                        TsiCpuPacket cpuPacket = (TsiCpuPacket)packet;
+                        TsiNodeVo nodeVo = (TsiNodeVo)cpuPacket.getObj();
+                        if (nodeVo != null) {
+                            // 패킷 처리 완료
+                            nodeVo.packetProcessingFinished(cpuPacket);
+                        }
+                    }
                 }
             }
         } finally {
@@ -142,7 +151,7 @@ public class TsiCvimPacketWorker extends AbstractTsiCvimWorker implements Runnab
                         }
                     }
                     if (isLogging) {
-                        this.loggingService.add(packet, loggingIdx);
+                        this.loggingProcess.add(packet, loggingIdx);
                     }
                 } catch (Exception e) {
                     if (nodeVo.isConnect()) {
@@ -228,7 +237,7 @@ public class TsiCvimPacketWorker extends AbstractTsiCvimWorker implements Runnab
 
             // 로그큐로 전송한다.
             if (isLogging) {
-                this.loggingService.add(packet, loggingIdx);
+                this.loggingProcess.add(packet, loggingIdx);
             }
 
             // 연등지 인 경우
@@ -240,7 +249,7 @@ public class TsiCvimPacketWorker extends AbstractTsiCvimWorker implements Runnab
                     cpuPacket.getAddNodes().get(ii).setEnd(packet.getEnd());
                     cpuPacket.getAddNodes().get(ii).setAvg(packet.getAvg());
                     if (isLogging) {
-                        this.loggingService.add(cpuPacket.getAddNodes().get(ii), loggingIdx);
+                        this.loggingProcess.add(cpuPacket.getAddNodes().get(ii), loggingIdx);
                     }
                 }
             }

+ 1 - 0
tsi-comm-server/src/main/java/com/tsi/comm/server/protocol/AbstractTsiPacket.java

@@ -28,6 +28,7 @@ public abstract class AbstractTsiPacket {
         this.nodeId = nodeId;
         this.remoteIp = remoteIpAddr;
         this.remotePort = remotePort;
+        this.end = 0;
     }
 
     public int getPacketLength() {

+ 2 - 0
tsi-comm-server/src/main/java/com/tsi/comm/server/protocol/TsiCpuPacket.java

@@ -313,6 +313,8 @@ public class TsiCpuPacket extends AbstractTsiPacket {
         this.length = ByteUtils.getUnsignedShort(this.buf, INDEX_LENGTH);
         this.count = (int)(this.buf[INDEX_STATUS_HDR+3] & 0x7F);
 
+        obj.setSigCount(this.count);    // 신호현시 갯수 저장
+
         // CVIM 데이터 및 TEST 데이터가 생성됨
         makeCvimPacket();
 

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

@@ -110,7 +110,7 @@ public class TsiReportManager {
                         alarm.setAlarmCode(TsiAlarmConfigVo.COMM_02);
                         alarm.setAlarmTarget(node.getKey());
                         alarm.setAlarmValue(ipAddress);
-                        this.dbmsProcess.add(alarm, (int) Thread.currentThread().getId());
+                        this.dbmsProcess.add(alarm, node.getDbmsQIdx());
 
                         connectTm = sdf.format(new Date(node.getConnectTm()));
                         lastCommTm = sdf.format(new Date(node.getLastCommTm()));

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

@@ -1,12 +1,11 @@
 package com.tsi.comm.server.repository;
 
-import com.tsi.comm.server.config.TsiCvimServerConfig;
-import com.tsi.comm.server.xnet.NetUtils;
+import com.tsi.comm.server.process.dbms.TsiCvimDbmsProcess;
+import com.tsi.comm.server.protocol.TsiCpuPacket;
 import com.tsi.comm.server.vo.mariadb.AbstractDbmsVo;
 import com.tsi.comm.server.vo.mariadb.CommStatusVo;
 import com.tsi.comm.server.vo.mariadb.KafkaTransVo;
-import com.tsi.comm.server.process.dbms.TsiCvimDbmsProcess;
-import com.tsi.comm.server.protocol.TsiCpuPacket;
+import com.tsi.comm.server.xnet.NetUtils;
 import com.tsi.common.utils.Counter;
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
@@ -63,7 +62,7 @@ public class TsiTpmsManager {
         stat.setTrans(trans);
         stat.setBytes(bytes);
         stat.setSessions(this.sessionManager.getChannelCount());
-        this.dbmsProcess.add(stat, (int)Thread.currentThread().getId());
+        this.dbmsProcess.add(stat, 0);
 
         /*log.info("Second Statistics: {} sessions, {} TRX, {}. {}",
                 TsiSessionManager.getInstance().get(),

+ 10 - 7
tsi-comm-server/src/main/java/com/tsi/comm/server/tcp/TsiCvimServer.java

@@ -1,14 +1,9 @@
 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;
-import com.tsi.comm.server.tcp.codec.CvimServerByteBufMessageDecoder;
-import com.tsi.comm.server.tcp.codec.CvimServerEncoder;
-import com.tsi.comm.server.tcp.handler.CvimServerInboundMessageHandler;
 import com.tsi.comm.server.tcp.initializer.CvimServerInitializer;
-import com.tsi.common.spring.SpringUtils;
+import com.tsi.comm.server.xnet.NettyTcpServer;
+import com.tsi.comm.server.xnet.NettyUtils;
 import com.tsi.common.utils.OS;
 import io.netty.channel.epoll.Epoll;
 import lombok.extern.slf4j.Slf4j;
@@ -42,4 +37,12 @@ public class TsiCvimServer extends NettyTcpServer {
             log.info("Server is running in Windows NIO mode.");
         }
     }
+
+//    @PreDestroy
+//    public void terminate() {
+//        log.info("TsiCvimServer shutting down.");
+//        stop();
+//        log.info("TsiCvimServer shut down.");
+//    }
+
 }

+ 7 - 0
tsi-comm-server/src/main/java/com/tsi/comm/server/tcp/handler/CvimServerPacketProcessHandler.java

@@ -121,6 +121,13 @@ public class CvimServerPacketProcessHandler extends SimpleChannelInboundHandler<
             this.dbmsProcess.add(status, nodeVo.getDbmsQIdx());
         }
 
+        final long DUPLICATE_PACKET_INTERVAL_MS = 600;
+        if (!nodeVo.isProcessable(DUPLICATE_PACKET_INTERVAL_MS)) {
+            return; // 패킷을 버리고 처리를 종료
+        }
+
+        nodeVo.packetProcessingStarted();   // 패킷 처리시작
+
         packet.setObj(nodeVo); // 핸들러에서 찾아낸 최종 nodeVo를 패킷에 설정
 
         nodeVo.setLastCommTm(System.currentTimeMillis());   // 통신 수신시각 저장

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

@@ -73,7 +73,7 @@ public class ConnectionLifecycleService {
             status.setNodeId(nodeVo.getNodeId());
             status.setStatus(0);
             status.setIpAddr(remoteIpAddr);
-            this.dbmsProcess.add(status, (int)Thread.currentThread().getId());
+            this.dbmsProcess.add(status, nodeVo.getDbmsQIdx());
 
             // Disconnected 패킷 큐잉
             TsiCpuDisconnected packet = new TsiCpuDisconnected(nodeVo.getNodeId(), ctx.channel());
@@ -123,7 +123,7 @@ public class ConnectionLifecycleService {
                     alarm.setAlarmCode(TsiAlarmConfigVo.COMM_02);
                     alarm.setAlarmTarget(remoteIpAddr);
                     alarm.setAlarmValue(remoteIpAddr);
-                    this.dbmsProcess.add(alarm, (int) Thread.currentThread().getId());
+                    this.dbmsProcess.add(alarm, 0);
                 }
             } else {
                 log.warn("userEventTriggered: READER_IDLE: {}, {}", remoteIpAddr, nodeVo.getNodeId());
@@ -133,7 +133,7 @@ public class ConnectionLifecycleService {
                     alarm.setAlarmCode(TsiAlarmConfigVo.COMM_02);
                     alarm.setAlarmTarget(nodeVo.getKey());
                     alarm.setAlarmValue(remoteIpAddr);
-                    this.dbmsProcess.add(alarm, (int) Thread.currentThread().getId());
+                    this.dbmsProcess.add(alarm, nodeVo.getDbmsQIdx());
                 }
                 TsiChannelSession.sessionTimeout(nodeVo, remoteIpAddr);
             }

+ 74 - 0
tsi-comm-server/src/main/java/com/tsi/comm/server/vo/TsiNodeVo.java

@@ -9,6 +9,10 @@ import lombok.Getter;
 import lombok.Setter;
 import lombok.extern.slf4j.Slf4j;
 
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
 @Slf4j
 @Getter
 @Setter
@@ -54,6 +58,19 @@ public class TsiNodeVo {
     private int dbmsQIdx;
     private int logQIdx;
 
+    private int sigCount;
+
+    // 빈번한 패킷 수신 처리를 위한 변수
+    private final AtomicLong lastProcessTime = new AtomicLong(0);
+    private final AtomicLong droppedPacketCount = new AtomicLong(0);
+
+    // 작업 완료 여부 확인을 위한 변수
+    private final AtomicInteger pendingPacketCount = new AtomicInteger(0);
+    private final AtomicLong overlappingPacketCount = new AtomicLong(0);
+
+    // 패킷 수신부터 처리까지 평균 작업 시간
+    private final AtomicLong averageProcessingTimeNanos = new AtomicLong(0);
+
     public TsiNodeVo(long nodeId, String ipAddr, boolean sendTest, boolean sendNode, boolean sendCvim) {
         this.nodeId = nodeId;
         this.ipAddr = ipAddr;
@@ -85,6 +102,63 @@ public class TsiNodeVo {
         return (this.channel != null);
     }
 
+    /**
+     * 기준시간 이상 간격으로 패킷이 수신되지 않으면 해당 패킷을 버린다.
+     */
+    public synchronized boolean isProcessable(long intervalMillis) {
+        long now = System.currentTimeMillis();
+        long last = this.lastProcessTime.get();
+
+        if (now - last < intervalMillis) {
+            // 마지막 처리 후 intervalMillis가 지나지 않았으면, 중복 카운트를 증가시키고 false를 반환
+            this.droppedPacketCount.incrementAndGet();
+            return false;
+        }
+        // intervalMillis가 지났으면, 마지막 처리 시각을 현재로 업데이트하고 true 반환
+        this.lastProcessTime.set(now);
+        return true;
+    }
+    public long getDroppedPacketCount() {
+        return this.droppedPacketCount.get();
+    }
+
+    public void packetProcessingStarted() {
+        // pendingPacketCount를 1 증가시키고, 증가시키기 '전'의 값을 가져옴.
+        if (this.pendingPacketCount.getAndIncrement() > 0) {
+            // 이전 값이 0보다 컸다면, 이미 처리 중인 패킷이 있었다는 의미임.
+            this.overlappingPacketCount.incrementAndGet();
+        }
+    }
+    public void packetProcessingFinished(TsiCpuPacket packet) {
+        // 패킷처리 완료
+        this.pendingPacketCount.decrementAndGet();
+
+        if (packet.getEnd() > 0) {
+            long startTime = packet.getTimespec().timestamp();
+            if (startTime > 0) {
+                long durationNanos = System.nanoTime() - startTime;
+                updateAverageProcessingTime(durationNanos);
+            }
+        }
+    }
+    public long getOverlappingPacketCount() {
+        return this.overlappingPacketCount.get();
+    }
+
+    public synchronized void updateAverageProcessingTime(long durationNanos) {
+        long currentAvg = this.averageProcessingTimeNanos.get();
+        if (currentAvg == 0) {
+            // 첫 번째 측정인 경우, 그대로 값을 설정
+            this.averageProcessingTimeNanos.set(durationNanos);
+        } else {
+            // 기존 평균과 새로운 값의 중간값을 계산하여 이동 평균을 구함
+            long newAvg = (currentAvg + durationNanos) / 2L;
+            this.averageProcessingTimeNanos.set(newAvg);
+        }
+    }
+    public long getAverageProcessingTimeMicros() {
+        return TimeUnit.NANOSECONDS.toMicros(this.averageProcessingTimeNanos.get());
+    }
 
     /**
      * 채널을 설정을 동기화를 처리하여 스레드 안전성을 보장.

+ 1 - 0
tsi-comm-server/src/main/java/com/tsi/comm/server/vo/mariadb/AbstractDbmsVo.java

@@ -10,6 +10,7 @@ import lombok.ToString;
 @ToString
 public abstract class AbstractDbmsVo {
 
+    public static final int DBMS_NONE = 0;
     public static final int DBMS_NODE_STATUS = 1;
     public static final int DBMS_TPMS_STAT_1S = 2;
     public static final int DBMS_KAFKA_TRANS_HS = 3;

+ 17 - 0
tsi-comm-server/src/main/java/com/tsi/comm/server/vo/mariadb/ProcessExitVo.java

@@ -0,0 +1,17 @@
+package com.tsi.comm.server.vo.mariadb;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+@Getter
+@Setter
+@ToString
+public class ProcessExitVo extends AbstractDbmsVo {
+
+    protected String desc;
+
+    public ProcessExitVo(int dbmsType) {
+        super(dbmsType);
+    }
+}