Ver código fonte

add system health check

shjung 2 semanas atrás
pai
commit
c3c46863bd

+ 22 - 0
evps-comm-server/src/main/java/com/evps/comm/server/EvpsCommServerApplication.java

@@ -1,10 +1,12 @@
 package com.evps.comm.server;
 
+import com.evps.comm.server.config.ApplicationConfig;
 import com.evps.comm.server.config.TraceConfig;
 import com.evps.comm.server.process.dbms.DbmsDataProcess;
 import com.evps.comm.server.repository.ApplicationRepository;
 import com.evps.comm.server.xnet.server.EvpsCommServerService;
 import com.evps.comm.server.xnet.server.process.work.DataPacketProcess;
+import com.evps.common.utils.ApplicationUtils;
 import com.its.common.spring.SpringUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.DisposableBean;
@@ -20,6 +22,9 @@ import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.event.ContextClosedEvent;
 import org.springframework.transaction.annotation.EnableTransactionManagement;
 
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 
@@ -32,6 +37,12 @@ public class EvpsCommServerApplication implements CommandLineRunner, Application
     public static final String APPLICATION_NAME = "evps-comm-server";
 
     public static void main(String[] args) {
+        Path pidPath = ApplicationUtils.getApplicationPidPath(EvpsCommServerApplication.class, APPLICATION_NAME);
+        if (ApplicationUtils.isRunning(pidPath)) {
+            log.warn("EvpsCommServerApplication is already running. Check the PID file: {}", pidPath);
+            System.exit(1);
+        }
+
         SpringApplication application = new SpringApplicationBuilder()
                 .sources(EvpsCommServerApplication.class)
                 .listeners(new ApplicationPidFileWriter("./conf/" + APPLICATION_NAME + ".pid"))
@@ -54,6 +65,9 @@ public class EvpsCommServerApplication implements CommandLineRunner, Application
         log.info("** startup: {}", sdfDate.format(new Date()));
         log.info("************************************************************************************");
 
+        ApplicationConfig applicationConfig = SpringUtils.getBean(ApplicationConfig.class);
+        applicationConfig.setStartSchedule(false);
+
         DbmsDataProcess dbmsDataProcess = SpringUtils.getBean(DbmsDataProcess.class);
         dbmsDataProcess.run();
 
@@ -71,9 +85,17 @@ public class EvpsCommServerApplication implements CommandLineRunner, Application
         EvpsCommServerService evpsCommServerService = SpringUtils.getBean(EvpsCommServerService.class);
         evpsCommServerService.run();
 
+        applicationConfig.setStartSchedule(true);
+
         Runtime.getRuntime().addShutdownHook(new Thread(() -> {
             log.error("on shutdown hook.");
+            applicationConfig.setStartSchedule(false);
             terminate();
+            try {
+                Path pidPath = ApplicationUtils.getApplicationPidPath(EvpsCommServerApplication.class, APPLICATION_NAME);
+                Files.deleteIfExists(pidPath);
+            } catch (IOException ignored) {
+            }
         }));
     }
 

+ 2 - 1
evps-comm-server/src/main/java/com/evps/comm/server/config/ApplicationConfig.java

