laravel5.2和redis_cluster配置

綱要:

  • laravel中redis集羣的應用php

  • predis對redis集羣模式的底層實現laravel

laravel中redis集羣的應用

這部分我想分享下laravel5.2中redis集羣的配置(官網也有redis集羣的配置講解,可是5.2版仍是有點不足,只是說了將cluster配置項設爲true,但光這樣一個選項不能表明,一個新手直接可用redis集羣,這部分還包括predis客戶端的事,因此後面我也會分享下關於predis的源碼分析)。redis

redis—cluster的搭建:Easy Building Redis-cluster (輕鬆搭建reids集羣)算法

系統軟件清單:segmentfault

配置文件:config/database.phpide

'redis' => [

    'cluster' => env('REDIS_CLUSTER', true),
    **'options'=>['cluster'=>'redis']**,  //官網沒說這個,這是必須的!!後面會講爲何這麼配?
    'default' => [
        'host' => env('REDIS_HOST', '127.0.0.1'), //任選一個master節點
        'port' => env('REDIS_PORT',6379),
        'database' => 0,
        'timeout'=>15,
        'read_write_timeout'=>1800 //redis客戶端鏈接之後的讀寫超時時間(默認是60s)
    ],
    'extra'=>[                      
        'host'=>env('REDIS_EXTRA_HOST','127.0.0.1'),  //任意一個集羣中的節點便可
        'port'=>env('REDIS_EXTRA_PORT',7001)
    ]
 ]

ok,配完上面的步驟,redis集羣就能夠用了.
具體使用redis集羣的應用場景根據業務需求有不少種,好比集羣存session等.
app('request')->session()->put('key','value');就存到集羣中了.源碼分析

predis對redis集羣的底層實現

ok,想要了解配置文件中的參數,仍是得看源代碼,固然也是predis,上代碼.

Illuminate\Support\ServiceProvider\RedisServiceProvider;

    public function register()
    {
        $this->app->singleton('redis', function ($app) {
            return new Database($app['config']['database.redis']);
        });
    }
 

 Illuminate\Redis\Database;
    
     public function __construct(array $servers = [])
     {
        $cluster = Arr::pull($servers, 'cluster');   //獲取'cluster'的鍵值
       
        $options = (array) Arr::pull($servers, 'options');
       //options 就是database.php中'options'的鍵值,是一個數組(但官網沒有提到,是個坑.)
        if ($cluster) {  
            $this->clients = $this->createAggregateClient($servers, $options);   //集羣模式'cluster=true'
        } else {
            $this->clients = $this->createSingleClients($servers, $options);   //單機模式 'cluster=false'
        }
    }

   protected function createAggregateClient(array $servers, array $options = [])
    {                
        return ['default' => new Client(array_values($servers), $options)];   //predis的Client類
    }
    


----------


注意:這裏提醒一下各參數的值:
此時$servers=[
    [
      'host' => env('REDIS_HOST', '127.0.0.1'),
      'port' => env('REDIS_PORT',6379),
      'database' => 0,
      'timeout'=>15,
      'read_write_timeout'=>1800
    ],
    [
      'host'=>env('REDIS_EXTRA_HOST','127.0.0.1'),
      'port'=>env('REDIS_EXTRA_PORT',7001)
    ]
]
$options = ['cluster'=>'redis']

其實到這兒,就能夠解釋在database.php中增長options選項,並且是必選項,由於底層代碼須要判斷數據切片的方式.
除了看源碼,
predis的包文檔也作了解釋.https://packagist.org/packages/predis/predis
-------

接下來咱們看看這些底層要初始化的類吧.

Predis\Client;
      public function __construct($parameters = null, $options = null)
    {
        $this->options = $this->createOptions($options ?: array());
        #$this->connection = $this->createConnection($parameters ?: array());
        #$this->profile = $this->options->profile;
    }
    
    protected function createOptions($options)
    {
        if (is_array($options)) {
            return new Options($options);  //如你所見,實例化Options類
        }

        if ($options instanceof OptionsInterface) {
            return $options;
        }

        throw new \InvalidArgumentException('Invalid type for client options.');
    }
   
    public function __construct(array $options = array())
    {
        $this->input = $options;
        $this->options = array();
        $this->handlers = $this->getHandlers();
    }

$this->connection = $this->createConnection($parameters ?: array())

Predis\Client 文件

    protected function createConnection($parameters)
    {
       # if ($parameters instanceof ConnectionInterface) {
       #     return $parameters;
       # }

       # if ($parameters instanceof ParametersInterface || is_string($parameters)) {
       #     return $this->options->connections->create($parameters);
       # }

       # if (is_array($parameters)) {
       #     if (!isset($parameters[0])) {
       #         return $this->options->connections->create($parameters);
       #     }

            $options = $this->options;

       #     if ($options->defined('aggregate')) {
       #         $initializer = $this->getConnectionInitializerWrapper($options->aggregate);
       #         $connection = $initializer($parameters, $options);
       #     } else {
       #         if ($options->defined('replication') && $replication = $options->replication) {
       #             $connection = $replication;
       #         } else {
                
                    $connection = $options->cluster; //
       #         }

                $options->connections->aggregate($connection, $parameters);
       #     }

            return $connection;
       # }

       # if (is_callable($parameters)) {
       #     $initializer = $this->getConnectionInitializerWrapper($parameters);
       #     $connection = $initializer($this->options);

       #     return $connection;
       # }

       # throw new \InvalidArgumentException('Invalid type for connection parameters.');
    }
    
