Java8で任意処理をRetryするユーティリティを書く

はじめに

例外発生時に指定時間スリープして、処理をリトライさせたいことがちょこちょこあります。

  • DBコネクションの接続
  • Socket間の通信
  • ファイルの読み書き … etc

いろいろありますね。今回はそんな時のためのリトライユーティリティをJava8で書いてみます。

サンプルコード

このサンプルでは、以下の動作仕様のプログラムとします。

動作仕様

  • タスクはCallableを実装している
  • タスクの処理はただ例外を投げるだけ(とりあえずリトライしていることを確認するため)
  • タスクの実行時に例外が発生した場合、3回リトライし、各リトライの間は5秒のsleepする
  • リトライがすべて失敗したら例外のスタックトレースを出力する

ソースコード

  • Main.java メインクラス。タスクをExecutorServiceで実行する
  • Task.java Callableを実装した任意のタスク
  • Retryable.java リトライのユーティリティインターフェース。Supplierにリトライさせたい処理を入れる

Main.java

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Main {
    public static void main(String[] args){
        Task task = new Task();
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<Object> f = executor.submit(task);
        try {
            f.get();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executor.shutdown();
        }
    }
}

Task.java

import java.util.concurrent.Callable;

class Task implements Callable<Object>, Retryable<Object>{

    @Override
    public Object call() throws Exception {
        return executeWithRetry(this::execute);
    }

    private Object execute() throws RuntimeException{
        //例外発生時、リトライさせたい処理をここに書く
        throw new RuntimeException("Failed.");
    }
}

Retryable.java

import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;

interface Retryable<T> {

    Logger LOGGER = Logger.getLogger(Retryable.class.getCanonicalName());
    int MAX_TRIES = 3;
    int DELAY_MILLISEC = 1000;

    default T executeWithRetry(int count, Supplier<T>  func) throws InterruptedException {
        if(count >= MAX_TRIES) {
            LOGGER.log(Level.SEVERE, "Retry out.");
            throw new RuntimeException();
        }
        try {
            return func.get();
        } catch (Exception e){
            LOGGER.log(Level.WARNING, "func failed.");
            Thread.sleep(DELAY_MILLISEC);
            return executeWithRetry(count + 1, func);
        }
    }
}

まとめ

今回はJava8のinterfaceのデフォルトメソッドを使って、リトライユーティリティを書いてみました。
interfaceは多重継承が可能なので、こんな感じでRetryableクラスをimplementsするだけで既存処理をリトライする実装にできます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA