概述:python 实现一致性哈希的功能,然后通过使用功能来了解原理
我在其他博客中看了很多关于一致性hash的原理,很详细。没有实际的例子,感觉这个理论的应用无从下手。这里我就从实际的例子分析下一致性hash
先上代码
import md5 class HashRing(object): def __init__(self, nodes=None, replicas=3): """ nodes: 节点数 replicas: 虚拟节点个数 """ self.replicas = replicas self.ring = dict() self._sorted_keys = [] if nodes: for node in nodes: self.add_node(node) def add_node(self, node): """添加一个节点到 hash 环中, 包括所有的虚拟节点""" for i in xrange(0, self.replicas): key = self.gen_key('%s:%s' % (node, i)) self.ring[key] = node self._sorted_keys.append(key) self._sorted_keys.sort() def remove_node(self, node): """从 hash 环上移除一个节点, 包括所有的虚拟节点一并移除""" for i in xrange(0, self.replicas): key = self.gen_key('%s:%s' % (node, i)) del self.ring[key] self._sorted_keys.remove(key) def get_node(self, string_key): """ 根据关键字获取所属节点 注意: 此处关键字即为需要计算的字符串 """ return self.get_node_pos(string_key)[0] def get_node_pos(self, string_key): """计算虚拟节点""" if not self.ring: return None, None key = self.gen_key(string_key) nodes = self._sorted_keys for i in xrange(0, len(nodes)): node = nodes[i] if key <= node: return self.ring[node], i return self.ring[nodes[0]], 0 def get_nodes(self, string_key): """Given a string key it returns the nodes as a generator that can hold the key. The generator is never ending and iterates through the ring starting at the correct position. """ if not self.ring: yield None, None node, pos = self.get_node_pos(string_key) for key in self._sorted_keys[pos:]: yield self.ring[key] while True: for key in self._sorted_keys: yield self.ring[key] def gen_key(self, key): """Given a string key it returns a long value, this long value represents a place on the hash ring. md5 is currently used because it mixes well. """ m = md5.new() m.update(key) return long(m.hexdigest(), 16) def print_all(self): """打印 hash 环所有节点信息""" if not self.ring: return None, None p = dict() for k in list(set(self.ring.values())): p[k] = 0 for i, v in enumerate(self._sorted_keys): p[self.ring[v]] += 1 # print "ring slot %d is node %s, hash vale is %s" % (i, self.ring[i], v) print "[{0}] node:{1}#{2} value:{3}".format(i, self.ring[v], p[self.ring[v]], v)
第一步: 创建2个节点的环,每个节点包含3个虚拟节点
上图中前面的 0-5 代表总共有 6 个节点,a#1 表示 a 节点的第一个虚拟节点。图上的value值是按照从小到大的顺序排列,在 hash 环上分布如下图所示
第二步: 计算 key 所属节点,以 ip 举例(字符串类型的都能计算)
上图计算了这三个 ip 所归属的节点,下面具体说明是怎么计算出来的
首先计算 '192.168.184.1' 的 hash 值,然后从环上找到第一个大于该值的节点返回
注意:1. 这里是找到的虚拟节点,然后返回的实际节点
2. 如果没有找到就返回第一个节点
添加一个节点
添加的 c 节点,包含了所有的虚拟节点,总共节点数增加了 3
删除一个节点
这里将 b 节点删除了, 在 hash 环上所有的虚拟节点都被删除了
注意:添加节点或者删除节点后,需要将两个节点间的数据重新计算然后分配