我们正在构建一个高度并发的Web应用程序,并且最近开始广泛使用异步编程(使用TPL和async
/await
)。
我们拥有一个分布式环境,其中应用程序通过REST API相互通信(构建在ASP.NET Web API之上)。在一个特定的应用程序中,我们有一个DelegatingHandler
,在调用base.SendAsync
后(即计算响应后)将响应记录到文件中。我们在日志中包含响应的基本信息(状态代码、标头和内容):
public static string SerializeResponse(HttpResponseMessage response)
{
var builder = new StringBuilder();
var content = ReadContentAsString(response.Content);
builder.AppendFormat("HTTP/{0} {1:d} {1}", response.Version.ToString(2), response.StatusCode);
builder.AppendLine();
builder.Append(response.Headers);
if (!string.IsNullOrWhiteSpace(content))
{
builder.Append(response.Content.Headers);
builder.AppendLine();
builder.AppendLine(Beautified(content));
}
return builder.ToString();
}
private static string ReadContentAsString(HttpContent content)
{
return content == null ? null : content.ReadAsStringAsync().Result;
}
问题在于:当代码在服务器负载很重的情况下达到content.ReadAsStringAsync().Result
时,请求有时会在IIS上挂起。当它发生时,有时会返回一个响应 - 但在IIS上却像没有返回一样挂起 - 或者在其他时间根本不会返回。
我还尝试过使用ReadAsByteArrayAsync
读取内容,然后将其转换为String
,但没有成功。
当我把代码全部改成异步操作时,结果变得更加奇怪:
public static async Task<string> SerializeResponseAsync(HttpResponseMessage response)
{
var builder = new StringBuilder();
var content = await ReadContentAsStringAsync(response.Content);
builder.AppendFormat("HTTP/{0} {1:d} {1}", response.Version.ToString(2), response.StatusCode);
builder.AppendLine();
builder.Append(response.Headers);
if (!string.IsNullOrWhiteSpace(content))
{
builder.Append(response.Content.Headers);
builder.AppendLine();
builder.AppendLine(Beautified(content));
}
return builder.ToString();
}
private static Task<string> ReadContentAsStringAsync(HttpContent content)
{
return content == null ? Task.FromResult<string>(null) : content.ReadAsStringAsync();
}
现在,在调用content.ReadAsStringAsync()
之后,HttpContext.Current
为null,并且在所有后续请求中它始终为null !我知道这听起来难以置信——我花了一些时间,有三个同事在场才接受了这个事实。
这是一种预期的行为吗?我做错了什么吗?
ReadContentAsStringAsync
然后立即在其上调用Result
实际上就是抵消了异步性,对吧?这样会阻塞直到任务完成。而且,在await
之后HttpContext.Current
变为null
的问题听起来就像是它没有在await
点传递过去一样,这真让人恼火,但也不完全令我惊讶。你可以在异步方法的开始处获取它并使用局部变量... - Jon SkeetHttpResponseMessage.Content
吗?在我看来,试图读取自己的输出流可能不被支持。 - Stephen ClearyObjectContent
,我建议尝试读取Value
而不是将其作为流进行阻塞异步读取。 - Stephen Cleary