NexusPHP优化(1)支持Redis缓存

代码以nexusphp.v1.5.beta5.20120707为例,进行讲解。

打开classes/class_cache.php分析代码:
调用缓存Memcache的地方有:

  1. 【连接】__construct方法内:$this->connect($host, $port);
  2. 【删除】unlock方法内:$this->delete('lock_'.$Key);
  3. 【写】cache_value方法内:$this->set($Key, $Value, 0, $Duration);
  4. 【读】get_value方法内:$this->get($Key);
  5. 【删】delete_value方法内:$this->delete($Key);

总结一下,只需要实现4个基本的方法即可支持任何缓存,分别是:
$this->connect()$this->get()$this->set()$this->delete()

根据类的继承特性,我们只需要编写一个RedisCache类作为基类,实现上述4个基本方法即可:

class RedisCache
{
    public static $redis = null;    // 缓存对象
    private $type = 'http';            // Redis连接方式 unix,http
    private $socket = '';            // unix方式连接时,需要配置
    private $host = '127.0.0.1';    // Redis域名
    private $port = 6379;            // Redis端口,默认为6379
    private $prefix = '';            // Redis key prefix
    private $auth = '';                // Redis 身份验证
    private $db = 0;                // Redis库,默认0
    private $timeout = 60;            // 连接超时时间,单位秒,默认60
    //连接Redis
    public function connect($host, $port)
    {
        // 单例模式
        if (self::$redis === null) {
            try {
                if (extension_loaded('Redis')) {
                    self::$redis = new Redis();
    
                    // 连接
                    if ($this->type === 'unix' && $this->socket !== '') {
                        self::$redis->connect($this->socket);
                    } else {
                        self::$redis->connect($this->host, $this->port, $this->timeout);
                    }
    
                    // 验证
                    if ($this->auth != '') {
                        self::$redis->auth($this->auth);
                    }
            
                    // 选择
                    self::$redis->select($this->db);

                    return self::$redis;
                }
            } catch (Exception $e) {
                echo $e->getMessage();
            }
            return false;
        }
        return self::$redis;
    }

    /**
     * 将value 的值赋值给key,生存时间为expire秒
     */
    public function set($key, $value, $What, $expire = 600)
    {
        self::$redis->setex($this->formatKey($key), $expire, $this->formatValue($value));
    }

    public function get($key)
    {
        $value = self::$redis->get($this->formatKey($key));
        return $value !== false ? $this->unformatValue($value) : null;
    }

    public function delete($key)
    {
        return self::$redis->delete($this->formatKey($key));
    }

    /**
     * 检测是否存在key,若不存在则赋值value
     */
    public function setnx($key, $value)
    {
        return self::$redis->setnx($this->formatKey($key), $this->formatValue($value));
    }

    public function lPush($key, $value)
    {
        return self::$redis->lPush($this->formatKey($key), $this->formatValue($value));
    }

    public function rPush($key, $value)
    {
        return self::$redis->rPush($this->formatKey($key), $this->formatValue($value));
    }

    public function lPop($key)
    {
        $value = self::$redis->lPop($this->formatKey($key));
        return $value !== false ? $this->unformatValue($value) : null;
    }

    public function rPop($key)
    {
        $value = self::$redis->rPop($this->formatKey($key));
        return $value !== false ? $this->unformatValue($value) : null;
    }

    protected function formatKey($key)
    {
        return $this->prefix . $key;
    }

    protected function formatValue($value)
    {
        return @serialize($value);
    }

    protected function unformatValue($value)
    {
        return @unserialize($value);
    }

    /**
     * 获取Redis实例,当封装的方法未能满足时,可调用此接口获取Reids实例进行操作
     */
    public function getRedis()
    {
        return self::$redis;
    }
}

完整代码为:

<?php
class RedisCache
{
    public static $redis = null;    // 缓存对象
    private $type = 'http';            // Redis连接方式 unix,http
    private $socket = '';            // unix方式连接时,需要配置
    private $host = '127.0.0.1';    // Redis域名
    private $port = 6379;            // Redis端口,默认为6379
    private $prefix = '';            // Redis key prefix
    private $auth = '';                // Redis 身份验证
    private $db = 0;                // Redis库,默认0
    private $timeout = 60;            // 连接超时时间,单位秒,默认60
    //连接Redis
    public function connect($host, $port)
    {
        // 单例模式
        if (self::$redis === null) {
            try {
                if (extension_loaded('Redis')) {
                    self::$redis = new Redis();
    
                    // 连接
                    if ($this->type === 'unix' && $this->socket !== '') {
                        self::$redis->connect($this->socket);
                    } else {
                        self::$redis->connect($this->host, $this->port, $this->timeout);
                    }
    
                    // 验证
                    if ($this->auth != '') {
                        self::$redis->auth($this->auth);
                    }
            
                    // 选择
                    self::$redis->select($this->db);

                    return self::$redis;
                }
            } catch (Exception $e) {
                echo $e->getMessage();
            }
            return false;
        }
        return self::$redis;
    }

