您的位置:首页 > 编程语言 > Java开发

hash一致性java实现

2017-09-06 17:53 387 查看
简单的用代码表述一下hash一致性
package com;

import java.util.SortedMap;
import java.util.TreeMap;

/**
* hash一致性算法
* 存在雪崩的情况,所以我们创建多个虚拟节点对应物理机可以利用虚拟节点
* Created by lijianzhen1 on 2017/9/6.
*/
public class HashIdentical {
//待添加入Hash环的服务器列表
protected static String[] servers = {"192.168.0.0:111", "192.168.0.1:111",
"192.168.0.2:111", "192.168.0.3:111", "192.168.0.4:111"};
//key表示服务器的hash值,value表示服务器
private static SortedMap<Integer, String> sortedMap = new TreeMap<Integer, String>();

//程序初始化,将所有的服务器放入sortedMap中
static {
for (int i = 0; i < servers.length; i++) {
int hash = getHash(servers[i]);
//System.out.println("[" + servers[i] + "]加入集合中, 其Hash值为" + hash);
sortedMap.put(hash, servers[i]);
}
System.out.println();
}

//使用FNV1_32_HASH算法计算服务器的Hash值,这里不使用重写hashCode的方法,最终效果没区别
protected static int getHash(String server) {
final int p = 16777619;
int hash = (int) 2166136261L;
for (int i = 0; i < server.length(); i++)
hash = (hash ^ server.charAt(i)) * p;
//以下操作注意尽量使用质数
hash += hash << 5;
hash ^= hash >> 3;
hash += hash << 11;
hash ^= hash >> 17;
hash += hash << 19;
// 如果算出来的值为负数则取其绝对值
if (hash < 0)
hash = Math.abs(hash);
return hash;
}

//获得当前路由的节点
private static String getServer(String key) {
//得到对应的hash值
int hash = getHash(key);
//得到大于hash值中所有map
SortedMap<Integer, String> subMap = sortedMap.tailMap(hash);
//注意下边实现了hash一致环
if (subMap.isEmpty()) {
//如果当前的值没有大于该值的,从第一个节点开始
Integer i = sortedMap.firstKey();
//返回对应的服务器地址
return sortedMap.get(i);
} else {
//顺时针下个节点位置,离当前hash值最近的那个位置
Integer i = subMap.firstKey();
return subMap.get(i);
}
}

public static void main(String[] args) {
String[] keys = {"美国", "俄罗斯", "中国"};
for (String key : keys)
System.out.println(key + "-的hash值为" + getHash(key) + ",被路由到节点" + getServer(key));

}
}
package com;

import org.springframework.util.StringUtils;

import java.util.*;

/**
* hash一致性算法
* 存在雪崩的情况,所以我们创建多个虚拟节点对应物理机可以利用虚拟节点
* 为了分布均匀,创建虚拟节点
* Created by lijianzhen1 on 2017/9/6.
*/
public class VirtualHashIdentical extends HashIdentical {
//真实结点列表,考虑到服务器上线、下线的场景,即添加、删除的场景会比较频繁,这里使用LinkedList会更好
private static List<String> realNodes = new LinkedList<String>();
//虚拟节点,key表示虚拟节点的hash值,value表示虚拟节点的名称
private static SortedMap<Integer, String> virtualNodes = new TreeMap<Integer, Str
e3ed
ing>();
//虚拟节点的数目,这里写死,为了演示需要,一个真实结点对应5个虚拟节点
private static final int VIRTUAL_NODES = 5;

static {
//先把原始的服务器添加到真实结点列表中
for (int i = 0; i < servers.length; i++)
realNodes.add(servers[i]);

//再添加虚拟节点,遍历LinkedList使用foreach循环效率会比较高
for (String str : realNodes) {
virtualNodes.putAll(generateVitureNode(str));
}
System.out.println();
}

/**
* 移除一致性hash环中的真实节点
*/
private static void delTrueNode(String server) {
//移除虚拟节点
SortedMap<Integer, String> rmVitureNode = generateVitureNode(server);
for (Iterator<Map.Entry<Integer, String>> entry = rmVitureNode.entrySet().iterator(); entry.hasNext(); ) {
Map.Entry<Integer, String> eMap = entry.next();
System.out.println("移除真实节点为" + server + "的虚拟地址[" + eMap.getKey() + ":" + eMap.getValue() + "]");
virtualNodes.remove(eMap.getKey());
}
//移除真实的物理节点--这里其实不需要移除也可以,如果将来不再接入时候可以考虑移除,但是2^32已经足够大了
}

/**
* 添加真实节点
*
* @param server
*/
private static void addTrueNote(String server) {
//添加真实节点,如果移除真实节点需要添加,没有移除的话就保留就好了
//添加虚拟节点
SortedMap<Integer, String> rmVitureNode = generateVitureNode(server);
for (Iterator<Map.Entry<Integer, String>> entry = rmVitureNode.entrySet().iterator(); entry.hasNext(); ) {
Map.Entry<Integer, String> eMap = entry.next();
System.out.println("生成真实节点为" + server + "的虚拟地址[" + eMap.getKey() + ":" + eMap.getValue() + "]");
}
virtualNodes.putAll(rmVitureNode);
}

/**
* 通过真实的物理机获得虚拟节点
*
* @param trueNode
* @return
*/
private static SortedMap<Integer, String> generateVitureNode(String trueNode) {
SortedMap<Integer, String> virtualNodes = new TreeMap<Integer, String>();
for (int i = 0; i < VIRTUAL_NODES; i++) {
String virtualNodeName = trueNode + "&&VN" + String.valueOf(i);
//这里可以删除
System.out.println("虚拟节点[" + virtualNodeName + "]被添加, hash值为" + getHash(virtualNodeName));
virtualNodes.put(getHash(virtualNodeName), virtualNodeName);
}
return virtualNodes;
}

//获得当前路由的节点
private static String getServer(String key) {
//得到对应的hash值
int hash = getHash(key);
//得到大于hash值中所有map
SortedMap<Integer, String> subMap = virtualNodes.tailMap(hash);
String virtualNode;
//注意下边实现了hash一致环
if (subMap.isEmpty()) {
//如果当前的值没有大于该值的,从第一个节点开始
Integer i = virtualNodes.firstKey();
//返回对应的服务器地址
virtualNode = virtualNodes.get(i);
} else {
//顺时针下个节点位置,离当前hash值最近的那个位置
Integer i = subMap.firstKey();
//返回对应的服务器
virtualNode = subMap.get(i);
}//virtualNode虚拟节点名称要截取一下
if (!StringUtils.isEmpty(virtualNode)) {
return virtualNode.substring(0, virtualNode.indexOf("&&"));
}
return null;
}

public static void main(String[] args) {
String[] keys = {"美国", "俄罗斯", "中国"};
for (String key : keys)
System.out.println(key + "-的hash值为" + getHash(key) + ",被路由到节点" + getServer(key)); System.out.println();
try {
Thread.sleep(1110);
} catch (InterruptedException e) {
e.printStackTrace();
}

//移除某一真实的节点
delTrueNode("192.168.0.0:111");
System.out.println("移除物理机192.168.0.0:111后分配的hash地址\n");
for (String key : keys)
System.out.println(key + "-的hash值为" + getHash(key) + ",被路由到节点" + getServer(key)); System.out.println();
try {
Thread.sleep(1101);
} catch (InterruptedException e) {
e.printStackTrace();
}
addTrueNote("192.168.0.0:111");
System.out.println("添加物理机192.168.0.0:111后分配的hash地址\n");
for (String key : keys)
System.out.println(key + "-的hash值为" + getHash(key) + ",被路由到节点" + getServer(key));
}
}
运行后的结果如下:
虚拟节点[192.168.0.0:111&&VN0]被添加, hash值为441276990虚拟节点[192.168.0.0:111&&VN1]被添加, hash值为7212075虚拟节点[192.168.0.0:111&&VN2]被添加, hash值为1956600191虚拟节点[192.168.0.0:111&&VN3]被添加, hash值为880099582虚拟节点[192.168.0.0:111&&VN4]被添加, hash值为1775594257虚拟节点[192.168.0.1:111&&VN0]被添加, hash值为1589670672虚拟节点[192.168.0.1:111&&VN1]被添加, hash值为1624001080虚拟节点[192.168.0.1:111&&VN2]被添加, hash值为1773104951虚拟节点[192.168.0.1:111&&VN3]被添加, hash值为1648864917虚拟节点[192.168.0.1:111&&VN4]被添加, hash值为1605726925虚拟节点[192.168.0.2:111&&VN0]被添加, hash值为281646770虚拟节点[192.168.0.2:111&&VN1]被添加, hash值为866565852虚拟节点[192.168.0.2:111&&VN2]被添加, hash值为1993194878虚拟节点[192.168.0.2:111&&VN3]被添加, hash值为1815821653虚拟节点[192.168.0.2:111&&VN4]被添加, hash值为1285435563虚拟节点[192.168.0.3:111&&VN0]被添加, hash值为206429284虚拟节点[192.168.0.3:111&&VN1]被添加, hash值为1198719942虚拟节点[192.168.0.3:111&&VN2]被添加, hash值为1004742944虚拟节点[192.168.0.3:111&&VN3]被添加, hash值为1620159605虚拟节点[192.168.0.3:111&&VN4]被添加, hash值为1133756370虚拟节点[192.168.0.4:111&&VN0]被添加, hash值为1109095769虚拟节点[192.168.0.4:111&&VN1]被添加, hash值为458370133虚拟节点[192.168.0.4:111&&VN2]被添加, hash值为1490699463虚拟节点[192.168.0.4:111&&VN3]被添加, hash值为1198431876虚拟节点[192.168.0.4:111&&VN4]被添加, hash值为554406美国-的hash值为413938569,被路由到节点192.168.0.0:111俄罗斯-的hash值为1196559832,被路由到节点192.168.0.4:111中国-的hash值为1625483750,被路由到节点192.168.0.1:111虚拟节点[192.168.0.0:111&&VN0]被添加, hash值为441276990虚拟节点[192.168.0.0:111&&VN1]被添加, hash值为7212075虚拟节点[192.168.0.0:111&&VN2]被添加, hash值为1956600191虚拟节点[192.168.0.0:111&&VN3]被添加, hash值为880099582虚拟节点[192.168.0.0:111&&VN4]被添加, hash值为1775594257移除真实节点为192.168.0.0:111的虚拟地址[7212075:192.168.0.0:111&&VN1]移除真实节点为192.168.0.0:111的虚拟地址[441276990:192.168.0.0:111&&VN0]移除真实节点为192.168.0.0:111的虚拟地址[880099582:192.168.0.0:111&&VN3]移除真实节点为192.168.0.0:111的虚拟地址[1775594257:192.168.0.0:111&&VN4]移除真实节点为192.168.0.0:111的虚拟地址[1956600191:192.168.0.0:111&&VN2]移除物理机192.168.0.0:111后分配的hash地址美国-的hash值为413938569,被路由到节点192.168.0.4:111俄罗斯-的hash值为1196559832,被路由到节点192.168.0.4:111中国-的hash值为1625483750,被路由到节点192.168.0.1:111虚拟节点[192.168.0.0:111&&VN0]被添加, hash值为441276990虚拟节点[192.168.0.0:111&&VN1]被添加, hash值为7212075虚拟节点[192.168.0.0:111&&VN2]被添加, hash值为1956600191虚拟节点[192.168.0.0:111&&VN3]被添加, hash值为880099582虚拟节点[192.168.0.0:111&&VN4]被添加, hash值为1775594257生成真实节点为192.168.0.0:111的虚拟地址[7212075:192.168.0.0:111&&VN1]生成真实节点为192.168.0.0:111的虚拟地址[441276990:192.168.0.0:111&&VN0]生成真实节点为192.168.0.0:111的虚拟地址[880099582:192.168.0.0:111&&VN3]生成真实节点为192.168.0.0:111的虚拟地址[1775594257:192.168.0.0:111&&VN4]生成真实节点为192.168.0.0:111的虚拟地址[1956600191:192.168.0.0:111&&VN2]添加物理机192.168.0.0:111后分配的hash地址美国-的hash值为413938569,被路由到节点192.168.0.0:111俄罗斯-的hash值为1196559832,被路由到节点192.168.0.4:111中国-的hash值为1625483750,被路由到节点192.168.0.1:111
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 算法