| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- package com.its.common.cluster.config;
- import com.its.common.cluster.vo.ClusterNET;
- import com.its.common.cluster.vo.ClusterNode;
- import io.netty.util.AttributeKey;
- import lombok.Data;
- import lombok.extern.slf4j.Slf4j;
- import javax.annotation.PostConstruct;
- import java.io.IOException;
- import java.util.*;
- import java.util.stream.Collectors;
- @Slf4j
- @Data
- public abstract class AbstractClusterConfig {
- public static final AttributeKey<ClusterNode> CLUSTER_ATTRIBUTE_KEY = AttributeKey.valueOf("clusterInfo");
- private boolean enabled = true; // 클러스터 기능 사용 여부
- // 어플리케이션 클러스터 정보
- private boolean master = true;
- private int id = -1; // 서버 ID (1부터 시작, 0은 사용하지 않음)
- private boolean autoFailbackEnabled = false; // client 모드시 작업을 바로 넘겨주는 옵션(기본 false)
- private int masterId = -1;
- private int syncSeconds = 5; // 데이터 동기화 주기 (초 단위, 기본 5초, 최소 2초, 최대 60초)
- private String ip = "127.0.0.1"; // 클러스터 서버의 IP 주소
- private int port = 13888; // 데이터 동기화를 위한 포트
- private boolean logging = false; // 라이브러리 내 로깅 여부
- private boolean packetLogging = false; // 패킷 로깅 여부
- private int electionScheduleSeconds = 2; // 마스터 선출 스케줄 실행 주기 (단위: 초, 기본값 2초)
- private int connectTimeoutSeconds = 5; // 클라이언트(Slave)가 서버(Master)에 연결을 시도할 때의 타임아웃 (단위: 초, 기본값 5초)
- private boolean witnessNode = false; // 이 서버 인스턴스가 Witness 노드인지 여부
- private List<ClusterNode> nodes;
- private ClusterNode myCluster;
- private final HashMap<Integer, ClusterNode> clusterMap = new HashMap<>();
- @PostConstruct
- private void init() throws IOException {
- if (this.nodes == null) {
- this.nodes = new ArrayList<>();
- }
- if (this.electionScheduleSeconds < 2) {
- this.electionScheduleSeconds = 2;
- }
- if (this.electionScheduleSeconds > 10) {
- this.electionScheduleSeconds = 10;
- }
- }
- public boolean isClusterAlive(int clusterId) {
- ClusterNode clusterNode = this.clusterMap.get(clusterId);
- if (clusterNode == null) {
- return true;
- }
- return clusterNode.getElectionState().getState() != ClusterNET.CLOSED;
- }
- public ClusterNode getClusterNode(int clusterId) {
- return this.clusterMap.get(clusterId);
- }
- public void loggingInfo() {
- log.info("Cluster config enabled: {}", this.enabled);
- log.info(" id: {}", this.id);
- log.info(" master: {}", this.master);
- log.info(" syncSeconds: {}", this.syncSeconds);
- log.info(" ip: {}", this.ip);
- log.info(" port: {}", this.port);
- log.info(" logging: {}", this.logging);
- log.info(" packetLogging: {}", this.packetLogging);
- log.info(" node: {} EA.", this.nodes.size());
- for (ClusterNode node : this.nodes) {
- log.info(" ----------------node id: {}", node.getId());
- log.info(" master: {}", node.isMaster());
- log.info(" ip: {}", node.getIp());
- log.info(" port: {}", node.getPort());
- log.info(" logging/packetLogging: {}/{}", node.isLogging(), node.isPacketLogging());
- }
- }
- private boolean setDefaults() {
- // 클러스터가 하나라 단일 클러스터 정보로 설정
- this.master = true;
- this.id = 1;
- this.masterId = 1;
- this.syncSeconds = 5;
- this.ip = "127.0.0.1";
- this.port = 13888;
- this.logging = false;
- this.packetLogging = false;
- // 단일 노드 정보 설정
- this.nodes = new ArrayList<>();
- ClusterNode masterNode = new ClusterNode();
- masterNode.setMaster(this.master);
- masterNode.setId(this.id);
- masterNode.setIp(this.ip);
- masterNode.setPort(this.port);
- masterNode.setLogging(this.logging);
- masterNode.setPacketLogging(this.packetLogging);
- masterNode.setWitness(false);
- this.nodes.add(masterNode);
- this.myCluster = masterNode;
- this.enabled = false;
- // 나 자신의 네트워크 상태정보를 항상 CONNECT로 지정
- masterNode.getElectionState().connect(null);
- masterNode.getSyncState().connect(null);
- return true;
- }
- public boolean validateClusterInfo() {
- if (!this.enabled) {
- return setDefaults();
- }
- if (this.nodes == null || this.nodes.isEmpty()) {
- log.error("클러스터 노드 리스트가 비어 있습니다.");
- 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)
- .sorted()
- .collect(Collectors.toList());
- // 중복 제거 후 크기 비교
- Set<Integer> uniqueIds = new HashSet<>(ids);
- if (uniqueIds.size() != ids.size()) {
- log.error("클러스터 노드 ID에 중복이 있습니다: {}", ids);
- return false;
- }
- // 순차성 검증: 1부터 시작해서 1씩 증가해야 함
- for (int ii = 0; ii < ids.size(); ii++) {
- int expected = ii + 1;
- if (ids.get(ii) != expected) {
- log.error("클러스터 노드 ID가 순차적이지 않습니다. 예상: {}, 실제: {}", expected, ids.get(ii));
- return false;
- }
- }
- // 검증 통과, 클러스터 정보 설정
- if (this.syncSeconds < 2) {
- this.syncSeconds = 2;
- }
- if (this.syncSeconds > 30) {
- this.syncSeconds = 30;
- }
- // Master and host information setting
- int masterId = Integer.MAX_VALUE;
- for (ClusterNode node : this.nodes) {
- this.clusterMap.put(node.getId(), node);
- int nodeId = node.getId();
- if (nodeId == this.id) {
- this.ip = node.getIp();
- this.port = node.getPort();
- }
- if (nodeId < masterId) {
- masterId = nodeId;
- }
- }
- // Master setting
- ClusterNode masterNode = this.clusterMap.get(masterId);
- masterNode.setMaster(true);
- this.master = (this.id == masterId);
- this.masterId = masterId;
- // 나 자신의 네트워크 상태정보를 항상 CONNECT로 지정
- ClusterNode localNode = this.clusterMap.get(this.id);
- localNode.getElectionState().connect(null);
- localNode.getSyncState().connect(null);
- this.myCluster = localNode;
- return true;
- }
- public ClusterNode get(String ipAddress) {
- for (Map.Entry<Integer, ClusterNode> entry : this.clusterMap.entrySet()) {
- ClusterNode cluster = entry.getValue();
- if (cluster.getIp().equals(ipAddress)) {
- return cluster;
- }
- }
- return null;
- }
- private int parseConfigData(String data) {
- try {
- return Integer.parseInt(data);
- }
- catch (NumberFormatException e) {
- return -1;
- }
- }
- // private String getStringValue(String item, String defValue) throws IOException {
- // if (!this.enabled) {
- // return defValue;
- // }
- // Pattern stringPattern = Pattern.compile(item + "=([a-zA-Z0-9]+)");
- // try (BufferedReader reader = new BufferedReader(new FileReader(this.configFile))) {
- // String line;
- // while ((line = reader.readLine()) != null) {
- // Matcher matcher = stringPattern.matcher(line);
- // if (matcher.matches()) {
- // return matcher.group(1);
- // }
- // }
- // }
- // return defValue;
- // }
- // private int getIntValue(String item, int defValue) throws IOException {
- // if (!this.enabled) {
- // return defValue;
- // }
- // Pattern serverIdPattern = Pattern.compile(item+"=(\\d+)");
- // try (BufferedReader reader = new BufferedReader(new FileReader(this.configFile))) {
- // String line;
- // while ((line = reader.readLine()) != null) {
- // Matcher matcher = serverIdPattern.matcher(line);
- // if (matcher.matches()) {
- // return parseConfigData(matcher.group(1));
- // }
- // }
- // }
- // return defValue;
- // }
- // private void setServerId() throws IOException {
- // Pattern serverIdPattern = Pattern.compile("server\\.id=(\\d+)");
- // try (BufferedReader reader = new BufferedReader(new FileReader(this.configFile))) {
- // String line;
- // while ((line = reader.readLine()) != null) {
- // Matcher matcher = serverIdPattern.matcher(line);
- // if (matcher.matches()) {
- // this.serverId = parseConfigData(matcher.group(1));
- // break;
- // }
- // }
- // }
- // }
- // private void loadClusterConfig() throws IOException {
- // this.serverId = getIntValue("server.id", 1);
- // this.syncSeconds = getIntValue("syncSeconds", 5);
- // if (this.syncSeconds < 2) {
- // this.syncSeconds = 2;
- // }
- // if (this.syncSeconds > 60) {
- // this.syncSeconds = 60;
- // }
- // if (!this.enabled) {
- // return;
- // }
- // Pattern serverPattern = Pattern.compile("server\\.(\\d+)=([\\d\\.]+):(\\d+)");
- // Pattern pattern = Pattern.compile("server\\.(\\d+)=([\\d\\.]+):(\\d+):(\\d+)");
- // int masterId = Integer.MAX_VALUE;
- // try (BufferedReader reader = new BufferedReader(new FileReader(this.configFile))) {
- // String line;
- // while ((line = reader.readLine()) != null) {
- // if (line.startsWith("server.")) {
- // Matcher matcher = serverPattern.matcher(line);
- // if (matcher.matches()) {
- // int serverId = parseConfigData(matcher.group(1));
- // String ipAddress = matcher.group(2);
- // int syncPort = parseConfigData(matcher.group(3));
- //
- // if (serverId == this.serverId) {
- // this.ipAddress = ipAddress;
- // this.syncPort = syncPort;
- // }
- // if (serverId < masterId) {
- // masterId = serverId;
- // }
- // ClusterNode haCluster = ClusterNode.builder()
- // .master(false)
- // .id(serverId)
- // .ip(ipAddress)
- // .syncPort(syncPort)
- // .logging(this.logging)
- // .electionState(new ClusterNetState())
- // .syncState(new ClusterNetState())
- // .build();
- // this.clusterMap.put(haCluster.getId(), haCluster);
- // log.info("{}", haCluster);
- // }
- // }
- // }
- // }
- // if (this.serverId == masterId) {
- // this.master = true;
- // }
- //
- // for (Map.Entry<Integer, ClusterNode> entry : this.clusterMap.entrySet()) {
- // ClusterNode cluster = entry.getValue();
- // if (cluster.getId() == masterId) {
- // cluster.setMaster(true);
- // break;
- // }
- // }
- // }
- }
|