一個簡單的Redis應用
原文 http://blog.csdn.net/raptor/article/details/44019401
需求
很早就聽說過Redis的大名,但是一直也沒有去研究。因為前一陣有朋友需要用到,來問過我時大致看了看,正好最近自己也碰到需求,就研究了一下。
我這里的情況其實很簡單:
在一個VPS上跑了一個應用,提供了短鏈接功能,原來的實現是直接在web server上 做了一個反向代理,通過urlrewrite映射到后端服務器上。這么用當然是沒有問題,功能是完全可以實現的,只是后來發現,當把短鏈接分享到社交網絡的時候,會有短時的大量訪問,這些訪問全部壓到后端去查詢數據庫,就會有一點壓力了——因為我的VPS配置很低,所以在突發高訪問時還是有一定的概率發生 50x錯誤。
于是考慮把反向代理用redis改寫成一個緩存代理。其實這種需求用memcached也可以,因為持久化并非必須,大不了重啟以后從后端再取一次就是了。不過因為我對redis更感興趣,所以還是用redis來做了。
安裝配置
redis的安裝很簡單,我用的是debian,直接apt-get install redis-server即可。
配置上也沒什么可說的,大部分都是用默認配置,只是對內存使用作了一點限制——VPS資源緊張,要節約。反正短鏈接的數據量也不大。
安裝完成以后可以用 service redis-server start 啟動。用 redis-cli 可以通過命令操作數據庫。
雖然我對Python比較熟,但是這種簡單應用就懶得折騰環境了,直接用現成的PHP做吧。
首先是需要下載安裝一個PHP的redis庫, 官方最推薦的兩個PHP庫 是 predis 和 phpredis ,雖然目測用C寫的phpredis應該性能好些,但為了圖方便,我還是用純PHP的predis。
下載最新穩定版的Predis后,運行bin/create-single-file(需要系統中安裝了php-cli)即可生成一個單獨的Predis.php,把這個文件放到你的項目路徑中就可以使用了,相當簡單。
功能實現
這個緩存功能很簡單:
先取得短鏈接的ID,然后用這個ID去redis里查詢。如果查到URL就直接返回302重定向到這個URL。如果查不到就向后端查詢,取得302響應返回的redirect_url,然后把這個URL保存到redis,并作302重定向。
需要注意的是,對于向后端查詢失敗的ID也要保存起來,并返回404錯誤,以避免錯誤的ID不斷向后端查詢,但是風險在于如果這個錯誤的ID以后用到了,也會查詢不到,所以還需要給錯誤的ID設置超時,過期后刪除以便可以重新查詢。
主體部分代碼就這么點:
function raise_404() { header('HTTP/1.1 404 Not Found'); header("status: 404 Not Found"); die(); } function redirect($url) { header("Location: $url"); } require 'Predis.php'; $redisdb = new Predis\Client([ 'database' => 1, ]); $query = (array) explode('/', $_SERVER['REQUEST_URI']); if (!isset($query[1]) || $query[1]=="") raise_404(); $id = $query[1]; $url = $redisdb->get($id); if (!isset($url)) { $url = get_redirect($id); $redisdb->set($id, $url); if ($url == "") { $redisdb->expire($id, 3600); } } if ($url != "") { redirect($url); } else { raise_404(); }
其中get_redirect是通過curl向后端查詢實際URL的函數,這里就不列出具體實現了。
實際跑了一段時間以后的效果還是很明顯的。從LOG統計上看,在這個緩存代理處理了500個請求的時候,后端實際上只處理了不到100個,而且隨著時間推移,緩存的ID越來越多,對后端的請求會越來越少。
這個試驗成功以后,我把RSS功能也緩存起來,不過這個就復雜一些,但原理還是差不多的,只不過因為字段多了,改為使用redis的 HashMap值方式(支持多種類型的value是redis的一大強頂),另外還加上了Last-Modified和Etag支持,期望爬蟲們能聰明一點,會用這兩個東西來進一步減少不必要的訪問量。