����JFIF��x�x����'
Server IP : 78.140.185.180 / Your IP : 3.144.216.188 Web Server : LiteSpeed System : Linux cpanel13.v.fozzy.com 4.18.0-513.11.1.lve.el8.x86_64 #1 SMP Thu Jan 18 16:21:02 UTC 2024 x86_64 User : builderbox ( 1072) PHP Version : 7.3.33 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /usr/local/lib/python3.6/site-packages/uhashring/ |
Upload File : |
from bisect import insort from collections import Counter from hashlib import md5 class KetamaRing: """Implement a ketama compatible consistent hashing ring.""" def __init__(self, replicas=4): """Create a new HashRing.""" self._distribution = Counter() self._keys = [] self._nodes = {} self._replicas = replicas self._ring = {} self._listbytes = lambda x: x def hashi(self, key, replica=0): """Returns a ketama compatible hash from the given key.""" dh = self._listbytes(md5(str(key).encode("utf-8")).digest()) rd = replica * 4 return (dh[3 + rd] << 24) | (dh[2 + rd] << 16) | (dh[1 + rd] << 8) | dh[0 + rd] def _hashi_weight_generator(self, node_name, node_conf): """Calculate the weight factor of the given node and yield its hash key for every configured replica. :param node_name: the node name. """ ks = ( node_conf["vnodes"] * len(self._nodes) * node_conf["weight"] ) // self._weight_sum for w in range(0, ks): w_node_name = f"{node_name}-{w}" for i in range(0, self._replicas): yield self.hashi(w_node_name, replica=i) @staticmethod def _listbytes(data): """Python 2 compatible int iterator from str. :param data: the string to int iterate upon. """ return map(ord, data) def _create_ring(self, nodes): """Generate a ketama compatible continuum/ring.""" _weight_sum = 0 for node_conf in self._nodes.values(): _weight_sum += node_conf["weight"] self._weight_sum = _weight_sum _distribution = Counter() _keys = [] _ring = {} for node_name, node_conf in self._nodes.items(): for h in self._hashi_weight_generator(node_name, node_conf): _ring[h] = node_name insort(_keys, h) _distribution[node_name] += 1 self._distribution = _distribution self._keys = _keys self._ring = _ring def _remove_node(self, node_name): """Remove the given node from the continuum/ring. :param node_name: the node name. """ try: self._nodes.pop(node_name) except Exception: raise KeyError( "node '{}' not found, available nodes: {}".format( node_name, self._nodes.keys() ) ) else: self._create_ring(self._nodes)