一致性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#实现的更多相关文章

  1. 一致性哈希算法与Java实现

    原文:http://blog.csdn.net/wuhuan_wp/article/details/7010071 一致性哈希算法是分布式系统中常用的算法.比如,一个分布式的存储系统,要将数据存储到具 ...

  2. 五分钟理解一致性哈希算法(consistent hashing)

    转载请说明出处:http://blog.csdn.net/cywosp/article/details/23397179 一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法 ...

  3. 每天进步一点点——五分钟理解一致性哈希算法(consistent hashing)

    转载请说明出处:http://blog.csdn.net/cywosp/article/details/23397179     一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT) ...

  4. 一致性哈希算法以及其PHP实现

    在做服务器负载均衡时候可供选择的负载均衡的算法有很多,包括:  轮循算法(Round Robin).哈希算法(HASH).最少连接算法(Least Connection).响应速度算法(Respons ...

  5. Java_一致性哈希算法与Java实现

    摘自:http://blog.csdn.net/wuhuan_wp/article/details/7010071 一致性哈希算法是分布式系统中常用的算法.比如,一个分布式的存储系统,要将数据存储到具 ...

  6. 一致性哈希算法(consistent hashing)【转】

    一致性哈希算法 来自:http://blog.csdn.net/cywosp/article/details/23397179       一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希 ...

  7. 一致性哈希算法学习及JAVA代码实现分析

    1,对于待存储的海量数据,如何将它们分配到各个机器中去?---数据分片与路由 当数据量很大时,通过改善单机硬件资源的纵向扩充方式来存储数据变得越来越不适用,而通过增加机器数目来获得水平横向扩展的方式则 ...

  8. 一致性哈希算法——算法解决的核心问题是当slot数发生变化时,能够尽量少的移动数据

    一致性哈希算法 摘自:http://blog.codinglabs.org/articles/consistent-hashing.html 算法简述 一致性哈希算法(Consistent Hashi ...

  9. 一致性哈希算法原理及Java实现

     一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似.一致性哈希修正了CARP使用的简 单 ...

  10. _00013 一致性哈希算法 Consistent Hashing 新的讨论,并出现相应的解决

    笔者博文:妳那伊抹微笑 博客地址:http://blog.csdn.net/u012185296 个性签名:世界上最遥远的距离不是天涯,也不是海角,而是我站在妳的面前.妳却感觉不到我的存在 技术方向: ...

随机推荐

  1. JavaDailyReports10_19

    今日学习超链接 1.文本链接 使用一对<a>标签 格式:< href ="目标URL" target="目标窗口"> 指针文本    & ...

  2. sql优化最佳实践

    1.选择最有效率的表连接顺序 首先要明白一点就是SQL 的语法顺序和执行顺序是不一致的 SQL的语法顺序: select   [distinct] ....from ....[xxx  join][o ...

  3. 十、scala、spark集群搭建

    spark集群搭建: 1.上传scala-2.10.6.tgz到master 2.解压scala-2.10.6.tgz 3.配置环境变量 export SCALA_HOME=/mnt/scala-2. ...

  4. JS 学习 一

  5. 设计模式之委派模式(Delegate Pattern)深入浅出

    学习目标:精简程序逻辑,提升代码的可读性. 内容定位:希望通过对委派模式的学习,让自己写出更加优雅的代码的人群. 委派模式定义: 委派模式(Delegate Pattern)的基本作用是负责任务的调度 ...

  6. 小米11和iPhone11 哪个好

    小米11:搭载最新一代三星的AMOLED屏幕,120Hz屏幕刷新iPhone11采用6.1英寸的分辨率1792828的LCD屏幕小米手机爆降800 优惠力度空前机会不容错过https://www.xi ...

  7. 如何保持json序列化的顺序性?

    说到json,相信没有人会陌生,我们天天都在用.那么,我们来讨论个问题,json有序吗?是谁来决定的呢?如何保持? 说到底,json是框架还是啥?实际上它只是一个数据格式,一个规范标准,它永远不会限制 ...

  8. 【函数分享】每日PHP函数分享(2021-1-9)

    implode() 将一个一维数组的值转化为字符串. string implode ( string $glue , array $pieces ) 参数描述 glue     默认为空的字符串. p ...

  9. Angular入门到精通系列教程(10)- 指令(Directive)

    1. 摘要 2. 组件与指令之间的关系 2.1. 指令的种类 3. Angular 中指令的用途 4. 指令举例 4.1. 指令功能 4.2. Anuglar CLI生成基本文件 4.3. Direc ...

  10. Go GRPC 入门(二)

    前言 最近较忙,其实准备一篇搞定的 中途有事,只能隔了一天再写 正文 pb.go 需要注意的是,在本个 demo 中,客户端与服务端都是 Golang,所以在客户端与服务端都公用一个 pb.go 模板 ...