APIから情報取得してリソースの情報次第で再取得したいことってたまにありますよね?
バックエンド
でワーカーが走ってて処理待ちとか。そんなときに使えそうなリトライ処理を実装してみました。
環境準備
今回はnode.jsで動作するサンプルにしました。ブラウザ上でも実装は可能です。
node、yarn (or npm)がインストールされている前提です。
> node --version v10.11.0 > yarn --version 1.12.3 > npm --version 6.4.1 > mkdir 任意のディレクトリ > cd 任意のディレクトリ > yarn init > yarn add axios retryx
実装
実装はGitHubにもアップしていますので、よければご参考ください。
https://github.com/kai-kou/node-js-axios-retry
リトライしない実装
リトライ処理を組み込んでいない状態の実装です。
Qiita API v2を利用させていただき、ユーザー情報を取得しています。
リトライしない実装
noRetry = () => { const axiosBase = require('axios'); const axios = axiosBase.create({ baseURL: 'https://qiita.com/api/v2/', }); axios.get('users/kai_kou') .then(function(res) { console.log(res.data); }); }; noRetry();
Promiseを利用して頑張る
上記実装にリトライ処理を組み込みます。
リトライする条件は、記事数items_count
が108
以下だったらリトライするようにしました。厳しい。
とりあえずライブラリなどを利用せず、自力で頑張ってみます。
といいつつ、下記を参考にさせていただきました。
Promiseチェーンの中で条件を満たすまで同じ処理を繰り返す(リトライ処理) – コンパイラかく語りき
http://chuckwebtips.hatenablog.com/entry/2017/07/17/081105
Promiseを利用して頑張るやつ
retry = () => { const axios = require('axios'); const client = axios.create({ baseURL: 'https://qiita.com/api/v2/', }); const retryPromise = (func, delay) => { const retry = (resolve, reject) => func() .then((result) => ({result, isCompleted: (result.items_count >= 108)})) .then((data) => { if (data.isCompleted) { resolve(data.result); } else { console.log('retry...'); setTimeout(() => retry(resolve, reject), delay); } }) .catch(reject); return new Promise(retry); }; const func = () => { return client.get('users/kai_kou') .then((res) => { return Promise.resolve(res.data); }); }; retryPromise(func, 1000) .then((result) => { console.log(result); }); }; retry();
実装の詳細は上記記事をご参考ください。
retryPromise
メソッドで再帰的に終了判定result.items_count >= 108
が満たされるまで、処理を繰り返しています。
リトライ回数や待機時間にexponential backoffアルゴリズムの実装などを組み込んでいないため、実用するならば、もう少し頑張らないとだめです。
AWSユーザーは必ず覚えておきたいExponential Backoffアルゴリズムとは何か – yoshidashingo
https://yoshidashingo.hatenablog.com/entry/2014/08/17/135017
ライブラリを利用する
get
した場合に、ネットワークエラーや5xx
エラーが発生してリトライしたい場合には下記のライブラリが利用できます。
JustinBeckwith/retry-axios: 🦖 A super flexible interceptor for Axios that makes it easy to retry requests.
https://github.com/JustinBeckwith/retry-axios
softonic/axios-retry: Axios plugin that intercepts failed requests and retries them whenever possible
https://github.com/softonic/axios-retry
ざっくり使ってみたところ、正常取得(2xx)時に自前でのリトライ判定を差し込むことはできませんでしたので、今回は下記ライブラリを利用させていただきました。
y13i/retryx: Promise-based retry workflow library.
https://github.com/y13i/retryx
よくよくみたら同じ部署の御方が作成されていました^^感謝!
Promise をリトライする何かを作った
https://cloudpack.media/31219
では実装です。
ライブラリを利用した実装
retry = () => { const axios = require('axios'); const retryx = require('retryx'); const client = axios.create({ baseURL: 'https://qiita.com/api/v2/', }); retryx(() => client.get('users/kai_kou') .then((res) => { if (res.data.items_count < 108) { return Promise.reject(Error('記事が少ないです')); } return Promise.resolve(res.data); }), { beforeWait: (tries) => { console.log('beforeWait'); console.log(tries); }, }) .then((res) => { console.log(res); }); }; retry();
うーん。シンプルで素敵です。ネットワークエラーや5xx
エラー時にもリトライされます。
試行回数や待機時間の指定、リトライを継続するかの判定などのオプションも豊富でいい感じに使えそうです。
まとめ
当初は、retry-axiosでなんとかなるかと考えて検証していたのですが、思いもかけずハマッてしまいましたが、探せばやっぱりあるものです。助かりました^^
参考
Promiseチェーンの中で条件を満たすまで同じ処理を繰り返す(リトライ処理) – コンパイラかく語りき
http://chuckwebtips.hatenablog.com/entry/2017/07/17/081105
AWSユーザーは必ず覚えておきたいExponential Backoffアルゴリズムとは何か – yoshidashingo
https://yoshidashingo.hatenablog.com/entry/2014/08/17/135017
JustinBeckwith/retry-axios: 🦖 A super flexible interceptor for Axios that makes it easy to retry requests.
https://github.com/JustinBeckwith/retry-axios
softonic/axios-retry: Axios plugin that intercepts failed requests and retries them whenever possible
https://github.com/softonic/axios-retry
y13i/retryx: Promise-based retry workflow library.
https://github.com/y13i/retryx
Promise をリトライする何かを作った – Qiita
https://cloudpack.media/31219