| 
					
				 | 
			
			
				@@ -1,17 +1,26 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 package com.evps.comm.server.scheduler;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.evps.comm.server.config.ApplicationConfig;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.evps.comm.server.config.TraceConfig;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.evps.comm.server.repository.ApplicationRepository;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.evps.comm.server.service.EvpsServiceManagerService;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.evps.comm.server.service.UnitSystService;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.evps.common.utils.SystemHealth;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.zaxxer.hikari.HikariDataSource;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.zaxxer.hikari.HikariPoolMXBean;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import lombok.RequiredArgsConstructor;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import lombok.extern.slf4j.Slf4j;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import org.springframework.scheduling.annotation.Async;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import org.springframework.scheduling.annotation.EnableScheduling;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import org.springframework.scheduling.annotation.Scheduled;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import org.springframework.stereotype.Component;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import javax.annotation.PreDestroy;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.lang.management.ManagementFactory;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.lang.management.ThreadInfo;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.lang.management.ThreadMXBean;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.text.DecimalFormat;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.EnumMap;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.Map;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 @Slf4j
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 @RequiredArgsConstructor
 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -20,41 +29,54 @@ import javax.annotation.PreDestroy; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 public class ApplicationScheduler {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private final TraceConfig traceConfig;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private final ApplicationConfig config;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private final UnitSystService unitSystService;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private final ApplicationRepository applicationRepository;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private final EvpsServiceManagerService evpsServiceManagerService;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private final HikariDataSource dataSource;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private final Map<Thread.State, Integer> stateCountMap = new EnumMap<>(Thread.State.class);
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private final SystemHealth systemHealth = new SystemHealth();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private final DecimalFormat df = new DecimalFormat("#.##");
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @PreDestroy
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     public void onShutDown() {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         log.info("ApplicationScheduler.onShutDown: Shutting down...");
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//    @ScheduleElapsed
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    @Async
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @Scheduled(cron = "0 * * * * *")
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     public void updateProcessState() {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            this.systemHealth.checkHealth(false);
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            loggingHealthCheck();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            double cpuUsage = this.systemHealth.getCpuUsage();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (cpuUsage > this.config.getCpuLimits()) {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//                ApplicationRepository.processState.setErrDesc("CPU 사용율이 너무 높음: " + String.format("%.2f", cpuUsage));
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                log.warn("[SKIP] ApplicationScheduler.updateProcessState: High CPU Usage, Limit({} %), Current({} %), Schedule Job SKIP...",
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        this.config.getCpuLimits(), String.format("%.2f", cpuUsage));
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                loggingThreads();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return;
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } catch (Exception e) {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            log.error("ApplicationScheduler.updateProcessState: System Health Check Exception {}", e.getMessage());
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         try {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             this.unitSystService.updateUnitSystStts();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         catch(Exception e) {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             log.error("ApplicationScheduler.updateProcessState: Exception {}", e.getMessage());
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    //    @ScheduleElapsed
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    @Async
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    @Scheduled(cron = "30 * * * * *")  // 1분주기 작업 실행
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    public void loadTraceConfig() {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         try {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             this.traceConfig.loadTraceInfo();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         catch(Exception e) {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            log.error("ApplicationScheduler.loadTraceConfig: Exception {}", e.getMessage());
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            log.error("ApplicationScheduler.loadTraceInfo: Exception {}", e.getMessage());
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    //    @ScheduleElapsed
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    @Async
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @Scheduled(cron = "0/30 * * * * *")
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     public void serviceMangerSchedule() {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         try {
 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -65,8 +87,6 @@ public class ApplicationScheduler { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//    @ScheduleElapsed
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    @Async
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @Scheduled(cron = "20 * * * * *")  // 1분주기 작업 실행
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     public void reportCenterSessions() {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         try {
 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -77,8 +97,6 @@ public class ApplicationScheduler { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//    @ScheduleElapsed
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    @Async
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @Scheduled(cron = "10 0/5 * * * *")  // 5분주기 작업 실행
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     public void loadBaseDatabase() {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         try {
 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -89,4 +107,68 @@ public class ApplicationScheduler { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void loggingHealthCheck() {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        log.info("----------------------------------------------------------------------------------------------------------");
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        loggingSystemHealth();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        logSessionStatus();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        loggingThreadState();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        log.info("----------------------------------------------------------------------------------------------------------");
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    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]",
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                this.systemHealth.getCpuCores(),
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                this.df.format(this.systemHealth.getCpuUsage()),
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                loadAvgStr,
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                this.df.format(this.systemHealth.getMemUsage()),
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                this.systemHealth.getUsedMemory(),
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                this.systemHealth.getMaxMemory(),
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                this.systemHealth.getThreadCount(),
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                this.systemHealth.getPeakThreadCount(),
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                this.systemHealth.getGcCount(),
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                this.systemHealth.getGcTime());
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void logSessionStatus() {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        HikariPoolMXBean poolStats = this.dataSource.getHikariPoolMXBean();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        int totalConnections = poolStats.getTotalConnections();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        int activeConnections = poolStats.getActiveConnections();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        int idleConnections = poolStats.getIdleConnections();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        int threadsAwaiting = poolStats.getThreadsAwaitingConnection();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        log.info("   DB SESSION: Total: {}, Active: {}, Idle: {}, Waiting: {}",
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                totalConnections, activeConnections, idleConnections, threadsAwaiting);
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void loggingThreadState() {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ThreadInfo[] infos = threadBean.dumpAllThreads(false, false);
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        this.stateCountMap.clear();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for (ThreadInfo info : infos) {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Thread.State state = info.getThreadState();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            this.stateCountMap.put(state, this.stateCountMap.getOrDefault(state, 0) + 1);
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        StringBuilder sb = new StringBuilder();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for (Map.Entry<Thread.State, Integer> entry : stateCountMap.entrySet()) {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            sb.append(String.format("%s(%d), ", entry.getKey().name(), entry.getValue()));
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 마지막 쉼표 제거
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (sb.length() > 0) {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            sb.setLength(sb.length() - 2);
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        log.info(" THREAD STATE: {}", sb.toString());
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void loggingThreads() {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ThreadInfo[] infos = threadBean.dumpAllThreads(false, false);
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        this.stateCountMap.clear();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for (ThreadInfo info : infos) {
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Thread.State state = info.getThreadState();
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            this.stateCountMap.put(state, this.stateCountMap.getOrDefault(state, 0) + 1);
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            log.info("         [" + info.getThreadId() + "] " + info.getThreadName() + " - " + info.getThreadState());
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+
 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }
 
			 |