1. 程式人生 > >laravel框架實現redis分散式叢集原理

laravel框架實現redis分散式叢集原理

在app/config/database.php中配置如下:

'redis' => array(
        'cluster' => true,
        'default' => array(
            'host' => '172.21.107.247',
            'port' => 6379,
        ),
        'redis1' => array(
            'host' => '172.21.107.248',
            'port' => 6379,
        ),
)

其中cluster選擇為true,接下來就可以作叢集使用了;
如果把session的driver設定為redis,則可以使用其叢集功能了:
我們來看下session的實現,當我們在程式碼中這樣寫:

Session::put('test', 124);

實際的執行流程是這樣的:

Illuminate\Support\Facades\Session
Illuminate\Support\Facades\Facade
Illuminate\Session\Facade::app['session']->put

Illuminate\Session\Facade::app[
'session']為Illuminate\Session\SessionManager Illuminate\Support\Manager::__call

Session會根據返回建立driver
$this->app['config']['session.driver']
即配置檔案中配置的,這裡我們配置為redis

Illuminate\Session\SessionManager::Illuminate\Session\SessionManager
最終由Illuminate\Session\Store來負責put的呼叫

而Store類負責儲存的類是Illuminate\Session\CacheBasedSessionHandler


後者又將請求轉發給$this->app['cache']->driver($driver)

……
經過一系列程式碼追查,儲存類為Predis\Client\Database,看其建構函式:

public function __construct(array $servers = array())
    {
        if (isset($servers['cluster']) && $servers['cluster'])
        {
            $this->clients = $this->createAggregateClient($servers);
        }
        else
        {
            $this->clients = $this->createSingleClients($servers);
        }
    }

如果設定為叢集,則呼叫createAggregateClient方法

protected function createAggregateClient(array $servers)
    {
        $servers = array_except($servers, array('cluster'));
        return array('default' => new Client(array_values($servers)));
    }

這裡會把所有伺服器放在default組中
實際存資料的類是Predis\Client,這裡有根據配置建立伺服器的程式碼,具體可以自己看下;

Predis\Cluster\PredisClusterHashStrategy類負責計算key的hash,關鍵函式:
getHash
getKeyFromFirstArgument

Predis\Cluster\Distribution\HashRing負責伺服器環的維護,關鍵函式
addNodeToRing
get
hash

大概原理是這樣,如執行以下redis命令
get ok
會將ok作crc32運算得到一個hash值
所有伺服器按一定演算法放到一個長度預設為128的陣列中,每個伺服器在其中佔幾項,由以下決定:
權重/總權重*總的伺服器數量*128,可參考Predis\Cluster\Distribution\HashRing::addNodeToRing方法

每一項的hash值是按伺服器ip:埠的格式,作crc32計算的

protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio)
    {
        $nodeObject = $node['object'];
        $nodeHash = $this->getNodeHash($nodeObject);
        $replicas = (int) round($weightRatio * $totalNodes * $replicas);

        for ($i = 0; $i < $replicas; $i++) {
            $key = crc32("$nodeHash:$i");
            $ring[$key] = $nodeObject;
        }
    }

key的hash值也有了,伺服器環也計算好了,剩下的就是查找了,二分法能較快的查詢相應的伺服器節點。