Predis\Configuration\Options;

    protected function getHandlers()
    {
        return array(
            'cluster' => 'Predis\Configuration\ClusterOption',
            'connections' => 'Predis\Configuration\ConnectionFactoryOption',
            #'exceptions' => 'Predis\Configuration\ExceptionsOption',
            #'prefix' => 'Predis\Configuration\PrefixOption',
            #'profile' => 'Predis\Configuration\ProfileOption',
            #'replication' => 'Predis\Configuration\ReplicationOption',
        );
    }

    public function __get($option)
    {
        #if (isset($this->options[$option]) || array_key_exists($option, $this->options)) {
        #    return $this->options[$option];
        #}

         if (isset($this->input[$option]) || array_key_exists($option, $this->input)) {
            $value = $this->input[$option];
            unset($this->input[$option]);

        #    if (is_object($value) && method_exists($value, '__invoke'){
        #        $value = $value($this, $option);
        #    }

            if (isset($this->handlers[$option])) {
                $handler = $this->handlers[$option];
                $handler = new $handler(); //會實例化Predis\Configuration\ClusterOption類
                $value = $handler->filter($this, $value);
            }

            return $this->options[$option] = $value;
        }

       # if (isset($this->handlers[$option])) {
       #     return $this->options[$option] = $this->getDefault($option);
       # }

       # return;
    }
    
Predis\Configuration\ClusterOption文件

    public function filter(OptionsInterface $options, $value)
    {
        if (is_string($value)) {
            $value = $this->createByDescription($options, $value);
        }

       # if (!$value instanceof ClusterInterface) {
       #     throw new \InvalidArgumentException(
       #         "An instance of type 'Predis\Connection\Aggregate\ClusterInterface' was expected."
       #     );
       # }

        return $value;
    }
       
       
        protected function createByDescription(OptionsInterface $options, $id)
    {
        switch ($id) {
         * Abstraction for a cluster of aggregate connections to various Redis servers
 * implementing client-side sharding based on pluggable distribution strategies.
          # case 'predis':
          # case 'predis-cluster':
          #      return new PredisCluster(); 
          //這個模式是客戶端經過CRC16算法在客戶端進行數據切片,
          顯然這種模式的集羣是脆弱的,若是一個master節點掛了,
          那其備節點若也掛了,那麼獲取數據就成問題了;
          再有這種模式擴展性不好,維護成本高,
          所以這個模式不推薦.固然用最新predis不存在這個問題.
          我這邊predis,1.0算比較老了.

            case 'redis':
            case 'redis-cluster':
                return new RedisCluster($options->connections);
            //這種模式是基於服務端的數據切片,相較於第一種模式,優勢也顯而易見,維護成本低,擴展性好等.
            default:
                return;
        }
    } 
    
        public function __get($option)
    {
        #if (isset($this->options[$option]) || array_key_exists($option, $this->options)) {
        #    return $this->options[$option];
        #}

        # if (isset($this->input[$option]) || array_key_exists($option, $this->input)) {
        #    $value = $this->input[$option];
        #    unset($this->input[$option]);

        #    if (is_object($value) && method_exists($value, '__invoke'){
        #        $value = $value($this, $option);
        #    }

        #    if (isset($this->handlers[$option])) {
        #       $handler = $this->handlers[$option];
        #        $handler = new $handler(); 
        #        $value = $handler->filter($this, $value);
        #    }

        #    return $this->options[$option] = $value;
        #}

         if (isset($this->handlers[$option])) {  //$options='connections'       
            return $this->options[$option] = $this->getDefault($option);
       # }

       # return;
    }  
    
    public function getDefault($option)
    {
        if (isset($this->handlers[$option])) {
            $handler = $this->handlers[$option]; //$handler = 'Predis\Configuration\ConnectionFactoryOption';
            $handler = new $handler();

            return $handler->getDefault($this);
        }
    }
    
 Predis\Configuration\ConnectionFactoryOption文件
    public function getDefault(OptionsInterface $options)
    {
        return new Factory(); //最後實例化了一個'工廠'類
    }

$this->profile = $this->options->profile;

Predis\Configuration\ProfileOption文件


        public function __get($option)
    {
        #if (isset($this->options[$option]) || array_key_exists($option, $this->options)) {
        #    return $this->options[$option];
        #}

        # if (isset($this->input[$option]) || array_key_exists($option, $this->input)) {
        #    $value = $this->input[$option];
        #    unset($this->input[$option]);

        #    if (is_object($value) && method_exists($value, '__invoke'){
        #        $value = $value($this, $option);
        #    }

        #    if (isset($this->handlers[$option])) {
        #       $handler = $this->handlers[$option];
        #        $handler = new $handler(); 
        #        $value = $handler->filter($this, $value);
        #    }

        #    return $this->options[$option] = $value;
        #}

         if (isset($this->handlers[$option])) {  //$options='profile'       
            return $this->options[$option] = $this->getDefault($option);
       # }

       # return;
    }  
    
        public function getDefault($option)
    {
        if (isset($this->handlers[$option])) {
            $handler = $this->handlers[$option]; //$handler = 'Predis\Configuration\ProfileOption';
            $handler = new $handler();

            return $handler->getDefault($this);
        }
    }
    
 Predis\Configuration\ProfileOption文件
     public function getDefault(OptionsInterface $options)
    {
        $profile = Factory::getDefault(); //實例化了Predis\Profile\RedisVersion300類
        $this->setProcessors($options, $profile);

        return $profile;  
    }
相關文章
相關標籤/搜索