LoginSignup
2
2

More than 5 years have passed since last update.

RxNET で RetryWhen

Last updated at Posted at 2017-06-23

RxNET に RetryWhen が見当たりません...1
条件付きリトライができずに困ったので自作してみました。

拡張メソッド

IObservable を返すC#の拡張メソッドとやらを実装してメソッドチェーンを維持します。

RetryWhenExtension.cs
using System;
using System.Reactive.Linq;

namespace RxNET.Extensions
{
    public static class RetryWhenExtension
    {
        public static IObservable<T> RetryWhen<T>(
            this IObservable<T> source,
            Func<IObservable<Exception>, IObservable<T>> predicate)
        {
            return RetryWhenRecursive(source, predicate);
        }

        private static IObservable<T> RetryWhenRecursive<T>(
            IObservable<T> source,
            Func<IObservable<Exception>, IObservable<T>> predicate)
        {
            return source.Catch((Exception e) =>
            {
                //
                // シーケンスから例外が Throw された場合、
                // predicate でリトライするかどうかを判定する。
                //
                return
                     predicate(Observable.Return(e))
                    .Catch((Exception ee) =>
                     {
                        //
                        // predicate から例外が Throw された場合、
                        // 後続のシーケンスに例外を流す(=リトライせずオブザーバーに例外を捕捉させる)。
                        //
                        return Observable.Throw<T>(e);
                     })
                    .SelectMany(_ =>
                     {
                        //
                        // predicate から例外が Throw されなかった場合、
                        // 再度、起点となったシーケンスに繋げる(=リトライ)。
                        //
                        return RetryWhenRecursive(source, predicate);
                     });
            });
        }
    }
}

試してみる

5回ごとにしか成功しないメソッドを、1秒おきに最大10回リトライします。

Progran.cs
using System;
using System.Linq;
using System.Reactive.Linq;

using RxNET.Extensions;

namespace RetryApp
{
    class MainClass
    {
        const int RETRY_MAX  = 10;
        const int RETRY_WAIT = 1;

        public static void Main(string[] args)
        {
            var service = new GreetingService();

            int retryAttempt = 0;

            service.Greet()
                   .RetryWhen(errors => {
                        return errors.SelectMany(error => {
                            if (error is NeedToRetryException && ++retryAttempt <= RETRY_MAX) {
                                return Observable.Return(String.Empty).Delay(TimeSpan.FromSeconds(RETRY_WAIT));
                            }
                            return Observable.Throw<string>(error);
                        });
                    })
                   .Subscribe(
                        greet => Console.WriteLine("OnNext: {0}", greet),
                        error => Console.WriteLine("OnError: {0}", error.Message),
                        ()    => Console.WriteLine("OnCompleted:")
                  );

            Console.ReadKey();
        }
    }

    class GreetingService
    {
        int attempt = 0;

        public IObservable<string> Greet()
        {
            return Observable.Defer(() => {
                Console.WriteLine("Defer [{0}]", ++attempt);
                if (attempt % 5 == 0) {
                    return Observable.Return("Hello");
                }
                return Observable.Throw<string>(new NeedToRetryException("Zzz..."));
            });
        }
    }

    class NeedToRetryException : Exception
    {
        public NeedToRetryException() : base() { }
        public NeedToRetryException(string message) : base(message) { }
        public NeedToRetryException(string message, Exception inner) : base(message, inner) { }
    }
}

これを実行してみた結果が↓です。
5回目(リトライ4回目)で成功して、処理完了しています。

Defer [1]
Defer [2]
Defer [3]
Defer [4]
Defer [5]
OnNext: Hello
OnCompleted:

ちなみに RETRY_MAX を 3 にしてみると、

Defer [1]
Defer [2]
Defer [3]
Defer [4]
OnError: Zzz...

4回目(リトライ3回目)で、成功を待たずに、エラー終了しています。

どうやら期待通りに動いている感じです。

とりあえずは以上です。

今回作成したコード

こちらを参考にしました

http://ufcpp.net/study/csharp/sp3_extension.html
http://safx-dev.blogspot.jp/2015/12/rxswiftretryretrywhen.html

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2