本文介绍了网页API +的HttpClient:异步模块或处理程序,而异步操作仍有待完成的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在写代理人到某些HTTP请求的ASP.NET Web API和我奋力识别间歇性错误的来源的应用程序。
这似乎是一个竞争状态......但我不能完全肯定。

I'm writing an application that proxies some HTTP requests using the ASP.NET Web API and I am struggling to identify the source of an intermittent error.It seems like a race condition... but I'm not entirely sure.

我细讲之前这里是应用程序的常规通信流程:

Before I go into detail here is the general communication flow of the application:


  • 客户端发出HTTP请求为代理1 即可。

  • 代理1 转发HTTP请求到代理服务器2的内容

  • 代理2 中继HTTP请求到目标Web应用程序的内容

  • 目标Web应用程序响应HTTP请求和响应流(块传输)为代理服务器2

  • 代理2 返回到响应的代理1 这反过来又响应原始调用的客户端

  • Client makes a HTTP request to Proxy 1.
  • Proxy 1 relays the contents of the HTTP request to Proxy 2
  • Proxy 2 relays the contents of the HTTP request to the Target Web Application
  • Target Web App responds to the HTTP request and the response is streamed (chunked transfer) to Proxy 2
  • Proxy 2 returns the response to Proxy 1 which in turn responds to the original calling Client.

代理应用程序都写在使用.NET 4.5的ASP.NET Web API RTM。
在code来进行中继看起来像这样:

The Proxy applications are written in ASP.NET Web API RTM using .NET 4.5.The code to perform the relay looks like so:

//Controller entry point.
public HttpResponseMessage Post()
{
    using (var client = new HttpClient())
    {
        var request = BuildRelayHttpRequest(this.Request);

        //HttpCompletionOption.ResponseHeadersRead - so that I can start streaming the response as soon
        //As it begins to filter in.
        var relayResult = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;

        var returnMessage = BuildResponse(relayResult);
        return returnMessage;
    }
}

private static HttpRequestMessage BuildRelayHttpRequest(HttpRequestMessage incomingRequest)
{
    var requestUri = BuildRequestUri();
    var relayRequest = new HttpRequestMessage(incomingRequest.Method, requestUri);
    if (incomingRequest.Method != HttpMethod.Get && incomingRequest.Content != null)
    {
       relayRequest.Content = incomingRequest.Content;
    }

    //Copies all safe HTTP headers (mainly content) to the relay request
    CopyHeaders(relayRequest, incomingRequest);
    return relayRequest;
}

private static HttpRequestMessage BuildResponse(HttpResponseMessage responseMessage)
{
    var returnMessage = Request.CreateResponse(responseMessage.StatusCode);
    returnMessage.ReasonPhrase = responseMessage.ReasonPhrase;
    returnMessage.Content = CopyContentStream(responseMessage);

    //Copies all safe HTTP headers (mainly content) to the response
    CopyHeaders(returnMessage, responseMessage);
}

private static PushStreamContent CopyContentStream(HttpResponseMessage sourceContent)
{
    var content = new PushStreamContent(async (stream, context, transport) =>
            await sourceContent.Content.ReadAsStreamAsync()
                            .ContinueWith(t1 => t1.Result.CopyToAsync(stream)
                                .ContinueWith(t2 => stream.Dispose())));
    return content;
}

这是间歇性发生的错误是:

The error that occurs intermittently is:

而异步操作仍然悬而未决的异步模块或处理程序完成。

这个错误通常发生在最初的几个请求之后,没有再见过错误的代理应用程序。

This error usually occurs on the first few requests to the proxy applications after which the error is not seen again.

时抛出Visual Studio中从来没有捕获该异常。
但该错误可以在Global.asax Application_Error事件捕获。
不幸的是,异常没有堆栈跟踪。

Visual Studio never catches the Exception when thrown.But the error can be caught in the Global.asax Application_Error event.Unfortunately the Exception has no Stack Trace.

代理应用程序托管在Azure的Web角色。

The proxy applications are hosted in Azure Web Roles.

任何标识的罪魁祸首帮助将AP preciated。

Any help identifying the culprit would be appreciated.

推荐答案

您的问题是一个微妙的:在异步你传递给 PushStreamContent 正在PTED为异步无效(因为<一间$ p $ href=\"http://msdn.microsoft.com/en-us/library/system.net.http.pushstreamcontent%28v=vs.108%29.aspx\"><$c$c>PushStreamContent构造只需要动作译文]参数)。因此,有你的模块/处理程序完成与该完成之间的竞争条件异步无效拉姆达。

Your problem is a subtle one: the async lambda you're passing to PushStreamContent is being interpreted as an async void (because the PushStreamContent constructor only takes Actions as parameters). So there's a race condition between your module/handler completing and the completion of that async void lambda.

PostStreamContent 检测到流结束,并把它伪装成年底的工作(完成模块/处理程序) ,所以你只需要确保有流被关闭后仍然可以运行没有异步无效方法。 异步任务方法都行,所以这应该修复它:

PostStreamContent detects the stream closing and treats that as the end of its Task (completing the module/handler), so you just need to be sure there's no async void methods that could still run after the stream is closed. async Task methods are OK, so this should fix it:

private static PushStreamContent CopyContentStream(HttpResponseMessage sourceContent)
{
  Func<Stream, Task> copyStreamAsync = async stream =>
  {
    using (stream)
    using (var sourceStream = await sourceContent.Content.ReadAsStreamAsync())
    {
      await sourceStream.CopyToAsync(stream);
    }
  };
  var content = new PushStreamContent(stream => { var _ = copyToStreamAsync(stream); });
  return content;
}

如果你希望你的代理来扩展好一点,我还建议摆脱所有的结果来电:

If you want your proxies to scale a bit better, I also recommend getting rid of all the Result calls:

//Controller entry point.
public async Task<HttpResponseMessage> PostAsync()
{
  using (var client = new HttpClient())
  {
    var request = BuildRelayHttpRequest(this.Request);

    //HttpCompletionOption.ResponseHeadersRead - so that I can start streaming the response as soon
    //As it begins to filter in.
    var relayResult = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);

    var returnMessage = BuildResponse(relayResult);
    return returnMessage;
  }
}

您前者code会阻碍一个线程为每个请求(直到收到的头);通过使用异步一路攀升到控制器的水平,你就不会在这段时间阻塞线程。

Your former code would block one thread for each request (until the headers are received); by using async all the way up to your controller level, you won't block a thread during that time.

这篇关于网页API +的HttpClient:异步模块或处理程序,而异步操作仍有待完成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-20 23:32