跳到主要內容
黯羽輕揚每天積累一點點

redis 快取服務

免費2017-04-30#Node#Solution#node redis缓存#redis nodejs#node内存缓存#nodejs redis配置

立竿見影的提速效果,20s->5s

一。問題背景

為了 HTTPS 拋棄了釘子戶國內虛擬主機,趁機用 Node 重寫了之前的 PHP 服務,放到好貴的 VPS 上,搬出去後發現抓取國內 RSS 經常超時,不超時的情況也需要 loading 20s 的樣子,完全不可用。搬意已決,那就想辦法提速

之前方案是請求時現抓,拿回來解析完畢後響應請求,過程看起來很慢,但實際很快,一般 loading 不超過 3s,自用可以接受,所以只做了客戶端記憶體快取和離線快取

現在 20s 完全無法忍受,所以先上見效最快的記憶體快取:

  • 定時抓取,預先存入 redis

  • redis 記憶體快取,簡單過期策略

每 2 小時去全部抓取一遍,存入 redis,請求先過快取檢查,快取裡有就不現抓,除非服務剛剛重啟過,才需要現抓。定時抓取時不影響正常響應,因為抓取場景可以不用考慮髒資料問題,新一點舊一點沒太大關係(但極端情況資料是 定時抓取間隔 + 客戶端快取過期時間 之前的,這時資料就很舊了)

二。安裝配置 redis

安裝

CentOS 環境,編譯安裝 redis stable:

# 下載
wget http://download.redis.io/releases/redis-stable.tar.gz
# 解壓
tar -axvf redis-stable.tar.gz
cd redis-stable
# 編譯安裝
make
make test
make install

預設安裝路徑在 /usr/local/bin

$ ls /usr/local/bin | grep 'redis'
redis-benchmark
redis-check-aof
redis-check-rdb
redis-cli
redis-sentinel
redis-server

配置

預設配置檔案在安裝包的根目錄 redis.conf

mkdir -p /etc/redis/
cp redis.conf etc/redis
# 修改配置項
vi etc/redis/redis.conf
# 後臺執行,預設不後臺
#   把 GENERAL 節的 daemonize no 改為 daemonize yes
# 改密碼,預設免密登入
#   把 SECURITY 節的 requirepass 去掉註釋,改為 requirepass <mypassword>

其它配置項埠號,日誌目錄等等無所謂,需要的話修改,然後啟動驗證:

# 啟動服務
redis-server /etc/redis/redis.conf
# 客戶端連線
redis-cli
auth <mypassword>
# 操作
set 'key' 'value'
get 'key'

P.S.redis 的更多命令,請檢視 Command reference – Redis,或者線上試玩Try Redis

新增到系統服務

redis-server /etc/redis/redis.conf 每次這樣啟動看著比較難受,新增到系統服務裡就可以通過 service redis <cmd> 管理了:

# 拷貝啟動指令碼
cp util/redis_init_script /etc/init.d/
# 改名
mv /etc/init.d/redis_init_script /etc/init.d/redis

然後修改配置項:

vi /etc/init.d/redis
# 修改第二行的 chkconfig xxx 為 chkconfig 2345 80 90
# 確認埠號正確 REDISPORT=6379
# 確認 server 可執行檔案路徑正確 EXEC=/usr/local/bin/redis-server
# 確認 cli 路徑正確 CLIEXEC=/usr/local/bin/redis-cli
# 確認 redis.conf 路徑正確 CONF="/etc/redis/${REDISPORT}.conf"
# start 改為後臺執行
#   把$EXEC $CONF 改為$EXEC $CONF &

P.S.# chkconfig 2345 80 90 中,2345 是指執行級別,80 90 分別表示啟動/關機優先順序,數值越小越優先,控制順序,更多資訊請檢視 chkconfig

預設讀取 /etc/redis/${REDISPORT}.conf,也就是 /etc/redis/6379.conf,以應對多例項情況,所以我們給配置檔案改名:

mv /etc/redis/redis.conf /etc/redis/6379.conf

最後註冊系統服務:

# 註冊
chkconfig --add redis
# 設定自啟動
chkconfig redis on

可以通過 service 命令管理了:

service redis start

三.node 接 redis

有現成的第三方模組 node_redis

npm install redis --save

嘗試連線:

const redis = require('redis');

const PORT = 6379;
const HOST = '127.0.0.1';
const PWD = 'mypassword';
const OPTS = {auth_pass: PWD};

// connect redis
let client = redis.createClient(PORT, HOST, OPTS);
client
    .on('error', (err) => {
        console.log('Error ' + err);
    })
    .on('ready', () => console.log('redis connected'));

connect 之後就可以隨便操作了,API 與 redis 命令一致:

// 寫
client.set(key, val, callback);
// 讀
client.get(key, (error, val) => {});
// 設定有效期
client.expire(key, seconds);
// 檢查過期
client.expire(url, (error, ttl) => {
    if (ttl > 0) console.log('alive');
    else console.log('died');
});

特別注意:所有 callback 都是 Node 經典方式,第一個引數是 err,而不是 data

做一個簡單的快取層,結構如下:

cache
  - queue
  - clearQueue()
  - expire()
  - ttl()

  + set()
  + get()
  + checkFresh()

配合訪問抓取和定時抓取:

fetch
  - onerror(error) => {
        emitter.emit('error', error);
    };
  - onsuccess(data) => {
        data && cache.set(url, data);
    };
  - fetchNow()

  + fetch() => {
        if (noCache) {
            cache.checkFresh(url, (fresh) => {
                if (!fresh) console.log('schedule force fetch now'), fetchNow();
                else oncancel('cache is still fresh now');
            }
        }
        else {
            cache.get(url, (data) => {
                if (data) console.log('fetch from cache'), onsuccess(data);
                else fetchNow();
            });
        }
    }

訪問抓取走快取,直接從快取去,沒有才抓。定時抓取強制不走快取,但檢查過期,如果資料還很新,就取消抓取任務,不新的話現抓,抓取成功就過快取層記錄下來

P.S. 定時抓取檢查過期是為了避免不必要的重複抓取,比如服務掛了重啟了,redis 的資料不受影響,仍然是新的,這時沒必要再抓一遍

四。總結

提速效果很明顯,之前訪問國內資源 20s 的 loading 縮短到 5-6s 了,國外資源也快了 1-2s 的樣子,比起之前的 PHP 服務,5-6s 還是挺慢,但接下來的優化項就沒這麼簡單粗暴了:

todo
1. 介面拆分,有介面返回 128K 文字,考慮分頁
2.redis 資料結構優化,目前是 url key ��應一個很大的 JSON 字串,應該有更科學的方式
3. 長連線,降低路途成本

評論

暫無評論,快來發表你的看法吧

提交評論