负载均衡-基础-一致性哈希算法及java实现
一致性hash算法,参考:
http://www.blogjava.net/hello-yun/archive/2012/10/10/389289.html
针对这篇文章,加入了自己的理解,在原有的代码上进行了修改。
https://github.com/luoqg/my-code/blob/master/j-algorithm/src/main/java/com/luoq/algorithm/consistencyhash/ConsistencyHash.java
/**
* 一致性hash 的java 实现
* @author luoqiang
* @data 2016/11/08
*/
public class ConsistencyHash { public ConsistencyHash(List<Node> shards){
shards = shards;
init();
} private static class Node{
private String name;
private String ip; public Node(String name, String ip) {
this.name = name;
this.ip = ip;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getIp() {
return ip;
} public void setIp(String ip) {
this.ip = ip;
} @Override
public String toString() {
return "Node{" +
"ip='" + ip + '\'' +
", name='" + name + '\'' +
'}';
}
} private static class Client{
public Client(String name, Long hashCode) {
this.name = name;
this.hashCode = hashCode;
} public Client(String name) {
this.name = name;
} private String name;
private Long hashCode; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Long getHashCode() {
return hashCode;
} public void setHashCode(Long hashCode) {
this.hashCode = hashCode;
}
} private static TreeMap<Long,Node> nodes;//虚拟节点hash值 到 真实主机 的映射 private static TreeMap<Long,Node> treeKey;//客户端hash值 到 真实节点 的映射 private static List<Node> shards = new ArrayList<Node>();//真实主机 private static List<Client> cliends = new ArrayList<Client>();//客户端 private static TreeMap<Long,Client> clientTree;//客户端自己hash 和客户端的映射 private static final int NODE_NUM = 100;//每个机器节点关联的虚拟节点个数 private void init(){
nodes = new TreeMap<Long, Node>();
treeKey = new TreeMap<Long, Node>();
clientTree = new TreeMap<Long, Client>();
for (int i = 0; i < shards.size(); i++) {
final Node shardInfo = shards.get(i);
for (int n = 0; n < NODE_NUM; n++) {
Long key = hash("SHARD-"+shardInfo.name+"-NODE-"+n);
nodes.put(key,shardInfo);
}
}
} /**
* 添加一个真实主机
*/
private void addNode(Node n){
System.out.println("添加主机"+n+"的变化:");
for (int i = 0; i < NODE_NUM; i++) {
Long lg = hash("SHARD-"+n.name+"-NODE-"+i);
SortedMap<Long,Node> head = nodes.headMap(lg);
SortedMap<Long,Node> between;
if(head.size() == 0){
between = treeKey.tailMap(nodes.lastKey());//建立在 最后一个虚拟主机的hash值 不和 客户端的hash值相等。
}else{
Long begin = head.lastKey();
between = treeKey.subMap(begin,lg);
}
nodes.put(lg,n);
for(Iterator<Long> it=between.keySet().iterator();it.hasNext();){
Long lo = it.next();
treeKey.put(lo, nodes.get(lg));
}
}
} /**
* 删除一个真实主机
* @param n
*/
private void deleteNode(Node n){
System.out.println("删除主机"+n+"的变化:");
for (int i = 0; i < NODE_NUM; i++) {
Long virturalHashCode = hash("SHARD-" + n.name + "-NODE-" + i);
SortedMap<Long,Node> tail = nodes.tailMap(virturalHashCode);// 等于和大于 此值 == 顺时针 环形此值后面
SortedMap<Long,Node> head = nodes.headMap(virturalHashCode);// 严格小于 此值(不等于) == 顺时针 环形此值前面
SortedMap<Long, Node> between;
if(head.size() == 0){
between = treeKey.tailMap(nodes.lastKey());
}else{
Long begin = head.lastKey();
Long end = tail.firstKey();
/**
* 方法用于返回此映射的键值从fromKey(包括)到toKey(不包括)的部分视图。
* (如果fromKey和toKey相等,则返回映射为空)返回的映射受此映射支持,
* 因此改变返回映射反映在此映射中,反之亦然。
*/
between = treeKey.subMap(begin,end);//在n节点的第i个虚拟节点的所有key的集合
}
nodes.remove(virturalHashCode);//从nodes中删除n节点的第i个虚拟节点
for(Iterator<Long> it = between.keySet().iterator();it.hasNext();){
Long lo = it.next();
treeKey.put(lo, nodes.get(tail.firstKey()));
}
}
} /**
* 客户端hash值 映射 到 真实主机
*/
private void keyToNode(List<Client> clients){
for (Client client : clients) {
Long hashCode = hash(client.getName());
SortedMap<Long, Node> tail = nodes.tailMap(hashCode); // 沿环的顺时针找到一个虚拟节点
Node node = tail.size() == 0 ? nodes.get(nodes.firstKey()) : nodes.get(tail.firstKey());
treeKey.put(hashCode,node);
client.setHashCode(hashCode);
clientTree.put(hashCode,client);
}
} /**
* 输出客户端 映射到 真实主机
*/
private void printKeyTree(){
for(Iterator<Long> it = treeKey.keySet().iterator();it.hasNext();){
Long lo = it.next();
System.out.println(clientTree.get(lo).name+"(hash:"+lo+")连接到主机->"+treeKey.get(lo));
}
} /**
* MurMurHash算法,是非加密HASH算法,性能很高,
* 比传统的CRC32,MD5,SHA-1
* (这两个算法都是加密HASH算法,复杂度本身就很高,带来的性能上的损害也不可避免)
* 等HASH算法要快很多,而且据说这个算法的碰撞率很低.
* http://murmurhash.googlepages.com/
*/
private Long hash(String key){ ByteBuffer buf = ByteBuffer.wrap(key.getBytes());
int seed = 0x1234ABCD; ByteOrder byteOrder = buf.order();
buf.order(ByteOrder.LITTLE_ENDIAN); long m = 0xc6a4a7935bd1e995L;
int r = 47; long h = seed ^ (buf.remaining() * m); long k;
while (buf.remaining() >= 8) {
k = buf.getLong(); k *= m;
k ^= k >>> r;
k *= m; h ^= k;
h *= m;
} if (buf.remaining() > 0) {
ByteBuffer finish = ByteBuffer.allocate(8).order(
ByteOrder.LITTLE_ENDIAN);
// for big-endian version, do this first:
// finish.position(8-buf.remaining());
finish.put(buf).rewind();
h ^= finish.getLong();
h *= m;
} h ^= h >>> r;
h *= m;
h ^= h >>> r; buf.order(byteOrder);
return h;
} public static void main(String[] args) {
/**
* 客户端的hash值 和 真实主机的100个虚拟节点的hash值
*
* 一起均匀地分布在顺时针由小到大这个环上。(0 - 2^32 )
*
* 具体 客户端 最终 连接到 哪个主机,
*
* 原则是:将客户端hash值,顺时针往后 最近的 虚拟节点hash值。
*
*/
Node s1 = new Node("s1", "192.168.1.1");
Node s2 = new Node("s2", "192.168.1.2");
Node s3 = new Node("s3", "192.168.1.3");
Node s4 = new Node("s4", "192.168.1.4");
Node s5 = new Node("s5", "192.168.1.5");
shards.add(s1);
shards.add(s2);
shards.add(s3);
shards.add(s4);
ConsistencyHash sh = new ConsistencyHash(shards);
System.out.println("添加客户端,一开始有4个主机,分别为s1,s2,s3,s4,每个主机有100个虚拟主机:");
for (int i = 1; i <= 9; i++) {
String name = "10"+i+"客户端";
cliends.add(new Client(name));
}
sh.keyToNode(cliends);
sh.printKeyTree();
sh.deleteNode(s2);
sh.printKeyTree();
sh.addNode(s5);
sh.printKeyTree();
}
}
控制台输出结果:

