覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

Failsafeによるリトライ処理 (2)

 前回エントリで FailSafe によるリトライ処理について書きました。

FailSafeは前回書いた内容よりももっと色々制御できるので、それらを紹介する内容のエントリになります。
基本的には上記の前回エントリの内容がベースにあります。


前回エントリでは、基本的にRetryPolicyを以下のように指定しました。

RetryPolicy retryPolicy = new RetryPolicy()
                .retryOn(BusinessLogicException.class)  // リトライ対象とする例外クラス
                .withDelay(2, TimeUnit.SECONDS)         // リトライ時の間隔
                .withMaxRetries(3);                     // 最大リトライ回数

これは、

  • 対象処理実行時にBusinessLogicExceptionがスローされたらリトライする
  • リトライする際に2秒のdelayを入れる
  • 最大リトライ回数は3回

という内容になります。

この内容をいじって、リトライの方法をもう少し制御します


複数の例外クラスをリトライ対象にする

BusinessLogicException以外にもFatalException がスローされる可能性があり、
そのいずれもリトライ対象としたい場合は以下のように記述できます。

RetryPolicy retryPolicy = new RetryPolicy()
                .retryOn(BusinessLogicException.class) // リトライ対象とする例外クラス1
                .retryOn(FatalException.class)         // リトライ対象とする例外クラス2
                .withDelay(2, TimeUnit.SECONDS)         // リトライ時の間隔
                .withMaxRetries(3);                     // 最大リトライ回数

または、

RetryPolicy retryPolicy = new RetryPolicy()
                .retryOn(BusinessLogicException.class, FatalException.class) // リトライ対象とする例外クラス
                .withDelay(2, TimeUnit.SECONDS)         // リトライ時の間隔
                .withMaxRetries(3);                     // 最大リトライ回数

このいずれかの記述をすることで、BusinessLogicException, FatalExceptionのいずれがスローされてもリトライされます。


リトライ対象の例外クラスを instanceofでチェックする

リトライ対象とする例外クラスをinstanceofを使って判定することも可能です。
以下のように記述します。

RetryPolicy retryPolicy = new RetryPolicy()
                .retryOn(failure -> failure instanceof BusinessLogicException) // リトライ対象とする例外をinstanceofでチェック
                .withDelay(2, TimeUnit.SECONDS)         // リトライ時の間隔
                .withMaxRetries(3);                     // 最大リトライ回数


リトライ時の遅延をランダムに変化させる

リトライ時の遅延を withDelay で指定しますが、これで指定した時間きっちり遅延させた上で次の再試行が行われます。
この遅延をランダムに変化させたい場合はwithJitterメソッドを利用します。

以下のように記述します。

RetryPolicy retryPolicy = new RetryPolicy()
                .retryOn(BusinessLogicException.class)  // リトライ対象とする例外クラス
                .withDelay(2, TimeUnit.SECONDS)         // リトライ時の間隔
                .withJitter(500, TimeUnit.MILLISECONDS) // Delayをランダムに変化させる値
                .withMaxRetries(3);                     // 最大リトライ回数

上記例でいくと、2秒にランダムで-500〜+500ミリ秒 を 加算した値が遅延時間になります。


リトライのたびに遅延時間を伸ばす

リトライするたびに遅延時間を伸ばしたいというケースがあると思います。
その場合、withBackoffメソッドを利用します。

RetryPolicy retryPolicy = new RetryPolicy()
                .retryOn(BusinessLogicException.class) // リトライ対象とする例外クラス
                .withBackoff(1, 10, TimeUnit.SECONDS)  // 遅延時間は最小値1秒, 最大値10秒
                .withMaxRetries(10);                   // 最大リトライ回数

上記の例の場合は、最初の遅延時間は1秒で最大の遅延時間は10秒です。
リトライのたびに、1, 2, 4, 8, 10, 10 ・・・(秒) という遅延をするようになります。
(初期値の遅延時間を2倍していって、最大値になったら遅延時間を伸ばすのは打ち止め というような動きになります)


上記例では遅延を伸ばすための係数が自動的に2 になっているので、2倍されていく動きになっていますが、
この係数を自分で指定したい場合は、withBackoff の第4引数で指定可能です。
以下例では係数は 3 となっています。

RetryPolicy retryPolicy = new RetryPolicy()
                .retryOn(BusinessLogicException.class)    // リトライ対象とする例外クラス
                .withBackoff(1, 10, TimeUnit.SECONDS, 3)  // 遅延時間を伸ばすための係数は3
                .withMaxRetries(10);                      // 最大リトライ回数

このケースでは、リトライのたびに、1, 3, 9, 10, 10, 10 ・・・(秒) という遅延をするようになります。


実行する処理の戻り値でリトライを判定する

これまで実行対象の処理が例外をスローした場合にリトライするようにしていましたが、
対象の処理が戻り値を返す場合にその戻り値でリトライの有無を判定することも可能です。
これを実現したい場合は、retryIfメソッドを利用します。

例えば戻り値がnullの場合はリトライするとしたい場合は、以下のようになります。

RetryPolicy retryPolicy = new RetryPolicy()
                .retryIf(result -> result == null)  // 戻り値がnullの場合はリトライ
                .withDelay(2, TimeUnit.SECONDS)     // リトライ時の間隔
                .withMaxRetries(3);                 // 最大リトライ回数


または以下のようにretryWhenというメソッドでも同様のことが実現可能です

RetryPolicy retryPolicy = new RetryPolicy()
                .retryWhen(null)                 // 戻り値がnullの場合はリトライ
                .withDelay(2, TimeUnit.SECONDS)  // リトライ時の間隔
                .withMaxRetries(3);              // 最大リトライ回数


ただし、これらの戻り値での判定をするRetryPolicyを利用する場合は、 実際の処理実行においてrunメソッドで使うのでなく

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


getメソッド(戻り値を取得する方法)を使う必要がありそうです。

Failsafe.with(retryPolicy)
        .get(() -> doSomething());


runメソッドだと挙動が意図しないものになるように思われます。


特定の例外がスローされたらリトライを中止する

対象の処理を実行している中で特定の例外がスローされたら場合はリトライを中止させたい、
そのような場合は、abortOnメソッドでリトライ中止対象の例外クラスを指定することで実現できます。

RetryPolicy retryPolicy = new RetryPolicy()
                .retryOn(BusinessLogicException.class) // リトライ対象とする例外クラス
                .abortOn(FatalException.class)         // この例外が発生したらリトライを中止する
                .withDelay(2, TimeUnit.SECONDS)         // リトライ時の間隔
                .withMaxRetries(3);                     // 最大リトライ回数

上記の例では、BusinessLogicExceptionはリトライ対象ですが、FatalExceptionが発生するとリトライが中止されます。


まとめ

RetryPolicyの内容を色々といじることで、リトライの方法を細かく制御できることがわかりました。



関連エントリ