一致性hash算法

# -*-coding:utf8 -*-

import sys
import math
from bisect import bisect

if sys.version_info >= (2, 5):
    import hashlib
    md5_constructor = hashlib.md5
else:
    import md5
    md5_constructor = md5.new


class HashRing(object):

    def __init__(self, nodes=None):

        # 最终生成的环 格式为: {key:node}
        self._ring = dict()
        # 最终生成的keys
        self._sorted_keys = []
        self.nodes = []

        total_weight = 0
        for node, weight in nodes.items():
            self.nodes.append(node)
            total_weight += weight

        # 给node生成对应的key
        for node in self.nodes:
            weight = nodes.get(node, 1)

            # 权重因子,意味着某个node会有些虚拟节点
            factor = math.floor(40 * len(self.nodes) * weight / total_weight)

            for i in range(int(factor)):
                # 拿到了key为'%s-%s' % (node, i)的种子数据
                hash_seed = self._hash_digest('%s-%s' % (node, i))
                has_key = self._hash_val(hash_seed)
                self._ring[has_key] = node
                self._sorted_keys.append(has_key)

        # 排序
        self._sorted_keys.sort()

    def get_node(self, key):

        if not self._ring:
            return None

        pos = self.get_node_pos(key)

        if pos is None:
            return None

        return self._ring[self._sorted_keys[pos]]

    def get_node_pos(self, key):
        key = self.gen_key(key)

        pos = bisect(self._sorted_keys, key)

        # 如果是最大的值,那么则落到第一位
        if len(self._sorted_keys) == pos:
            return 0

        return pos

    def gen_key(self, key):
        b_key = self._hash_digest(key)
        return self._hash_val(b_key)

    def _hash_val(self, b_key):

        """
            左移动是地位补0 例如 10 左移2位后 1000
            当一个数左移24数,这个数肯定在 2^24 到z^32之间
            当一个数左移16数,这个数肯定在 2^32 到z^24之间
            求 "|"运算后,最终这个数字就是在0 - 2^32之间
        """
        return ((b_key[3] << 24) | (b_key[2] << 16) | (b_key[1] << 8) | b_key[0]) # noqa

    def _hash_digest(self, key):
        """
            使用md5生成一个数组,长度为16,取数组里面的数字用于计算2^32位的值
        """
        m = md5_constructor()
        m.update(str(key))
        return list(map(ord, str(m.digest())))

if __name__ == "__main__":
    memcache_servers = {'192.168.0.246:11212': 1,
                        '192.168.0.247:11212': 2,
                        '192.168.0.249:11212': 3}

    ring = HashRing(memcache_servers)
    server = ring.get_node('my_key')

    print server

results matching ""

    No results matching ""