一致性hash及php實現(哈希環原理)

一致性hash及php實現(哈希環原理)

備註一下: 好久之前用Go和PHP寫過基於哈希環的一致性哈希,當時也補充了很多相關知識. 今天無意間看到這篇文章,感覺很不錯, 收藏一下.主要是對哈希環原理的說明, 簡單明瞭. 圖片已整理到微博圖牀

原文名稱: 一致性hash及php實現(通俗易懂)

轉載自: 一致性hash及php實現(通俗易懂)

一致性哈希算法在1997年由麻省理工學院提出的一種分佈式哈希(DHT)實現算法,設計目標是爲了解決因特網中的熱點(Hot spot)問題,初衷和CARP十分類似。一致性哈希修正了CARP使用的簡單哈希算法帶來的問題,使得分佈式哈希(DHT)可以在P2P環境中真正得到應用。在瞭解一致性哈希算法之前,最好先了解一下緩存中的一個應用場景,瞭解了這個應用場景之後,再來理解一致性哈希算法,就容易多了,也更能體現出一致性哈希算法的優點,那麼,我們先來描述一下這個經典的分佈式緩存的應用場景。

場景描述

假設,我們有三臺緩存服務器,用於緩存圖片,我們爲這三臺緩存服務器編號爲0號、1號、2號,現在,有3萬張圖片需要緩存,我們希望這些圖片被均勻的緩存到這3臺服務器上,以便它們能夠分攤緩存的壓力。也就是說,我們希望每臺服務器能夠緩存1萬張左右的圖片,那麼,我們應該怎樣做呢?如果我們沒有任何規律的將3萬張圖片平均的緩存在3臺服務器上,可以滿足我們的要求嗎?可以!但是如果這樣做,當我們需要訪問某個緩存項時,則需要遍歷3臺緩存服務器,從3萬個緩存項中找到我們需要訪問的緩存,遍歷的過程效率太低,時間太長,當我們找到需要訪問的緩存項時,時長可能是不能被接受的,也就失去了緩存的意義,緩存的目的就是提高速度,改善用戶體驗,減輕後端服務器壓力,如果每次訪問一個緩存項都需要遍歷所有緩存服務器的所有緩存項,想想就覺得很累,那麼,我們該怎麼辦呢?原始的做法是對緩存項的鍵進行哈希,將hash後的結果對緩存服務器的數量進行取模操作,通過取模後的結果,決定緩存項將會緩存在哪一臺服務器上,這樣說可能不太容易理解,我們舉例說明,仍然以剛纔描述的場景爲例,假設我們使用圖片名稱作爲訪問圖片的key,假設圖片名稱是不重複的,那麼,我們可以使用如下公式,計算出圖片應該存放在哪臺服務器上。

hash(圖片名稱)% N

因爲圖片的名稱是不重複的,所以,當我們對同一個圖片名稱做相同的哈希計算時,得出的結果應該是不變的,如果我們有3臺服務器,使用哈希後的結果對3求餘,那麼餘數一定是0、1或者2,沒錯,正好與我們之前的服務器編號相同,如果求餘的結果爲0, 我們就把當前圖片名稱對應的圖片緩存在0號服務器上,如果餘數爲1,就把當前圖片名對應的圖片緩存在1號服務器上,如果餘數爲2,同理,那麼,當我們訪問任意一個圖片的時候,只要再次對圖片名稱進行上述運算,即可得出對應的圖片應該存放在哪一臺緩存服務器上,我們只要在這一臺服務器上查找圖片即可,如果圖片在對應的服務器上不存在,則證明對應的圖片沒有被緩存,也不用再去遍歷其他緩存服務器了,通過這樣的方法,即可將3萬張圖片隨機的分佈到3臺緩存服務器上了,而且下次訪問某張圖片時,直接能夠判斷出該圖片應該存在於哪臺緩存服務器上,這樣就能滿足我們的需求了,我們暫時稱上述算法爲HASH算法或者取模算法,取模算法的過程可以用下圖表示。

