web-dev-qa-db-ja.com

失敗したロジックを「再試行」するための設計パターン?

ダウンしたリモートエンドポイントへの接続を定期的に確立しようとする再接続ロジックをいくつか書いています。基本的に、コードは次のようになります。

public void establishConnection() {
    try {
        this.connection = newConnection();
    } catch (IOException e) {
        // connection failed, try again.
        try { Thread.sleep(1000); } catch (InterruptedException e) {};

        establishConnection();
    }
}

上記のようなコードを使用して、この一般的な問題を何度も解決しましたが、結果にはほとんど満足していません。この問題に対処するために設計されたデザインパターンはありますか?

50
Naftuli Kay

チェックアウトする価値のあるライブラリの1つは Sarge です。これは、定義された計画に従って再試行を自動的に実行します。

3
Jonathan

恥知らずのプラグ:操作を再試行できるようにいくつかのクラスを実装しました。 ライブラリはまだ利用できませんが、 githubでフォーク。そして fork が存在します。

これにより、さまざまな柔軟な戦略を備えたRetryerを構築できます。例えば:

Retryer retryer = 
    RetryerBuilder.newBuilder()
                  .withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECOND))
                  .withStopStrategy(StopStrategies.stopAfterAttempt(3))
                  .retryIfExceptionOfType(IOException.class)
                  .build();

そして、Retryerでcallable(または複数のもの)を実行できます:

retryer.call(new Callable<Void>() {
    public Void call() throws IOException {
        connection = newConnection();
        return null;
    }
}
30
JB Nizet

べき等リトライパターン を試すことができます。

enter image description here

24
GalacticJello

AOPとJavaアノテーションを使用しています。 jcabi-aspects (I ' m開発者):

@RetryOnFailure(attempts = 3, delay = 1, unit = TimeUnit.SECONDS)
public void establishConnection() {
  this.connection = newConnection();
}

追伸 Cactoos からRetryScalarを試すこともできます。

10
yegor256

Failsafe (ここの著者)を使用:

RetryPolicy retryPolicy = new RetryPolicy()
  .retryOn(IOException.class)
  .withMaxRetries(5)
  .withDelay(1, TimeUnit.SECONDS);

Failsafe.with(retryPolicy).run(() -> newConnection());

注釈も魔法もありません。Springアプリである必要はありません。単純明快です。

5
Jonathan

私はこれが本当に好きですJava このブログ からの8つのコード)、クラスパスに追加のライブラリは必要ありません。

再試行クラスに関数を渡すだけです。

@Slf4j
public class RetryCommand<T> {

    private int maxRetries;

    RetryCommand(int maxRetries)
    {
        this.maxRetries = maxRetries;
    }

    // Takes a function and executes it, if fails, passes the function to the retry command
    public T run(Supplier<T> function) {
        try {
            return function.get();
        } catch (Exception e) {
            log.error("FAILED - Command failed, will be retried " + maxRetries + " times.");
            return retry(function);
        }
    }

    private T retry(Supplier<T> function) throws RuntimeException {

        int retryCounter = 0;
        while (retryCounter < maxRetries) {
            try {
                return function.get();
            } catch (Exception ex) {
                retryCounter++;
                log.error("FAILED - Command failed on retry " + retryCounter + " of " + maxRetries, ex);
                if (retryCounter >= maxRetries) {
                    log.error("Max retries exceeded.");
                    break;
                }
            }
        }
        throw new RuntimeException("Command failed on all of " + maxRetries + " retries");
    }
}

そしてそれを使用するには:

new RetryCommand<>(5).run(() -> client.getThatThing(id));
4
Dherik

また、目的の操作に対してループを実行するラッパー関数を作成することもできます。ラッパー関数を作成すると、ループから抜け出します。

public static void main(String[] args) {
    retryMySpecialOperation(7);
}

private static void retryMySpecialOperation(int retries) {
    for (int i = 1; i <= retries; i++) {
        try {
            specialOperation();
            break;
        }
        catch (Exception e) {
            System.out.println(String.format("Failed operation. Retry %d", i));
        }
    }
}

private static void specialOperation() throws Exception {
    if ((int) (Math.random()*100) % 2 == 0) {
        throw new Exception("Operation failed");
    }
    System.out.println("Operation successful");
}
2
klusht

spring-retry を試すことができます。きれいなインターフェイスがあり、使いやすいです。

例:

 @Retryable(maxAttempts = 4, backoff = @Backoff(delay = 500))
 public void establishConnection() {
    this.connection = newConnection();
 } 

例外の場合、500msのバックオフポリシーでメソッドestablishConnection()の4倍まで再試行(呼び出し)します

2
db80

Java 8を使用している場合、これが役立つ場合があります。

import Java.util.function.Supplier;

public class Retrier {
public static <T> Object retry(Supplier<T> function, int retryCount) throws Exception {
     while (0<retryCount) {
        try {
            return function.get();
        } catch (Exception e) {
            retryCount--;
            if(retryCount == 0) {
                throw e;
            }
        }
    }
    return null;
}

public static void main(String[] args) {
    try {
        retry(()-> {
            System.out.println(5/0);
            return null;
        }, 5);
    } catch (Exception e) {
        System.out.println("Exception : " + e.getMessage());
    }
}
}

おかげで、

プラビーン・R.

0

再試行に特別なことは何もありません-このクラスを例として取り上げてください http://www.docjar.com/html/api/org/springframework/jms/listener/DefaultMessageListenerContainer.Java.html As you春の開発者でさえ、まだ再試行のためのコードを書いているのを見ることができます-行791 ...そのような特別なパターンはありません。

リソースに対処するためにアドバイスできるのは、Apache commons pool libraryを取得することです-これを確認してください http://commons.Apache.org/pool/apidocs/org/Apache/commons/pool/impl/GenericObjectPool.html にアクセスして http://commons.Apache.org/pool

0
Andrey Borisov