    /**
     * 将value 的值赋值给key,生存时间为expire秒
     */
    public function set($key, $value, $What, $expire = 600)
    {
        self::$redis->setex($this->formatKey($key), $expire, $this->formatValue($value));
    }

    public function get($key)
    {
        $value = self::$redis->get($this->formatKey($key));
        return $value !== false ? $this->unformatValue($value) : null;
    }

    public function delete($key)
    {
        return self::$redis->delete($this->formatKey($key));
    }

    /**
     * 检测是否存在key,若不存在则赋值value
     */
    public function setnx($key, $value)
    {
        return self::$redis->setnx($this->formatKey($key), $this->formatValue($value));
    }

    public function lPush($key, $value)
    {
        return self::$redis->lPush($this->formatKey($key), $this->formatValue($value));
    }

    public function rPush($key, $value)
    {
        return self::$redis->rPush($this->formatKey($key), $this->formatValue($value));
    }

    public function lPop($key)
    {
        $value = self::$redis->lPop($this->formatKey($key));
        return $value !== false ? $this->unformatValue($value) : null;
    }

    public function rPop($key)
    {
        $value = self::$redis->rPop($this->formatKey($key));
        return $value !== false ? $this->unformatValue($value) : null;
    }

    protected function formatKey($key)
    {
        return $this->prefix . $key;
    }

    protected function formatValue($value)
    {
        return @serialize($value);
    }

    protected function unformatValue($value)
    {
        return @unserialize($value);
    }

    /**
     * 获取Redis实例,当封装的方法未能满足时,可调用此接口获取Reids实例进行操作
     */
    public function getRedis()
    {
        return self::$redis;
    }
}
//支持多种缓存:Memcached、RedisCache
class CACHE extends RedisCache
{
    public $isEnabled;
    public $clearCache = 0;
    public $language = 'en';
    public $Page = array();
    public $Row = 1;
    public $Part = 0;
    public $MemKey = "";
    public $Duration = 0;
    public $cacheReadTimes = 0;
    public $cacheWriteTimes = 0;
    public $keyHits = array();
    public $languageFolderArray = array();

    public function __construct($host = 'localhost', $port = 11211)
    {
        $success = $this->connect($host, $port); // Connect to memcache
        if ($success) {
            $this->isEnabled = 1;
        } else {
            $this->isEnabled = 0;
        }
    }

    public function getIsEnabled()
    {
        return $this->isEnabled;
    }

    public function setClearCache($isEnabled)
    {
        $this->clearCache = $isEnabled;
    }

    public function getLanguageFolderArray()
    {
        return $this->languageFolderArray;
    }

    public function setLanguageFolderArray($languageFolderArray)
    {
        $this->languageFolderArray = $languageFolderArray;
    }

    public function getClearCache()
    {
        return $this->clearCache;
    }

    public function setLanguage($language)
    {
        $this->language = $language;
    }

    public function getLanguage()
    {
        return $this->language;
    }

    public function new_page($MemKey = '', $Duration = 3600, $Lang = true)
    {
        if ($Lang) {
            $language = $this->getLanguage();
            $this->MemKey = $language."_".$MemKey;
        } else {
            $this->MemKey = $MemKey;
        }
        $this->Duration = $Duration;
        $this->Row = 1;
        $this->Part = 0;
        $this->Page = array();
    }

    public function set_key()
    {
    }

    //---------- Adding functions ----------//

    public function add_row()
    {
        $this->Part = 0;
        $this->Page[$this->Row] = array();
    }

    public function end_row()
    {
        $this->Row++;
    }

    public function add_part()
    {
        ob_start();
    }

    public function end_part()
    {
        $this->Page[$this->Row][$this->Part]=ob_get_clean();
        $this->Part++;
    }

    // Shorthand for:
    // add_row();
    // add_part();
    // You should only use this function if the row is only going to have one part in it (convention),
    // although it will theoretically work with multiple parts.
    public function add_whole_row()
    {
        $this->Part = 0;
        $this->Page[$this->Row] = array();
        ob_start();
    }

    // Shorthand for:
    // end_part();
    // end_row();
    // You should only use this function if the row is only going to have one part in it (convention),
    // although it will theoretically work with multiple parts.
    public function end_whole_row()
    {
        $this->Page[$this->Row][$this->Part]=ob_get_clean();
        $this->Row++;
    }

