`
xiaoliang330
  • 浏览: 111938 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

一致性hash算法测试

阅读更多
因为用memcached集群缓存数据,所以增删服务器节点 对缓存key的影响需要考虑一种策略来实现 数据缓存key所映射的节点变动至最小值(这句话好长啊,就是缓存服务器的增减,对在已经缓存了的数据影响降到最小,比如“test”这个数据之前存在a1节点服务器上,那么增加删除了服务器节点,‘test’依然在 a1上(有可能不在,这个原因可以看以下代码),用10个数据来说明吧,感觉有点只可意会不可言传,10个数据,在节点变化时,尽量只有2个数据发生变动,ok)


下面代码示例:




package com.xll;
//服务器对象
public class Server {

	private String name;
	private String password;
	private String ip;
	private String port;

	public Server() {
	}

	public Server(String name, String password, String ip, String port) {
		super();
		this.name = name;
		this.password = password;
		this.ip = ip;
		this.port = port;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getIp() {
		return ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}

	public String getPort() {
		return port;
	}

	public void setPort(String port) {
		this.port = port;
	}

	@Override
	public String toString() {
		return "name:" + name + "ip:" + ip + "port:" + port;
	}

}







package com.xll;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
//server注册,算法生成
public class ServerLoadBalance {

	private TreeMap<Long, Server> nodes;

	private List<Server> servers;

	private final int NODE_NUM = 200;

	public ServerLoadBalance(List<Server> servers) {
		super();
		this.servers = servers;
		init();
	}
	
	private void init() { // 初始化一致性hash环
		nodes = new TreeMap<Long, Server>();
		for (int i = 0; i != servers.size(); ++i) { // 每个真实机器节点都需要关联虚拟节点
			final Server shardInfo = servers.get(i);

			for (int n = 0; n < NODE_NUM; n++){
				// 一个真实机器节点关联NODE_NUM个虚拟节点
				Long hash_value = hash("SHARD-" + shardInfo.getIp() + "-NODE-" + n);
				//System.out.println("第"+i+"个server的hash 散列值="+hash_value);
				nodes.put(hash_value, shardInfo);
			}
				

		}
		System.out.println("Finish inited virtual node....");
	}

	/**
	 * 通过key的一致性hash值 得到 相应的 Server对象
	 * @param key
	 * @return
	 */
	public Server getShardInfo(String key) {
		Long hash_value = hash(key);
		//System.out.println("key="+key+"的hash值="+hash_value);
		
		SortedMap<Long, Server> tail = nodes.tailMap(hash_value); // 沿环的顺时针找到一个虚拟节点
		if (tail.size() == 0) {
			return nodes.get(nodes.firstKey());
		}
		return tail.get(tail.firstKey()); // 返回该虚拟节点对应的真实机器节点的信息
	}

	/**
	 * 一致性hash算法
	 * @param key
	 * @return
	 */
	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;
	}
}







package com.xll;

import java.util.ArrayList;
import java.util.List;
//主要的测试类
public class TestConsistenHash {
	
	public static Server s1 = new Server("ser1", "123", "192.168.216.1", "1");
	public static Server s2 = new Server("ser2", "123", "192.168.216.2", "2");
	public static Server s3 = new Server("ser3", "123", "192.168.216.3", "3");
	public static Server s4 = new Server("ser4", "123", "192.168.216.4", "4");
	public static Server s5 = new Server("ser5", "123", "192.168.216.5", "5");
	public static List<Server> list = new ArrayList<Server>();
	
	public static int count1 = 0;
	public static int count2 = 0;
	public static int count3 = 0;
	public static int count4 = 0;
	public static int count5 = 0;
	
	
	public static void main(String[] args) {
		
		
		
		/**
		 * 可以通过  增加 / 剔除 server来测试 被点击量 的变化
		 */
		list.add(s1);
		list.add(s2);
		list.add(s3);
		list.add(s4);
		list.add(s5);
		
		
		
		
		/**
		 * key的数量为1000个
		 * 
		 * 
		 * 
		 * 在ServerLoadBalance 的 virtual node 数量NODE_NUM 为200时(这个值能达到负载均衡了,100或更小时还没达到)
		 * 测试的两组数据(5台server和剔除server3时的4台server)
		 * 
		 * 
		 * server1 hit count = 197
		 * server2 hit count = 193
		 * server3 hit count = 210
		 * server4 hit count = 170
		 * server5 hit count = 230
		 * 
		 * 
		 * server1 hit count = 265
		 * server2 hit count = 248
		 * server3 hit count = 0
		 * server4 hit count = 214
		 * server5 hit count = 273
		 * 
		 * 
		 * 
		 * 
		 * 在ServerLoadBalance 的 virtual node 数量NODE_NUM 为1时(负载均衡在100或更小时还没达到)
		 * 测试的两组数据(5台server和剔除server3时的4台server)
		 * server1 hit count = 359
		 * server2 hit count = 94
		 * server3 hit count = 387
		 * server4 hit count = 67
		 * server5 hit count = 93
		 * 
		 * 
		 * server1 hit count = 359
		 * server2 hit count = 481
		 * server3 hit count = 0
		 * server4 hit count = 67
		 * server5 hit count = 93
		 * 
		 * 
		 * 
		 * 
		 */
		
		
		
		
		ServerLoadBalance server = new ServerLoadBalance(list);
		

		for (int i = 0; i < 1000; i++) {
			String key =  ""+(i+20);
			
			Server s = (Server)server.getShardInfo(key);
			if(s.toString().equals(s1.toString()))
				count1 ++;
			else if(s.toString().equals(s2.toString()))
				count2 ++;
			else if(s.toString().equals(s3.toString()))
				count3 ++;
			else if(s.toString().equals(s4.toString()))
				count4 ++;
			else
				count5 ++;
			
			
			//System.out.println("key" + i + ", server=" + s);
			
		}
		
		
		//得到各server的命中 情况
		System.out.println("#############");
		System.out.println("server1 hit count = "+TestConsistenHash.count1);
		System.out.println("server2 hit count = "+TestConsistenHash.count2);
		System.out.println("server3 hit count = "+TestConsistenHash.count3);
		System.out.println("server4 hit count = "+TestConsistenHash.count4);
		System.out.println("server5 hit count = "+TestConsistenHash.count5);
		System.out.println("#############");
		
		
		
//		String key = "test";
//		TestThread tt = new TestThread(shard, key);
//		
//		new Thread(tt, "0").start();
//		new Thread(tt, "10001").start();
//		new Thread(tt, "20001").start();
//		new Thread(tt, "30001").start();
//		new Thread(tt, "40001").start();
//		new Thread(tt, "50001").start();

		
		
		
		
	}

}







package com.xll;
//用于跑大量数据
public class TestThread extends Thread{
	ServerLoadBalance shard;
	String key;
	
	public TestThread(ServerLoadBalance shard,String key){
		this.shard = shard;
		this.key = key;
	}
	
	
	@Override
	public void run() {
		runHash();
		printCountValue();
	}
	
	public  void runHash(){
		
		String name = currentThread().getName();
		int start = Integer.parseInt(name);
		
			for (int i = start; i < start+10000; i++) {
				Server s = (Server)shard.getShardInfo(key+i);
				increaseCount(s);
			}
		
	}
	
	public void increaseCount(Server s){
		if(s.toString().equals(TestConsistenHash.s1.toString()))
			increase(1);
		else if(s.toString().equals(TestConsistenHash.s2.toString()))
			increase(2);
		else if(s.toString().equals(TestConsistenHash.s3.toString()))
			increase(3);
		else if(s.toString().equals(TestConsistenHash.s4.toString()))
			increase(4);
		else
			increase(5);
	}
	
	static synchronized void increase(int i){
		
		switch (i) {
		case 1:
			TestConsistenHash.count1 ++;
			break;
		case 2:
			TestConsistenHash.count2 ++;
			break;
		case 3:
			TestConsistenHash.count3 ++;
			break;
		case 4:
			TestConsistenHash.count4 ++;
			break;
		default:
			TestConsistenHash.count5 ++;
			break;
		}
		
	}

	
	void printCountValue(){
		System.out.println("#############");
		System.out.println("server1 hit count = "+TestConsistenHash.count1);
		System.out.println("server2 hit count = "+TestConsistenHash.count2);
		System.out.println("server3 hit count = "+TestConsistenHash.count3);
		System.out.println("server4 hit count = "+TestConsistenHash.count4);
		System.out.println("server5 hit count = "+TestConsistenHash.count5);
		System.out.println("#############");
		
	}
}







在测试类中,有几组数据对一致性的测试,当每个真实的服务器节点只有一个虚拟节点时,去掉一台机器,那么这台机器的负荷只会倾倒性的转移到一台服务器上,其他节点数据和负荷不发生变化, 这个验证了: 一致性hash在某个节点变化时,其他节点上的原来的数据依然保持


当每个节点的虚拟节点数增加至200个时, 这时去掉一台服务器节点,这个节点的访问量变为0, 其负载均衡的分摊至每个还存在的服务器节点上,这个验证了:一致性hash还能达到负载均衡,而不会导致某台负荷过重而导致 多米诺(相继挂掉)的效应




分享到:
评论

相关推荐

    Ketama一致性Hash算法(含Java代码) 1

    如果没有找到,则取整个环的第个节点。测试结果测试代码是整理的,主体法没有变分布平均性测试:测试随机成的众多key是否会平均分布到各个结点上测试结果如下:最上是参

    SpringBoot_shardDB_shardTable:SpringBoot集成Sharding-JDBC实现分库分表,自定义分片算法,基于一致性hash算法,易于扩容

    一致性Hash算法,易于扩容;添加了 单元测试,使用Spring提供的RestTemplate调用RestFul风格的API接口;整合了 quartz 定时任务框架 ,并进行了封装,只需在构建完定时任务Job类后,在 application-quartz....

    修复(一致性敏感哈希):使用一致性敏感哈希(CSH)的图像修复-matlab开发

    此提交使用一致性敏感散列 (CSH) 提供图像修复。 为了使用它,您需要来自http://www.eng.tau.ac.il/~simonk/CSH/index.html的 CSH 工具箱 包括一个测试脚本和几个测试图像。

    md5加密算法 C语言(经过测试验证完整版)

    MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特...

    基于go语言实现的分布式缓存系统完整源码+说明(以键值对的形式存储数据).zip

    基于go语言实现的分布式缓存系统源码+项目说明(以键值对的形式存储数据,一致性hash算法选择存储节点,Protobuf通信协议编解码。用户输入查询请求后,会优先在缓存系统查询,查不到则使用回调函数去源数据库查询,...

    Python基于hashlib模块的文件MD5一致性加密验证示例

    本文实例讲述了Python基于hashlib模块的文件MD5一致性加密验证。分享给大家供大家参考,具体如下: 使用hashlib模块,可对文件MD5一致性加密验证: #python 检测文件MD5值 #python version 2.6 import hashlib import...

    Java编写多个爬虫实例

    ConsistentHash 一致性Hash WordCount Map-Reduce算法例子 Retrive 文件下载 IP 获得IP地址示例 ip QQ纯真数据库示例 HtmlParser 网页内容提取库HtmlParser的源码项目 nekohtml-1.9.7 nekohtml的源码项目 RhinoTest ...

    【白雪红叶】JAVA学习技术栈梳理思维导图.xmind

    一致性hash 消峰 分库分表 锁 悲观锁 乐观锁 行级锁 分布式锁 分区排队 一致性 一致性算法 paxos zab nwr raft gossip 柔性事务(TCC) 一致性原理 CAP BASE 中间件 数据库 mysql 存储引擎 ...

    基于springBoot自研微服务框架+源代码+文档说明

    一致性hash算法,支持可配置虚拟节点数 #### 支持可插拔的注册中心 orion-register-zk-starter cp orion-register-etcd-starter cp orion-register-nacos-starter ap ...

    网站架构技术

    分布式缓存的一致性hash算法 数据存储服务器集群的伸缩性设计 关系数据库集群的伸缩性设计 nosql数据库的伸缩性设计 随需应变:网站的可扩展性 构建可扩展的网站架构 利用分布式消息队列降低系统耦合性 ...

    Jump-Consistent-Hashing-Evaluation:对一致性哈希的评估,尤其是 Google 的 Jump Consistent Hashing

    跳跃一致哈希计算 甚至服务器之间的数据分布也非常重要:另一个重要方面是能够... 关于一致性哈希,使用的算法是谷歌的论文“A Fast, Minimal Memory, Consistent Hash Algorithm”中提出的Jump Consistent Hashing。

    fastcoll_v1.0.0.5.exe

    MD5碰撞测试程序并提供源代码. 利用该程序可伪造出有相同MD5的不同程序. MD5是目前最热门的加密算法,我们通常用MD5值来验证文件的完整性。例如在一些比较正规的下载网站,通常会提供软件的MD5值,这样我们就可以对...

    integrity:提供Merkle DAG结构的Java库

    之所以称为“会话”,是因为它封装了您在创建和使用Hash-Tree时希望具有一致性的内容,例如使用的哈希算法,TreeBuilder的种类(目前仅在内存中)以及可能共享的实例(例如MessageDigest)。 可以将一个实例(使用...

    java 面试题 总结

    Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。 12、final, finally, finalize的区别。  final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。 ...

    超级有影响力霸气的Java面试题大全文档

    Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。 15、final, finally, finalize的区别。  final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。 ...

    Oracle9i的init.ora参数中文说明

    Oracle9i初始化参数中文说明 Blank_trimming: 说明: 如果值为TRUE, 即使源长度比目标长度 (SQL92 兼容) 更长, 也允许分配数据。 值范围: TRUE | FALSE 默认值: FALSE serializable: 说明: 确定查询是否获取表级...

Global site tag (gtag.js) - Google Analytics