Help us understand the problem. What is going on with this article?

axiosでget後に特定条件時にリトライするように実装してみた

More than 1 year has passed since last update.

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_count108 以下だったらリトライするようにしました。厳しい。

とりあえずライブラリなどを利用せず、自力で頑張ってみます。
といいつつ、下記を参考にさせていただきました。

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 をリトライする何かを作った - Qiita
https://qiita.com/y13i/items/6ba3c8849c8af80d2dde

では実装です。

ライブラリを利用した実装
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://qiita.com/y13i/items/6ba3c8849c8af80d2dde

kai_kou
2004年からWeb系のシステムエンジニアとして開発、運用、マネジメントを経験。現在はアイレット(クラウドパック)に所属。 べ、別にいいね貰えたからってモチベーションが上がって記事とコードの品質があがるわけじゃないんだからね///
https://twitter.com/k_aik_ou
cloudpack
Amazon Web Services (AWS) の導入設計、環境構築、運用・保守をサポートするマネジドホスティングサービス
https://cloudpack.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away