    // Set a variable that will only be availabe when the system is on its row
    // This variable is stored in the same way as pages, so don't use an integer for the $Key.
    public function set_row_value($Key, $Value)
    {
        $this->Page[$this->Row][$Key] = $Value;
    }

    // Set a variable that will always be available, no matter what row the system is on.
    // This variable is stored in the same way as rows, so don't use an integer for the $Key.
    public function set_constant_value($Key, $Value)
    {
        $this->Page[$Key] = $Value;
    }

    // Inserts a 'false' value into a row, which breaks out of while loops.
    // This is not necessary if the end of $this->Page is also the end of the while loop.
    public function break_loop()
    {
        if (count($this->Page)>0) {
            $this->Page[$this->Row] = false;
            $this->Row++;
        }
    }

    //---------- Locking functions ----------//

    // These functions 'lock' a key.
    // Users cannot proceed until it is unlocked.

    public function lock($Key)
    {
        $this->cache_value('lock_'.$Key, 'true', 3600);
    }

    public function unlock($Key)
    {
        $this->delete('lock_'.$Key);
    }

    //---------- Caching functions ----------//

    // Cache $this->Page and resets $this->Row and $this->Part
    public function cache_page()
    {
        $this->cache_value($this->MemKey, $this->Page, $this->Duration);
        $this->Row = 0;
        $this->Part = 0;
    }

    // Exact same as cache_page, but does not store the page in cache
    // This is so that we can use classes that normally cache values in
    // situations where caching is not required
    public function setup_page()
    {
        $this->Row = 0;
        $this->Part = 0;
    }

    // Wrapper for Memcache::set, with the zlib option removed and default duration of 1 hour
    public function cache_value($Key, $Value, $Duration = 3600)
    {
        $this->set($Key, $Value, 0, $Duration);
        $this->cacheWriteTimes++;
        $this->keyHits['write'][$Key] = !$this->keyHits['write'][$Key] ? 1 : $this->keyHits['write'][$Key]+1;
    }

    //---------- Getting functions ----------//

    // Returns the next row in the page
    // If there's only one part in the row, return that part.
    public function next_row()
    {
        $this->Row++;
        $this->Part = 0;
        if ($this->Page[$this->Row] == false) {
            return false;
        } elseif (count($this->Page[$this->Row]) == 1) {
            return $this->Page[$this->Row][0];
        } else {
            return $this->Page[$this->Row];
        }
    }

    // Returns the next part in the row
    public function next_part()
    {
        $Return = $this->Page[$this->Row][$this->Part];
        $this->Part++;
        return $Return;
    }

    // Returns a 'row value' (a variable that changes for each row - see above).
    public function get_row_value($Key)
    {
        return $this->Page[$this->Row][$Key];
    }

    // Returns a 'constant value' (a variable that doesn't change with the rows - see above)
    public function get_constant_value($Key)
    {
        return $this->Page[$Key];
    }

    // If a cached version of the page exists, set $this->Page to it and return true.
    // Otherwise, return false.
    public function get_page()
    {
        $Result = $this->get_value($this->MemKey);
        if ($Result) {
            $this->Row = 0;
            $this->Part = 0;
            $this->Page = $Result;
            return true;
        } else {
            return false;
        }
    }

    // Wrapper for Memcache::get. Why? Because wrappers are cool.
    public function get_value($Key)
    {
        if ($this->getClearCache()) {
            $this->delete_value($Key);
            return false;
        }
        // If we've locked it
        // Xia Zuojie: we disable the following lock feature 'cause we don't need it and it doubles the time to fetch a value from a key
        /*while($Lock = $this->get('lock_'.$Key)){
            sleep(2);
        }*/

        $Return = $this->get($Key);
        $this->cacheReadTimes++;
        $this->keyHits['read'][$Key] = !$this->keyHits['read'][$Key] ? 1 : $this->keyHits['read'][$Key]+1;
        return $Return;
    }

    // Wrapper for Memcache::delete. For a reason, see above.
    public function delete_value($Key, $AllLang = false)
    {
        if ($AllLang) {
            $langfolder_array = $this->getLanguageFolderArray();
            foreach ($langfolder_array as $lf) {
                $this->delete($lf."_".$Key);
            }
        } else {
            $this->delete($Key);
        }
    }

    public function getCacheReadTimes()
    {
        return $this->cacheReadTimes;
    }

    public function getCacheWriteTimes()
    {
        return $this->cacheWriteTimes;
    }

    public function getKeyHits($type='read')
    {
        return (array)$this->keyHits[$type];
    }
}
最后修改:2020 年 03 月 10 日 02 : 52 PM
如果觉得我的文章对你有用,请随意赞赏

发表评论