负载均衡-基础-一致性哈希算法及java实现的更多相关文章
- 一致性哈希算法与Java实现
原文:http://blog.csdn.net/wuhuan_wp/article/details/7010071 一致性哈希算法是分布式系统中常用的算法.比如,一个分布式的存储系统,要将数据存储到具 ...
- Java_一致性哈希算法与Java实现
摘自:http://blog.csdn.net/wuhuan_wp/article/details/7010071 一致性哈希算法是分布式系统中常用的算法.比如,一个分布式的存储系统,要将数据存储到具 ...
- [转载] 应用于负载均衡的一致性哈希及java实现
转载自http://blog.csdn.net/haitao111313/article/details/7537799 这几天看了几遍一致性哈希的文章,但是都没有比较完整的实现,因此试着实现了一下, ...
- 阿里巴巴的分布式应用框架-dubbo负载均衡策略--- 一致哈希算法
dubbo是阿里巴巴公司开发的一个开源分布式应用框架,基于服务的发布者和订阅者,服务者启动服务向注册中心发布自己的服务:消费者(订阅者)启动服务器向注册中心订阅所需要的服务.注册中心将订阅的服务注册列 ...
- 一致性哈希算法(consistent hashing)(转)
原文链接:每天进步一点点——五分钟理解一致性哈希算法(consistent hashing) 一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网 ...
- memcached 一致性哈希算法
本文转载自:http://blog.csdn.net/kongqz/article/details/6695417 一.概述 1.我们的memcache客户端使用了一致性hash算法ketama进行数 ...
- 7月目标 socket , 一致性哈希算法 ; mongodb分片; 分布式消息队列; 中间件的使用场景
分布式的基础:一致性哈希 路由算法的一致性hash http://www.jiacheo.org/blog/174 http://www.tuicool.com/articles/vQVbmai ...
- 一致性哈希算法(Consistent Hashing) .
应用场景 这里我先描述一个极其简单的业务场景:用4台Cache服务器缓存所有Object. 那么我将如何把一个Object映射至对应的Cache服务器呢?最简单的方法设置缓存规则:object.has ...
- 五分钟理解一致性哈希算法(consistent hashing)
转载请说明出处:http://blog.csdn.net/cywosp/article/details/23397179 一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法 ...
随机推荐
- js 批量设置css样式
在js中更换样式比较常见,但是批量设置比较少遇见: 但是在做就是插件时,不想额外的添加css文件(需要导入,还可能引起冲突),能批量设置就比较方便了. 以下代码是来自网上的三种方法,使用第二种最方便了 ...
- PHP 原创视频教程-网站开发新手视频教程
PHP 原创视频教程-网站开发新手视频教程 有偿招徒弟,,视频免费提供. 本视频教程,面向的是毫无经验的新手,快速上手的. 第一次做视频做的不好的,请各位看官多多包含. 第一部分,HTML 视频教程 ...
- [LeetCode] Summary Ranges 总结区间
Given a sorted integer array without duplicates, return the summary of its ranges. For example, give ...
- [LeetCode] The Skyline Problem 天际线问题
A city's skyline is the outer contour of the silhouette formed by all the buildings in that city whe ...
- NoSql系列目录
mongodb系列学习 Mongodb学习笔记一(Mongodb环境配置) Mongodb学习笔记二(Mongodb基本命令) Mongodb学习笔记三(Mongodb索引操作及性能测试) Mongo ...
- 【WPF】 通过FarPoint显示Excel
1.FarPoint 只支持winform,在Wpf中要引用:WindowsFormsIntegration.dll2.*.xaml文件引用 xmlns:wfi ="clr-names ...
- MongoDBDao 工具类(包含分页取数据)
mongdb工具类 package e16wifi.statistic.com.mongodb; import java.util.ArrayList; import java.util.List; ...
- iOS开发小技巧 - label中的文字添加点击事件
Label中的文字添加点击事件 GitHub地址:https://github.com/lyb5834/YBAttributeTextTapAction 以前老师讲过类似的功能,自己懒得回头看了,找了 ...
- python--django项目如何设置用自己的iP地址访问项目
一.首先需要执行>manage.py runserver 0.0.0.0:8000. 二.在setting.py里面需要添加ALLOWED_HOSTS="*".
- vue-Resource(与后端数据交互)
单来说,vue-resource就像jQuery里的$.ajax,用来和后端交互数据的.可以放在created或者ready里面运行来获取或者更新数据... vue-resource文档:https: ...