使用Reactive Extensions和Timers ......“For Dummies”版本

时间:2013-09-11 18:36:29

标签: .net-4.0 system.reactive reactive-programming

尽管社区提供了患者帮助herehereherehere,但我担心自己并不接近真正的理解。我试图将IntroToRx的概念锤入我的大脑,但用亚瑟王的话说:“这个新的学习让我感到困惑。再次解释一下羊的膀胱和地震。”哪个,到我的想法,就是我们不妨谈论的。

我会愚蠢到需求(已经改变了 - .NET 4.0是踢球者),而不是用我自己的(可能有缺陷的)实现走错路径:

  1. 我需要使用.NET Framework 4.0编写Windows服务。
  2. 我需要我的Windows服务来调用Web服务(WS),并将其传递给多个URL中的一个,但每次调用只需一个URL。
  3. 我需要能够为每个URL定义单独的间隔(1到 n )以调用WS。例如:我可能每小时为URL1调用WS,对URL2每隔四小时调用一次
  4. 如果Web服务返回该URL的错误,我需要能够停止为特定URL调用Web服务。
  5. 就是这样。从我理解的很少,这是一个完美的Rx场景。我很确定我想为序列创建Observable.Timer s,但如何在错误上禁用计时器?这可能是等待发生的CancellationToken(或者我只是完成定时器),但我似乎无法接受这些示例并将它们变成一个可靠的,可理解的解决方案。

    Whaddaya说,S.O。:任何人都想抓住这位老人并用小词解释做这种废话的正确方法?如果我没有解释清楚,或者我已经遗漏了一些东西,我会很乐意编辑。

    感谢。

1 个答案:

答案 0 :(得分:2)

1)我想你想看Observable.IntervalObservable.Timer是单个值序列,相比之下Observable.Interval将继续以给定的间隔生成值。如果对于每个项目/网址,此轮询周期是不变的,那么这可能就是您想要的。

2)您可以将IEnumerable(网址)和IObservable(投票事件)序列混合在一起。

3)错误将终止序列。您可以通过将序列中的值(Select)投影到WebRequest的输出来利用此功能。如果WebRequest失败,则序列将OnError,异常将作为OnError有效负载传播。

慢慢地穿过这个;

  • 我们想要一个URL / Period对列表,
  • 在给定时间内轮询网址
  • 如果对该URL的任何调用失败(并且可能记录它),则停止轮询URL
  • 停止轮询Windows服务是否已停止

让我们首先处理一个要投票的网址

protected override void OnStart(string [] args)
{
    var resource = new {Url="http://www.bing.com", Period=TimeSpan.FromMinutes(3)};

    var pollingSequence =  Observable.Interval(resource.Period)
                                     .Select(_=>Request(resource.Url));
    var subscription = pollingSequence.Subscribe(
            response=>Log(response),
            ex=>Log(ex)
        ));
    _subscriptions = new CompositeDisposable(subscription);
}

protected override void OnStop()
{
    Dispose();
}

private bool Request(string url)
{
    //TODO:
}

public void Dispose()
{
    _subscriptions.Dispose();
}

为了形象化,我们可以使用“大理石图”。这里每个char空间代表1分钟。 OnNext表示为'o'(大理石)。

    bing    --o--o--o--o-       (every 3rd minute)

为了更准确,我们实际上是在事件中获取值(即使我们忽略了值)

    bing    --0--1--2--3-       

然后我们接受每个事件并将其投影到一个请求,所以序列现在转到这个(其中'r'表示来自请求的响应)

    bing    --r--r--r--r-       

如果有任何请求失败,序列将会终止,例如3请求失败,我们显示带有'X'的OnError

    bing    --r--r--X

现在我们可以将示例扩展到资源列表,即IEnumerable<IObservable<T>>

protected override void OnStart(string [] args)
{
    var resources = new[] { 
        new {Url="http://www.bing.com", Period=TimeSpan.FromMinutes(3)},
        new {Url="http://www.google.com", Period=TimeSpan.FromMinutes(2)},
        new {Url="http://www.yahoo.com", Period=TimeSpan.FromMinutes(5)},
        new {Url="http://www.stackoverflow.com", Period=TimeSpan.FromMinutes(2)}
    };

    //The same as before, but now we create a polling sequence per resource.
    var pollingSequences = from resource in resources
                        select Observable.Interval(resource.Period)
                                            .Select(_=>Request(resource.Url));

    //Now we cant subscribe to an `IEnumerable<IObservable<T>>` *, 
    //  but we can subscribe to each sequence in it.
    // This turns our queries (IE<IO<T>>) into subscriptions (IE<IDisposable>).
    var subscriptions = pollingSequences.Select(x => 
        x.Subscribe(
            response=>Log(response),
            ex=>Log(ex)
        ));
    _subscriptions = new CompositeDisposable(subscriptions);
}

此处我们有一项服务将开始调用Request方法(供您实施),并在为Url指定的时间段内传递Url。如果抛出Request方法,则不再对Url进行轮询。如果您停止服务,则会处理订阅,不再进行轮询。

为了形象化,我们可以使用“大理石图”。因此,我们从每个“资源”(空间数据)的“行”开始,即。 var resources = new[] { ...};

    bing
    google
    yahoo
    SO

接下来,我们认为每一行还有一系列轮询事件(数据及时)。从上面的代码(使用1 char = 1分钟)

    bing    --1--2--3-      (every 3rd minute)
    google  -1-2-3-4-5      (every 2nd minute)
    yahoo   ----1----2      (every 5 minutes)
    SO      -1-2-3-4-5      (every 2nd minute)
  • 你不能订阅IEnumerable<IObservable<T>>,但有像Concat和Merge这样的Rx方法可以处理这类数据。 我们不想使用这些,因为它们将序列压缩成单个序列,这意味着如果任何失败,所有轮询都会停止。