在分布式系统中,全局唯一序列为分布式系统重要组成部分,而雪花算法(或基于雪花算法之上封装的类)在序列生成最为广泛,但是雪花算法需要获取应用所在服务器的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。