在Rx中,如何确保不会因异常而丢失通知

时间:2014-11-27 14:41:14

标签: c# system.reactive

我想确保我的Rx通知在与我的消费者Do代表进行处理后不会丢失。我有一个Producer,它生成我需要处理的消息,如果失败则重试处理。

理想的大理石图:

Producer   1 2 3 4 5
Consumer   1 X 2 3 X X 4 5
Downstream 1   2 3     4 5

标准Retry在这里没有帮助,因为它会在发生错误后重新订阅制作人。这将丢失处理失败的通知,并继续下一个通知。

Retry大理石图:

Producer  1 2 3 4 5
Consumer  1 X 3 X 5

到目前为止,我有这段代码,但它对我来说似乎不对:

static void RetryWithBacklog()
{
  var data = Enumerable.Range(1, 100).ToObservable();
  var backlog = new Subject<long>();

  backlog
    .Merge(data)
    .Retry()
    .Do(l =>
    {
      try
      {
        ProcessNotification(l);
      }
      catch (Exception)
      {
        backlog.OnNext(l);
      }
    })
    .Subscribe();

  Console.ReadKey();
}

Full code sample

背景

Do操作将根据制作人的通知执行网络请求。这些可能会失败,所以我需要重试它而不会丢失要传输的信息。当然,我可以盲目while try/catch网络请求,但只要知道网络连接已关闭,请求就不会发生(请参阅this question)。

解决方案

static void RetryBeforePassingDownstream()
{
  var data = Enumerable.Range(1, 100).ToObservable();

  data
    .Replay(l => l.SelectMany(ProcessNotification).Retry(), 1)
    .Subscribe(Downstream);

  Console.ReadKey();
}

static IObservable<int> ProcessNotification(int notification)
{
  Console.WriteLine("process: {0}", i);
  // either:
  // throw new Exception("error");

  // or:
  return Observable.Return(notification);
}

static void Downstream(int i)
{
  Console.WriteLine("downstream: {0}", i);
}

1 个答案:

答案 0 :(得分:1)

Retry运算符的意思是它通过重新订阅在可观察量上工作,因此每次“重试”时都会导致订阅副作用。例如,您的observable可能会在每次观察者订阅时发送Web请求。在您的示例中,Retry运算符没有任何意义,特别是考虑到Interval从不调用OnError

Retry语义在技术上可以分别存在于observable和观察者中。在您的情况下,您是否只需致电ProcessNotification,直到Do运营商成功为止?

或者问题只是下游观察员在Do投掷时不会观察到通知?我猜这不是你的意思,因为在这种情况下你可以简单地吞下异常。

或者您的问题是,如果要重播通知,通知可能会以某种方式发生变化或被上游运营商过滤掉?在这种情况下,您可能只需使用Do后跟Catch和递归来实现同样的事情而不需要Subject,但我无法弄清楚为什么你真的想要这样做。

<强>更新

Do运算符不是异步的。请改用SelectMany,以便它成为您查询的一部分。这样,取消订阅也将取消该请求。如果您的请求方法返回Task<T>而不是IObservable<T>,那么请考虑使用接受CancellationToken的重载。

有一种更简单的方法可以使 cold observable重放上一个通知,只需使用Retry运算符即可重试,如下所示。

(未测试的)

data.Replay(r => r.YourStateMachine.SelectMany(SendRequestAsync).Retry(), 1);

詹姆斯的回答here可以填写 YourStateMachine