Budujemy wysoce współbieżną aplikację internetową, a ostatnio zaczęliśmy intensywnie korzystać z programowania asynchronicznego (przy użyciu TPL i async
/await
).HttpContent.ReadAsStringAsync powoduje żądanie zawieszenia (lub inne dziwne zachowania).
Mamy rozproszone środowisko, w którym aplikacje komunikują się ze sobą za pośrednictwem interfejsów REST API (wbudowanych w ASP.NET Web API). W jednej konkretnej aplikacji mamy DelegatingHandler
, która po wywołaniu base.SendAsync
(tj. Po obliczeniu odpowiedzi) rejestruje odpowiedź na plik. Mamy zawierać podstawowe informacje na odpowiedź w dzienniku (kod stanu, nagłówki i treść):
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;
}
Problem jest taki: gdy kod osiągnie content.ReadAsStringAsync().Result
pod dużym obciążeniem serwera, żądanie czasami zawiesza się na IIS. Kiedy to robi, czasami zwraca odpowiedź - ale zawiesza się na IIS tak, jakby tego nie robił - lub w innych przypadkach nigdy nie zwraca.
Próbowałem również przeczytać zawartość przy użyciu ReadAsByteArrayAsync
, a następnie przekonwertować ją na String
, bez powodzenia.
Kiedy konwertować kod do wykorzystania asynchroniczny całym uzyskać rezultaty nawet dziwacznych w:
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();
}
Teraz HttpContext.Current
jest null po wywołaniu content.ReadAsStringAsync()
i utrzymuje będąc null dla wszystkich kolejnych żądań! Wiem, że to brzmi niewiarygodnie - zajęło mi to trochę czasu i obecność trzech współpracowników, aby zaakceptować fakt, że tak naprawdę się dzieje.
Czy jest to oczekiwane zachowanie? Czy robię coś złego tutaj?
Zdajesz sobie sprawę, że wywołanie 'ReadContentAsStringAsync' a następnie natychmiast dzwoni' Result' na to jest w zasadzie negując asynchrony, dobrze? To zablokuje, dopóki zadanie nie zostanie zakończone. A 'HttpContext.Current' mający' null' po oczekiwaniu brzmi, jakby po prostu nie płynął przez 'oczekujące' punkty, co jest irytujące, ale nie * całkowicie * zaskakuje mnie. Możesz go pobrać na * początku * metody asynchronicznej, a następnie użyć tej zmiennej lokalnej ... –
Prawdopodobnie nazwałbym wrap próbą złapania wokół wywołania response.EnsureSuccessStatusCode() przed próbą przetworzenia zawartości. – cgotberg
Czy jesteś pewien, że zawsze można przeczytać 'HttpResponseMessage.Content'? Wydaje mi się, że próba odczytu twojego własnego strumienia wyjściowego może nie być obsługiwana. –