HttpContent.ReadAsStringAsync provoque demande à accrocher (ou d'autres comportements étranges)
Nous construisons un très simultanées de l'application web, et nous avons récemment commencé à utiliser de la programmation asynchrone en profondeur (à l'aide de TPL et async
/await
).
Nous avons un environnement distribué, dans les applications qui communiquent les uns avec les autres via des Api REST (construite sur le toit de ASP.NET l'API Web). Dans une application particulière, nous avons un DelegatingHandler
qu'après l'appel de base.SendAsync
(c'est à dire, après le calcul de la réponse) les journaux de la réponse à un fichier. Nous incluons la réponse des informations de base dans le journal (code d'état, les en-têtes et le contenu):
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;
}
Le problème, c'est que lorsque le code atteint content.ReadAsStringAsync().Result
sous une lourde charge sur le serveur, la demande se bloque parfois sur IIS. Quand il le fait, il retourne parfois une réponse -- mais se bloque sur IIS comme si elle n'avait pas-ou en d'autres temps, il ne revient jamais.
J'ai aussi essayé de lire le contenu à l'aide de ReadAsByteArrayAsync
et de le convertir ensuite à String
, avec pas de chance.
Quand je convertir le code à utiliser asynchrone à travers je suis de plus en plus étrange résultats:
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();
}
Maintenant HttpContext.Current
est null après l'appel à content.ReadAsStringAsync()
, et , il continue à être nulle pour toutes les demandes subséquentes! Je sais que cela semble incroyable-et il m'a fallu du temps et de la présence de trois collègues à accepter que ce qui se passe réellement.
Ce n'est pas le comportement attendu? Suis-je en train de faire quelque chose de mal ici?
ReadContentAsStringAsync
, puis immédiatement à l'appel de Result
sur, fondamentalement, c'est la négation de l'asynchronie, droit? Qui permet de bloquer jusqu'à ce que le travail est terminé. Et HttpContext.Current
être null
après l'attendent sonne comme il n'est tout simplement pas s'écoulant à travers await
points, ce qui est ennuyeux, mais n'a pas entièrement surprise pour moi. Vous pourrait chercher à la démarrer de votre méthode async et ensuite utiliser cette variable locale...Je serais probablement appel envelopper un try catch autour d'un appel à la réaction.EnsureSuccessStatusCode() avant même de tenter de traiter le contenu.
Êtes-vous sûr qu'il est toujours possible de lire
HttpResponseMessage.Content
? Il me semble que d'essayer de lire vos propres flux de sortie ne peuvent pas être pris en charge.oui je suis conscient de cela. La seule raison pour laquelle j'ai "bloqué" l'opération a été d'essayer d'éviter de perdre
HttpContext.Current
"forever" qui, je l'ai fait, mais ensuite il y a la pendaison de problème. En passant, je ne pense pas que vous avez bien compris, mais HttpContext.Current
n'est pas perdu jusqu'à la fin de la Requête HTTP. Il est perdu pour la suite des Requêtes HTTP. Je ne peux obtenir de retour avec un iisreset
.qu'entendez-vous par là? Aussi loin que je suis concerné, je ne suis pas l'écriture d'un flux directement à
response.Content
, je suis en utilisant ObjectContent
à la place.OriginalL'auteur alextercete | 2013-07-22
Vous devez vous connecter pour publier un commentaire.
J'ai eu ce problème. Bien que, je n'ai pas entièrement encore testé, à l'aide de CopyToAsync au lieu de ReadAsStringAsync semble résoudre le problème:
Quand j'ai essayé, j'ai été capable de lire le flux de données pour la sécurité de mon chèque, mais alors quand WebApi acheminé à ma méthode, le paramètre a la valeur null. J'ai eu autour de lui en utilisant ReadAsStreamAsync au lieu de cela, puis de revenir au début du flux lorsque j'ai été fait. Cependant, avec cette approche, vous devez utiliser le constructeur StreamReader qui prend 5 arguments, et de prendre le dernier "vrai" pour garder le flux sous-jacent de l'ouvrir.
OriginalL'auteur victordscott
En ce qui concerne votre deuxième question, la async/await est sucre syntaxique pour le compilateur de la construction d'une machine à l'état où l'appel à une fonction précédé de "en attente" renvoie immédiatement sur le thread en cours...celui qui contient HttpContext.Actuelle dans son thread local storage. La fin de l'appel asynchrone peut se produire sur un différents fil...celui qui n'a PAS HttpContext.Actuelle dans son thread local storage.
Si vous voulez l'achèvement de s'exécuter sur le même fil (donc ayant les mêmes objets dans le thread local storage comme HttpContext.Actuel), alors vous devez être conscient de ce comportement. Ceci est particulièrement important pour les appels à partir de l'INTERFACE principale du fil (si vous êtes la construction d'une application Windows) ou en ASP.NET-des appels à partir d'un ASP.NET demande thread où vous êtes dépendant HttpContext.Actuel.
Voir référence docs sur ConfigureAwait(false). Aussi, la vue de certains Canaux 9 tutoriels sur la TPL. Une fois le "facile" choses est grokked, le présentateur va invariablement parler de cette question, car il provoque des problèmes subtils qui ne sont pas facile à comprendre, sauf si vous savez ce que le TPL est en train de faire sous les couvertures.
Bonne chance.
En ce qui concerne votre premier problème, si l'appelant obtient un résultat, je ne suis pas convaincu qu'IIS n'a pas terminé la demande. Comment allez-vous déterminer que les ASP.NET demande thread initié par la présente de l'appelant est bloqué dans IIS?
OriginalL'auteur triple.vee