@@ -33,10 +33,11 @@ public class ApplicationConfig extends NettyServerConfig {
     private int dbmsWorkers = 0;
     private int queueSize = 0;
     private String bootingTime;
+    private boolean startSchedule;
 
     @PostConstruct
     private void init() {
-
+        this.startSchedule = false;
         this.bootingTime = TimeUtils.now();
 
         configure();

+ 3 - 3
evps-comm-server/src/main/java/com/evps/comm/server/scheduler/ApplicationScheduler.java

@@ -118,7 +118,7 @@ public class ApplicationScheduler {
     private void loggingSystemHealth() {
         double loadAvg = this.systemHealth.getLoadAverage();
         String loadAvgStr = (loadAvg < 0) ? "N/A" : this.df.format(loadAvg);
-        log.info("SYSTEM HEALTH: CPU {} Cores[{} %, LoadAvg({})], Memory[{} %, Used({} MB), Max({} MB)], Threads[{}, Peak({})], GC[{}, {} ms]",
+        log.info("SYSTEM HEALTH: CPU {} Cores[{} %, LoadAvg({})], Memory[{} %, Used({} MB), Max({} MB)], Threads[{}, Peak({})], GC(T/R)[{}/{} ms]",
                 this.systemHealth.getCpuCores(),
                 this.df.format(this.systemHealth.getCpuUsage()),
                 loadAvgStr,
@@ -127,8 +127,8 @@ public class ApplicationScheduler {
                 this.systemHealth.getMaxMemory(),
                 this.systemHealth.getThreadCount(),
                 this.systemHealth.getPeakThreadCount(),
-                this.systemHealth.getGcCount(),
-                this.systemHealth.getGcTime());
+                this.systemHealth.getGcTotalAvgTime(),
+                this.systemHealth.getGcRecentAvgTime());
     }
 
     private void logSessionStatus() {

+ 92 - 0
evps-common/src/main/java/com/evps/common/utils/ApplicationUtils.java

@@ -0,0 +1,92 @@
+package com.evps.common.utils;
+
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStreamReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class ApplicationUtils {
+
+    private ApplicationUtils() {
+    }
+
+    /**
+     * Returns the path to the PID file for the application.
+     *
+     * @param applicationClass The class of the application.
+     * @param appName      The name of the application.
+     * @return The path to the PID file.
+     */
+    public static Path getApplicationPidPath(Class<?> applicationClass, String appName) {
+        String pidFileName = appName + ".pid";
+        try {
+            File appDir = new File(applicationClass
+                    .getProtectionDomain()
+                    .getCodeSource()
+                    .getLocation()
+                    .toURI())
+                    .getParentFile();
+
+            return appDir.toPath()
+                    .resolve("conf")
+                    .resolve(pidFileName);
+        } catch (Exception e) {
+            return Paths.get("./conf", pidFileName);
+        }
+    }
+
+//    public static boolean isAlreadyRunning(Path pidPath) {
+//        if (Files.exists(pidPath)) {
+//            try {
+//                String pidStr = new String(Files.readAllBytes(pidPath)).trim();
+//                long pid = Long.parseLong(pidStr);
+//                // Java 9 이상에서는 ProcessHandle를 사용하여 프로세스 상태 확인
+//                return ProcessHandle.of(pid).map(ProcessHandle::isAlive).orElse(false);
+//            } catch (Exception e) {
+//                // 잘못된 PID 내용이거나 파일 읽기 실패 시에는 실행 중 아님 처리
+//                return false;
+//            }
+//        }
+//        return false;
+//    }
+
+    public static boolean isRunning(Path pidPath) {
+        if (Files.exists(pidPath)) {
+            try {
+                String pidStr = new String(Files.readAllBytes(pidPath)).trim();
+                long pid = Long.parseLong(pidStr);
+
+                String os = System.getProperty("os.name").toLowerCase();
+                Process process;
+                if (os.toLowerCase().contains("win")) {
+                    // 윈도우: tasklist 명령어로 PID 검색
+                    process = new ProcessBuilder("tasklist", "/FI", "PID eq " + pid).start();
+                } else {
+                    // 리눅스/유닉스: ps 명령어로 PID 검색
+                    //process = new ProcessBuilder("ps", "-p", String.valueOf(pid)).start();
+                    return false; // 리눅스/유닉스에서는 ps 명령어로 PID 검색을 하지 않음
+                }
+
+                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
+                    String line;
+                    while ((line = reader.readLine()) != null) {
+                        if (line.contains(String.valueOf(pid))) {
+                            return true; // 실행 중인 프로세스 발견
+                        }
+                    }
+                } finally {
+                    process.destroy();
+                }
+            } catch (Exception e) {
+                // 읽기 오류 또는 명령 실행 오류 → 실행 중 아님 처리
+                System.out.println("Error checking if application is running: " + e.getMessage());
+                return false;
+            }
+        }
+        return false;
+    }
+
+}

+ 27 - 10
evps-common/src/main/java/com/evps/common/utils/SystemHealth.java

@@ -9,8 +9,6 @@ import java.lang.management.ManagementFactory;
 import java.lang.management.MemoryMXBean;
 import java.time.Instant;
 import java.time.ZoneId;
-import java.util.HashMap;
-import java.util.Map;
 
 @Getter
 public class SystemHealth {
@@ -41,11 +39,17 @@ public class SystemHealth {
     private long diskFree;
     private double diskUsage;
 
-    private final Map<String, GcStats> gcStatsMap = new HashMap<>();
+//    private final Map<String, GcStats> gcStatsMap = new HashMap<>();
     private String gcName;
-    private long gcCount = 0;
-    private long gcTime = 0;
-    private String jvmStartTime;
+    private long gcTotalCount = 0;
+    private long gcTotalTime = 0;
+    private long gcTotalAvgTime = 0;
+
+    private long gcRecentCount = 0;
+    private long gcRecentTime = 0;
+    private long gcRecentAvgTime = 0;
+
+    private final String jvmStartTime;
 
     public SystemHealth() {
         this.jvmStartTime = Instant.ofEpochMilli(
@@ -109,13 +113,26 @@ public class SystemHealth {
             }
         }
 
+        long totalGcCount = 0;
+        long totalGcTime = 0;
         for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
-            this.gcName = gc.getName();
-            this.gcCount += gc.getCollectionCount();
-            this.gcTime += gc.getCollectionTime();
-            gcStatsMap.put(gc.getName(), new GcStats(gc.getCollectionCount(), gc.getCollectionTime()));
+            totalGcCount += gc.getCollectionCount();
+            totalGcTime += gc.getCollectionTime();
+//            this.gcName = gc.getName();
+//            gcStatsMap.put(gc.getName(), new GcStats(gc.getCollectionCount(), gc.getCollectionTime()));
         }
 
+        // 누적 평균
+        this.gcTotalAvgTime = totalGcTime > 0 ? Math.round((double) totalGcTime / totalGcCount) : 0;
+
+        // 최근 변화량
+        this.gcRecentCount = totalGcCount - this.gcTotalCount;
+        this.gcRecentTime = totalGcTime - this.gcTotalTime;
+        this.gcRecentAvgTime = gcTotalCount > 0 ? Math.round((double) this.gcRecentTime  / gcTotalCount) : 0;
+
+        this.gcTotalCount = totalGcCount;
+        this.gcTotalTime = totalGcTime;
+
 //        for (File root : File.listRoots()) {
 //            if (root.exists() && root.canRead()) {
 //                long total = root.getTotalSpace();