zoukankan      html  css  js  c++  java
  • 一致性哈希算法——PHP实现代码

      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 (!empty($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[$target] as $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 (empty($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 (empty($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 }

    From:http://blog.csdn.net/mayongzhan/article/details/4298834

  • 相关阅读:
    XML to Excel
    C# 位域[flags]
    使用windows7的System帐户
    VS.NET 控件命名规范
    Microsoft Robotics Studio到底能做什么?
    SQLServer系统表及其应用(转)
    利用xslt、xml,ajax实现了一个无限级树型导航
    利用xslt实现一个树形导航
    网页信息抓取如何获取延迟加载的网页数据
    站长盈利盈利方式面面观
  • 原文地址:https://www.cnblogs.com/liqiu/p/3148586.html
Copyright © 2011-2022 走看看