网页API +的HttpClient:异步模块或处理程序,而异步操作仍有待完成 [英] Web Api + HttpClient: An asynchronous module or handler completed while an asynchronous operation was still pending

查看:1065
本文介绍了网页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:

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

An asynchronous module or handler completed while an asynchronous operation was still pending.

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

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:异步模块或处理程序,而异步操作仍有待完成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