第十一章 自己实现一致性hash算法
2016-02-28 00:27
316 查看
关于一致性hash算法的意义以及其相对于简单求余法(除数求余法)的好处,查看第六章 memcached剖析
注意:真实的hash环的数据结构是二叉树,这里为了简便使用了列表List
1、一致性hash算法的使用地方
memcached服务器
Jedis分片机制
2、真实服务器节点没有虚拟化的一致性hash算法实现
ServerNode:真实服务器节点
View Code
注意:
在实际操作中,一台memcached服务器虚拟成150台比较合适(100~200)
从环上删除节点的算法写的较差,但是考虑到删除节点的操作在实际使用中用的比较少(宕机比较少,人为的删除节点也较少),也无所谓
删除节点的时候,注意使用foreach语法糖去遍历的时候,在遍历的过程中不可以做删除、增加操作,否则会抛出并发修改异常,具体的原因见注释和第二章 ArrayList源码解析;想要实现在遍历的过程中进行删除、增加操作,使用简单for循环,见如上代码
注意:真实的hash环的数据结构是二叉树,这里为了简便使用了列表List
1、一致性hash算法的使用地方
memcached服务器
Jedis分片机制
2、真实服务器节点没有虚拟化的一致性hash算法实现
ServerNode:真实服务器节点
package hash2; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.zip.CRC32; /** * 具有虚拟节点的一致性hash实现(数据结构:list) * 一致性hash的真正数据结构是二叉树 */ public class ConsistentHashWithVirtualNode { private List<VirtualServerNode> virtualServers = new ArrayList<VirtualServerNode>();//存放虚拟节点 private static final int virtualCount = 8;//每个真实节点虚拟成8个虚拟节点 /** 计算服务器和存储的键的hash值 */ public long hash(String str){ CRC32 crc32 = new CRC32(); crc32.update(str.getBytes()); return crc32.getValue(); } /** * 添加server的虚拟节点到环上 * @param serverName ip:port */ public void addServer(String serverName){ for(int count=0;count<virtualCount;count++){ VirtualServerNode node = new VirtualServerNode(); node.setServerName(serverName); node.setVirtualServerHash(hash(serverName+"-"+count));//虚拟节点的名字:serverName+"-"+count virtualServers.add(node); } Collections.sort(virtualServers, new VirtualServerComparator()); } /** * 从环上删除server节点(需要删除所有的该server节点对应的虚拟节点) */ public void deleteServer(String serverName){ /* * 在这种删除的时候,会出现java.util.ConcurrentModificationException * 这是因为此处的遍历方式为使用ArrayList内部类Itr进行遍历, * 在遍历的过程中发生了remove、add等操作,导致modCount发生了变化, * 产生并发修改异常, * 可以使用下边的那一种方式来进行遍历(遍历方式不是Itr), * 再这样的遍历过程中,add和remove都是没有问题的 */ /*for(VirtualServerNode node : virtualServers){ if(node.getServerName().equals(serverName)){ virtualServers.remove(node); } }*/ for(int i=0;i<virtualServers.size();i++) { VirtualServerNode node = virtualServers.get(i); if(node.getServerName().equals(serverName)) { virtualServers.remove(node); } } } /** * 获取一个缓存key应该存放的位置 * @param cachekey 缓存的key * @return 缓存的服务器节点 */ public VirtualServerNode getServer(String cachekey){ long keyHash = hash(cachekey); for(VirtualServerNode node : virtualServers){ if(keyHash<=node.getVirtualServerHash()){ return node; } } return virtualServers.get(0);//如果node没有合适放置位置,放在第一台服务器上去 } /****************测试*******************/ public void printServers(){ for(VirtualServerNode server : virtualServers){ System.out.println(server.getServerName()+"-->"+server.getVirtualServerHash()); } } public static void main(String[] args) { ConsistentHashWithVirtualNode ch = new ConsistentHashWithVirtualNode(); ch.addServer("127.0.0.1:11211"); ch.addServer("127.0.0.1:11212"); ch.addServer("127.0.0.2:11211"); ch.addServer("127.0.0.2:11212"); ch.printServers(); VirtualServerNode node = ch.getServer("hello"); System.out.println(ch.hash("hello")+"-->"+node.getServerName()+"-->"+node.getVirtualServerHash()); VirtualServerNode node2 = ch.getServer("name"); System.out.println(ch.hash("name")+"-->"+node2.getServerName()+"-->"+node2.getVirtualServerHash()); VirtualServerNode node3 = ch.getServer("a"); System.out.println(ch.hash("a")+"-->"+node3.getServerName()+"-->"+node3.getVirtualServerHash()); /*********************删除节点之后**********************/ ch.deleteServer("127.0.0.1:11212"); ch.printServers(); VirtualServerNode node0 = ch.getServer("hello"); System.out.println(ch.hash("hello")+"-->"+node0.getServerName()+"-->"+node0.getVirtualServerHash()); VirtualServerNode node02 = ch.getServer("name"); System.out.println(ch.hash("name")+"-->"+node02.getServerName()+"-->"+node02.getVirtualServerHash()); VirtualServerNode node03 = ch.getServer("a"); System.out.println(ch.hash("a")+"-->"+node03.getServerName()+"-->"+node03.getVirtualServerHash()); } }
View Code
注意:
在实际操作中,一台memcached服务器虚拟成150台比较合适(100~200)
从环上删除节点的算法写的较差,但是考虑到删除节点的操作在实际使用中用的比较少(宕机比较少,人为的删除节点也较少),也无所谓
删除节点的时候,注意使用foreach语法糖去遍历的时候,在遍历的过程中不可以做删除、增加操作,否则会抛出并发修改异常,具体的原因见注释和第二章 ArrayList源码解析;想要实现在遍历的过程中进行删除、增加操作,使用简单for循环,见如上代码
相关文章推荐
- 前端框架学习资源
- 懂得钩子Hook以及在Thinkphp下利用钩子使用行为扩展
- (转)从零开始学习OpenWrt完美教程
- 第三百三十一天 how can I 坚持
- 两张图让你看懂事件派发机制
- c语言:两个整数的正整数差(单分支结构)
- [LeetCode] 104 二叉树最大深度
- 《核心分析》第一周
- 2-4-单链表链式存储结构-线性表-第2章-《数据结构》课本源码-严蔚敏吴伟民版
- windows 清理邮件菜单
- tomcat 下部署 php
- SSL连接出现的问题
- java38.Socket通信------使用ServerSocket建立聊天服务器---2
- IIR型高斯滤波的原理及实现
- [hdu3943]K-th Nya Number
- android framework初步理解
- Android系统之G-sersor调试
- tomcat端口被占用问题解决
- android-tip-关于SpannableString的使用
- [LeetCode][JavaScript]Happy Number