PHP代码下实现hash一致性和简单的容灾 1. [文件] 一致性hash.php~6KB 下载 (0) ?php/** * 一致性hahs实现类 * */class FlexiHash{/** * var int * 虚拟节点 */private $_replicas = 200;/** * 使用hash方法 */private $_h
1. [文件] 一致性hash.php ~ 6KB 下载(0)
<?php /** * 一致性hahs实现类 * */ class FlexiHash{ /** * var int * 虚拟节点 */ private $_replicas = 200; /** * 使用hash方法 */ private $_hasher = null; /** * 真实节点计数器 * */ private $_tagertCount = 0; /** * 位置对应节点,用户lookup中根据位置确定要访问的节点 */ private $_positionToTarget = array(); /** * 节点对应位置,用于删除节点 */ private $_targetToPositions = array(); /** * 是否已排序 */ private $_positionToTargetSorted = false; /** * @ $hasher hash方法 * @ $replicas 虚拟节点的个数 * * 确定要使用的hash方法和虚拟的节点数,虚拟节点越多,分布越均匀,但程序的分布式运算越慢 */ public function __construct(FlexiHash_Hasher $hasher=null, $replicas = null){ // hash方法 $this->_hasher = $hasher?$hasher: new FlexiHash_Crc32Hasher(); // 虚拟节点的个数 if (!empty($replicas)){ $this->_replicas = $replicas; } } /** * 增加节点,根据虚拟节点数,把节点分布到更多的虚拟位置上 */ public function addTarget($target){ if (isset($this->_targetToPositions[$target])) { throw new FlexiHash_Exception("Target $target already exists."); } $this->_targetToPositions[$target] = array(); for ($i = 0; $i < $this->_replicas; $i++) { // 根据规定的方法hash $position = $this->_hasher->hash($target.$i); // 虚拟节点对应的真实的节点 $this->_positionToTarget[$position] = $target; // 真实节点包含的虚拟节点 $this->_targetToPositions[$target][] = $position; } $this->_positionToTargetSorted = false; // 真实节点个数 $this->_targetCount++; return $this; } /** * 添加多个节点 * */ public function addTargets($targets){ foreach ($targets as $target){ $this->addTarget($target); } return $this; } /** * 移除某个节点 * */ public function removeTarget($target){ if (!isset($this->_targetToPositions[$target])){ throw new FlexiHash_Exception("target $target does not exist\n"); } foreach($this->_targetToPositions[$target] as $position){ unset($this->_positionToTarget[$position]); } unset($this->_targetToPositions[$target]); $this->_targetCount--; return $this; } /** * 获取所有节点 * */ public function getAllTargets(){ return array_keys($this->_targetToPositions); } /** * 根据key查找hash到的真实节点 * */ public function lookup($resource){ $targets = $this->lookupList($resource, 1); if (empty($targets)){ throw new FlexiHash_Exception("no targets exist"); } return $targets[0]; } /** * 查找资源存在的节点 * * 描述:根据要求的数量,返回与$resource哈希后数值相等或比其大并且是最小的数值对应的节点,若不存在或数量不够,则从虚拟节点排序后的前一个或多个 */ public function lookupList($resource, $requestedCount){ if (!$requestedCount) { throw new FlexiHash_Exception('Invalid count requested'); } if (empty($this->_positionToTarget)) { return array(); } // 直接节点只有一个的时候 if ($this->_targetCount == 1 ){ return array_unique(array_values($this->_positionToTarget)); } // 获取当前key进行hash后的值 $resourcePosition = $this->_hasher->hash($resource); $results = array(); $collect = false; $this->_sortPositionTargets(); // 查找与$resourcePosition 相等或比其大并且是最小的数 foreach($this->_positionToTarget as $key => $value){ if (!$collect && $key > $resourcePosition){ $collect = true; } if ($collect && !in_array($value, $results)){ $results[] = $value; } // 找到$requestedCount 或个数与真实节点数量相同 if (count($results) == $requestedCount || count($results) == $this->_targetCount){ return $results; } } // 如数量不够或者未查到,则从第一个开始,将$results中不存在前$requestedCount-count($results),设置为需要的节点 foreach ($this->_positionToTarget as $key => $value){ if (!in_array($value, $results)){ $results[] = $value; } if (count($results) == $requestedCount || count($results) == $this->_targetCount){ return $results; } } return $results; } /** * 根据虚拟节点进行排序 */ private function _sortPositionTargets(){ if (!$this->_positionToTargetSorted){ ksort($this->_positionToTarget, SORT_REGULAR); $this->_positionToTargetSorted = true; } } }// end class /** * hash方式 */ interface FlexiHash_Hasher{ public function hash($string); } class FlexiHash_Crc32Hasher implements FlexiHash_Hasher{ public function hash($string){ return sprintf("%u",crc32($string)); } } class FlexiHash_Md5Hasher implements FlexiHash_Hasher{ public function hash($string){ return substr(md5($string), 0, 8); } } class FlexiHash_Exception extends Exception{ } $runData['BEGIN_TIME'] = microtime(true); for($i=0;$i<10000;$i++) { $targetsArray = array( "127.0.0.1:11211", "127.0.0.1:11212", "127.0.0.1:11213", "127.0.0.1:11214", "127.0.0.1:11215" ); $flexiHashObj = new FlexiHash(new FlexiHash_Crc32Hasher(),1); $result = $flexiHashObj->addTargets($targetsArray); $key = md5(mt_rand()); $targets = $flexiHashObj->lookup($key); // var_dump($targets); } echo "一致性hash:"; var_dump(number_format(microtime(true) - $runData['BEGIN_TIME'],6)); $runData['BEGIN_TIME'] = microtime(true); $m= new Memcache; $m->connect('127.0.0.1', 11211); for($i=0;$i<10000;$i++) { $key = md5(mt_rand()); $b = $m->set($key, time(), 0, 10); } echo "单台机器:"; var_dump(number_format(microtime(true) - $runData['BEGIN_TIME'],6)); ?>
2. [代码]虚拟节点的hash一致性
<?php class memcacheHashMap{ private $_node = array(); private $_nodeData = array(); private $_keyNode = 0; private $_memcache = null; // 每个物理服务器生成虚拟节点的个数 private $_virtualNodeNum = 200; private function __construct(){ // 配置文件 $config = array( "127.0.0.1:11211", "127.0.0.1:11212", "127.0.0.1:11213", "127.0.0.1:11214", "127.0.0.1:11215" ); if (!$config){ throw new Exception("cache config null"); } // 设置虚拟节点 foreach($config as $key=>$value){ for ($i = 0; $i < $this->_virtualNodeNum; $i++){ $this->_node[sprintf("%u", crc32($value."#".$i))] = $value."#".$i; } } // 排序 ksort($this->_node); // print_r($this->_node); } // 单例模式 static public function getInstance(){ static $memcacheObj = null; if (!is_object($memcacheObj)) { $memcacheObj = new self(); } return $memcacheObj; } private function _connectMemcache($key){ $this->_nodeData = array_keys($this->_node); // echo "all node:\n"; // print_r($this->_nodeData); $this->_keyNode = sprintf("%u", crc32($key)); // $this->_keyNode = 1803717635; // var_dump($this->_keyNode); // 获取key值对应的最近的节点 $nodeKey = $this->_findServerNode(0, count($this->_nodeData)-1); // var_dump($nodeKey); // echo "$this->_keyNode :search node:$nodeKey IP:{$this->_node[$nodeKey]}\n"; //获取对应的真实ip list($config, $num) = explode("#", $this->_node[$nodeKey]); if (empty($config)){ throw new Exception("serach ip config error"); } if (!isset($this->_memcache[$config])){ $this->_memcache[$config] = new Memcache; list($host, $port) = explode(":", $config); $this->_memcache[$config]->connect($host, $port); } return $this->_memcache[$config]; } /** * 采用二分法从虚拟memcache节点中查找最近的节点 * @param int $low 开始位置 * @param int $high 结束位置 * */ private function _findServerNode($low, $high){ // 开始下标小于结束下标 if ($low < $high){ $avg = intval(($low+$high)/2); if ($this->_nodeData[$avg] == $this->_keyNode){ return $this->_nodeData[$avg]; }elseif ($this->_keyNode < $this->_nodeData[$avg]){ return $this->_findServerNode($low, $avg-1); }else{ return $this->_findServerNode($avg+1, $high); } }else if(($low == $high)){ // 大于平均值 if ($low ==0 || $low == count($this->_nodeData)-1){ return $this->_nodeData[$low]; } // var_dump($low); if ($this->_nodeData[$low] < $this->_keyNode){ if (abs($this->_nodeData[$low] - $this->_keyNode) < abs($this->_nodeData[$low+1]-$this->_keyNode)){ return $this->_nodeData[$low]; }else{ return $this->_nodeData[$low+1]; } }else { if (abs($this->_nodeData[$low] - $this->_keyNode) < abs($this->_nodeData[$low-1]-$this->_keyNode)){ return $this->_nodeData[$low]; }else{ return $this->_nodeData[$low-1]; } } }else{ if ( ($low == 0)&&($high < 0) ){ return $this->_nodeData[$low]; } if (abs($this->_nodeData[$low] - $this->_keyNode) < abs($this->_nodeData[$high]-$this->_keyNode)){ return $this->_nodeData[$low]; }else{ return $this->_nodeData[$high]; } } } public function set($key, $value, $expire=0){ // var_dump($key); return $this->_connectMemcache($key)->set($key, json_encode($value), 0, $expire); } public function add($key, $vakue, $expire=0){ return $this->_connectMemcache($key)->add($key, json_encode($value), 0, $expire); } public function get($key){ return $this->_connectMemcache($key)->get($key, true); } public function delete($key){ return $this->_connectMemcache($key)->delete($key); } } $runData['BEGIN_TIME'] = microtime(true); //测试一万次set加get for($i=0;$i<10000;$i++) { $key = md5(mt_rand()); // var_dump($key); $b = memcacheHashMap::getInstance()->set($key, time(), 10); } echo "一致性hash:"; var_dump(number_format(microtime(true) - $runData['BEGIN_TIME'],6)); $runData['BEGIN_TIME'] = microtime(true); $m= new Memcache; $m->connect('127.0.0.1', 11211); for($i=0;$i<10000;$i++) { $key = md5(mt_rand()); $b = $m->set($key, time(), 0, 10); } echo "单台机器:"; var_dump(number_format(microtime(true) - $runData['BEGIN_TIME'],6));