PHP laravel框架Redis門面的誤用

使用laravel的Redis時候亂用Facades踩了一個坑。。。


判斷Redis是否有某個key值得時候是可以這樣寫,

Redis::exists(key值)

因爲使用了Redis門面,所以可以直接使用Redis::exists而不用先建立實例,就是文檔中介紹的“Facade 基類使用魔術方法 __callStatic() 從你的門面中調用解析對象”。

簡單說就是沒有exists這個靜態方法,使用的時候會調用__callStatic(),建立實例:

$instance = static::getFacadeRoot();


使用Redis時候沒有使用默認庫0,所以按照文檔上的例子指定服務,

代碼如下:

Redis::connection('users');
Redis::exists(key值)

這麼做不能判斷key值是否存在,測試以後發現第二行Redis::exists連接的仍然是默認庫0。


亂用Facades踩到坑了!


我用的是laravel5.2,redis使用Predis。

雖然Redis的Facades是一個靜態代理,在使用時候"Redis::"也是靜態唯一的,BUT connection並不是一個靜態方法。

Redis對應的Facades底層類 Illuminate\Redis\Database,connection方法如下

    /**
     * Get a specific Redis connection instance.
     *
     * @param  string  $name
     * @return \Predis\ClientInterface|null
     */
    public function connection($name = 'default')
    {
        return Arr::get($this->clients, $name ?: 'default');
    }


返回redis的鏈接實例。

實際上$this->clients在Redis::調用__callStatic()建立實例的時候就已經初始化,

    /**
     * Create a new Redis connection instance.
     *
     * @param  array  $servers
     * @return void
     */
    public function __construct(array $servers = [])
    {
        $cluster = Arr::pull($servers, 'cluster');
        $options = (array) Arr::pull($servers, 'options');
        if ($cluster) {
            $this->clients = $this->createAggregateClient($servers, $options);
        } else {
            $this->clients = $this->createSingleClients($servers, $options);
        }
    }

打印發現 $this->clients 是將 redis的所有配置都初始化,當connection傳入配置名的時候選擇這個配置鏈接實例。

雖然Redis實例是靜態的,但是這個redis鏈接並不是。


Redis::exists()更是和connection()方法無關,redis門面調用__callStatic()後會執行 Illuminate\Redis\Database 的exists方法,但是Database沒有這個方法,所以觸發__call()方法。

    /**
     * Dynamically make a Redis command.
     *
     * @param  string  $method
     * @param  array   $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        return $this->command($method, $parameters);
    }


而command() 方法使用的是default配置,也就是說Redis::所有方法除了connection都是默認庫。

    /**
     * Run a command against the Redis database.
     *
     * @param  string  $method
     * @param  array   $parameters
     * @return mixed
     */
    public function command($method, array $parameters = [])
    {
        return call_user_func_array([$this->clients['default'], $method], $parameters);
    }


所以要使用非默認配置的同一個redis鏈接時候必須保存redis實例,

$redis = Redis::connection("user");
$redis->exists(key值);


$redis->exists 不會調用database的command方法,redis繼續使用connection選擇的初始化鏈接,不會選擇default配置。

這其實還涉及到Predis底層的實現,已經超出我的理解範圍。


看完代碼感覺還是有點暈。。。

反正Redis門面還是不要亂用。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章