但是,使用上述HASH算法進行緩存時,會出現一些缺陷,試想一下,如果3臺緩存服務器已經不能滿足我們的緩存需求,那麼我們應該怎麼做呢?沒錯,很簡單,多增加兩臺緩存服務器不就行了,假設,我們增加了一臺緩存服務器,那麼緩存服務器的數量就由3臺變成了4臺,此時,如果仍然使用上述方法對同一張圖片進行緩存,那麼這張圖片所在的服務器編號必定與原來3臺服務器時所在的服務器編號不同,因爲除數由3變爲了4,被除數不變的情況下,餘數肯定不同,這種情況帶來的結果就是當服務器數量變動時,所有緩存的位置都要發生改變,換句話說,當服務器數量發生改變時,所有緩存在一定時間內是失效的,當應用無法從緩存中獲取數據時,則會向後端服務器請求數據,同理,假設3臺緩存中突然有一臺緩存服務器出現了故障,無法進行緩存,那麼我們則需要將故障機器移除,但是如果移除了一臺緩存服務器,那麼緩存服務器數量從3臺變爲2臺,如果想要訪問一張圖片,這張圖片的緩存位置必定會發生改變,以前緩存的圖片也會失去緩存的作用與意義,由於大量緩存在同一時間失效,造成了緩存的雪崩,此時前端緩存已經無法起到承擔部分壓力的作用,後端服務器將會承受巨大的壓力,整個系統很有可能被壓垮,所以,我們應該想辦法不讓這種情況發生,但是由於上述HASH算法本身的緣故,使用取模法進行緩存時,這種情況是無法避免的,爲了解決這些問題,一致性哈希算法誕生了。

我們來回顧一下使用上述算法會出現的問題。

問題1:當緩存服務器數量發生變化時,會引起緩存的雪崩,可能會引起整體系統壓力過大而崩潰(大量緩存同一時間失效)。

問題2:當緩存服務器數量發生變化時,幾乎所有緩存的位置都會發生改變,怎樣才能儘量減少受影響的緩存呢?

其實,上面兩個問題是一個問題,那麼,一致性哈希算法能夠解決上述問題嗎?

我們現在就來了解一下一致性哈希算法。

一致性哈希算法的基本概念

其實,一致性哈希算法也是使用取模的方法,只是,剛纔描述的取模法是對服務器的數量進行取模,而一致性哈希算法是對2^32取模,什麼意思呢?我們慢慢聊。

首先,我們把二的三十二次方想象成一個圓,就像鐘錶一樣,鐘錶的圓可以理解成由60個點組成的圓,而此處我們把這個圓想象成由2^32個點組成的圓,示意圖如下:

圓環的正上方的點代表0,0點右側的第一個點代表1,以此類推,2、3、4、5、6……直到2^32-1,也就是說0點左側的第一個點代表2^32-1

我們把這個由2的32次方個點組成的圓環稱爲hash環。

那麼,一致性哈希算法與上圖中的圓環有什麼關係呢?我們繼續聊,仍然以之前描述的場景爲例,假設我們有3臺緩存服務器,服務器A、服務器B、服務器C,那麼,在生產環境中,這三臺服務器肯定有自己的IP地址,我們使用它們各自的IP地址進行哈希計算,使用哈希後的結果對2^32取模,可以使用如下公式示意。

hash(服務器A的IP地址) % 2^32

通過上述公式算出的結果一定是一個0到2^32-1之間的一個整數,我們就用算出的這個整數,代表服務器A,既然這個整數肯定處於0到2^32-1之間,那麼,上圖中的hash環上必定有一個點與這個整數對應,而我們剛纔已經說明,使用這個整數代表服務器A,那麼,服務器A就可以映射到這個環上,用下圖示意

同理,服務器B與服務器C也可以通過相同的方法映射到上圖中的hash環中

hash(服務器B的IP地址) % 2^32

hash(服務器C的IP地址) % 2^32

通過上述方法,可以將服務器B與服務器C映射到上圖中的hash環上,示意圖如下

假設3臺服務器映射到hash環上以後如上圖所示(當然,這是理想的情況,我們慢慢聊)。

好了,到目前爲止,我們已經把緩存服務器與hash環聯繫在了一起,我們通過上述方法,把緩存服務器映射到了hash環上,那麼使用同樣的方法,我們也可以將需要緩存的對象映射到hash環上。

