#!/usr/bin/env python # -*- coding: utf-8 -*- # Tencent is pleased to support the open source community by making Tars available. # # Copyright (C) 2016THL A29 Limited, a Tencent company. All rights reserved. # # Licensed under the BSD 3-Clause License (the "License"); you may not use this file except # in compliance with the License. You may obtain a copy of the License at # # https://opensource.org/licenses/BSD-3-Clause # # Unless required by applicable law or agreed to in writing, software distributed # under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR # CONDITIONS OF ANY KIND, either express or implied. See the License for the # specific language governing permissions and limitations under the License. # import sys from threading import Lock import hashlib from xml.etree import cElementTree as ET from .exception import TarsException class util: @staticmethod def printHex(buff): count = 0 for c in buff: sys.stdout.write("0X%02X " % ord(c)) count += 1 if count % 16 == 0: sys.stdout.write("\n") sys.stdout.write("\n") sys.stdout.flush() @staticmethod def mapclass(ktype, vtype): class mapklass(dict): def size(self): return len(self) setattr(mapklass, '__tars_index__', 8) setattr(mapklass, '__tars_class__', "map<" + ktype.__tars_class__ + "," + vtype.__tars_class__ + ">") setattr(mapklass, 'ktype', ktype) setattr(mapklass, 'vtype', vtype) return mapklass @staticmethod def vectorclass(vtype): class klass(list): def size(self): return len(self) setattr(klass, '__tars_index__', 9) setattr(klass, '__tars_class__', "list<" + vtype.__tars_class__ + ">") setattr(klass, 'vtype', vtype) return klass class boolean: __tars_index__ = 999 __tars_class__ = "bool" class int8: __tars_index__ = 0 __tars_class__ = "char" class uint8: __tars_index__ = 1 __tars_class__ = "short" class int16: __tars_index__ = 1 __tars_class__ = "short" class uint16: __tars_index__ = 2 __tars_class__ = "int32" class int32: __tars_index__ = 2 __tars_class__ = "int32" class uint32: __tars_index__ = 3 __tars_class__ = "int64" class int64: __tars_index__ = 3 __tars_class__ = "int64" class float: __tars_index__ = 4 __tars_class__ = "float" class double: __tars_index__ = 5 __tars_class__ = "double" class bytes: __tars_index__ = 13 __tars_class__ = "list" class string: __tars_index__ = 67 __tars_class__ = "string" class struct: __tars_index__ = 1011 def xml2dict(node, dic={}): ''' @brief: 将xml解析树转成字典 @param node: 树的根节点 @type node: cElementTree.Element @param dic: 存储信息的字典 @type dic: dict @return: 转换好的字典 @rtype: dict ''' dic[node.tag] = ndic = {} [xml2dict(child, ndic) for child in node.getchildren() if child != node] ndic.update([list(map(str.strip, exp.split('=')[:2])) for exp in node.text.splitlines() if '=' in exp]) return dic def configParse(filename): ''' @brief: 解析tars配置文件 @param filename: 文件名 @type filename: str @return: 解析出来的配置信息 @rtype: dict ''' tree = ET.parse(filename) return xml2dict(tree.getroot()) class NewLock(object): def __init__(self): self.__count = 0 self.__lock = Lock() self.__lockForCount = Lock() pass def newAcquire(self): self.__lockForCount.acquire() self.__count += 1 if self.__count == 1: self.__lock.acquire() self.__lockForCount.release() pass def newRelease(self): self.__lockForCount.acquire() self.__count -= 1 if self.__count == 0: self.__lock.release() self.__lockForCount.release() class LockGuard(object): def __init__(self, newLock): self.__newLock = newLock self.__newLock.newAcquire() def __del__(self): self.__newLock.newRelease() class ConsistentHashNew(object): def __init__(self, nodes=None, nodeNumber=3): """ :param nodes: 服务器的节点的epstr列表 :param n_number: 一个节点对应的虚拟节点数量 :return: """ self.__nodes = nodes self.__nodeNumber = nodeNumber # 每一个节点对应多少个虚拟节点,这里默认是3个 self.__nodeDict = dict() # 用于记录虚拟节点的hash值与服务器epstr的对应关系 self.__sortListForKey = [] # 用于存放所有的虚拟节点的hash值,这里需要保持排序,以找出对应的服务器 if nodes: for node in nodes: self.addNode(node) @property def nodes(self): return self.__nodes @nodes.setter def nodes(self, value): self.__nodes = value def addNode(self, node): """ 添加node,首先要根据虚拟节点的数目,创建所有的虚拟节点,并将其与对应的node对应起来 当然还需要将虚拟节点的hash值放到排序的里面 这里在添加了节点之后,需要保持虚拟节点hash值的顺序 :param node: :return: """ for i in range(self.__nodeNumber): nodeStr = "%s%s" % (node, i) key = self.__genKey(nodeStr) self.__nodeDict[key] = node self.__sortListForKey.append(key) self.__sortListForKey.sort() def removeNode(self, node): """ 这里一个节点的退出,需要将这个节点的所有的虚拟节点都删除 :param node: :return: """ for i in range(self.__nodeNumber): nodeStr = "%s%s" % (node, i) key = self.__genKey(nodeStr) del self.__nodeDict[key] self.__sortListForKey.remove(key) def getNode(self, key): """ 返回这个字符串应该对应的node,这里先求出字符串的hash值,然后找到第一个小于等于的虚拟节点,然后返回node 如果hash值大于所有的节点,那么用第一个虚拟节点 :param : hashNum or keyStr :return: """ keyStr = '' if isinstance(key, int): keyStr = "the keyStr is %d" % key elif isinstance(key, type('a')): keyStr = key else: raise TarsException("the hash code has wrong type") if self.__sortListForKey: key = self.__genKey(keyStr) for keyItem in self.__sortListForKey: if key <= keyItem: return self.__nodeDict[keyItem] return self.__nodeDict[self.__sortListForKey[0]] else: return None def __genKey(self, keyStr): """ 通过key,返回当前key的hash值,这里采用md5 :param key: :return: """ md5Str = hashlib.md5(keyStr).hexdigest() return int(md5Str, 16)