Java | 「リトライ処理」を安全に書く最善の方法

Java Java
スポンサーリンク

では、実務でよくある 「リトライ処理」を安全に書くベストプラクティス を整理して解説します。
目的は「安全に何度か再試行しつつ、無限ループや例外落ちを防ぐこと」です。


1️⃣ 基本ルール

  1. 最大試行回数を必ず設ける
    • 無限ループにならないようにする。
    • 例:最大3回、5回など。
  2. 例外を捕まえて処理する
    • ネットワーク、DB、ファイルなどは失敗する可能性がある。
    • try/catch で例外を捕まえ、ログや再試行に活かす。
  3. ログを出す
    • 何回目のリトライか、例外内容をログに残す。
    • デバッグや障害対応が簡単になる。
  4. ウェイト(待機時間)を入れる
    • サーバーやAPIに負荷をかけすぎないように短時間待機。
    • 実務では指数バックオフ(Exponential Backoff)が推奨される。
  5. 成功・失敗判定を明確にする
    • 成功したら即終了、失敗したら再試行するロジックをシンプルに書く。

2️⃣ 実務向けのリトライ処理例(Java)

import java.io.IOException;
import java.util.Random;

public class SafeRetryExample {

    public static void main(String[] args) {
        final int MAX_RETRIES = 5;  // 最大試行回数
        int attempt = 0;
        boolean success = false;

        while (attempt < MAX_RETRIES && !success) {
            attempt++;
            try {
                System.out.println("試行 " + attempt + " 回目: API呼び出し中...");
                callApi();  // 実際の処理(例: ネットワーク通信)
                success = true;  // 成功した場合
                System.out.println("API呼び出し成功!");
            } catch (IOException e) {
                System.err.println("失敗: " + e.getMessage());
                if (attempt < MAX_RETRIES) {
                    try {
                        // 待機してから再試行(指数バックオフも可)
                        Thread.sleep(attempt * 1000L); // 1秒、2秒、3秒...と増やす
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        System.err.println("待機中に割り込み発生");
                        break;
                    }
                } else {
                    System.err.println("上限に達したため処理を中止します");
                }
            }
        }
    }

    // 疑似API呼び出し(ランダムに例外を発生)
    private static void callApi() throws IOException {
        if (new Random().nextBoolean()) {
            throw new IOException("API通信エラー");
        }
    }
}
Java

3️⃣ ポイント解説

  1. while (attempt < MAX_RETRIES && !success)
    • 成功するか上限に達するまでループ。
    • 無限ループにならない安全設計。
  2. try/catch で例外を捕まえる
    • ネットワーク・DB・ファイル操作など、外部依存処理は必ず例外処理。
  3. Thread.sleep で待機
    • サーバー負荷を避ける。
    • attempt * 1000L で「1秒、2秒、3秒…」と徐々に増やす簡単な指数バックオフ。
  4. ログ出力
    • 成功・失敗・試行回数を出すことで運用・デバッグが容易。
  5. 割り込み・中断対応
    • InterruptedException を無視せず、Thread.currentThread().interrupt() で割り込み状態を維持。

4️⃣ さらに安全にするポイント

  • 結果を返す関数にまとめる
    • 複数箇所で再利用しやすい。
  • 上限試行回数 + タイムアウト制御
    • ネットワークがずっと応答しない場合でも無限に待たない。
  • 例外をラップして呼び出し元に伝える
    • ただログに出すだけで終わらず、必要なら再度 throw する。

💡 実務でよくあるシナリオ

  • 外部API呼び出し → ネットワーク断 → 3回まで再試行
  • DB接続 → 一時的にロック → 2秒ごとに再試行
  • ファイル読み込み → 読み込み失敗 → 5回まで再試行して最後に警告
タイトルとURLをコピーしました