ale*_*ete 9 c# async-await c#-5.0 httpcontent
我们正在构建一个高度并发的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;
}
Run Code Online (Sandbox Code Playgroud)
问题是:当代码达到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();
}
Run Code Online (Sandbox Code Playgroud)
HttpContext.Current调用后现在为null content.ReadAsStringAsync(),并且对于所有后续请求它保持为null!我知道这听起来令人难以置信 - 我花了一些时间和三个同事的存在才接受这种情况真的发生了.
这是某种预期的行为吗?我在这里做错了吗?
vic*_*ott 10
我有这个问题.虽然,我还没有完全测试,使用CopyToAsync而不是ReadAsStringAsync似乎解决了这个问题:
var ms = new MemoryStream();
await response.Content.CopyToAsync(ms);
ms.Seek(0, SeekOrigin.Begin);
var sr = new StreamReader(ms);
responseContent = sr.ReadToEnd();
Run Code Online (Sandbox Code Playgroud)
关于您的第二个问题, async/await 是编译器构建状态机的语法糖,其中对“await”前面的函数的调用立即在当前线程上返回...其中包含 HttpContext.Current 的线程线程本地存储。该异步调用的完成可以发生在不同的线程上...线程本地存储中没有 HttpContext.Current 的线程。
如果您希望完成在同一线程上执行(从而在线程本地存储中具有相同的对象,如 HttpContext.Current),那么您需要注意此行为。这对于来自主 UI 线程(如果您正在构建 Windows 应用程序)的调用或在 ASP.NET 中(来自依赖于 HttpContext.Current 的 ASP.NET 请求线程的调用)尤其重要。
请参阅有关ConfigureAwait(false) 的参考文档。另外,请查看 TPL 上的一些 Channel 9 教程。一旦理解了“简单”的内容,演示者总是会谈论这个问题,因为它会导致不易理解的微妙问题,除非您知道 TPL 在幕后做什么。
祝你好运。
关于您的第一个问题,如果调用者得到结果,我不相信 IIS 尚未完成请求。您如何确定此调用者发起的 ASP.NET 请求线程在 IIS 中挂起?
| 归档时间: |
|
| 查看次数: |
11574 次 |
| 最近记录: |