假設,我們需要使用緩存服務器緩存圖片,而且我們仍然使用圖片的名稱作爲找到圖片的key,那麼我們使用如下公式可以將圖片映射到上圖中的hash環上。

hash(圖片名稱) % 2^32

映射後的示意圖如下,下圖中的橘黃色圓形表示圖片

好了,現在服務器與圖片都被映射到了hash環上,那麼上圖中的這個圖片到底應該被緩存到哪一臺服務器上呢?上圖中的圖片將會被緩存到服務器A上,爲什麼呢?因爲從圖片的位置開始,沿順時針方向遇到的第一個服務器就是A服務器,所以,上圖中的圖片將會被緩存到服務器A上,如下圖所示。

沒錯,一致性哈希算法就是通過這種方法,判斷一個對象應該被緩存到哪臺服務器上的,將緩存服務器與被緩存對象都映射到hash環上以後,從被緩存對象的位置出發,沿順時針方向遇到的第一個服務器,就是當前對象將要緩存於的服務器,由於被緩存對象與服務器hash後的值是固定的,所以,在服務器不變的情況下,一張圖片必定會被緩存到固定的服務器上,那麼,當下次想要訪問這張圖片時,只要再次使用相同的算法進行計算,即可算出這個圖片被緩存在哪個服務器上,直接去對應的服務器查找對應的圖片即可。

剛纔的示例只使用了一張圖片進行演示,假設有四張圖片需要緩存,示意圖如下

1號、2號圖片將會被緩存到服務器A上,3號圖片將會被緩存到服務器B上,4號圖片將會被緩存到服務器C上。

一致性哈希算法的優點

經過上述描述,我想兄弟你應該已經明白了一致性哈希算法的原理了,但是話說回來,一致性哈希算法能夠解決之前出現的問題嗎,我們說過,如果簡單的對服務器數量進行取模,那麼當服務器數量發生變化時,會產生緩存的雪崩,從而很有可能導致系統崩潰,那麼使用一致性哈希算法,能夠避免這個問題嗎?我們來模擬一遍,即可得到答案。

假設,服務器B出現了故障,我們現在需要將服務器B移除,那麼,我們將上圖中的服務器B從hash環上移除即可,移除服務器B以後示意圖如下。

在服務器B未移除時,圖片3應該被緩存到服務器B中,可是當服務器B移除以後,按照之前描述的一致性哈希算法的規則,圖片3應該被緩存到服務器C中,因爲從圖片3的位置出發,沿順時針方向遇到的第一個緩存服務器節點就是服務器C,也就是說,如果服務器B出現故障被移除時,圖片3的緩存位置會發生改變

但是,圖片4仍然會被緩存到服務器C中,圖片1與圖片2仍然會被緩存到服務器A中,這與服務器B移除之前並沒有任何區別,這就是一致性哈希算法的優點,如果使用之前的hash算法,服務器數量發生改變時,所有服務器的所有緩存在同一時間失效了,而使用一致性哈希算法時,服務器的數量如果發生改變,並不是所有緩存都會失效,而是隻有部分緩存會失效,前端的緩存仍然能分擔整個系統的壓力,而不至於所有壓力都在同一時間集中到後端服務器上。

這就是一致性哈希算法所體現出的優點。

hash環的偏斜

在介紹一致性哈希的概念時,我們理想化的將3臺服務器均勻的映射到了hash環上,如下圖所示

但是,理想很豐滿,現實很骨感,我們想象的與實際情況往往不一樣。

在實際的映射中,服務器可能會被映射成如下模樣。

聰明如你一定想到了,如果服務器被映射成上圖中的模樣,那麼被緩存的對象很有可能大部分集中緩存在某一臺服務器上,如下圖所示。

