Analyse détaillée des exceptions de délai d'attente WCF

94

Nous avons une application qui a un service WCF (* .svc) fonctionnant sur IIS7 et divers clients interrogeant le service. Le serveur exécute Win 2008 Server. Les clients exécutent Windows 2008 Server ou Windows 2003 Server. J'obtiens l'exception suivante, que j'ai vue peut en fait être liée à un grand nombre de problèmes potentiels de WCF.

System.TimeoutException: The request channel timed out while waiting for a reply after 00:00:59.9320000. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. ---> System.TimeoutException: The HTTP request to 'http://www.domain.com/WebServices/myservice.svc/gzip' has exceeded the allotted timeout of 00:01:00. The time allotted to this operation may have been a portion of a longer timeout. 

J'ai augmenté le délai d'expiration à 30 minutes et l'erreur s'est toujours produite. Cela me dit que quelque chose d'autre est en jeu, car la quantité de données ne pourrait jamais prendre 30 minutes à télécharger ou à télécharger.

L'erreur va et vient. Pour le moment, c'est plus fréquent. Cela n'a pas d'importance si j'ai 3 clients en cours d'exécution simultanément ou 100, cela se produit encore de temps en temps. La plupart du temps, il n'y a pas de timeouts mais j'en ai quand même quelques-uns par heure. L'erreur provient de l'une des méthodes appelées. L'une de ces méthodes n'a pas de paramètres et renvoie un peu de données. Un autre prend beaucoup de données en tant que paramètre mais s'exécute de manière asynchrone. Les erreurs proviennent toujours du client et ne font jamais référence à aucun code sur le serveur dans la trace de pile. Cela se termine toujours par:

 at System.Net.HttpWebRequest.GetResponse()
  at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)

Sur le serveur: j'ai essayé (et j'ai actuellement) les paramètres de liaison suivants:

maxBufferSize="2147483647" maxReceivedMessageSize="2147483647" maxBufferPoolSize="2147483647"

Cela ne semble pas avoir d'impact.

J'ai essayé (et j'ai actuellement) les paramètres de limitation suivants:

<serviceThrottling maxConcurrentCalls="1500"   maxConcurrentInstances="1500"    maxConcurrentSessions="1500"/>

Cela ne semble pas avoir d'impact.

J'ai actuellement les paramètres suivants pour le service WCF.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]

J'ai couru avec ConcurrencyMode.Multiplependant un moment et l'erreur s'est toujours produite.

J'ai essayé de redémarrer IIS, de redémarrer mon serveur SQL sous-jacent, de redémarrer la machine. Tout cela ne semble pas avoir d'impact.

J'ai essayé de désactiver le pare-feu Windows. Cela ne semble pas avoir d'impact.

Sur le client, j'ai ces paramètres:

maxReceivedMessageSize="2147483647"

<system.net>
    <connectionManagement>
    <add address="*" maxconnection="16"/>
</connectionManagement> 
</system.net>

Mon client ferme ses connexions:

var client = new MyClient();

try
{
    return client.GetConfigurationOptions();
}
finally
{
    client.Close();
}

J'ai modifié les paramètres de registre pour autoriser plus de connexions sortantes:

MaxConnectionsPerServer=24, MaxConnectionsPer1_0Server=32.

J'ai récemment essayé SvcTraceViewer.exe. J'ai réussi à attraper une exception du côté du client. Je vois que sa durée est de 1 minute. En regardant la trace côté serveur, je peux voir que le serveur n'est pas au courant de cette exception. La durée maximale que je peux voir est de 10 secondes.

J'ai regardé les connexions de base de données actives utilisant exec sp_whosur le serveur. Je n'en ai que quelques-uns (2-3). J'ai regardé les connexions TCP d'un client utilisant TCPview. Il est généralement d'environ 2-3 et j'en ai vu jusqu'à 5 ou 6.

En termes simples, je suis perplexe. J'ai essayé tout ce que j'ai pu trouver, et il doit manquer quelque chose de très simple qu'un expert WCF pourrait voir. J'ai le sentiment instinctif que quelque chose bloque mes clients au bas niveau (TCP), avant que le serveur ne reçoive réellement le message et / ou que quelque chose met les messages en file d'attente au niveau du serveur et ne les laisse jamais traiter.

Si vous avez des compteurs de performance que je devrais consulter, faites-le moi savoir. (veuillez indiquer quelles valeurs sont mauvaises, car certains de ces compteurs sont difficiles à déchiffrer). En outre, comment puis-je enregistrer la taille du message WCF? Enfin, existe-t-il des outils qui me permettraient de tester le nombre de connexions que je peux établir entre mon client et mon serveur (indépendamment de mon application)

Merci pour votre temps!

Informations supplémentaires ajoutées le 20 juin:

Mon application WCF fait quelque chose de similaire à ce qui suit.

while (true)
{
   Step1GetConfigurationSettingsFromServerViaWCF(); // can change between calls
   Step2GetWorkUnitFromServerViaWCF();
   DoWorkLocally(); // takes 5-15minutes. 
   Step3SendBackResultsToServerViaWCF();
}

En utilisant WireShark, j'ai vu que lorsque l'erreur se produit, j'ai cinq retransmissions TCP suivies d'une réinitialisation TCP plus tard. Je suppose que le RST vient de WCF qui tue la connexion. Le rapport d'exception que je reçois provient de l'expiration de l'étape 3.

J'ai découvert cela en regardant le flux tcp "tcp.stream eq 192". J'ai ensuite étendu mon filtre à "tcp.stream eq 192 et http et http.request.method eq POST" et j'ai vu 6 POSTs pendant ce flux. Cela semblait étrange, alors j'ai vérifié avec un autre flux tel que tcp.stream eq 100. J'avais trois POST, ce qui semble un peu plus normal car je fais trois appels. Cependant, je ferme ma connexion après chaque appel WCF, donc je me serais attendu à un appel par flux (mais je ne sais pas grand-chose sur TCP).

En enquêtant un peu plus, j'ai vidé la charge du paquet http sur le disque pour voir ce que ces six appels où.

1) Step3
2) Step1
3) Step2
4) Step3 - corrupted
5) Step1
6) Step2

Je suppose que deux clients simultanés utilisent la même connexion, c'est pourquoi j'ai vu des doublons. Cependant, j'ai encore quelques problèmes que je ne peux pas comprendre:

a) Pourquoi le paquet est-il corrompu? Fluke réseau aléatoire - peut-être? La charge est gzippée à l'aide de cet exemple de code: http://msdn.microsoft.com/en-us/library/ms751458.aspx - Le code peut-il être bogué de temps en temps lorsqu'il est utilisé simultanément? Je devrais tester sans la bibliothèque gzip.

b) Pourquoi devrais-je voir les étapes 1 et 2 s'exécuter APRÈS que l'opération corrompue a expiré? Il me semble que ces opérations n'auraient pas dû avoir lieu. Peut-être que je ne regarde pas le bon flux parce que ma compréhension de TCP est imparfaite. J'ai d'autres flux qui se produisent en même temps. Je devrais enquêter sur d'autres flux - un rapide coup d'œil sur les flux 190-194 montre que le POST Step3 a des données de charge utile appropriées (non corrompues). Me poussant à regarder à nouveau la bibliothèque gzip.

Jason Kealey
la source
Jason - avez-vous déjà résolu ce problème? Était-ce le paramètre DefaultConnectionLimit?
SFun28
2
@JasonKealey - Contrairement à de nombreuses autres questions, vous ne pouvez pas être accusé de ne pas avoir essayé par vous-même avant de poster la question :) J'adore que votre question soit si détaillée et inclut tous les détails importants. Les symptômes que vous décrivez ressemblent beaucoup aux miens, alors j'espère que la solution est également la même :)
Øyvind Bråthen

Réponses:

51

Si vous utilisez un client .Net, vous n'avez peut-être pas défini

//This says how many outgoing connection you can make to a single endpoint. Default Value is 2
System.Net.ServicePointManager.DefaultConnectionLimit = 200;

voici la question d'origine et la réponse Limitation du service WCF

Mise à jour :

Cette configuration va dans l'application cliente .Net peut être au démarrage ou à tout moment mais avant de démarrer vos tests.

De plus, vous pouvez l'avoir dans le fichier app.config comme suit

<system.net>
    <connectionManagement>
      <add maxconnection = "200" address ="*" />
    </connectionManagement>
  </system.net>
