: This article mainly introduces consistent hash-php. if you are interested in the PHP Tutorial, refer to it. /**
* Flexihash-A simple consistent hashing implementation for PHP.
*
* The MIT License
*
* Copyright (c) 2008 Paul Annesley
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* Of this software and associated documentation files (the "Software"), to deal
* In the Software without restriction, including without limitation the rights
* To use, copy, modify, merge, publish, distriense, sublicense, and/or decrypt
* Copies of the Software, and to permit persons to whom the Software is
* Furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be encoded in
* All copies or substantial portions of the Software.
*
* The software is provided "as is", without warranty of any kind, EXPRESS OR
* IMPLIED, including but not limited to the warranties of merchantability,
* Fitness for a participant purpose and noninfringement. IN NO EVENT SHALL
* Authors or copyright holders be liable for any claim, DAMAGES OR OTHER
* LIABILITY, whether in an action of contract, tort or otherwise, arising from,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* The software.
*
* @ Author Paul Annesley
* @ Link http://paul.annesley.cc/
* @ Copyright, Paul Annesley, 2008
* @ Comment by MyZ (http://blog.csdn.net/mayongzhan)
*/
/**
* A simple consistent hashing implementation with pluggable hash algorithms.
*
* @ Author Paul Annesley
* @ Package Flexihash
* @ Licence http://www.opensource.org/licenses/mit-license.php
*/
Class Flexihash
{
/**
* The number of positions to hash each target.
*
* @ Var int
* @ Comment the number of virtual nodes to solve the problem of unevenly distributed nodes
*/
Private $ _ replicas = 64;
/**
* The hash algorithm, encapsulated in a Flexihash_Hasher implementation.
* @ Var object Flexihash_Hasher
* @ Comment: md5, crc32
*/
Private $ _ hasher;
/**
* Internal counter for current number of targets.
* @ Var int
* @ Comment node calculator
*/
Private $ _ targetCount = 0;
/**
* Internal map of positions (hash outputs) to targets
* @ Var array {position => target ,...}
* @ Comment: specifies the node to be accessed in lookup.
*/
Private $ _ positionToTarget = array ();
/**
* Internal map of targets to lists of positions that target is hashed.
* @ Var array {target => [position, position,...],...}
* @ Comment node location, used to delete a node
*/
Private $ _ targetToPositions = array ();
/**
* Whether the internal map of positions to targets is already sorted.
* @ Var boolean
* @ Comment: whether it is sorted
*/
Private $ _ positionToTargetSorted = false;
/**
* Constructor
* @ Param object $ hasher Flexihash_Hasher
* @ Param int $ replicas Amount of positions to hash each target.
* @ Comment constructor: determine the hash method and number of nodes to be used. the more virtual nodes, the more uniform the distribution, but the slower the distributed operation of the program.
*/
Public function _ construct (Flexihash_Hasher $ hasher = null, $ replicas = null)
{
$ This-> _ hasher = $ hasher? $ Hasher: new Flexihash_Crc32Hasher ();
If (! Empty ($ replicas) $ this-> _ replicas = $ replicas;
}
/**
* Add a target.
* @ Param string $ target
* @ Chainable
* @ Comment add nodes and distribute nodes to multiple virtual locations based on the number of virtual nodes
*/
Public function addTarget ($ target)
{
If (isset ($ this-> _ targetToPositions [$ target])
{
Throw new Flexihash _
Exception("Target '$ target' already exists .");
}
$ This-> _ targetToPositions [$ target] = array ();
// Hash the target into multiple positions
For ($ I = 0; $ I <$ this-> _ replicas; $ I ++)
{
$ Position = $ this-> _ hasher-> hash ($ target. $ I );
$ This-> _ positionToTarget [$ position] = $ target; // lookup
$ This-> _ targetToPositions [$ target] [] = $ position; // target removal
}
$ This-> _ positionToTargetSorted = false;
$ This-> _ targetCount ++;
Return $ this;
}
/**
* Add a list of targets.
* @ Param array $ targets
* @ Chainable
*/
Public function addTargets ($ targets)
{
Foreach ($ targets as $ target)
{
$ This-> addTarget ($ target );
}
Return $ this;
}
/**
* Remove a target.
* @ Param string $ target
* @ Chainable
*/
Public function removeTarget ($ target)
{
If (! Isset ($ this-> _ targetToPositions [$ target])
{
Throw new Flexihash _
Exception("Target '$ target' does not exist .");
}
Foreach ($ this-> _ targetToPositions [$ target] as $ position)
{
Unset ($ this-> _ positionToTarget [$ position]);
}
Unset ($ this-> _ targetToPositions [$ target]);
$ This-> _ targetCount --;
Return $ this;
}
/**
* A list of all potential targets
* @ Return array
*/
Public function getAllTargets ()
{
Return array_keys ($ this-> _ targetToPositions );
}
/**
* Looks up the target for the given resource.
* @ Param string $ resource
* @ Return string
*/
Public function lookup ($ resource)
{
$ Targets = $ this-> lookupList ($ resource, 1 );
If (empty ($ targets) throw new Flexihash _
Exception('No targets exist ');
Return $ targets [0];
}
/**
* Get a list of targets for the resource, in order of precedence.
* Up to $ requestedCount targets are returned, less if there are fewer in total.
*
* @ Param string $ resource
* @ Param int $ requestedCount The length of the list to return
* @ Return array List of targets
* @ Comment: Find the node corresponding to the current resource,
* If the node is empty, null is returned. if there is only one node, this node is returned,
* Hash the current resource, sort all locations, and search for the current resource location in the ordered position column.
* When all the resources are not found, determine the resource location as the first (forming a ring) in an ordered position)
* Return the node found.
*/
Public function lookupList ($ resource, $ requestedCount)
{
If (! $ RequestedCount)
Throw new Flexihash _
Exception('Invalidcount requested ');
// Handle no targets
If (empty ($ this-> _ positionToTarget ))
Return array ();
// Optimize single target
If ($ this-> _ targetCount = 1)
Return array_unique (array_values ($ this-> _ positionToTarget ));
// Hash resource to a position
$ ResourcePosition = $ this-> _ hasher-> hash ($ resource );
$ Results = array ();
$ Collect = false;
$ This-> _ sortPositionTargets ();
// Search values above the resourcePosition
Foreach ($ this-> _ positionToTarget as $ key => $ value)
{
// Start collecting targets after passing resource position
If (! $ Collect & $ key> $ resourcePosition)
{
$ Collect = true;
}
// Only collect the first instance of any target
If ($ collect &&! In_array ($ value, $ results ))
{
$ Results [] = $ value;
}
// Return when enough results, or list exhausted
If (count ($ results) = $ requestedCount | count ($ results) = $ this-> _ targetCount)
{
Return $ results;
}
}
// Loop to start-search values below the resourcePosition
Foreach ($ this-> _ positionToTarget as $ key => $ value)
{
If (! In_array ($ value, $ results ))
{
$ Results [] = $ value;
}
// Return when enough results, or list exhausted
If (count ($ results) = $ requestedCount | count ($ results) = $ this-> _ targetCount)
{
Return $ results;
}
}
// Return results after iterating through both "parts"
Return $ results;
}
Public function _ toString ()
{
Return sprintf (
'% S {targets: [% s]}',
Get_class ($ this ),
Implode (',', $ this-> getAllTargets ())
);
}
//----------------------------------------
// Private methods
/**
* Sorts the internal mapping (positions to targets) by position
*/
Private function _ sortPositionTargets ()
{
// Sort by key (position) if not already
If (! $ This-> _ positionToTargetSorted)
{
Ksort ($ this-> _ positionToTarget, SORT_REGULAR );
$ This-> _ positionToTargetSorted = true;
}
}
}
/**
* Hashes given values into a sortable fixed size address space.
*
* @ Author Paul Annesley
* @ Package Flexihash
* @ Licence http://www.opensource.org/licenses/mit-license.php
*/
Interface Flexihash_Hasher
{
/**
* Hashes the given string into a 32bit address space.
*
* Note that the output may be more than 32 bits of raw data, for example
* Hexidecimal characters representing a 32bit value.
*
* The data must have 0 xFFFFFFFF possible values, and be sortable
* PHP sort functions using SORT_REGULAR.
*
* @ Param string
* @ Return mixed A sortable format with 0 xFFFFFFFF possible values
*/
Public function hash ($ string );
}
/**
* Uses CRC32 to hash a value into a signed 32bit int address space.
* Under 32bit PHP this (safely) overflows into negatives ints.
*
* @ Author Paul Annesley
* @ Package Flexihash
* @ Licence http://www.opensource.org/licenses/mit-license.php
*/
Class Flexihash_Crc32Hasher
Implements Flexihash_Hasher
{
/* (Non-phpdoc)
* @ See Flexihash_Hasher: hash ()
*/
Public function hash ($ string)
{
Return crc32 ($ string );
}
}
/**
* Uses CRC32 to hash a value into a 32bit binary string data address space.
*
* @ Author Paul Annesley
* @ Package Flexihash
* @ Licence http://www.opensource.org/licenses/mit-license.php
*/
Class Flexihash_Md5Hasher
Implements Flexihash_Hasher
{
/* (Non-phpdoc)
* @ See Flexihash_Hasher: hash ()
*/
Public function hash ($ string)
{
Return substr (md5 ($ string), 0, 8); // 8 hexits = 32bit
// 4 bytes of binary md5 data cocould also be used,
// Performance seems to be the same.
}
}
/**
*
ExceptionThrown by Flexihash.
*
* @ Author Paul Annesley
* @ Package Flexihash
* @ Licence http://www.opensource.org/licenses/mit-license.php
*/
Class Flexihash _
ExceptionExtends
Exception
{
}
The above introduces consistent hash-php, including Exception content, and hopes to help friends who are interested in PHP tutorials.