上圖中,1號、2號、3號、4號、6號圖片均被緩存在了服務器A上,只有5號圖片被緩存在了服務器B上,服務器C上甚至沒有緩存任何圖片,如果出現上圖中的情況,A、B、C三臺服務器並沒有被合理的平均的充分利用,緩存分佈的極度不均勻,而且,如果此時服務器A出現故障,那麼失效緩存的數量也將達到最大值,在極端情況下,仍然有可能引起系統的崩潰,上圖中的情況則被稱之爲hash環的偏斜,那麼,我們應該怎樣防止hash環的偏斜呢?一致性hash算法中使用"虛擬節點"解決了這個問題,我們繼續聊。

虛擬節點

話接上文,由於我們只有3臺服務器,當我們把服務器映射到hash環上的時候,很有可能出現hash環偏斜的情況,當hash環偏斜以後,緩存往往會極度不均衡的分佈在各服務器上,聰明如你一定已經想到了,如果想要均衡的將緩存分佈到3臺服務器上,最好能讓這3臺服務器儘量多的、均勻的出現在hash環上,但是,真實的服務器資源只有3臺,我們怎樣憑空的讓它們多起來呢,沒錯,就是憑空的讓服務器節點多起來,既然沒有多餘的真正的物理服務器節點,我們就只能將現有的物理節點通過虛擬的方法複製出來,這些由實際節點虛擬複製而來的節點被稱爲"虛擬節點"。加入虛擬節點以後的hash環如下。

"虛擬節點"是"實際節點"(實際的物理服務器)在hash環上的複製品,一個實際節點可以對應多個虛擬節點。

從上圖可以看出,A、B、C三臺服務器分別虛擬出了一個虛擬節點,當然,如果你需要,也可以虛擬出更多的虛擬節點。引入虛擬節點的概念後,緩存的分佈就均衡多了,上圖中,1號、3號圖片被緩存在服務器A中,5號、4號圖片被緩存在服務器B中,6號、2號圖片被緩存在服務器C中,如果你還不放心,可以虛擬出更多的虛擬節點,以便減小hash環偏斜所帶來的影響,虛擬節點越多,hash環上的節點就越多,緩存被均勻分佈的概率就越大。

php實現

理論說完了,就要說實現了,來看一下具體的php實現:

<?php
//一致性hash算法的PHP實現
/**
 * 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, distribute, sublicense, and/or sell
 * 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 included 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 PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * 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 to.
     *
     * @var int
     * @comment 虛擬節點數,解決節點分佈不均的問題
     */
    private $_replicas = 64;

    /**
     * The hash algorithm, encapsulated in a Flexihash_Hasher implementation.
     * @var object Flexihash_Hasher
     * @comment 使用的hash方法 : md5,crc32
     */
    private $_hasher;

    /**
     * Internal counter for current number of targets.
     * @var int
     * @comment 節點記數器
     */
    private $_targetCount = 0;

    /**
     * Internal map of positions (hash outputs) to targets
     * @var array { position => target, ... }
     * @comment 位置對應節點,用於lookup中根據位置確定要訪問的節點
     */
    private $_positionToTarget = array();

    /**
     * Internal map of targets to lists of positions that target is hashed to.
     * @var array { target => [ position, position, ... ], ... }
     * @comment 節點對應位置,用於刪除節點
     */
    private $_targetToPositions = array();

    /**
     * Whether the internal map of positions to targets is already sorted.
     * @var boolean
     * @comment 是否已排序
     */
    private $_positionToTargetSorted = false;

    /**
     * Constructor
     * @param object $hasher Flexihash_Hasher
     * @param int $replicas Amount of positions to hash each target to.
     * @comment 構造函數,確定要使用的hash方法和需擬節點數,虛擬節點數越多,分佈越均勻,但程序的分佈式運算越慢
     */
    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 添加節點,根據虛擬節點數,將節點分佈到多個虛擬位置上
     */
    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);
    }

    /**
     * A list of all potential targets
     * @return array
     */
    public function getAll()
    {
        return array(
            "targers"=>$this->_positionToTarget,
            "positions"=>$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]; //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 查找當前的資源對應的節點,
     *          節點爲空則返回空,節點只有一個則返回該節點,
     *          對當前資源進行hash,對所有的位置進行排序,在有序的位置列上尋找當前資源的位置
     *          當全部沒有找到的時候,將資源的位置確定爲有序位置的第一個(形成一個環)
     *          返回所找到的節點
     */
    public function lookupList($resource, $requestedCount)
    {
        if (!$requestedCount)
            throw new Flexihash_Exception('Invalid count 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;
                //var_dump($results);
            }
            // return when enough results, or list exhausted
            //var_dump(count($results));
            //var_dump($requestedCount);
            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 32bits of raw data, for example
     * hexidecimal characters representing a 32bit value.
     *
     * The data must have 0xFFFFFFFF possible values, and be sortable by
     * PHP sort functions using SORT_REGULAR.
     *
     * @param string
     * @return mixed A sortable format with 0xFFFFFFFF 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 could also be used, but
        // performance seems to be the same.
    }

}

