一致性哈希算法C#实现
一致性hash实现,以下实现没有考虑多线程情况,也就是没有加锁,需要的可以自行加上。因为换行的问题,阅读不太方便,可以拷贝到本地再读。
1 /// <summary>
2 /// 一致性哈希。
3 /// </summary>
4 public static class ConsistentHashing
5 {
6 /// <summary>
7 /// 虚拟节点倍数
8 /// </summary>
9 private static int _virtualNodeMultiple = 100;
10
11 /// <summary>
12 /// 真实节点信息
13 /// </summary>
14 private static readonly List<string> Nodes = new List<string>();
15
16 /// <summary>
17 /// 虚拟节点信息(int类型主要是为了获取虚拟节点时的二分查找)
18 /// </summary>
19 private static readonly List<int> VirtualNode = new List<int>();
20
21 /// <summary>
22 /// 虚拟节点和真实节点映射,在获取到虚拟节点之后,能以O(1)的时间复杂度返回真实节点
23 /// </summary>
24 private static readonly Dictionary<int, string> VirtualNodeAndNodeMap = new Dictionary<int, string>();
25
26 /// <summary>
27 /// 增加节点
28 /// </summary>
29 /// <param name="hosts">节点集合</param>
30 /// <returns>操作结果</returns>
31 public static bool AddNode(params string[] hosts)
32 {
33 if (hosts == null || hosts.Length == 0)
34 {
35 return false;
36 }
37 Nodes.AddRange(hosts); //先将节点增加到真实节点信息中。
38 foreach (var item in hosts)
39 {
40 for (var i = 1; i <= _virtualNodeMultiple; i++) //此处循环为类似“192.168.3.1”这样的真实ip字符串从1加到1000,算作虚拟节点。192.168.3.11,192.168.3.11000
41 {
42 var currentHash = HashAlgorithm.GetHashCode(item + i) & int.MaxValue; //计算一个hash,此处用自定义hash算法原因是字符串默认的哈希实现不保证对同一字符串获取hash时得到相同的值。和int.MaxValue进行位与操作是为了将获取到的hash值设置为正数
43 if (!VirtualNodeAndNodeMap.ContainsKey(currentHash)) //因为hash可能会重复,如果当前hash已经包含在虚拟节点和真实节点映射中,则以第一次添加的为准,此处不再进行添加
44 {
45 VirtualNode.Add(currentHash);//将当前虚拟节点添加到虚拟节点中
46 VirtualNodeAndNodeMap.Add(currentHash, item);//将当前虚拟节点和真实ip放入映射中。
47 }
48 }
49 }
50 VirtualNode.Sort(); //操作完成之后进行一次映射,是为了后面根据key的hash值查找虚拟节点时使用二分查找。
51 return true;
52 }
53
54 /// <summary>
55 /// 移除节点
56 /// </summary>
57 /// <param name="host">指定节点</param>
58 /// <returns></returns>
59 public static bool RemoveNode(string host)
60 {
61 if (!Nodes.Remove(host)) //如果将指定节点从真实节点集合中移出失败,后序操作不需要进行,直接返回
62 {
63 return false;
64 }
65 for (var i = 1; i <= _virtualNodeMultiple; i++)
66 {
67 var currentHash = HashAlgorithm.GetHashCode(host + i) & int.MaxValue; //计算一个hash,此处用自定义hash算法原因是字符串默认的哈希实现不保证对同一字符串获取hash时得到相同的值。和int.MaxValue进行位与操作是为了将获取到的hash值设置为正数
68 if (VirtualNodeAndNodeMap.ContainsKey(currentHash) && VirtualNodeAndNodeMap[currentHash] == host) //因为hash可能会重复,所以此处判断在判断了哈希值是否存在于虚拟节点和节点映射中之后还需要判断通过当前hash值获取到的节点是否和指定节点一致,如果不一致,则证明这个这个虚拟节点不是当前hash值所拥有的
69 {
70 VirtualNode.Remove(currentHash); //从虚拟节点中移出
71 VirtualNodeAndNodeMap.Remove(currentHash); //从虚拟节点和真实ip映射中移出
72 }
73 }
74 VirtualNode.Sort(); //操作完成之后进行一次映射,是为了后面根据key的hash值查找虚拟节点时使用二分查找。
75 return true;
76 }
77
78 /// <summary>
79 /// 获取所有节点
80 /// </summary>
81 /// <returns></returns>
82 public static List<string> GetAllNodes()
83 {
84 var nodes = new List<string>(Nodes.Count);
85 nodes.AddRange(Nodes);
86 return nodes;
87 }
88
89 /// <summary>
90 /// 获取节点数量
91 /// </summary>
92 /// <returns></returns>
93 public static int GetNodesCount()
94 {
95 return Nodes.Count;
96 }
97
98 /// <summary>
99 /// 重新设置虚拟节点倍数
100 /// </summary>
101 /// <param name="multiple"></param>
102 public static void ReSetVirtualNodeMultiple(int multiple)
103 {
104 if (multiple < 0 || multiple == _virtualNodeMultiple)
105 {
106 return;
107 }
108 var nodes = new List<string>(Nodes.Count);
109 nodes.AddRange(Nodes); //将现有的真实节点拷贝出来
110 _virtualNodeMultiple = multiple; //设置倍数
111 Nodes.Clear();
112 VirtualNode.Clear();
113 VirtualNodeAndNodeMap.Clear(); //清空数据
114 AddNode(nodes.ToArray()); //重新添加
115 }
116
117 /// <summary>
118 /// 获取节点
119 /// </summary>
120 /// <param name="key"></param>
121 /// <returns></returns>
122 public static string GetNode(string key)
123 {
124 var hash = HashAlgorithm.GetHashCode(key) & int.MaxValue;
125 var start = 0;
126 var end = VirtualNode.Count - 1;
127 while (end - start > 1)
128 {
129 var index = (start + end) / 2;
130 if (VirtualNode[index] > hash)
131 {
132 end = index;
133 }
134 else if (VirtualNode[index] < hash)
135 {
136 start = index;
137 }
138 else
139 {
140 start = end = index;
141 }
142 }
143 return VirtualNodeAndNodeMap[VirtualNode[start]];
144 }
145
146 /// <summary>
147 /// hash
148 /// </summary>
149 private static class HashAlgorithm
150 {
151 public static int GetHashCode(string key)
152 {
153 return Hash(ComputeMd5(key));
154 }
155
156 private static int Hash(byte[] digest, int nTime = 0)
157 {
158 long rv = ((long)(digest[3 + nTime * 4] & 0xFF) << 24)
159 | ((long)(digest[2 + nTime * 4] & 0xFF) << 16)
160 | ((long)(digest[1 + nTime * 4] & 0xFF) << 8)
161 | ((long)digest[0 + nTime * 4] & 0xFF);
162 return (int)(rv & 0xffffffffL);
163 }
164 private static byte[] ComputeMd5(string k)
165 {
166 MD5 md5 = new MD5CryptoServiceProvider();
167 byte[] keyBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(k));
168 md5.Clear();
169 return keyBytes;
170 }
171 }
172 }
一致性hash实现
测试代码:
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 ConsistentHashing.AddNode(new[]
6 {
7 "192.168.137.1",
8 "192.168.137.2",
9 "192.168.137.3",
10 "192.168.137.4",
11 "192.168.137.5",
12 "192.168.137.6",
13 "192.168.137.7",
14 "192.168.137.8",
15 "192.168.137.9",
16 "192.168.137.10"
17 });
18 var data = LoadTestData();
19
20 Stopwatch stop = new Stopwatch();
21 stop.Start();
22 foreach (var item in data)
23 {
24 var node = ConsistentHashing.GetNode(item);
25 }
26 stop.Stop();
27
28 var map10 = new Dictionary<string, string>();
29 var mapCount10 = new Dictionary<string, int>();
30
31 var map11 = new Dictionary<string, string>();
32 var mapCount11 = new Dictionary<string, int>();
33
34 var map9 = new Dictionary<string, string>();
35 var mapCount9 = new Dictionary<string, int>();
36
37 #region 10个节点
38 foreach (var item in data)
39 {
40 var host = ConsistentHashing.GetNode(item);
41 if (!map10.ContainsKey(item))
42 {
43 map10.Add(item, host);
44 }
45 if (!mapCount10.ContainsKey(host))
46 {
47 mapCount10.Add(host, 1);
48 }
49 else
50 {
51 mapCount10[host]++;
52 }
53 }
54 #endregion
55
56 #region 11个节点
57 ConsistentHashing.AddNode("192.168.137.11");
58 foreach (var item in data)
59 {
60 var host = ConsistentHashing.GetNode(item);
61 if (!map11.ContainsKey(item))
62 {
63 map11.Add(item, host);
64 }
65 if (!mapCount11.ContainsKey(host))
66 {
67 mapCount11.Add(host, 1);
68 }
69 else
70 {
71 mapCount11[host]++;
72 }
73 }
74 #endregion
75
76 #region 9个节点
77 ConsistentHashing.RemoveNode("192.168.137.11");
78 ConsistentHashing.RemoveNode("192.168.137.10");
79 foreach (var item in data)
80 {
81 var host = ConsistentHashing.GetNode(item);
82 if (!map9.ContainsKey(item))
83 {
84 map9.Add(item, host);
85 }
86 if (!mapCount9.ContainsKey(host))
87 {
88 mapCount9.Add(host, 1);
89 }
90 else
91 {
92 mapCount9[host]++;
93 }
94 }
95 #endregion
96
97 #region 数据比较和存储
98 var tenAndNine = 0;
99 foreach (var item in map9)
100 {
101 if (map10[item.Key] != item.Value)
102 {
103 tenAndNine++;
104 }
105 }
106 var tenAndEleven = 0;
107 foreach (var item in map11)
108 {
109 if (map10[item.Key] != item.Value)
110 {
111 tenAndEleven++;
112 }
113 }
114 List<string> csv = new List<string>();
115 csv.Add("ip,10,10分布,9,9分布,11,11分布");
116 foreach (var item in mapCount11)
117 {
118 var str = item.Key;
119 if (mapCount10.ContainsKey(item.Key))
120 {
121 str += "," + mapCount10[item.Key];
122 str += "," + (mapCount10[item.Key] / (double)100000).ToString("F2");
123 }
124 else
125 {
126 str += ",";
127 str += ",";
128 }
129 if (mapCount9.ContainsKey(item.Key))
130 {
131 str += "," + mapCount9[item.Key];
132 str += "," + (mapCount9[item.Key] / (double)100000).ToString("F2");
133 }
134 else
135 {
136 str += ",";
137 str += ",";
138 }
139 str += "," + mapCount11[item.Key];
140 str += "," + (mapCount11[item.Key] / (double)100000).ToString("F2");
141 csv.Add(str);
142 }
143 csv.Add(string.Format("10-1的失效数据:{0},比例:{2:F2}。10+1的失效数据:{1},比例:{3:F2}", tenAndNine, tenAndEleven, (tenAndNine / (double)1000000), (tenAndEleven / (double)1000000)));
144 File.WriteAllLines(@"E:\1000.csv", csv, Encoding.UTF8);
145 #endregion
146
147 Console.ReadKey();
148 }
149
150 /// <summary>
151 /// 生成测试key
152 /// </summary>
153 /// <param name="count"></param>
154 /// <returns></returns>
155 private static List<string> LoadTestData(int count = 1000000)
156 {
157 var data = new List<string>(count);
158
159 for (var i = 0; i < count; i++)
160 {
161 data.Add(GetRandomString(15, true, true, true, false, ""));
162 }
163 return data;
164 }
165
166 ///<summary>
167 ///生成随机字符串
168 ///</summary>
169 ///<param name="length">目标字符串的长度</param>
170 ///<param name="useNum">是否包含数字,1=包含,默认为包含</param>
171 ///<param name="useLow">是否包含小写字母,1=包含,默认为包含</param>
172 ///<param name="useUpp">是否包含大写字母,1=包含,默认为包含</param>
173 ///<param name="useSpe">是否包含特殊字符,1=包含,默认为不包含</param>
174 ///<param name="custom">要包含的自定义字符,直接输入要包含的字符列表</param>
175 ///<returns>指定长度的随机字符串</returns>
176 public static string GetRandomString(int length, bool useNum, bool useLow, bool useUpp, bool useSpe, string custom)
177 {
178 byte[] b = new byte[4];
179 new System.Security.Cryptography.RNGCryptoServiceProvider().GetBytes(b);
180 Random r = new Random(BitConverter.ToInt32(b, 0));
181 string s = null, str = custom;
182 if (useNum == true) { str += "0123456789"; }
183 if (useLow == true) { str += "abcdefghijklmnopqrstuvwxyz"; }
184 if (useUpp == true) { str += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
185 if (useSpe == true) { str += "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; }
186 for (int i = 0; i < length; i++)
187 {
188 s += str.Substring(r.Next(0, str.Length - 1), 1);
189 }
190 return s;
191 }
192 }
测试代码
测试结果,key总数100万,图中的分布都是以10万为基数:
一致性哈希算法C#实现的更多相关文章
- 一致性哈希算法与Java实现
原文:http://blog.csdn.net/wuhuan_wp/article/details/7010071 一致性哈希算法是分布式系统中常用的算法.比如,一个分布式的存储系统,要将数据存储到具 ...
- 五分钟理解一致性哈希算法(consistent hashing)
转载请说明出处:http://blog.csdn.net/cywosp/article/details/23397179 一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法 ...
- 每天进步一点点——五分钟理解一致性哈希算法(consistent hashing)
转载请说明出处:http://blog.csdn.net/cywosp/article/details/23397179 一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT) ...
- 一致性哈希算法以及其PHP实现
在做服务器负载均衡时候可供选择的负载均衡的算法有很多,包括: 轮循算法(Round Robin).哈希算法(HASH).最少连接算法(Least Connection).响应速度算法(Respons ...
- Java_一致性哈希算法与Java实现
摘自:http://blog.csdn.net/wuhuan_wp/article/details/7010071 一致性哈希算法是分布式系统中常用的算法.比如,一个分布式的存储系统,要将数据存储到具 ...
- 一致性哈希算法(consistent hashing)【转】
一致性哈希算法 来自:http://blog.csdn.net/cywosp/article/details/23397179 一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希 ...
- 一致性哈希算法学习及JAVA代码实现分析
1,对于待存储的海量数据,如何将它们分配到各个机器中去?---数据分片与路由 当数据量很大时,通过改善单机硬件资源的纵向扩充方式来存储数据变得越来越不适用,而通过增加机器数目来获得水平横向扩展的方式则 ...
- 一致性哈希算法——算法解决的核心问题是当slot数发生变化时,能够尽量少的移动数据
一致性哈希算法 摘自:http://blog.codinglabs.org/articles/consistent-hashing.html 算法简述 一致性哈希算法(Consistent Hashi ...
- 一致性哈希算法原理及Java实现
一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似.一致性哈希修正了CARP使用的简 单 ...
- _00013 一致性哈希算法 Consistent Hashing 新的讨论,并出现相应的解决
笔者博文:妳那伊抹微笑 博客地址:http://blog.csdn.net/u012185296 个性签名:世界上最遥远的距离不是天涯,也不是海角,而是我站在妳的面前.妳却感觉不到我的存在 技术方向: ...
随机推荐
- Kafka超详细学习笔记【概念理解,安装配置】
目录 本篇要点 Kafka介绍 作为流处理平台的三种特性 主要应用 四个核心API 基本术语 快速开始 安装配置Zookeeper 下载kafka 配置文件 Windows的命令 启动Zookeepe ...
- flowable中传入审批人是list
package org.springblade.flow.engine.listener; import org.flowable.engine.delegate.DelegateExecution; ...
- esx.problem.hyperthreading.unmitigated
是因为VMware新发布的一个漏洞补丁导致的具体解释可参阅VMware官方kb,有详细解释和版本说明. 可选择屏蔽该问题告警 选中主机>配置>高级系统设置>编辑>修改" ...
- Light Probes
LightProbes (光照探针,光探测器?) 提供了一种方法用于捕获和使用 穿过场景中空白空间的 光(light)的信息. 和光照贴图相似(lightmaps),Light Probes也存储关于 ...
- JAVE JDK安装步骤
1.安装JDK 选择安装目录 安装过程中会出现两次 安装提示 .第一次是安装 jdk ,第二次是安装 jre .建议两个都安装在同一个java文件夹中的不同文件夹中.(不能都安装在java文件夹的根目 ...
- 【分布式锁】Redis实现可重入的分布式锁
一.前言 之前写的一篇文章<细说分布式锁>介绍了分布式锁的三种实现方式,但是Redis实现分布式锁关于Lua脚本实现.自定义分布式锁注解以及需要注意的问题都没描述.本文就是详细说明如何利用 ...
- IDEA关联mysql失败Server returns invalid timezone. Go to 'Advanced' tab and set 'serverTimezon'
时区错误,MySQL默认的时区是UTC时区 要修改mysql的时长 在mysql的命令模式下,输入: set global time_zone='+8:00'; 再次连接成功
- win8.1下jdk的安装和环境变量的配置 eclipse的安装和汉化
1.首先下载jdk安装包,安装的时候会有两个文件安装,一个是jdk一个是jre建议两个文件不要安装在一个目录下 2.安装jdk后面就是配置环境变量,path和classpath,path要在用户变量中 ...
- linux 笔记的注意事项
声明:本人Linux的笔记是根据<鸟哥私房菜>而写的 command [-option] parameter1 parameter2 ... command 是命令的名称: [ ]中括号是 ...
- 上班如何优雅的使用idea刷LeetCode(力扣)
打开idea file->setting ->plugins 搜索 "LeetCode" install "LeetCode editor" 重启后 ...