zookeeper中注册应用所在机器节点(机器ID),全局唯一序列使用

在分布式系统中,全局唯一序列为分布式系统重要组成部分,而雪花算法(或基于雪花算法之上封装的类)在序列生成最为广泛,但是雪花算法需要获取应用所在服务器的ID--机器ID。如果配置文件为统一管理(配置中心),则无法在配置文件中设置机器ID,此时需要通过zookeeper进行机器ID的创建。看代码。

首先引用jar包

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.68</version>
        </dependency>
        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.9</version>
        </dependency>
package com.example.demo.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.I0Itec.zkclient.ZkClient;
import org.apache.zookeeper.CreateMode;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;

/**
 * 该类仅用于在注册zookeeper时,有zk进行应用节点分配,在分布式应用中,
 * 同一应用不同的节点(机器ID),主要用于后期使用雪花算法进行唯一序列创建使用。
 * 如果使用docker部署,则该类最为重要,如果使用普通方法进行部署,则节点可以在
 * properties进行配置,不用机器配置不同数字即可
 */
@Component
public class CurrentNodeConfig {

    /**
     * 根节点路径
     */
    private static String ROOT_PATH = "/zkCurrentNodeRoot";

    /**
     * 临时节点路径
     */
    private static String CURRENT_IP_NODE_PATH = "/zkCurrentNodeRoot/zkCurrentIPNode";

    /**
     * 根节点数据List
     */
    private static String IP_NODE_DATAlIST_PATH = "/zkCurrentNodeRoot/zkIPNodeDataList";

    /**
     * 当前节点
     */
    public static int currentNode;

    /**
     * zk链接,只要leader地址
     */
    public static String address = "127.0.0.1:2181";
    /**
     * zk链接,超时时间
     */
    public static int timeOut = 30000;
    /**
     * zk链接,用户名
     */
    public static String username = "admin";
    /**
     * zk链接,密码
     */
    public static String password = "123456";

    public static int getCurrentNode() {
        return currentNode;
    }

    public static void setCurrentNode(int currentNode) {
        CurrentNodeConfig.currentNode = currentNode;
    }

    public static ZkClient zkClient = null;

    public CurrentNodeConfig() {
        //创建根Client
        genClient();
        //初始化节点
        initNode();
    }

    /**
     * 注册Zk
     */
    private static synchronized void genClient() {
        if (zkClient == null) {
            zkClient = new ZkClient(address, timeOut);
            if (!StringUtils.isEmpty(username)) {
                String allUser = username + ":" + password;
                zkClient.addAuthInfo("digest", allUser.getBytes());
            }
        }
    }

    /**
     * 创建节点,如果没有节点则直接创建节点
     *
     * @param path 路径
     * @param data 数据
     * @param mode 创建模式
     */
    public void create(String path, String data, CreateMode mode) {
        if (!exists(path)) {
            zkClient.create(path, data, mode);
        }
    }

    /**
     * 删除节点
     *
     * @param path 路径
     */
    public void delete(String path) {
        if (exists(path)) {
            zkClient.delete(path);
        }
    }

    /**
     * 修改节点值
     *
     * @param path 路径
     * @param data 数据值
     */
    public void modify(String path, String data) {
        if (exists(path)) {
            zkClient.writeData(path, data);
        }
    }

    /**
     * 查看节点值
     *
     * @param path 路径
     * @return
     */
    public String queryData(String path) {
        String pathData = null;
        if (exists(path)) {
            pathData = zkClient.readData(path);
        }
        return pathData;
    }

    /**
     * 查看节点列表
     *
     * @param path 节点
     * @return
     */
    public List<String> queryChildNodeList(String path) {
        List<String> list = null;
        if (exists(path)) {
            list = zkClient.getChildren(path);
        }
        return list;
    }

    /**
     * 判断路径是否存在
     *
     * @param path 路径
     * @return 存在-true 不存在-false
     */
    public boolean exists(String path) {
        return zkClient.exists(path);
    }

    /**
     * 初始化所有节点及数据
     */
    public void zkIPNodePersistent() {
        //创建根节点路径
        create(ROOT_PATH, null, CreateMode.PERSISTENT);
        //创建临时节点路径
        create(CURRENT_IP_NODE_PATH, null, CreateMode.PERSISTENT);
        //创建根节点数据List
        create(IP_NODE_DATAlIST_PATH, null, CreateMode.PERSISTENT);
    }

    public int getNodeIndex(Collection<Object> object, int currentNodeNum) {
        int nodeIndex = 1;
        if (currentNodeNum == 0 && object.size() == 0) {
            return nodeIndex;
        }
        List<Integer> arrData = getConversion(object);
        Collections.sort(arrData);
        int listMaxIndex = arrData.size() - 1;
        //当当前数组数据和当前数据最大值相同时,直接+1
        if (arrData.size() == arrData.get(listMaxIndex)) {
            nodeIndex = arrData.get(listMaxIndex) + 1;
        } else {
            //判断只有一个节点数据情况处理
            if (arrData.size() == 1) {
                return getListSizeOneIndex(nodeIndex, arrData);
            }
            //处理不是连续节点,补全空缺值,并返回
            nodeIndex = getListSizeMoreIndex(listMaxIndex, nodeIndex, arrData);
        }
        return nodeIndex;
    }

