Rubyで外部API通信を実装する方法|タイムアウトとリトライ設計の基本と実践例

Ruby

Rubyで外部APIと連携するアプリケーションを開発する場合、通信エラーへの対策は非常に重要です。外部サービスは常に正常に応答するとは限らず、一時的なネットワーク障害やサーバー混雑によって処理が失敗することがあります。この記事では、RubyでAPI通信を実装する際に必要となるタイムアウト設定やリトライ処理の考え方、安定したシステムを作るための設計ポイントについて解説します。

外部API通信でタイムアウト設計が必要な理由

外部APIを呼び出す処理では、相手側のサーバーが応答を返すまで待機する時間が発生します。しかし、タイムアウトを設定していない場合、API側で障害が発生した際にRubyアプリケーションが長時間停止してしまう可能性があります。

例えば、ユーザーが注文処理を実行した際に決済APIへ通信し、そのAPIが応答しない状態になると、タイムアウト設定がなければ画面が数分間固まるような状態になることがあります。

そのため、外部API通信では「どの程度待つのか」を明確に決め、接続タイムアウトと読み取りタイムアウトを適切に設定することが重要です。

Rubyで設定するタイムアウトの種類

RubyのHTTP通信では、主に以下のようなタイムアウトを考慮します。

  • 接続タイムアウト(connect timeout):APIサーバーへ接続するまでの待機時間
  • 読み取りタイムアウト(read timeout):接続後にレスポンスを受け取るまでの待機時間
  • 書き込みタイムアウト(write timeout):リクエスト送信時の待機時間

例えばRuby標準ライブラリのNet::HTTPでは以下のように設定できます。

require "net/http"

uri = URI("https://api.example.com/data")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.open_timeout = 5
http.read_timeout = 10

response = http.get(uri.request_uri)

この例では、接続に5秒、レスポンス取得に10秒以上かかった場合にタイムアウトとして処理します。

リトライ処理を設計するときの基本的な考え方

外部API通信では、一時的な失敗であれば再試行によって成功する場合があります。そのため、タイムアウトや一時的なネットワークエラーに対してリトライ処理を設けることがあります。

ただし、すべてのエラーでリトライすればよいわけではありません。認証エラーや不正なパラメータによる400系エラーなどは、何度実行しても成功しないためリトライ対象から外す必要があります。

一般的には以下のようなエラーをリトライ対象にします。

  • ネットワーク接続失敗
  • タイムアウトエラー
  • 一時的なサーバーエラー(HTTP 500番台)
  • サービス側の混雑によるエラー

Rubyでリトライ処理を実装する例

Rubyではbeginとrescueを利用して例外発生時の再試行処理を作成できます。

max_retry = 3
retry_count = 0

begin
retry_count += 1
response = call_external_api
rescue Timeout::Error, Errno::ECONNRESET => e
retry if retry_count < max_retry
raise e
end

この例では最大3回までAPI呼び出しを試行し、それでも失敗した場合はエラーとして処理を終了します。

例えば外部の天気APIから情報を取得する処理では、一時的な通信失敗なら数秒後に再試行することでユーザーへの影響を減らせます。

指数バックオフでAPI負荷を抑える

リトライを実装する場合、すぐに連続して再試行すると、障害中のAPIサーバーへさらに負荷をかけてしまいます。

そのため、多くのシステムでは指数バックオフという方式を利用します。これはリトライするたびに待機時間を増やす方法です。

例えば以下のような間隔になります。

試行回数 待機時間例
1回目失敗後 1秒待機
2回目失敗後 2秒待機
3回目失敗後 4秒待機

この仕組みにより、障害発生時でも相手サービスへのアクセス集中を防ぎながら復旧を待つことができます。

APIの種類に応じたリトライ設計の注意点

リトライを設計するときに特に注意すべきなのが、同じ処理を複数回実行しても問題ないかという点です。

例えばデータ取得APIの場合、何度呼び出しても結果が変わらないためリトライしやすいです。一方で、決済処理や注文登録などは複数回実行されると二重登録につながる可能性があります。

このような場合は、API側が提供する冪等性キーを利用したり、自分のシステム側で処理済みフラグを管理したりする設計が必要になります。

外部API連携を安定させるための設計ポイント

本番環境で利用するAPI連携では、単純に通信処理を書くのではなく、障害発生時の動作まで考慮することが重要です。

  • タイムアウト時間を必ず設定する
  • リトライ回数に上限を設ける
  • リトライ対象のエラーを限定する
  • 指数バックオフを利用する
  • ログを記録して原因調査できるようにする

例えば外部決済サービスとの連携では、通信失敗時にユーザーへエラーを表示するだけでなく、後から管理者が状態を確認できるログや再処理機能を用意すると、より安全なシステムになります。

まとめ

Rubyで外部API通信を実装する場合、タイムアウトとリトライはシステムの安定性を左右する重要な設計要素です。

タイムアウトでは接続時間やレスポンス待機時間を適切に制御し、リトライでは対象エラーや回数制限、指数バックオフを考慮する必要があります。

また、更新系APIでは二重実行による問題を防ぐため、冪等性も意識した設計が必要です。外部サービスとの通信は失敗する可能性がある前提で設計することで、利用者にとって安定したRubyアプリケーションを構築できます。

コメント

タイトルとURLをコピーしました