PHP版的一致性哈希算法实现

版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章原始出版、作者信息和本声明。否则将追究法律责任。http://blog.csdn.net/mayongzhan - 马永占,myz,mayongzhan

[php]  view plain copy
  1. <?php  
  2. /** 
  3.  * Flexihash - A simple consistent hashing implementation for PHP. 
  4.  *  
  5.  * The MIT License 
  6.  *  
  7.  * Copyright (c) 2008 Paul Annesley 
  8.  *  
  9.  * Permission is hereby granted, free of charge, to any person obtaining a copy 
  10.  * of this software and associated documentation files (the "Software"), to deal 
  11.  * in the Software without restriction, including without limitation the rights 
  12.  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
  13.  * copies of the Software, and to permit persons to whom the Software is 
  14.  * furnished to do so, subject to the following conditions: 
  15.  *  
  16.  * The above copyright notice and this permission notice shall be included in 
  17.  * all copies or substantial portions of the Software. 
  18.  *  
  19.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
  20.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
  21.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
  22.  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
  23.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
  24.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
  25.  * THE SOFTWARE. 
  26.  *  
  27.  * @author Paul Annesley 
  28.  * @link http://paul.annesley.cc/ 
  29.  * @copyright Paul Annesley, 2008 
  30.  * @comment by MyZ (http://blog.csdn.net/mayongzhan) 
  31.  */  
  32.   
  33. /** 
  34.  * A simple consistent hashing implementation with pluggable hash algorithms. 
  35.  * 
  36.  * @author Paul Annesley 
  37.  * @package Flexihash 
  38.  * @licence http://www.opensource.org/licenses/mit-license.php 
  39.  */  
  40. class Flexihash  
  41. {  
  42.   
  43.     /** 
  44.      * The number of positions to hash each target to. 
  45.      * 
  46.      * @var int 
  47.      * @comment 虚拟节点数,解决节点分布不均的问题 
  48.      */  
  49.     private $_replicas = 64;  
  50.   
  51.     /** 
  52.      * The hash algorithm, encapsulated in a Flexihash_Hasher implementation. 
  53.      * @var object Flexihash_Hasher 
  54.      * @comment 使用的hash方法 : md5,crc32 
  55.      */  
  56.     private $_hasher;  
  57.   
  58.     /** 
  59.      * Internal counter for current number of targets. 
  60.      * @var int 
  61.      * @comment 节点记数器 
  62.      */  
  63.     private $_targetCount = 0;  
  64.   
  65.     /** 
  66.      * Internal map of positions (hash outputs) to targets 
  67.      * @var array { position => target, ... } 
  68.      * @comment 位置对应节点,用于lookup中根据位置确定要访问的节点 
  69.      */  
  70.     private $_positionToTarget = array();  
  71.   
  72.     /** 
  73.      * Internal map of targets to lists of positions that target is hashed to. 
  74.      * @var array { target => [ position, position, ... ], ... } 
  75.      * @comment 节点对应位置,用于删除节点 
  76.      */  
  77.     private $_targetToPositions = array();  
  78.   
  79.     /** 
  80.      * Whether the internal map of positions to targets is already sorted. 
  81.      * @var boolean 
  82.      * @comment 是否已排序 
  83.      */  
  84.     private $_positionToTargetSorted = false;  
  85.   
  86.     /** 
  87.      * Constructor 
  88.      * @param object $hasher Flexihash_Hasher 
  89.      * @param int $replicas Amount of positions to hash each target to. 
  90.      * @comment 构造函数,确定要使用的hash方法和需拟节点数,虚拟节点数越多,分布越均匀,但程序的分布式运算越慢 
  91.      */  
  92.     public function __construct(Flexihash_Hasher $hasher = null, $replicas = null)  
  93.     {  
  94.         $this->_hasher = $hasher ? $hasher : new Flexihash_Crc32Hasher();  
  95.         if (!emptyempty($replicas)) $this->_replicas = $replicas;  
  96.     }  
  97.   
  98.     /** 
  99.      * Add a target. 
  100.      * @param string $target 
  101.      * @chainable 
  102.      * @comment 添加节点,根据虚拟节点数,将节点分布到多个虚拟位置上 
  103.      */  
  104.     public function addTarget($target)  
  105.     {  
  106.         if (isset($this->_targetToPositions[$target]))  
  107.         {  
  108.             throw new Flexihash_Exception("Target '$target' already exists.");  
  109.         }  
  110.   
  111.         $this->_targetToPositions[$target] = array();  
  112.   
  113.         // hash the target into multiple positions  
  114.         for ($i = 0; $i < $this->_replicas; $i++)  
  115.         {  
  116.             $position = $this->_hasher->hash($target . $i);  
  117.             $this->_positionToTarget[$position] = $target// lookup  
  118.             $this->_targetToPositions[$target] []= $position// target removal  
  119.         }  
  120.   
  121.         $this->_positionToTargetSorted = false;  
  122.         $this->_targetCount++;  
  123.   
  124.         return $this;  
  125.     }  
  126.   
  127.     /** 
  128.      * Add a list of targets. 
  129.      * @param array $targets 
  130.      * @chainable 
  131.      */  
  132.     public function addTargets($targets)  
  133.     {  
  134.         foreach ($targets as $target)  
  135.         {  
  136.             $this->addTarget($target);  
  137.         }  
  138.   
  139.         return $this;  
  140.     }  
  141.   
  142.     /** 
  143.      * Remove a target. 
  144.      * @param string $target 
  145.      * @chainable 
  146.      */  
  147.     public function removeTarget($target)  
  148.     {  
  149.         if (!isset($this->_targetToPositions[$target]))  
  150.         {  
  151.             throw new Flexihash_Exception("Target '$target' does not exist.");  
  152.         }  
  153.   
  154.         foreach ($this->_targetToPositions[$targetas $position)  
  155.         {  
  156.             unset($this->_positionToTarget[$position]);  
  157.         }  
  158.   
  159.         unset($this->_targetToPositions[$target]);  
  160.   
  161.         $this->_targetCount--;  
  162.   
  163.         return $this;  
  164.     }  
  165.   
  166.     /** 
  167.      * A list of all potential targets 
  168.      * @return array 
  169.      */  
  170.     public function getAllTargets()  
  171.     {  
  172.         return array_keys($this->_targetToPositions);  
  173.     }  
  174.   
  175.     /** 
  176.      * Looks up the target for the given resource. 
  177.      * @param string $resource 
  178.      * @return string 
  179.      */  
  180.     public function lookup($resource)  
  181.     {  
  182.         $targets = $this->lookupList($resource, 1);  
  183.         if (emptyempty($targets)) throw new Flexihash_Exception('No targets exist');  
  184.         return $targets[0];  
  185.     }  
  186.   
  187.     /** 
  188.      * Get a list of targets for the resource, in order of precedence. 
  189.      * Up to $requestedCount targets are returned, less if there are fewer in total. 
  190.      * 
  191.      * @param string $resource 
  192.      * @param int $requestedCount The length of the list to return 
  193.      * @return array List of targets 
  194.      * @comment 查找当前的资源对应的节点, 
  195.      *          节点为空则返回空,节点只有一个则返回该节点, 
  196.      *          对当前资源进行hash,对所有的位置进行排序,在有序的位置列上寻找当前资源的位置 
  197.      *          当全部没有找到的时候,将资源的位置确定为有序位置的第一个(形成一个环) 
  198.      *          返回所找到的节点 
  199.      */  
  200.     public function lookupList($resource$requestedCount)  
  201.     {  
  202.         if (!$requestedCount)  
  203.             throw new Flexihash_Exception('Invalid count requested');  
  204.   
  205.         // handle no targets  
  206.         if (emptyempty($this->_positionToTarget))  
  207.             return array();  
  208.   
  209.         // optimize single target  
  210.         if ($this->_targetCount == 1)  
  211.             return array_unique(array_values($this->_positionToTarget));  
  212.   
  213.         // hash resource to a position  
  214.         $resourcePosition = $this->_hasher->hash($resource);  
  215.   
  216.         $results = array();  
  217.         $collect = false;  
  218.   
  219.         $this->_sortPositionTargets();  
  220.   
  221.         // search values above the resourcePosition  
  222.         foreach ($this->_positionToTarget as $key => $value)  
  223.         {  
  224.             // start collecting targets after passing resource position  
  225.             if (!$collect && $key > $resourcePosition)  
  226.             {  
  227.                 $collect = true;  
  228.             }  
  229.   
  230.             // only collect the first instance of any target  
  231.             if ($collect && !in_array($value$results))  
  232.             {  
  233.                 $results []= $value;  
  234.             }  
  235.   
  236.             // return when enough results, or list exhausted  
  237.             if (count($results) == $requestedCount || count($results) == $this->_targetCount)  
  238.             {  
  239.                 return $results;  
  240.             }  
  241.         }  
  242.   
  243.         // loop to start - search values below the resourcePosition  
  244.         foreach ($this->_positionToTarget as $key => $value)  
  245.         {  
  246.             if (!in_array($value$results))  
  247.             {  
  248.                 $results []= $value;  
  249.             }  
  250.   
  251.             // return when enough results, or list exhausted  
  252.             if (count($results) == $requestedCount || count($results) == $this->_targetCount)  
  253.             {  
  254.                 return $results;  
  255.             }  
  256.         }  
  257.   
  258.         // return results after iterating through both "parts"  
  259.         return $results;  
  260.     }  
  261.   
  262.     public function __toString()  
  263.     {  
  264.         return sprintf(  
  265.             '%s{targets:[%s]}',  
  266.             get_class($this),  
  267.             implode(','$this->getAllTargets())  
  268.         );  
  269.     }  
  270.   
  271.     // ----------------------------------------  
  272.     // private methods  
  273.   
  274.     /** 
  275.      * Sorts the internal mapping (positions to targets) by position 
  276.      */  
  277.     private function _sortPositionTargets()  
  278.     {  
  279.         // sort by key (position) if not already  
  280.         if (!$this->_positionToTargetSorted)  
  281.         {  
  282.             ksort($this->_positionToTarget, SORT_REGULAR);  
  283.             $this->_positionToTargetSorted = true;  
  284.         }  
  285.     }  
  286.   
  287. }  
  288.   
  289.   
  290. /** 
  291.  * Hashes given values into a sortable fixed size address space. 
  292.  * 
  293.  * @author Paul Annesley 
  294.  * @package Flexihash 
  295.  * @licence http://www.opensource.org/licenses/mit-license.php 
  296.  */  
  297. interface Flexihash_Hasher  
  298. {  
  299.   
  300.     /** 
  301.      * Hashes the given string into a 32bit address space. 
  302.      * 
  303.      * Note that the output may be more than 32bits of raw data, for example 
  304.      * hexidecimal characters representing a 32bit value. 
  305.      * 
  306.      * The data must have 0xFFFFFFFF possible values, and be sortable by 
  307.      * PHP sort functions using SORT_REGULAR. 
  308.      * 
  309.      * @param string 
  310.      * @return mixed A sortable format with 0xFFFFFFFF possible values 
  311.      */  
  312.     public function hash($string);  
  313.   
  314. }  
  315.   
  316.   
  317. /** 
  318.  * Uses CRC32 to hash a value into a signed 32bit int address space. 
  319.  * Under 32bit PHP this (safely) overflows into negatives ints. 
  320.  * 
  321.  * @author Paul Annesley 
  322.  * @package Flexihash 
  323.  * @licence http://www.opensource.org/licenses/mit-license.php 
  324.  */  
  325. class Flexihash_Crc32Hasher  
  326.     implements Flexihash_Hasher  
  327. {  
  328.   
  329.     /* (non-phpdoc) 
  330.      * @see Flexihash_Hasher::hash() 
  331.      */  
  332.     public function hash($string)  
  333.     {  
  334.         return crc32($string);  
  335.     }  
  336.   
  337. }  
  338.   
  339.   
  340. /** 
  341.  * Uses CRC32 to hash a value into a 32bit binary string data address space. 
  342.  * 
  343.  * @author Paul Annesley 
  344.  * @package Flexihash 
  345.  * @licence http://www.opensource.org/licenses/mit-license.php 
  346.  */  
  347. class Flexihash_Md5Hasher  
  348.     implements Flexihash_Hasher  
  349. {  
  350.   
  351.     /* (non-phpdoc) 
  352.      * @see Flexihash_Hasher::hash() 
  353.      */  
  354.     public function hash($string)  
  355.     {  
  356.         return substr(md5($string), 0, 8); // 8 hexits = 32bit  
  357.   
  358.         // 4 bytes of binary md5 data could also be used, but  
  359.         // performance seems to be the same.  
  360.     }  
  361.   
  362. }  
  363.   
  364.   
  365. /** 
  366.  * An exception thrown by Flexihash. 
  367.  * 
  368.  * @author Paul Annesley 
  369.  * @package Flexihash 
  370.  * @licence http://www.opensource.org/licenses/mit-license.php 
  371.  */  
  372. class Flexihash_Exception extends Exception  
  373. {  
  374. }  



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值