    /**
     * 处理存在多个节点情况
     *
     * @param listMaxIndex
     * @param nodeIndex    当前默认节点
     * @param arrData      历史节点列表
     * @return
     */
    private int getListSizeMoreIndex(int listMaxIndex, int nodeIndex, List<Integer> arrData) {
        int currentIndex = listMaxIndex - 1;
        for (int i = 0; i < currentIndex; i++) {
            int currentData = arrData.get(i);
            int nextData = arrData.get(i + 1);
            int difData = nextData - currentData;
            if (difData > 1) {
                nodeIndex = currentData + 1;
            }
        }
        //处理极端异常情况
        if (arrData.contains(nodeIndex)) {
            nodeIndex = arrData.get(listMaxIndex) + 1;
        }
        return nodeIndex;
    }

    /**
     * 处理只有一个节点情况
     *
     * @param nodeIndex 当前节点
     * @param arrData   节点列表
     * @return
     */
    private int getListSizeOneIndex(int nodeIndex, List<Integer> arrData) {
        int currentData = arrData.get(0);
        if (currentData == 1) {
            nodeIndex = currentData + 1;
        } else {
            nodeIndex = currentData - 1;
        }
        return nodeIndex;
    }

    /**
     * 将Collection集合转化为List
     *
     * @param object 集合
     * @return
     */
    private List<Integer> getConversion(Collection<Object> object) {
        List<Integer> list = new ArrayList<>();
        for (Object o : object) {
            list.add(Integer.parseInt(o.toString()));
        }
        return list;
    }

    /**
     * 主机ipNodeIndex保存到zk持久化节点数据中,去掉当前节点不存在的数据
     *
     * @param zkIPNodeObject
     * @param currentDataList
     */
    public void amendmentIpNodeIndexData(JSONObject zkIPNodeObject, List<String> currentDataList) {
        Set<String> ipNodeIndexSet = zkIPNodeObject.keySet();
        Iterator<String> iterator = ipNodeIndexSet.iterator();
        String ipNode = null;
        while (iterator.hasNext()) {
            ipNode = iterator.next();
            if (currentDataList.contains(ipNode)) {
                iterator.remove();
            }
        }
    }

    /**
     * 初始化node节点
     */
    public void initNode() {
        //处理所有根节点
        zkIPNodePersistent();
        //获取当前地址的IP
        String localAddress = "127.0.0.1";
        try{
            InetAddress inetAddress = InetAddress.getLocalHost();
            localAddress = inetAddress.getHostAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        //获取zk中持久存在的address node IP 数据
        String IPNodeDataListData = queryData(IP_NODE_DATAlIST_PATH);
        JSONObject zkIPNodeObject = null;
        if(null == IPNodeDataListData){
            zkIPNodeObject = new JSONObject();
        } else {
            zkIPNodeObject = (JSONObject)JSON.parse(IPNodeDataListData);
        }
        Set<String> nodeDataSet = zkIPNodeObject.keySet();
        //获取zk中持久存在的address node IP 数据
        List<String> currentDataList = queryChildNodeList(CURRENT_IP_NODE_PATH);
        System.out.println("初始化currentDataList:" + currentDataList);
        Collection<Object> object = zkIPNodeObject.values();
        int currentNodeIndex = 0;
        //判断当前IP是否在zk中注册
        boolean notExistNDSFlag = nodeDataSet.contains(localAddress);
        if(notExistNDSFlag){
            //如果已经注册成功,则直接获取当前节点IP的currentNodeIndex
            currentNodeIndex = zkIPNodeObject.getIntValue(localAddress);
        } else {
            //如果没有注册,则进行节点创建
            currentNodeIndex = getNodeIndex(object, currentDataList.size());
        }
        System.out.println("currentNodeIndex:" + currentNodeIndex);
        //当前操作主机IP nodeIndex 保存到zk持久节点数据中
        zkIPNodeObject.put(localAddress, currentNodeIndex);
        //当前操作主机IP保存到临时节点中
        currentDataList.add(localAddress);
        System.out.println("zkIPNodeObject:" + zkIPNodeObject);
        System.out.println("currentDataList" + currentDataList);
        //主机IP nodeindex 保存到zk持久化节点数据,去掉当前节点不存在的数据
        amendmentIpNodeIndexData(zkIPNodeObject, currentDataList);
        System.out.println("主机IP nodeindex 保存到zk持久化节点数据,去掉当前节点不存在的数据");
        System.out.println("zkIPNodeObject" + zkIPNodeObject);
        System.out.println("currentDataList" + currentDataList);
        String addressIPNodePath = CURRENT_IP_NODE_PATH + "/" + localAddress;
        create(addressIPNodePath, null, CreateMode.EPHEMERAL);
        modify(IP_NODE_DATAlIST_PATH, zkIPNodeObject.toJSONString());
        currentNode = currentNodeIndex;
        System.out.println("CURRENT_IP_NODE_PATH存在?--》" + exists(CURRENT_IP_NODE_PATH));
        System.out.println("IP_NODE_DATAlIST_PATH --》" + queryData(IP_NODE_DATAlIST_PATH));
        System.out.println("*******zk加入结束*********");
        System.out.println("CURRENT_IP_NODE_PATH --》" + queryChildNodeList(CURRENT_IP_NODE_PATH));
        System.out.println("*******zk加入彻底结束*********");
    }
}

说明:此方法需要在服务器的hosts中配置hostname对应的机器IP,否则,获取的机器IP为127.0.0.1。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值