如何创建一个Observable序列,在超时后重新发送HTTP请求?

时间:2014-12-10 12:21:52

标签: c# .net asynchronous system.reactive

我是Rx的新手,我正在尝试创建一个Observable序列,允许我执行以下操作:

  1. 使用发送HTTP POST请求到URI System.Net.Http.HttpClient.SendAsync(request,cancelToken)
  2. 等待可返回响应的可配置时间段或请求超时。
  3. 如果请求超时,请重复请求。
  4. 继续重复请求,直到收到回复(不一定是200 OK)或最大值。达到了重试次数。
  5. 如果最多达到重试次数然后我必须知道它,以便我可以记录错误。
  6. 我一直在玩:

    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "Some URI");
    ...
    CancellationTokenSource tokenSource = new CancellationTokenSource();
    CancellationToken token = tokenSource.Token;
    
    ...
    
    try
    {
        var res = Observable.FromAsync(() => _client.SendAsync(request, token))
                                                    .Timeout(TimeSpan.FromSeconds(5));
        try
        {
            HttpResponseMessage response = res.Wait();
    
            // Process response
            ...
        }
        catch (TaskCancelledException)
        {
            ....
        }
    }
    catch (TimeoutException)
    {
        ....
    }
    

    但我不确定在超时发生后再次启动请求的最佳方法,以及我应该如何检查我是否已达到最大值。重试次数。

    另外,我不确定是否应该对可观察序列设置超时策略,或者我是否应该在HttpClient对象本身上设置Timeout属性。

    [2014年12月11日编辑]

    根据以下评论,我更新了@TheZenCoder的代码,以便它使用await

    var attempts = 0;
    HttpResponseMessage response = null;
    
    try
    {
        response = await Observable
            .FromAsync((ct) =>
            {
                attempts++;
                return SendRequest(token, ct);
            })
            .Retry(5)
            .LastAsync();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    

    到目前为止,我只进行了有限的测试,但似乎工作正常。

1 个答案:

答案 0 :(得分:6)

要发送请求,设置请求超时并使用取消令牌,您可以将其包装到以下方法中:

private static Task<HttpResponseMessage> SendRequest(CancellationToken token)
{
    var client = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };

    var request = new HttpRequestMessage(HttpMethod.Get, new Uri("http://www.google.ba"));

    return client.SendAsync(request, token);
}

我认为在HTTP客户端上设置超时是一个更清晰的选项,而不是使用Observable超时。

然后,您可以使用IObservable Retry方法多次重试此操作。如果你不害怕开源,那么RXX Extensions中也有一些更灵活的重试方法,正如@Dave Sexton在评论中所指出的那样。

var attempts = 0;

try
{
    var response = Observable
        .FromAsync(() =>
        {
            attempts++;
            return SendRequest(token);
        })
        .Retry(5)
        .Wait();
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

如果SendRequest方法中发生HTTP超时或其他错误,则重试将继续。在包含有关错误的信息的最后一次重试之后将抛出异常。重试次数设置为尝试变量。

请记住,使用Wait方法可以有效地阻止调用线程执行,直到结果可用,这不是您在使用异步代码时想要做的事情。也许你有一些特定的场景,这就是为什么我把它留在我的例子中。