/**
 * An exception thrown by Flexihash.
 *
 * @author Paul Annesley
 * @package Flexihash
 * @licence http://www.opensource.org/licenses/mit-license.php
 */
class Flexihash_Exception extends Exception
{
}

測試代碼
$hash = new Flexihash();
$targets=array(
    "192.168.1.1:11011",
    "192.168.1.1:11012",
    "192.168.1.1:11013",
    "192.168.1.1:11014",
    "192.168.1.1:11015",
);
$hash->addTargets($targets);
for ($i=0; $i < 25; $i++) {
    $resource = sprintf("format %d",$i);
    var_dump($resource." --> ".$hash->lookup($resource));
}

輸出

string(30) "format 0 --> 192.168.1.1:11015"
string(30) "format 1 --> 192.168.1.1:11015"
string(30) "format 2 --> 192.168.1.1:11015"
string(30) "format 3 --> 192.168.1.1:11015"
string(30) "format 4 --> 192.168.1.1:11011"
string(30) "format 5 --> 192.168.1.1:11011"
string(30) "format 6 --> 192.168.1.1:11011"
string(30) "format 7 --> 192.168.1.1:11011"
string(30) "format 8 --> 192.168.1.1:11012"
string(30) "format 9 --> 192.168.1.1:11013"
string(31) "format 10 --> 192.168.1.1:11013"
string(31) "format 11 --> 192.168.1.1:11011"
string(31) "format 12 --> 192.168.1.1:11012"
string(31) "format 13 --> 192.168.1.1:11011"
string(31) "format 14 --> 192.168.1.1:11014"
string(31) "format 15 --> 192.168.1.1:11014"
string(31) "format 16 --> 192.168.1.1:11014"
string(31) "format 17 --> 192.168.1.1:11014"
string(31) "format 18 --> 192.168.1.1:11012"
string(31) "format 19 --> 192.168.1.1:11012"
string(31) "format 20 --> 192.168.1.1:11013"
string(31) "format 21 --> 192.168.1.1:11012"
string(31) "format 22 --> 192.168.1.1:11012"
string(31) "format 23 --> 192.168.1.1:11014"
string(31) "format 24 --> 192.168.1.1:11012"
[Finished in 0.1s]

redis分佈式代碼設計

<?php
require_once("Flexihash.php");
$config=array(
    "127.0.0.1:6371",
    "127.0.0.1:6372",
    "127.0.0.1:6373",
    "127.0.0.1:6374",
);
class RedisCollect {
    //redis實例
    private $_redis = null;
    //hash實例
    private $_hash = null;
    //初始化
    public function __construct() {
        global $config;
        $this->_redis = new Redis();
        $this->_hash = new Flexihash();
        $this->_hash->addTargets($config);
    }
    public function set($key="", $value="") {
        $m = $this->switchConncetion($key);
        return $m->set($key, $value);
    }
    public function get($key) {
        $m = $this->switchConncetion($key);
        return $m->get($key);
    }
    private function switchConncetion($key) {
        $hostinfo = $this->_hash->lookup($key);
        $m = $this->connect($hostinfo);
        return $m;
    }
    private function connect($hostinfo) {
        list($host, $port) = explode(":", $hostinfo);
        //printf("host = %s, port = %s\n",$host,$port);
        if(empty($host) || empty($port)) {
            return false;
        }
        try {
            $this->_redis->connect($host, $port);
            return $this->_redis;
        } catch(Exception $e) {
            die($e->getMessage());
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章