_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); $key="lihuibin"; for($i=0;$i<10;$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:11218" ); $flexiHashObj = new FlexiHash(new FlexiHash_Crc32Hasher(),1); $result = $flexiHashObj->addTargets($targetsArray); $key=$key."$i"; $targets = $flexiHashObj->lookup($key); var_dump($targets); #$key = md5(mt_rand()); #$targets = $flexiHashObj->lookup($key); #var_dump($targets); } echo "一致性hash:"; var_dump(number_format(microtime(true) - $runData['BEGIN_TIME'],6)); exit; $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)); ?>