简体   繁体   English

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

[英]How to create an Observable sequence that will resend HTTP requests after a timeout?

I'm new to Rx and I'm trying to create an Observable sequence that will allow me to do the following: 我是Rx的新手,我正在尝试创建一个Observable序列,它允许我执行以下操作:

  1. Send a HTTP POST request to a URI using System.Net.Http.HttpClient.SendAsync(request, cancelToken) 使用System.Net.Http.HttpClient.SendAsync(request,cancelToken)向URI发送HTTP POST请求
  2. Wait for a configurable time period for a response to be returned or for the request to timeout. 等待可配置的时间段以返回响应或请求超时。
  3. If the request times out, then repeat the request. 如果请求超时,则重复请求。
  4. Continue repeating the request until either a response is received (doesn't have to be 200 OK) or a max. 继续重复请求,直到收到响应(不一定是200 OK)或最大值。 number of retries is reached. 达到了重试次数。
  5. If the max. 如果最大 number of retries is reached then I have to know about it so I can log an error. 达到重试次数然后我必须知道它,所以我可以记录错误。

I've been playing around with: 我一直在玩:

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)
{
    ....
}

but I'm not sure of the best way to kick off the request again after a timeout occurs, and also how I should check that I have reached the max. 但我不确定在超时发生后再次启动请求的最佳方法,以及我应该如何检查我是否已达到最大值。 number of retries. 重试次数。

Also, I'm not sure whether I should be putting a timeout policy on the observable sequence or whether I should be setting the Timeout property on the HttpClient object itself. 另外,我不确定是否应该对可观察序列设置超时策略,或者是否应该在HttpClient对象本身上设置Timeout属性。

[Edited on 11 Dec 2014] [2014年12月11日编辑]

Based on comments below, I have updated the code from @TheZenCoder so that it uses an await 根据下面的评论,我更新了@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);
}

I've only done limited testing so far but it seems to work ok. 到目前为止我只进行了有限的测试,但似乎工作正常。

For sending the request, setting a request timeout and using a cancellation token you can wrap that into a method like: 要发送请求,设置请求超时并使用取消令牌,您可以将其包装到以下方法中:

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);
}

I think setting the timeout on the HTTP client is a cleaner option instead of using the Observable timeout. 我认为在HTTP客户端上设置超时是一个更清晰的选项,而不是使用Observable超时。

Then you can use the IObservable Retry method to retry this operation multiple times. 然后,您可以使用IObservable Retry方法多次重试此操作。 If you are not afraid of opensource there are also some more flexible retry methods in the RXX Extensions as noted by @Dave Sexton in the comments. 如果你不害怕开源,那么@Xave Sexton在评论中指出RXX扩展中还有一些更灵活的重试方法。

var attempts = 0;

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

If an HTTP timeout or another error occurs in the SendRequest method the retries will continue. 如果在SendRequest方法中发生HTTP超时或其他错误,则重试将继续。 An exception will be thrown after the last retry containing information about the error. 在包含有关错误的信息的最后一次重试之后将抛出异常。 The number of retries is set to the attempts variable. 重试次数设置为attempts变量。

Have in mind that using the Wait method effectively blocks the calling thread execution until a result is available, and that is not something you ever want to do when using Async code. 请记住,使用Wait方法可以有效地阻止调用线程执行,直到结果可用,这不是您在使用异步代码时想要做的事情。 Maybe you have some specific scenario on mind, and that is why i left it there in my example. 也许你有一些特定的场景,这就是我在我的例子中留下它的原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM