きゃまなかのブログ

新卒6年目の WEB エンジニアです。 Ruby on Rails の TIPS を中心にブログ書いてます。 去年まで運用・保守のエンジニアだったので、サーバサイドの記事もたまに書きます。 よろしくお願いします。

【Ruby on Rails】POST リクエストにリトライ処理を入れる

概要

HTTP リクエストは必ずしも成功するものではありません。

リクエストを受け取るサーバ側の問題だったり、ネットワークの問題だったりで稀に失敗する事があります。

そう言った場合にリトライ処理(再送処理)を入れて、ユーザーからのリクエストを失敗させないようにします。(リトライ処理を入れる事で失敗する確率を下げます)

今回は HTTP リクエストの POST で再送処理を行う方法をまとめました。

はじめに

今回のリトライ処理はあくまで相手側のサーバまでリクエストが到達しなかった場合に再送する事を想定しています。

相手側のサーバまでリクエストが到達したものの、レスポンスが 200 Success 以外だったため再送すると言ったものではありません。

例えば、リクエストパラメータが不足した状態でリクエストすると 400 Bad Request が返ってきます。

リクエストパラメータが不足した状態でリトライし続けても成功することはありません。

また、相手側のサーバで何か問題が発生している場合 500 Internal Server Error が返ってきます。

同じくこの状態でも、リトライを繰り返しても成功する確率は低いと思います。

この場合はリトライ処理で何度も試すのではなく、相手側のサーバが復旧するまで待つのが得策かと思われます。

対応方針

ruby の例外処理には retry 機能が用意されているので、それを利用します。

retry は、rescue 節で begin 式をはじめからもう一度実行するのに使用します。 retry を使うことである処理が成功するまで処理を繰り返すようなループを作 ることができます。

begin
  do_something # exception raised
rescue
  # handles error
  retry  # restart from beginning
end

参考:制御構造 (Ruby 2.5.0)

サンプルコード

今回、用意したソースコードがこちらです。

  • リクエスト処理は、HTTPS のサイトに JSON 形式で POST する事を想定して作っています
  • リトライ処理は、リクエスト処理で例外が発生した場合、5 秒間隔で 3 回繰り返す様に作っています

ENDPOINT と body に好きな値を入れればコピペして使えるかもしれません。

今回は POST 用のサンプルコードですが、GET や DELETE、PUT/PATCH でも少し書き換えれば使えると思います。

HTTPS へのリクエストですが証明書の検証は無し OpenSSL::SSL::VERIFY_NONE を指定しているので、必要があれば書き換えてください。

以下の値も適宜書き換えて利用してください。

  • RETRY_MAX_COUNT:リトライ処理の回数
  • RETRY_WAIT_TIME:リトライ処理の間隔(待ち時間)
  • REQUEST_TIMEOUT:リクエスト時のタイムアウト秒
require 'net/https'
require 'openssl'
require 'json'

ENDPOINT = '' # お好きなリクエスト先を指定してください
RETRY_MAX_COUNT = 3 # 回
RETRY_WAIT_TIME = 5 # 秒
REQUEST_TIMEOUT = 5 # 秒

def post(body)
  uri = URI.parse(ENDPOINT)
  # JSON 形式でリクエストを送信する場合の MIME タイプ
  header = { 'Content-Type' => 'application/json' }
  req = Net::HTTP::Post.new(uri.request_uri, header)
  req.body = body.to_json # JSON 形式でリクエストを送信
  http = Net::HTTP.new(uri.host, uri.port)
  http.open_timeout = REQUEST_TIMEOUT
  http.use_ssl = true # https 通信
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE # 今回はテスト用なので証明書の検証なし
  http.start { http.request(req) }
end

def post_with_retry(body)
  retry_count = 0
  begin
    post(body) # POST メソッド呼び出し
# raise 'POST できません' # 動作確認用(意図的に Exception を発生させる) rescue => e if retry_count < RETRY_MAX_COUNT sleep RETRY_WAIT_TIME puts "retry count: #{retry_count += 1}" # retry count を表示しながらカウントアップ retry else puts e.message end end end body = '' # 相手側の仕様に従って記述してください
# 例) body = { message: 'hogehoge' } # post メソッド内で to_json しているのでここはシンボル形式で OK です response = post_with_retry(body) puts response.code # レスポンスコードを表示

まとめ

 ruby の例外処理で用意されている retry を利用する事で簡単に POST リクエストの再送処理を行うことができます。

GET リクエストは取得できなかった場合分かりやすいですし、ユーザーもブラウザのリロードなどで勝手に(リトライ処理的な)対応をしてくれる事が多いです。

POST リクエストが失敗すると、ユーザーに初めから入力フォームに入力し直す手間をかけたり、悪い印象も与えます。

ネットワークの瞬断やサーバの障害は起こるものなのでリトライ処理は POST に限らず記載しておくといいと思います。

コピペ用のソースコード良かったら使ってくださいー!