php实现一致性hash算法
使用php实现一致性hash算法
<?php use Flexihash\Hasher\HasherInterface; use Flexihash\Hasher\Crc32Hasher; use Flexihash\Exception; /** * 参考:https://github.com/pda/flexihash * 一致性哈希算法实现 * Class MyFlexiHash * 主要的方法有三个:addTarget、removeTarget、lookupList,其它都是语法糖 * lookupList(key)的实现方式是在一个有序列表上先搜索大于key的部分,数量不够再从头选取元素进行补充 */ class MyFlexiHash { private $replicas = 64; private $hasher = null; private $positionTargetMap = array(); private $targetPositionsMap = array(); public function __construct(HasherInterface $hasher = null, $replicas = null) { $this->hasher = isset($hasher) ? $hasher : new Crc32Hasher(); isset($replicas) && $this->replicas = $replicas; } public function addTarget($target, $weight = 1) { $positions = array(); $existPositions = array_keys($this->positionTargetMap); for ($i = 0; $i < $this->replicas * $weight; $i++) { //find an position not in exist positions $suffix = ''; do { $suffix .= $i; $position = $this->hasher->hash($target.''.$suffix); } while (in_array($position, $existPositions)); //record position's target $this->positionTargetMap[$position] = $target; //collect position $positions[] = $position; } //sort position ksort($this->positionTargetMap); //record target's positions $this->targetPositionsMap[$target] = $positions; } public function removeTarget($target) { if (!isset($this->targetPositionsMap[$target])) { throw new Exception('remove not exists target: '.$target); } $positions = $this->targetPositionsMap[$target]; //remove target's positions unset($this->targetPositionsMap[$target]); //remove position's target foreach ($positions as $position) { unset($this->positionTargetMap[$position]); } } public function lookup($resource) { return $this->lookupList($resource, 1)[0]; } public function lookupList($resource, $count) { if ($this->isTargetsEmpty()) { throw new Exception('lookup targets empty'); } if ($count > count($this->targetPositionsMap)) { throw new Exception('lookup targets not enough'); } $targetList = array(); $hash = $this->hasher->hash($resource); //find in the above positions foreach ($this->positionTargetMap as $position => $t) { if (count($targetList) == $count) { break; } if ($hash >= $position) { $targetList[] = $t; } } //find in the below positions if (count($targetList) < $count) { foreach ($this->positionTargetMap as $position => $t) { if (count($targetList) == $count) { break; } $targetList[] = $t; } } return $targetList; } public function isTargetsEmpty() { return count($this->targetPositionsMap) == 0; } public function addTargets($targets) { foreach ($targets as $target) { $this->addTarget($target); } } public function removeTargets($targets) { foreach ($targets as $target) { $this->removeTarget($target); } } }