Node.jsでリトライ処理を実装してみる
概要
HTTPリクエストが失敗した場合や、DBへの接続に失敗した場合など
何回かリトライ処理を含めたい場合ってありますよね?
そこで、Node.jsを使ったリトライ処理用モジュールを試しに作ってみようと思います。
環境
Node.js v4.4.7 OS Mac OS X 10.11.5
リトライ処理用モジュール
特に深い理由は無いですが、
Promiseベースのリトライ処理が出来るようなものを作ってみたいと思います。
作るもの
- Promiseで結果が受け取れる
- リトライ数が設定できる
- リトライ間隔が設定できる
- デバッグ用のログが出せる
promise-retry.js
'use strict'; class Retry { constructor() { this._maxTimes = 3; this._interval = 100; // ms this._debug = false; } /** * @public */ maxTimes(count) { this._maxTimes = count; return this; } /** * @public */ interval(ms) { this._interval = ms; return this; } /** * @public */ debug(flag) { this._debug = flag; return this; } /** * @public */ execute(fn) { let retryCount = 0; let maxTimes = this._maxTimes; return new Promise((resolve, reject) => { this.doRetry(fn, retryCount, resolve, reject); }); } /** * @private */ doRetry(fn, retryCount, resolve, reject) { let maxTimes = this._maxTimes; fn(retryCount) .then(function() { // Don't use arrow function. // In that case, `arguments` will be global scope variable. resolve.apply(Promise, arguments); }) .catch((err) => { if (retryCount >= maxTimes) { reject(err); } else { retryCount++; setTimeout(() => { this.log(retryCount, err); this.doRetry(fn, retryCount, resolve, reject); }, this._interval); } }); } /** * @private */ log(retryCount, err) { if (this._debug) { let hrtime = process.hrtime(); console.log( 'retry cnt: %d, at: %d.%d, prev_err: %s', retryCount, hrtime[0], hrtime[1], err.message ); } } } module.exports = () => new Retry();
リトライ処理をやってみる
では、作ったモジュールを使ってリトライ処理を実装してみたいと思います。
- 最大リトライ数:5
- リトライ間隔:20ms
- デバッグモード:ON
index.js
'use strict'; const knex = require('knex')({ ... }); const retry = require('./promise-retry'); retry.debug(true) .maxTimes(5) .interval(20) .execute(retryCount => { return knex.first('*') .from('users') .where('id', 'umatoma'); }) .then(user => console.log(user)) .catch(err => console.log(err));
地味にハマったポイント
ES6から導入された アロー関数 を使うと this
と同様に arguments
も束縛されてしまうんですね。
知りませんでした...
まとめ
単純な処理ではありますが、複数箇所で同じようなことをやる可能性も高いと思うので、
モジュール化して、使いまわすといいかもしれないですね。