最近でNode.jsで Redis Clusterを触る機会があったのですが
意外とRedis Cluster × Node.jsの日本語記事が無かったため、備忘録を兼ねて記事に起こしておきます。
今回利用するパッケージはこちら。
GitHub - luin/ioredis: 🚀A robust, performance-focused and full-featured Redis client for Node.js.
typescriptを使う場合は 一緒に型もimportしておきましょう。
npm i ioredis
npm i -D @types/ioredis
Constructor
cluster形式の場合は利用する nodeを配列形式で指定する必要があります。
他に何もOptionを指定する必要が無いのであれば、基本この設定だけで良いです。
const driver: IORedis.Cluster = new IORedis.Cluster([
{ port: 6380, host: '127.0.0.1'},
{ port: 6381, host: '127.0.0.1'},
]);
redisOptions
おそらく一番触る必要が出てくる箇所。
名前の通り Redisに対するオプションを設定します。
例えばパスワードが必要な場合は以下のように redisOptionで指定することが出来ます。
const driver: IORedis.Cluster = new IORedis.Cluster([],
redisOptions: {
password: config.password
}
);
設定できるオプションはAPI - ioredis / new Redis([port], [host], [options]で確認でき、
今回は個人的に使う機会がありそうな項目を列挙しておきます。
オプション名 | デフォルト値 | 詳細 |
---|---|---|
db | 0 | 使用するデータベースインデックス |
password | null | パスワード |
dropBufferSupport | FALSE | バッファサポートの削除を有効化,。巨大な配列の応答を処理するときに有効にするとパフォーマンスが向上するらしい(未検証) |
enableReadyCheck | TRUE | Redisのサーバーステータスを確認してからコマンドを送信する |
connectTimeout | 10000 | 初期接続中にタイムアウトが発生するまでのミリ秒 |
tls | null | TLS接続のサポート (https://github.com/luin/ioredis#tls-options) |
readOnly | FALSE | 読み取り専用 |
その他のオプション
redisOption以外にも幾つか指定できるオプションがあるため、こちらも記載。
こちらも同様にAPI - ioredis / new Cluster(startupNodes, options) に設定項目が載っています。
一応 使いそうなものをピックアップしています。
オプション名 | デフォルト値 | 詳細 |
---|---|---|
clusterRetryStrategy | ノードに到達できない場合に呼び出されるcallback。 returnした数値分待機して再接続を試みる | |
scaleReads | master | 読み取り対象のnodeを指定する。指定可能なのは master, slave, all のいずれか |
maxRedirections | 16 | ターゲット nodeからエラー(MOVED, CLUSTERDOWN等)が返った場合に他のnodeにリダイレクトする回数 |
retryDelayOnFailover | 100 | ターゲット nodeが切断されている場合に指定秒後にコマンドを再送信する |
const driver: IORedis.Cluster = new IORedis.Cluster([],
redisOptions: {},
retryDelayOnFailover: 50
);
ただ、redisOptionsと違って こちらを指定するケースは殆どなさそうです。
Command
基本Commandを中心に。
特に難しくありませんが、writeに関しては書き込む際の型に注意が必要です。
read
await redis.get('key');
write
await redis.set('key', 'value');
expire を指定する場合は 以下のように EX と 有効期限(秒)を指定します。
redis.set('key', 'value', 'EX', 10);
注意点としては Object型をvalueとしてセットすると [Object object]
という形で保存されてしまうため、
Object型を保存するときは JSON.stringify
で文字型に変換して入れるようにしましょう。
delete
await this.redis.del('key');
keys
正規表現マッチで取得する。
getでは 未ヒット時は nullが返りますが、こちらは空配列が返ります。
await this.redis.keys('regExp');
Event
Redisへの接続状態に合わせてイベントがトリガーされます。
発火されるイベントは以下の通り。
イベント | 詳細 |
---|---|
connect | Redisサーバーへの接続が確立した時 |
ready | コマンドを受付が可能な時(enableReadyCheck で trueを確認できた時) |
error | 接続中にエラーが発生した時 |
close | Redisサーバー接続が閉じた時 |
reconnecting | 再接続が行われた時 |
end | Redisサーバーとの接続が閉じた時 |
発火したイベントは以下のようにすることでハンドリングできます。
this.redis.on('connect', () => {
console.log('trigger connect');
});
その他 Tips
自分が実際に対応した細々とした内容です。
master nodeへの負荷を低減したい
ioredisでは基本的に commandは全て masterへ送信されます。
そのため、 masterの cpu消費が激しくパフォーマンスが低下する問題を引き起こす可能性があります。
そこでコンストラクタのオプション scaleRead
で read commandの向き先を slaveに変更します。
const driver: IORedis.Cluster = new IORedis.Cluster([],
redisOptions: {},
scaleReads: 'slave'
);
これで read commandは slaveに向くので masterへの負荷は軽減されます。
ただし、レプリケーション遅延により、master, slave間でデータの差分が発生する可能性があるので注意が必要です。
writeするデータを圧縮したい
例えば api cacheなどで大量のデータを Redisに書き込む場合に幾つかの問題が発生することが予想されます。
- ネットワーク帯域を圧迫して帯域詰まりを起こす
- レイテンシが悪化する
- Redisの容量を圧迫する
これらの問題に対する一つの解決策としてデータを圧縮するという方法がありますが、
ioredisはデータを圧縮しないため圧縮機構を自前で用意する必要があります。
圧縮・解凍するライブラリは幾つかありますが 今回は高速が売りの lz-string
を使っていきます
GitHub - pieroxy/lz-string: LZ-based compression algorithm for JavaScript
npm i lz-string
npm i -D @types/lz-string
import * as IORedis from 'ioredis';
import {compress, decompress} from 'lz-string'
//
// 省略
//
/**
* 読み込み
* @param {string} key
*/
async function get(key: string): Promise<string | null> {
const buf: string | null = await redis.get(key);
if (buf == null) return null;
return decompress(buf);
}
/**
* 書き込み
* @param {string} key
* @param {string} value
* @param {number} expireSec
*/
async function set(key: string, value: string, expireSec: number): Promise<void> {
await redis.set(key, compress(value), 'EX', expireSec);
}
注意点としては 圧縮・解凍はCPUを消費する処理のため、パフォーマンスが低下する可能性があります。
なので導入する場合はCPUの消費を計測してリソースの再調整をする必要があります。
※レイテンシが悪化する問題に対してはデータサイズによっては逆効果だったするため、
計測して判断する必要があります。
参考
Redis Cluster自体の仕組みについて (非常にわかりやすかったです)
https://qiita.com/keitatata/items/44678ad472e61a4606c5