我们正在构建一个高度并发的 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();
}
调用后nowHttpContext.Current
为 null content.ReadAsStringAsync()
,并且对于所有后续请求它一直为 null!我知道这听起来令人难以置信——我花了一些时间和三位同事的在场才接受这真的发生了。
这是某种预期的行为吗?我在这里做错了吗?