Mubashar
la source
Cela semble prometteur. J'ai inclus ceci pour être testé lors de mon prochain test d'évolutivité. Cela ressemble exactement au genre de réglage aléatoire qui le ferait planter :) Merci pour le pointeur.
Jason Kealey
1
@Jason: Si vous êtes programmeur de serveur, vous savez à quel point il est important de maintenir l'évolutivité du serveur entre vos mains et aussi celui qui souffre actuellement du problème de concurrence même après avoir utilisé ci-dessus. S'il vous plaît, si vous pouvez examiner la question suivante stackoverflow.com/questions/2637175/wcf-network-cost en bref, je souffre d'une latence de 31 ms entre le client et le serveur et j'ai besoin de la réduire.
Mubashar
3
Cela n'a pris qu'un an, mais j'ai finalement exécuté un autre test de résistance sur l'application avec cet ensemble d'indicateurs. Le problème semble résolu, je vous donne donc la meilleure réponse. Je ne serais pas surpris que ce soit la dernière pièce du puzzle qui était nécessaire, mais que tous les autres éléments devaient être en place pour garantir que l'erreur ne se produise pas. Merci beaucoup!
Jason Kealey
2
@Aris: dans l'application client .net, au démarrage ou partout où vous définissez votre configuration globale, si vous voulez la garder configurable, vous pouvez l'ajouter dans le fichier de configuration ainsi que ceci <system.net> <connectionManagement> <add maxconnection = "200" address = "*" /> </connectionManagement> </system.net>
Mubashar
3

Si vous ne l'avez pas déjà essayé, encapsulez vos opérations WCF côté serveur dans des blocs try / finally et ajoutez une journalisation pour vous assurer qu'elles sont effectivement renvoyées.

Si ceux-ci montrent que les opérations sont terminées, ma prochaine étape serait d'aller à un niveau inférieur et d'examiner la couche de transport réelle.

Wireshark ou un autre outil de capture de paquets similaire peut être très utile à ce stade. Je suppose que cela fonctionne sur HTTP sur le port standard 80.

Exécutez Wireshark sur le client. Dans les options lorsque vous démarrez la capture, définissez le filtre de capture sur tcp http and host service.example.com - cela réduira la quantité de trafic non pertinent.

Si vous le pouvez, modifiez votre client pour vous informer de l'heure de début exacte de l'appel et de l'heure à laquelle le délai d'attente s'est produit. Ou surveillez-le de près.

Lorsque vous obtenez une erreur, vous pouvez parcourir les journaux Wireshark pour trouver le début de l'appel. Faites un clic droit sur le premier paquet sur lequel votre client l'appelle (cela devrait être quelque chose comme GET /service.svc ou POST /service.svc) et sélectionnez Suivre le flux TCP.

Wireshark décodera l'intégralité de la conversation HTTP, vous pouvez donc vous assurer que WCF renvoie réellement des réponses.


