Rx back off and retry

后端 未结 2 2044
眼角桃花
眼角桃花 2020-12-16 19:55

This is based on the code presented in this SO : Write an Rx "RetryAfter" extension method

I am using the code by Markus Olsson (evaluation only at the mom

2条回答
  •  情书的邮戳
    2020-12-16 20:15

    For anyone else that happens upon this post, it was indeed fixed by suggestions made by James World, and Brandon (thanks chaps).

    Here is full working code

    class Attempt1
    {
        private static bool shouldThrow = true;
    
        static void Main(string[] args)
        {
            Generate().RetryWithBackoffStrategy(3, MyRxExtensions.ExponentialBackoff,
                ex =>
                {
                    return ex is NullReferenceException;
                }, Scheduler.TaskPool)
                .Subscribe(
                    OnNext,
                    OnError
                );
    
            Console.ReadLine();
        }
    
        private static void OnNext(int val)
        {
            Console.WriteLine("subscriber value is {0} which was seen on threadId:{1}", 
                val, Thread.CurrentThread.ManagedThreadId);
        }
    
        private static void OnError(Exception ex)
        {
            Console.WriteLine("subscriber bad {0}, which was seen on threadId:{1}", 
                ex.GetType(),
                Thread.CurrentThread.ManagedThreadId);
        }
    
        static IObservable Generate()
        {
            return Observable.Create(
                o =>
                {
                    Scheduler.TaskPool.Schedule(() =>
                        {
                            if (shouldThrow)
                            {
                                shouldThrow = false;
                                Console.WriteLine("ON ERROR NullReferenceException");
                                o.OnError(new NullReferenceException("Throwing"));
                            }
                            Console.WriteLine("Invoked on threadId:{0}", 
                                Thread.CurrentThread.ManagedThreadId);
    
                            Console.WriteLine("On nexting 1");
                            o.OnNext(1);
                            Console.WriteLine("On nexting 2");
                            o.OnNext(2);
                            Console.WriteLine("On nexting 3");
                            o.OnNext(3);
                            o.OnCompleted();
                            Console.WriteLine("On complete");
                            Console.WriteLine("Finished on threadId:{0}", 
                                Thread.CurrentThread.ManagedThreadId);
                        });
    
                    return () => { };
                });
        }
    }
    
    public static class MyRxExtensions
    {
        /// 
        /// An exponential back off strategy which starts with 1 second and then 4, 9, 16...
        /// 
        public static readonly Func ExponentialBackoff = n => TimeSpan.FromSeconds(Math.Pow(n, 2));
    
        public static IObservable RetryWithBackoffStrategy(
            this IObservable source,
            int retryCount = 3,
            Func strategy = null,
            Func retryOnError = null,
            IScheduler scheduler = null)
        {
            strategy = strategy ?? MyRxExtensions.ExponentialBackoff;
    
            int attempt = 0;
    
            return Observable.Defer(() =>
            {
                return ((++attempt == 1) ? source : source.DelaySubscription(strategy(attempt - 1), scheduler))
                    .Select(item => new Tuple(true, item, null))
                    .Catch, Exception>(e =>
                        retryOnError(e)
                        ? Observable.Throw>(e)
                        : Observable.Return(new Tuple(false, default(T), e)));
            })
            .Retry(retryCount)
            .SelectMany(t => t.Item1
                ? Observable.Return(t.Item2)
                : Observable.Throw(t.Item3));
        }
    
        public static IObservable DelaySubscription(this IObservable source, 
            TimeSpan delay, IScheduler scheduler = null)
        {
            if (scheduler == null)
            {
                return Observable.Timer(delay).SelectMany(_ => source);
            }
            return Observable.Timer(delay, scheduler).SelectMany(_ => source);
        }
    }
    

    Which produces the desired output of

    ON ERROR NullReferenceException
    Invoked on threadId:11
    On nexting 1
    On nexting 2
    On nexting 3
    On complete
    Finished on threadId:11
    Invoked on threadId:11
    On nexting 1
    subscriber value is 1 which was seen on threadId:11
    On nexting 2
    subscriber value is 2 which was seen on threadId:11
    On nexting 3
    subscriber value is 3 which was seen on threadId:11
    On complete
    Finished on threadId:11
    

提交回复
热议问题