问题描述
我正在写代理人到某些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$c$c>构造只需要动作
译文]参数)。因此,有你的模块/处理程序完成与该完成之间的竞争条件异步无效
拉姆达。
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 Action
s 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:异步模块或处理程序,而异步操作仍有待完成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!