一.問題の背景
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.长连接,降低路途成本
コメントはまだありません