一致性哈希

概述: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 环上所有的虚拟节点都被删除了

注意:添加节点或者删除节点后,需要将两个节点间的数据重新计算然后分配

原文地址:https://www.cnblogs.com/newguy/p/9163032.html