la source
J'ai une connexion sur le serveur - il n'y a pas d'erreur à cette fin. J'utilise WireShark en ce moment pour voir ce que je peux trouver. Compte tenu du volume élevé du trafic, l'analyse sera pénible, mais je vous ferai un rapport si je peux trouver quelque chose.
Jason Kealey
J'ai couru WireShark au cours des six dernières heures et collecté environ 60 000 images. Une seule exception a été signalée par ce client aujourd'hui. J'ai vu une connexion TCP marquée comme RST (réinitialisation), apparemment après l'envoi de l'e-mail d'erreur, qui est probablement WCF qui met fin à la connexion. J'ai enregistré la charge utile (525k) sur le disque. J'ai vérifié qu'il y avait 87 autres invocations avec des charges utiles de taille similaire. J'ai vu quelques retransmissions TCP, mais j'en ai vu aussi dans d'autres appels (cela n'a pas échoué). Je commence à m'interroger sur mon matériel réseau + câbles.
Jason Kealey
Même sur un réseau local, la présence d'un TCP Retransmits n'est pas forcément mauvaise. S'il est possible de connecter physiquement deux des points finaux à un seul commutateur, cela vaut peut-être la peine d'être essayé, mais je ne voudrais pas espérer que cela résoudra le problème. Si vous le pouvez, créez une application client très basique qui ne fait que transmettre un peu de trafic à votre serveur, et rien d'autre. Cela peut aider à éliminer tout problème dans votre application pouvant entraîner des délais d'expiration.
En outre, vous mentionnez avoir vu le paquet TCP Reset - le serveur a-t-il fourni une réponse quelconque à ce stade (ou peut-être attendait-il plus de données)? Y a-t-il eu un délai appréciable entre le RST et le paquet précédent?
Le serveur est distant. Je prévois de créer un environnement de test localement pour voir si cela aide. Quant au RST, il a été envoyé 34 secondes après la dernière des cinq retransmission TCP. (1 à 8 secondes d'intervalle entre les retransmissions). Cela vous donne-t-il des indices?
Jason Kealey
2

de: http://www.codeproject.com/KB/WCF/WCF_Operation_Timeout_.aspx

Pour éviter cette erreur de délai d'expiration, nous devons configurer la propriété OperationTimeout pour Proxy dans le code client WCF. Cette configuration est quelque chose de nouveau contrairement à d'autres configurations telles que Send Timeout, Receive Timeout etc., dont j'ai discuté au début de l'article. Pour définir cette configuration de propriété de délai d'expiration d'opération, nous devons convertir notre proxy en IContextChannel dans l'application cliente WCF avant d'appeler les méthodes de contrat d'opération.

Joël Martinez
la source
J'ai essayé ceci. Indépendamment du délai que j'ai mis, il expire toujours mais cela n'a aucun sens car l'opération n'est pas si longue et parce que tous les autres clients qui font les mêmes requêtes fonctionnent pendant ce temps.
Jason Kealey
Mes tests ont prouvé que OperationTimeout remplace simplement ReceiveTimeout de la configuration. Ainsi, cela ne sert à rien.
dudeNumber4
2

J'ai un problème très similaire. Dans le passé, cela était lié à des problèmes de sérialisation. Si vous rencontrez toujours ce problème, pouvez-vous vérifier que vous pouvez correctement sérialiser les objets que vous renvoyez. Plus précisément, si vous utilisez des objets Linq-To-Sql qui ont des relations, il existe des problèmes de sérialisation connus si vous placez une référence arrière sur un objet enfant à l'objet parent et marquez cette référence arrière en tant que DataMember.

Vous pouvez vérifier la sérialisation en écrivant une application console qui sérialise et désérialise vos objets à l'aide de DataContractSerializer côté serveur et des méthodes de sérialisation utilisées par votre client. Par exemple, dans notre application actuelle, nous avons à la fois des clients WPF et Compact Framework. J'ai écrit une application console pour vérifier que je peux sérialiser à l'aide d'un DataContractSerializer et désérialiser à l'aide d'un XmlDesserializer. Vous pourriez essayer ça.

De plus, si vous renvoyez des objets Linq-To-Sql qui ont des collections enfants, vous pouvez essayer de vous assurer que vous les avez chargés avec impatience côté serveur. Parfois, en raison du chargement différé, les objets renvoyés ne sont pas remplis et peuvent provoquer le comportement que vous voyez lorsque la demande est envoyée plusieurs fois à la méthode de service.

Si vous avez résolu ce problème, j'aimerais savoir comment, car je suis également coincé avec. J'ai vérifié que mon problème n'était pas la sérialisation, donc je suis perdu.

MISE À JOUR: Je ne sais pas si cela vous aidera, mais l'outil de visualisation de trace de service vient de résoudre mon problème après 5 jours d'expérience très similaire à la vôtre. En configurant le traçage puis en regardant le XML brut, j'ai trouvé les exceptions qui causaient mes problèmes de sérialisation. Il était lié aux objets Linq-to-SQL qui avaient parfois plus d'objets enfants qu'il ne pouvait être sérialisé avec succès. L'ajout des éléments suivants à votre fichier web.config doit activer le traçage:

<sharedListeners>
    <add name="sharedListener"
         type="System.Diagnostics.XmlWriterTraceListener"
         initializeData="c:\Temp\servicetrace.svclog" />
  </sharedListeners>
  <sources>
    <source name="System.ServiceModel" switchValue="Verbose, ActivityTracing" >
      <listeners>
        <add name="sharedListener" />
      </listeners>
    </source>
    <source name="System.ServiceModel.MessageLogging" switchValue="Verbose">
      <listeners>
        <add name="sharedListener" />
      </listeners>
    </source>
  </sources>

Le fichier résultant peut être ouvert avec l'outil de visualisation de trace de service ou simplement dans IE pour examiner les résultats.

Brett Bim
la source
2

Fermez-vous la connexion au service WCF entre les demandes? Si vous ne le faites pas, vous verrez ce délai d'expiration exact (éventuellement).

aridlehoover
la source
2

Je viens de résoudre le problème, j'ai constaté que les nœuds du fichier App.config étaient mal configurés.

<client>
<endpoint name="WCF_QtrwiseSalesService" binding="wsHttpBinding" bindingConfiguration="ws" address="http://cntgbs1131:9005/MyService/TGE.ISupplierClientManager" contract="*">
</endpoint>
</client>

<bindings>
    <wsHttpBinding>
        <binding name="ws" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" messageEncoding="Text">
            <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
            <**security mode="None">**
                <transport clientCredentialType="None"></transport>
            </security>
        </binding>
    </wsHttpBinding>
</bindings>

Confirmez votre configuration dans le nœud <security>, la valeur de l'attribut "mode" est "Aucun". Si votre valeur est "Transport", l'erreur se produit.

alexanderlc
la source
Cela n'affecte-t-il pas la sécurité? Si tel est le cas, ce n'est peut-être pas une solution pour la plupart des applications réelles
Veverke
0

Avez-vous essayé d'utiliser clientVia pour voir le message envoyé, en utilisant la boîte à outils SOAP ou quelque chose du genre? Cela pourrait aider à voir si l'erreur vient du client lui-même ou d'ailleurs.

Philippe
la source
Connaissez-vous des outils plus récents que la boîte à outils SOAP obsolète qui me permettrait de consigner plus facilement ces informations dans les appels WCF?
Jason Kealey
SOAP Toolkit isdeprecated
Kiquenet
0

Avez-vous vérifié les traces WCF? WCF a tendance à avaler les exceptions et à ne renvoyer que la dernière exception, qui est le délai d'expiration que vous obtenez, car le point final n'a rien renvoyé de significatif.

Miki Watts
la source
J'ai essayé SvcTraceViewer et la seule exception qu'il a signalée était le délai d'attente (sur le client). Rien n'a été signalé sur le serveur.
Jason Kealey
Ouvrez toutes les options de la trace, vous n'aurez peut-être pas toutes les options de trace ouvertes. Vérifiez également les fichiers de suivi des événements et des messages.
Miki Watts
0

Vous recevrez également cette erreur si vous renvoyez un objet au client qui contient une propriété de type enum qui n'est pas définie par défaut et que enum n'a pas une valeur mappée à 0. ie enum MyEnum{ a=1, b=2};

Tim
la source
0

Il semble que ce message d'exception soit assez générique et peut être reçu pour diverses raisons. Nous avons rencontré cela lors du déploiement du client sur des machines Windows 8.1. Notre client WCF s'exécute à l'intérieur d'un service Windows et interroge en permanence le service WCF. Le service Windows s'exécute sous un utilisateur non administrateur. Le problème a été résolu en définissant clientCredentialType sur «Windows» dans la configuration WCF pour permettre l'authentification directe, comme dans l'exemple suivant:

      <security mode="None">
        <transport clientCredentialType="Windows" proxyCredentialType="None"
          realm="" />
        <message clientCredentialType="UserName" algorithmSuite="Default" />
      </security>
Alexander Liberson
la source
0

Je ne suis pas un expert WCF mais je me demande si vous ne rencontrez pas de protection DDOS sur IIS. Je sais par expérience que si vous exécutez un tas de connexions simultanées d'un seul client à un serveur à un moment donné, le serveur cesse de répondre aux appels car il suspecte une attaque DDOS. Il maintiendra également les connexions ouvertes jusqu'à leur expiration afin de ralentir le client dans ses attaques.

Les connexions multiples provenant de différentes machines / IP ne devraient cependant pas poser de problème.

Il y a plus d'informations dans cet article MSDN:

http://msdn.microsoft.com/en-us/library/bb463275.aspx

Découvrez la sproperty MaxConcurrentSession.

Jurgenb
la source
Je pense que c'est ce qui se passe, d'après tout ce que j'ai vu, mais j'ai (sur le serveur): <serviceThrottling maxConcurrentCalls = "150" maxConcurrentInstances = "150" maxConcurrentSessions = "150" /> <serviceDebug includeExceptionDetailInFaults = "true" /> Y aurait-il un moniteur de performances ou un journal IIS que je pourrais surveiller pour voir si cela se produit?
Jason Kealey