Parcourir la source

cluster update

HANTE il y a 1 mois
Parent
commit
78e9c86600

+ 30 - 10
its-cluster/src/main/java/com/its/common/cluster/vo/AbstractClusterConfig.java → its-cluster/src/main/java/com/its/common/cluster/config/AbstractClusterConfig.java

@@ -1,5 +1,6 @@
-package com.its.common.cluster.vo;
+package com.its.common.cluster.config;
 
+import com.its.common.cluster.vo.ClusterNode;
 import io.netty.util.AttributeKey;
 import lombok.Data;
 import lombok.extern.slf4j.Slf4j;
@@ -26,12 +27,13 @@ public abstract class AbstractClusterConfig {
     private boolean packetLogging = false;  // 패킷 로깅 여부
     private List<ClusterNode> nodes;
 
-
     private final HashMap<Integer, ClusterNode> clusterMap = new HashMap<>();
 
     @PostConstruct
     private void init() throws IOException {
-//        loadClusterConfig();
+        if (this.nodes == null) {
+            this.nodes = new ArrayList<>();
+        }
     }
 
     public void loggingInfo() {
@@ -47,14 +49,13 @@ public abstract class AbstractClusterConfig {
         for  (ClusterNode node : this.nodes) {
             log.info("  ----------------node id: {}", node.getId());
             log.info("                   master: {}", node.isMaster());
-            log.info("              syncSeconds: {}", node.getSyncSeconds());
             log.info("                       ip: {}", node.getIp());
             log.info("                     port: {}", node.getPort());
             log.info("    logging/packetLogging: {}/{}", node.isLogging(), node.isPacketLogging());
         }
     }
 
-    private void setDefaults() {
+    private boolean setDefaults() {
         // 클러스터가 하나라 단일 클러스터 정보로 설정
         this.master = true;
         this.id = 1;
@@ -69,18 +70,22 @@ public abstract class AbstractClusterConfig {
         ClusterNode masterNode = new ClusterNode();
         masterNode.setMaster(this.master);
         masterNode.setId(this.id);
-        masterNode.setSyncSeconds(this.syncSeconds);
         masterNode.setIp(this.ip);
         masterNode.setPort(this.port);
         masterNode.setLogging(this.logging);
         masterNode.setPacketLogging(this.packetLogging);
         this.nodes.add(masterNode);
+        this.enabled = false;
+
+        // 나 자신의 네트워크 상태정보를 항상 CONNECT로 지정
+        masterNode.getElectionState().connect(null);
+        masterNode.getSyncState().connect(null);
+        return true;
     }
 
     public boolean validateClusterInfo() {
         if (!this.enabled) {
-            setDefaults();
-            return true;
+            return setDefaults();
         }
 
         if (this.nodes == null || this.nodes.isEmpty()) {
@@ -88,6 +93,16 @@ public abstract class AbstractClusterConfig {
             return false;
         }
 
+        if (this.nodes.size() == 1) {
+            int firstNodeId = this.nodes.get(0).getId();
+            if (firstNodeId == 1) {
+                return setDefaults();
+            }
+            log.error("클러스터 노드가 1개 인데 노드 ID 값이 1 이 아닙니다.: {}", firstNodeId);
+            log.error("클러스터 노드가 1개 인 경우 클러스터 사용을 false 로 설정하세요.");
+            return false;
+        }
+
         // ID만 추출해서 정렬
         List<Integer> ids = this.nodes.stream()
                 .map(ClusterNode::getId)
@@ -114,8 +129,8 @@ public abstract class AbstractClusterConfig {
         if (this.syncSeconds < 2) {
             this.syncSeconds = 2;
         }
-        if (this.syncSeconds > 60) {
-            this.syncSeconds = 60;
+        if (this.syncSeconds > 30) {
+            this.syncSeconds = 30;
         }
         // Master and host information setting
         int masterId = Integer.MAX_VALUE;
@@ -135,6 +150,11 @@ public abstract class AbstractClusterConfig {
         ClusterNode masterNode = this.clusterMap.get(masterId);
         masterNode.setMaster(true);
         this.master = (this.id == masterId);
+
+        // 나 자신의 네트워크 상태정보를 항상 CONNECT로 지정
+        ClusterNode localNode = this.clusterMap.get(this.id);
+        localNode.getElectionState().connect(null);
+        localNode.getSyncState().connect(null);
         return true;
     }
 

+ 4 - 3
its-cluster/src/main/java/com/its/common/cluster/handler/ClusterMasterHandler.java

@@ -3,7 +3,7 @@ package com.its.common.cluster.handler;
 import com.its.common.cluster.service.AbstractClusterMasterService;
 import com.its.common.cluster.vo.ClusterMessage;
 import com.its.common.cluster.utils.ClusterUtils;
-import com.its.common.cluster.vo.AbstractClusterConfig;
+import com.its.common.cluster.config.AbstractClusterConfig;
 import com.its.common.cluster.vo.ClusterNode;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandlerContext;
@@ -29,7 +29,7 @@ public class ClusterMasterHandler extends ChannelInboundHandlerAdapter {
             if (cluster == null) {
                 log.error("ClusterMasterHandler.channelRead: [{}], {}, [FROM: clusterId: {}, master: {}, serverTime: {}], Not Found Channel Cluster Object. Will be closed.",
                         this.clusterConfig.getId(), ClusterUtils.getTcpAddress(ctx.channel()),
-                        clusterMsg.getClusterId(), clusterMsg.isMaster(), clusterMsg.getServerTime());
+                        clusterMsg.getClusterId(), clusterMsg.isMaster(), clusterMsg.getCurrentTimeMillis());
 
                 closeChannel(ctx.channel());
                 return;
@@ -40,13 +40,14 @@ public class ClusterMasterHandler extends ChannelInboundHandlerAdapter {
 
                 log.info("ClusterMasterHandler.channelRead: [{}], {}, [FROM: clusterId: {}, master: {}, serverTime: {}]",
                         this.clusterConfig.getId(), ClusterUtils.getTcpAddress(ctx.channel()),
-                        clusterMsg.getClusterId(), clusterMsg.isMaster(), clusterMsg.getServerTime());
+                        clusterMsg.getClusterId(), clusterMsg.isMaster(), clusterMsg.getCurrentTimeMillis());
 
                 MDC.remove(cluster.getLogKey());
                 MDC.clear();
             }
 
             cluster.getElectionState().setLastRecvTime();
+            cluster.getSyncState().setLastRecvTime();
             this.masterService.onClusterMessage(clusterMsg);
         }
     }

+ 24 - 12
its-cluster/src/main/java/com/its/common/cluster/service/AbstractClusterMasterService.java

@@ -3,7 +3,7 @@ package com.its.common.cluster.service;
 import com.its.common.cluster.vo.ClusterMessage;
 import com.its.common.cluster.utils.ClusterPlatform;
 import com.its.common.cluster.utils.ClusterUtils;
-import com.its.common.cluster.vo.AbstractClusterConfig;
+import com.its.common.cluster.config.AbstractClusterConfig;
 import com.its.common.cluster.vo.ClusterNode;
 import com.its.common.cluster.vo.ClusterNET;
 import io.netty.bootstrap.ServerBootstrap;
@@ -41,7 +41,8 @@ public abstract class AbstractClusterMasterService {
     public void start() {
         if (!ClusterPlatform.isWindows()) {
             if (!Epoll.isAvailable()) {
-                Epoll.unavailabilityCause().printStackTrace();
+                // 윈도우 서버가 아닌데 EPOLL 을 사용할 수 없으면 오류 메시지 표출
+                log.warn("Is not Windows system but can't use epoll networking: {}", Epoll.unavailabilityCause().getMessage());
             }
         }
         if (ClusterUtils.isEpollAvailable()) {
@@ -97,40 +98,51 @@ public abstract class AbstractClusterMasterService {
         serverBootstrap.childOption(ChannelOption.SO_REUSEADDR, true);     // 소켓 재사용
         serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true);      // Nagle 알고리즘 비활성화
 
-        ClusterMasterInitializer clusterMasterInitializer = new ClusterMasterInitializer(this, this.clusterConfig);
-        serverBootstrap.childHandler(clusterMasterInitializer);
+        serverBootstrap.childHandler(new ClusterMasterInitializer(this, this.clusterConfig));
 
         return serverBootstrap;
     }
 
-    public abstract void election(int clusterId, boolean isMaster);
+    public abstract void election(int clusterId, boolean isMaster, boolean isMasterChanged);
+//    {
+//        if (isMasterChanged) {
+//            log.info("ClusterMasterService:election: master state changed: clusterId: {}, master: {}", clusterId, isMaster);
+//        }
+//    }
+
     public abstract void onClusterMessage(ClusterMessage message);
 
     private void electionMasterSchedule() {
+        // 2초 주기로 실행되며 클러스터의 마스터/슬래이브 정보를 업데이트 함
+        // 클러스터맵에는 나 자신의 정보가 포함되어 있음.
         this.taskFuture = this.taskScheduler.scheduleAtFixedRate(() -> {
             int masterId = Integer.MAX_VALUE;
             for (Map.Entry<Integer, ClusterNode> entry : this.clusterConfig.getClusterMap().entrySet()) {
                 ClusterNode cluster = entry.getValue();
+                // 마스터에 연결된 슬래이브 클러스터의 네트워크 상태를 기준으로 평가함.
                 if (cluster.getElectionState().getState() != ClusterNET.CLOSED) {
                     if (cluster.getId() < masterId) {
                         masterId = cluster.getId();
                     }
                 }
             }
-//            log.info("ClusterMasterService:electionMasterSchedule: clusterId: {}, masterId: {}", this.clusterConfig.getId(), masterId);
-            if (masterId == Integer.MAX_VALUE || masterId > this.clusterConfig.getId()) {
-                this.clusterConfig.setMaster(true);
-            }
-            else {
-                this.clusterConfig.setMaster(false);
+
+            boolean isMaster = false;
+            if (masterId == Integer.MAX_VALUE || masterId >= this.clusterConfig.getId()) {
+                // 위의 클러스터의 나 자신의 네트워크 상태는 CLOSED로 되어 있기 때문에 나자신보다 현재 마스터 ID가
+                // 현재 masterId 가 나 자신의 clusterId보다 같거나 크면 나 자신이 master가 된 것임
+                isMaster = true;
             }
 
+            boolean isChanged = this.clusterConfig.isMaster() != isMaster;
+            this.clusterConfig.setMaster(isMaster);
+
             if (this.clusterConfig.isLogging()) {
                 log.info("ClusterMasterService:electionMasterSchedule: clusterId: {}, Master: {}.",
                         this.clusterConfig.getId(), this.clusterConfig.isMaster());
             }
 
-            election(this.clusterConfig.getId(), this.clusterConfig.isMaster());
+            election(this.clusterConfig.getId(), this.clusterConfig.isMaster(), isChanged);
         }, 2 * 1000L);
     }
 

+ 15 - 11
its-cluster/src/main/java/com/its/common/cluster/service/AbstractClusterSlaveService.java

@@ -1,5 +1,6 @@
 package com.its.common.cluster.service;
 
+import com.its.common.cluster.config.AbstractClusterConfig;
 import com.its.common.cluster.vo.*;
 import com.its.common.cluster.utils.ClusterUtils;
 import io.netty.channel.Channel;
@@ -47,6 +48,7 @@ public abstract class AbstractClusterSlaveService {
         for (Map.Entry<Integer, ClusterNode> entry : this.clusterConfig.getClusterMap().entrySet()) {
             ClusterNode cluster = entry.getValue();
             if (cluster.getId() == this.clusterConfig.getId()) {
+                // 자기 자신은 접속하지 않도록 한다. 즉 타 클러스터 서버에 접속한다.
                 continue;
             }
             ClusterSlave slaveClient = new ClusterSlave(this, clusterConfig, cluster, this.bootstrapFactory);
@@ -102,6 +104,7 @@ public abstract class AbstractClusterSlaveService {
             log.error("ClusterSlaveService.channelClose");
         }
     }
+
     private String getSysTime() {
         SimpleDateFormat sdfDate = new SimpleDateFormat("yyyyMMddHHmmss");
         Date dtLog = new Date();
@@ -115,18 +118,18 @@ public abstract class AbstractClusterSlaveService {
         return ClusterMessage.builder()
                 .clusterId(this.clusterConfig.getId())
                 .master(this.clusterConfig.isMaster())
-                .serverTime(getSysTime())
+                .currentTimeMillis(System.currentTimeMillis())
                 .infos(details)
                 .build();
     }
 
     // 스케쥴 주기 이외에 클러스터 메시지를 즉시 전송하기 위한 인터페이스 함수
-    public void notifyClusterMessage() {
-        if (this.clusterConfig.isLogging()) {
-            log.info("ClusterSlaveService:notifyClusterMessage:---");
-        }
-        dataSyncSchedule();
-    }
+//    public void notifyClusterMessage() {
+//        if (this.clusterConfig.isLogging()) {
+//            log.info("ClusterSlaveService:notifyClusterMessage:---");
+//        }
+//        dataSyncSchedule();
+//    }
 
     private synchronized void dataSyncSchedule() {
         if (this.clusterConfig.isLogging()) {
@@ -136,15 +139,14 @@ public abstract class AbstractClusterSlaveService {
             for (Map.Entry<Integer, ClusterNode> entry : this.clusterConfig.getClusterMap().entrySet()) {
                 ClusterNode cluster = entry.getValue();
                 if (cluster.getId() == this.clusterConfig.getId()) {
+                    // 자기 자신은 전송하지 않음.
                     continue;
                 }
                 if (cluster.getSyncState().getState() != ClusterNET.CLOSED) {
                     sendSyncData(cluster, cluster.getSyncState().getChannel());
                 }
             }
-
         }, this.clusterConfig.getSyncSeconds() * 1000L);
-
     }
 
     public void sendSyncData(final ClusterNode cluster, final Channel channel) {
@@ -158,17 +160,19 @@ public abstract class AbstractClusterSlaveService {
             ChannelFuture f = channel.writeAndFlush(clusterMsg);
             f.awaitUninterruptibly();
             if (f.isDone() || f.isSuccess()) {
+                cluster.getSyncState().setLastSendTime();   // 데이터 정송 시각 설정
+
                 if (this.clusterConfig.isLogging()) {
                     log.info("ClusterSlaveService.sendSyncData: [{}], {}, [--TO: clusterId: {}, (clusterId: {}, serverTime: {})]",
                             this.clusterConfig.getId(), ClusterUtils.getTcpAddress(channel),
-                            cluster.getId(), clusterMsg.getClusterId(), clusterMsg.getServerTime());
+                            cluster.getId(), clusterMsg.getClusterId(), clusterMsg.getCurrentTimeMillis());
                 }
             }
         }
         catch (Exception e) {
             log.error("ClusterSlaveService.sendSyncData: [{}], {}, Failed: [--TO: clusterId: {}, (clusterId: {}, serverTime: {})]",
                     this.clusterConfig.getId(), ClusterUtils.getTcpAddress(channel),
-                    cluster.getId(), clusterMsg.getClusterId(), clusterMsg.getServerTime());
+                    cluster.getId(), clusterMsg.getClusterId(), clusterMsg.getCurrentTimeMillis());
             log.error("ClusterSlaveService.sendSyncData: [{}], {}, Failed: {}",
                     this.clusterConfig.getId(), ClusterUtils.getTcpAddress(channel), e.getMessage());
         }

+ 19 - 21
its-cluster/src/main/java/com/its/common/cluster/service/ClusterMasterInitializer.java

@@ -4,7 +4,7 @@ import com.its.common.cluster.codec.ClusterMessageDecoder;
 import com.its.common.cluster.codec.ClusterMessageEncoder;
 import com.its.common.cluster.handler.ClusterMasterHandler;
 import com.its.common.cluster.utils.ClusterUtils;
-import com.its.common.cluster.vo.AbstractClusterConfig;
+import com.its.common.cluster.config.AbstractClusterConfig;
 import com.its.common.cluster.vo.ClusterNode;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelInitializer;
@@ -28,51 +28,49 @@ public class ClusterMasterInitializer extends ChannelInitializer<Channel> {
 
     @Override
     protected void initChannel(Channel channel) throws Exception {
-//        InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();
-//        String clientIP = remoteAddress.getAddress().getHostAddress();
-//        int clientPort = remoteAddress.getPort();
 
         String ipAddress  = ClusterUtils.getRemoteIpAddress(channel);
         int clientPort = ClusterUtils.getRemotePort(channel);
         int clusterId = clientPort - this.clusterConfig.getPort();
 
-        if (this.clusterConfig.isLogging()) {
-            log.info("ClusterMasterInitializer.initChannel: connected from: {}:{}, clusterId: {}.", ipAddress, clientPort, clusterId);
-        }
+        log.info("ClusterMasterInitializer.initChannel: [clusterId: {}, IP Address: {}.{}], connected.", clusterId, ipAddress, clientPort);
 
         // 하나의 서버에 여러 개의 클러스터가 있을 수 있기 때문에 IP Address 로 찾는 것은 위험함.
-        // HaClusterConfig.HaCluster cluster = this.clusterConfig.get(ipAddress);
-        ClusterNode cluster = this.clusterConfig.getClusterMap().get(clusterId);
-        if (cluster == null) {
-            log.error("ClusterMasterInitializer.initChannel: [clusterId: {}, IP Address: {}], Unknown Server Id. will be closed.", clusterId, ipAddress);
+        // 클러스터가 분산된 경우에는 이런 처리가 필요하지 않음.(IP Address 만으로도 해당 클러스터 서버를 찾을 수 있음.
+        // checking cluster id, find from clusterMap
+        ClusterNode clusterNode = this.clusterConfig.getClusterMap().get(clusterId);
+        if (clusterNode == null) {
+            log.error("ClusterMasterInitializer.initChannel: [clusterId: {}, IP Address: {}.{}], Unknown Server Id. will be closed.", clusterId, ipAddress, clientPort);
             channel.disconnect();
             channel.close();
             return;
         }
-        if (!cluster.getIp().equals(ipAddress)) {
-            log.error("ClusterMasterInitializer.initChannel: [clusterId: {}, IP Address: {}], Unknown IP Address. will be closed.", clusterId, ipAddress);
+
+        // checking ip address
+        if (!clusterNode.getIp().equals(ipAddress)) {
+            log.error("ClusterMasterInitializer.initChannel: [clusterId: {}, IP Address: {}.{}], Unknown IP Address. will be closed.", clusterId, ipAddress, clientPort);
             channel.disconnect();
             channel.close();
             return;
         }
 
         if (this.clusterConfig.isLogging()) {
-            MDC.put("id", cluster.getLogKey());
-            log.info("ClusterMasterInitializer.initChannel: [{}, {}].", cluster.getLogKey(), cluster.getIp());
+            MDC.put("id", clusterNode.getLogKey());
+            log.info("ClusterMasterInitializer.initChannel: [clusterId: {}, IP Address: {}.{}], completed.", clusterId, ipAddress, clientPort);
         }
 
-        if (cluster.getElectionState().getChannel() != null) {
-            log.warn("ClusterMasterInitializer.initChannel: {}, {}, Already Connected. Old Connection will be closed.", ipAddress, cluster.getId());
+        if (clusterNode.getElectionState().getChannel() != null) {
+            log.warn("ClusterMasterInitializer.initChannel: [clusterId: {}, IP Address: {}.{}], Already Connected. Old Connection will be closed.", clusterId, ipAddress, clientPort);
             // 이벤트 핸들러 에서 중복 처리 되지 않도록 속성 값을 제거
             channel.attr(AbstractClusterConfig.CLUSTER_ATTRIBUTE_KEY).set(null);
-            cluster.getElectionState().disConnect();
+            clusterNode.getElectionState().disConnect();
 
             channel.disconnect();
             channel.close();
         }
 
-        cluster.getElectionState().connect(channel);
-        channel.attr(AbstractClusterConfig.CLUSTER_ATTRIBUTE_KEY).set(cluster);
+        clusterNode.getElectionState().connect(channel);
+        channel.attr(AbstractClusterConfig.CLUSTER_ATTRIBUTE_KEY).set(clusterNode);
 
         ChannelPipeline pipeline = channel.pipeline();
 
@@ -86,7 +84,7 @@ public class ClusterMasterInitializer extends ChannelInitializer<Channel> {
         pipeline.addLast(new ClusterMasterHandler(this.masterService, this.clusterConfig));
 
         if (this.clusterConfig.isLogging()) {
-            MDC.remove(cluster.getLogKey());
+            MDC.remove(clusterNode.getLogKey());
             MDC.clear();
         }
     }

+ 1 - 2
its-cluster/src/main/java/com/its/common/cluster/service/ClusterSlave.java

@@ -3,7 +3,7 @@ package com.its.common.cluster.service;
 import com.its.common.cluster.codec.ClusterMessageDecoder;
 import com.its.common.cluster.codec.ClusterMessageEncoder;
 import com.its.common.cluster.handler.ClusterSlaveHandler;
-import com.its.common.cluster.vo.AbstractClusterConfig;
+import com.its.common.cluster.config.AbstractClusterConfig;
 import com.its.common.cluster.vo.ClusterNode;
 import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.*;
@@ -141,7 +141,6 @@ public class ClusterSlave implements Callable<Object> {
 
     /**
      * 연결 종료시 처리 이벤트
-     * @param future
      */
     protected synchronized void channelClosed(ChannelFuture future) {
         Channel channel = future.channel();

+ 16 - 5
its-cluster/src/main/java/com/its/common/cluster/utils/ClusterEvenAllocator.java

@@ -1,5 +1,8 @@
 package com.its.common.cluster.utils;
 
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
 import lombok.extern.slf4j.Slf4j;
 
 import java.util.*;
@@ -82,9 +85,9 @@ public final class ClusterEvenAllocator {
         return allocation;
     }
 
-    public static Map<String, List<String>> sequenceAllocate(List<String> servers, List<String> clients) {
-        Map<String, List<String>> allocation = new HashMap<>();
-        for (String server : servers) {
+    public static <K, D> Map<K, List<D>> sequenceAllocate(List<K> servers, List<D> clients) {
+        Map<K, List<D>> allocation = new HashMap<>();
+        for (K server : servers) {
             allocation.put(server, new ArrayList<>());
         }
 
@@ -97,8 +100,8 @@ public final class ClusterEvenAllocator {
         // 여분을 앞쪽 서버에 우선 배정하는 방식
 //        int startIdx = 0;
         for (int ii = 0; ii < numServers; ii++) {
-            String server = servers.get(ii);
-            List<String> serverAllocation = allocation.get(server);
+            K server = servers.get(ii);
+            List<D> serverAllocation = allocation.get(server);
 
 //            int additional = (ii < remainder) ? 1 : 0;
 //            int endIdx = Math.min(startIdx + allocateSize + additional, numClients);
@@ -159,6 +162,14 @@ public final class ClusterEvenAllocator {
         return allocation;
     }
 
+    @Getter
+    @Setter
+    private static class ClusterAllocator {
+
+        private String clusterId;
+        public ClusterAllocator() {}
+    }
+
     public static void main(String[] args) {
         List<String> serverList = Arrays.asList("Server-1", "Server-2", "Server-3");
         List<String> clientList = Arrays.asList(

+ 16 - 0
its-cluster/src/main/java/com/its/common/cluster/utils/ClusterUtils.java

@@ -14,6 +14,9 @@ import io.netty.channel.socket.nio.NioSocketChannel;
 import io.netty.util.concurrent.DefaultThreadFactory;
 
 import java.net.*;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Enumeration;
 
@@ -244,6 +247,19 @@ public class ClusterUtils {
         return "";
     }
 
+    public static String timeToString(long timestamp) {
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss")
+                .withZone(ZoneId.systemDefault());
+        return formatter.format(Instant.ofEpochMilli(timestamp));
+
+    }
+    public static String timeToStringYmd(long timestamp) {
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
+                .withZone(ZoneId.systemDefault());
+        return formatter.format(Instant.ofEpochMilli(timestamp));
+
+    }
+
     static {
         if (OS_NAME != null && OS_NAME.toLowerCase().contains("linux")) {
             isLinuxPlatform = true;

+ 1 - 1
its-cluster/src/main/java/com/its/common/cluster/vo/ClusterMessage.java

@@ -13,7 +13,7 @@ public class ClusterMessage implements Serializable {
 
     private int clusterId;
     private boolean master;
-    private String serverTime;
+    private long currentTimeMillis;
 
     private List<ClusterMessageData> infos;
 }

+ 34 - 11
its-cluster/src/main/java/com/its/common/cluster/vo/ClusterNetState.java

@@ -4,6 +4,9 @@ import io.netty.channel.Channel;
 import lombok.Data;
 
 import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.util.Calendar;
 import java.util.Date;
 
@@ -54,6 +57,13 @@ public class ClusterNetState {
         this.connectTime = new Date();
         this.retryCount = 0;
         setLastRecvTime();
+        // channel 이 null 인 경우 나 자신의 정보를 저장하기위함(항상 통신이 정상이라고 판단하기 위해서)
+        if (channel == null) {
+            // 송신 및 전송시각을 초기 기동시각으로 설정하는 것과 같음
+            setLastRecvTime();
+            setLastSendTime();
+        }
+        loginOk();
     }
     public void loginOk() {
         this.state = ClusterNET.DATA_TRANS;
@@ -72,21 +82,34 @@ public class ClusterNetState {
     public boolean isActive() {
         return this.channel != null && this.channel.isActive();
     }
-    public String getConnectTimeString() {
-        if (this.connectTime == null) {
-            return "----/--/-- --:--:--";
+
+    private String timeToString(long timestamp) {
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
+                .withZone(ZoneId.systemDefault());
+        return formatter.format(Instant.ofEpochMilli(timestamp));
+
+    }
+    public String getLastRecvTimeString() {
+        return timeToString(this.lastRecvTime);
+    }
+    public String getLastSendTimeString() {
+        return timeToString(this.lastSendTime);
+    }
+
+    private String timeToString(Date dateTime) {
+        if (dateTime == null) {
+            return "1970-01-01 00:00:00";
         }
         Calendar cal = Calendar.getInstance();
-        cal.setTime(this.connectTime);
-        return (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(cal.getTime());
+        cal.setTime(dateTime);
+        return (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(cal.getTime());
+    }
+
+    public String getConnectTimeString() {
+        return timeToString(this.connectTime);
     }
 
     public String getDisconnectTimeString() {
-        if (this.disconnectTime == null) {
-            return "----/--/-- --:--:--";
-        }
-        Calendar cal = Calendar.getInstance();
-        cal.setTime(this.disconnectTime);
-        return (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(cal.getTime());
+        return timeToString(this.disconnectTime);
     }
 }

+ 2 - 3
its-cluster/src/main/java/com/its/common/cluster/vo/ClusterNode.java

@@ -13,12 +13,11 @@ public class ClusterNode {
     private int id = -1;
     private String ip = "";
     private int port = 13888;       // 포트 1: 데이터 동기화를 위한 포트
-    private int syncSeconds = 5;
     private boolean logging = false;
     private boolean packetLogging = false;
 
-    private ClusterNetState electionState = new ClusterNetState();
-    private ClusterNetState syncState = new ClusterNetState();
+    private ClusterNetState electionState = new ClusterNetState();  // 서버로 동작하며 클라이언트의 네트워크 상태를 저장
+    private ClusterNetState syncState = new ClusterNetState();      // 클라이언트로 동작하며 서버와의 네트워크 상태를 저장
 
     public String getLogKey() {
         return String.valueOf(this.id);