推薦:《php視頻教程》《redis教程》
php redis 實現(xiàn)全頁緩存系統(tǒng)
之前的一個項目說的一個功能,需要在后臺預(yù)先存入某個頁面信息放到數(shù)據(jù)庫,比如app的注冊協(xié)議,用戶協(xié)議,這種.然后在寫成一個php頁面,app在調(diào)用接口的時候訪問這個頁面.當(dāng)時我就發(fā)現(xiàn)一個問題,這些協(xié)議往往幾個月才會修改一次,而每一次用戶查看這些協(xié)議的時候,nginx都會重新從數(shù)據(jù)庫讀取文件,速度會很慢慢了.
如下圖m_about.php是我生成的數(shù)據(jù)頁,
在虛擬機環(huán)境下從數(shù)據(jù)庫加載出來重新生成文件需要2.4s(當(dāng)然實際的測試環(huán)境會快一點).
既然這種頁面數(shù)據(jù)都是更新少,為什么不緩存起來呢,想到之前看的redis常用應(yīng)用里面有一個全頁緩存系統(tǒng)(full page cache).不如寫一個試試看.
代碼思路
redis使用的是phpredis擴展,當(dāng)然你也可是用predis擴展,只不過需要更改里面幾個讀取函數(shù)而已.
關(guān)于緩存系統(tǒng)的接口,我這里參考了laravel里面cache系統(tǒng).這個系統(tǒng)的設(shè)計接口我覺得設(shè)置的很清晰,里面不只是包含redis,還可以使用文件,mysql,memcache.
當(dāng)然全頁緩存用不到那么多東西.只是借用他的函數(shù)設(shè)計.首先是函數(shù)geturltext,這個是獲取全頁面的數(shù)據(jù),這里沒有想到太多,直接使用file_get_contents,當(dāng)然你也可以改寫成curl函數(shù)
/ * 獲取對應(yīng)的url的信息 * @param string $url 對應(yīng)的地址 * @return boolean|string */ public function geturltext($url) { if (empty($url)) { return false; } return file_get_contents($url); }其次是幾個借鑒cache系統(tǒng)的函數(shù),remember函數(shù),記憶緩存,這個是對外的最重要的接口,一般在緩存系統(tǒng)里面直接使用它就好.
/ * 記錄對應(yīng)的緩存,如果之前存在則返回原本的緩存 * @param string $cachename 緩存名 * @param string | callback $urlorcallback 需要緩存的數(shù)據(jù)地址.可以是一個 網(wǎng)頁地址也一個可回調(diào)類型,如果不是可回調(diào)類型,則判定是一個網(wǎng)址 * @param null | int $ttl 緩存過期時間,如果不過期就是用默認(rèn)值null * @throws \\\\exception 如果無法訪問地址 * @return boolean|string 緩存成功返回獲取到的頁面地址 */ public function remember($cachename, $urlorcallback, $ttl = null) { $value = $this->get($cachename);//檢查緩存是否存在 if (!$value) { //之前沒有使用鍵 if (is_callable($urlorcallback)) { $text = $urlorcallback(); } else { //如果不是回調(diào)類型,則嘗試讀取網(wǎng)址 $text = $this->geturltext($urlorcallback); } if (empty($text)) { throw new \\\\exception('can not get value:' . $urlorcallback); } $this->put($cachename, $text, $ttl); return $text; } else { return $value; } }refresh函數(shù),刷新緩存函數(shù),如果緩存頁面被更新了,就去刷新它.
/ * 更新緩存,并返回當(dāng)前的緩存 * @param string $cachename 緩存名 * @param string | callback $urlorcallback 需要緩存的數(shù)據(jù)地址.可以是一個 網(wǎng)頁地址也一個可回調(diào)類型,如果不是可回調(diào)類型,則判定是一個網(wǎng)址 * @param null | int $ttl 過期時間,如果不過期就是用默認(rèn)值null * @return boolean|string 緩存成功返回獲取到的頁面地址 */public function refresh($cachename, $urlorcallback, $ttl = null){ $this->delete($cachename); return $this->remember($cachename, $urlorcallback, $ttl);}剩下的兩個代碼文件.一個是redisfpc.php,這是全頁緩存的demo,一個是測試用的文件
fpctest.php
這里是用的是github,連接到我本人的git博客上面.如果連接github有問題,可以看本文最后給的完整代碼.
測試
我們在這里測試,第一次加載因為需要讀取對應(yīng)的m_ahout的信息,所以慢一點
第二次加載因為從redislimian 讀取了,所以會快的多
使用建議
代碼我認(rèn)為已經(jīng)給了足夠多的接口了,在第一次緩存的時候使用remember函數(shù)記錄緩存,之后如果緩存變化后使用refresh函數(shù),更新緩存即可.如果可能的話,盡量使用ttl設(shè)置緩存的過期時間.
完整代碼
redisfpc.php
<?phpnamespace redisfpc;class redisfpc{ / * php redis的訪問類 * @var unknown */ private $redis; / * 構(gòu)造函數(shù) * @param array $redis 使用phpredis的類 * @param 是否連接成功 */ public function __construct($redis = []) { //$this->redis = $redis; $this->redis = new \\\\redis(); return $this->redis->connect('127.0.0.1'); } / * 記錄對應(yīng)的緩存,如果之前存在則返回原本的緩存 * @param string $cachename 緩存名 * @param string | callback $urlorcallback 需要緩存的數(shù)據(jù)地址.可以是一個 網(wǎng)頁地址也一個可回調(diào)類型,如果不是可回調(diào)類型,則判定是一個網(wǎng)址 * @param null | int $ttl 緩存過期時間,如果不過期就是用默認(rèn)值null * @throws \\\\exception 如果無法訪問地址 * @return boolean|string 緩存成功返回獲取到的頁面地址 */ public function remember($cachename, $urlorcallback, $ttl = null) { $value = $this->get($cachename);//檢查緩存是否存在 if (!$value) { //之前沒有使用鍵 if (is_callable($urlorcallback)) { $text = $urlorcallback(); } else { //如果不是回調(diào)類型,則嘗試讀取網(wǎng)址 $text = $this->geturltext($urlorcallback); } if (empty($text)) { throw new \\\\exception('can not get value:' . $urlorcallback); } $this->put($cachename, $text, $ttl); return $text; } else { return $value; } } / * 獲取對應(yīng)的緩存值 * @param string $cache