Partage
  • Partager sur Facebook
  • Partager sur Twitter

[WCF/Silverlight] TimeoutException inexplicable.

Sujet résolu
    3 mai 2011 à 20:03:35

    Bonsoir à tous,

    J'ai un service WCF et un client Silverlight 4 qui consomme ce service.

    Pour une raison que je n'explique pas, mon client Silverlight ne peut consommer les méthodes du service qu'une seule et unique fois . Si j’essaie d'appeler une méthode du service en passant exactement les même paramètres, j'ai une "TimeoutException".

    Côté service, le serveur traite correctement le message entrant. Mes méthodes sont appelées correctement et retournent les même résultat. Visiblement, seul le client Silverlight n'arrive pas à traiter la réponse.

    J'ai déjà essayé en augmentant le délai imparti pour la réception des données...Evidemment, ça n'a rien résolu : Le deuxième appel à la méthode est toujours infructueux.

    Quelqu'un aurait-il une idée sur le pourquoi du comment de cette TimeoutException inexplicable??
    • Partager sur Facebook
    • Partager sur Twitter
      3 mai 2011 à 20:27:18

      Là comme ça, je ne vois pas... :o

      Quel binding utilises-tu ?
      Peux-tu reproduire le problème avec un code assez simplifié pour qu'on puisse voir de quoi il s'agit ?
      • Partager sur Facebook
      • Partager sur Twitter
        3 mai 2011 à 21:29:51

        C'est un service un peu particulier que j'ai fait pour être le plus "modulable" possible avec un minimum d'effort. Mon projet final est composé de plusieurs couches (Une cliente, une métier-cliente, une service et une données).

        Ma couche service est en réalité un pont entre le métier-client et la couche données. Cette couche données définit un jeu d'entité ADO.NET Entity Framework, des POCO, des méthodes d'extensions pour convertir Entité<->POCO et des opérations (méthodes) appelable à travers le service.

        La couche service se charge juste d'appeler les opérations de données tout en assurant que l'utilisateur a bien les droits nécessaires pour effectuer cette opération. Le binding utilisé pour le dialogue avec le client Silverlight est le PollingHttpDuplex, une sorte de sur-binding au binding BasicHttpBinding qui ajoute la gestion des canaux de rappels WCF (PollingHttpDuplexBinding envoi toutes les <x> millisecondes un message SOAP au service qui lui retourne si nécessaire les messages à destination du client qui en fait la demande).

        Sachant ça, voici une opération appelée depuis le client Silverlight :
        ApplicationEngine.Current.Service.Channel.BeginGetOperation("GetUserDatas", null, this.UserDatasRetrieved, null);
        [...]
        private void UserDatasRetrieved(IAsyncResult res)
        {
            User usr = (User)ApplicationEngine.Current.Service.Channel.EndGetOperation(res);
        }
        


        Ce code me retourne les informations de l'utilisateur connecté. Dans l'ordre, BeginGetOperation prend en argument le nom de l'opération de données, les arguments optionnels de l'opération de données, le rappel asynchrone et un objet utilisateur transmit à la méthode de rappel asynchrone. Cette technique appelle avec succès la méthode de service GetOperation (qui prend en argument le nom d'opération et les arguments d'opération), qui, elle-même, appelle correctement la méthode de données GetUserDatas.

        Tout ce code marchera très bien....Une seule et unique fois o_O . Si, par malheur, j'avais besoin de récupérer les informations utilisateur en utilisant ce code, j'aurais droit à une TimeoutException sur le EndGetOperation.

        Edit : Comme indiqué dans mon premier post, le premier appel service comme le deuxième appel service sont bien gérés sur le serveur. En plaçant des points d'arrêts, j'arrive bien à récupérer les même données. C'est comme si le client [qui s'exécute dans un navigateur] avait mis en cache l'appel de service avec ces paramètres lors du premier appel et lors de l'appel suivant, il renverrait bien la demande mais c'est le cache du navigateur qui répondrait -rien- à la place du service o_O . J'ai jamais eu ce genre de problèmes auparavant....Et je ne vois vraiment pas comment le résoudre o_O
        • Partager sur Facebook
        • Partager sur Twitter
          3 mai 2011 à 22:31:15

          Effectivement, tu as mis en place un système assez générique - mais tu perds le typage statique en conséquence. ;)
          Je suis cependant étonné de voir que ton service GetOperation peut finalement renvoyer à peu près n'importe quoi... Je ne vois pas trop comment WCF parvient à désérialiser correctement la réponse SOAP alors que le type de réponse n'est pas décrit dans la définition du service. :o
          J'ai mis en place un système un peu similaire pour mon projet, mais le contenu de mes messages est transmis sous forme de byte[] et je dois gérer moi-même la sérialisation/désérialisation.

          Je pense que le mieux est encore d'observer ce qui transite entre le navigateur et le serveur. Fiddler marche assez bien pour ça, et il existe un plugin pour Fiddler capable de désérialiser les messages WCF pour observer leur contenu. En comparant les trames échangées au cours du premier et du deuxième appel tu pourras peut-être avoir un début de piste :)
          • Partager sur Facebook
          • Partager sur Twitter
            3 mai 2011 à 22:46:50

            Erf...Encore de l'exploration de trame :ninja: . J'testerais ça à l'occaz'. Mais si ça venait d'une histoire cache navigateur? Personne n'aurait déjà entendu parler de ce genre de problèmes? Ce qui me fait penser à ça, c'est le fait que, quand je lance mon appli, ça marche une fois...Il me suffit de quitter l'appli puis de la relancer pour que ça ne remarche qu'une fois encore >_< .

            Citation : Orwell


            Je suis cependant étonné de voir que ton service GetOperation peut finalement renvoyer à peu près n'importe quoi... Je ne vois pas trop comment WCF parvient à désérialiser correctement la réponse SOAP alors que le type de réponse n'est pas décrit dans la définition du service. :o
            J'ai mis en place un système un peu similaire pour mon projet, mais le contenu de mes messages est transmis sous forme de byte[] et je dois gérer moi-même la sérialisation/désérialisation.


            En jonglant avec les KnownTypeAttribute et les ServiceKnownTypeAttribute, on peut faire un système capable de prendre en charge n'importe quel objet. Dans mon cas, j'ai une librairie à part qui contient les contrats de service + tous mes POCO (organisés autour d'une classe de base centrale).

            Une des surcharges des attributs Known[xxx]Attribute demande un nom de méthode qui retourne la liste des types connus par le (dé)sérialiseur WCF. Un petit coup de reflexion pour rechercher tous les types qui héritent de la classe de base, une mise en cache dans un dictionnaire et ça donne un système qui me semble flexible (pas besoin de spécifier à la main les types connus dans la classe de base) et rapide (après une première passe uniquement. Seul le démarrage de l'application -et donc la recherche des types par reflexion- est un poil plus long. Après, ça se joue surement à quelques ms tant qu'on recherche pas des milliers de types...Ca reste largement jouable pour une application qui contient une quarantaine de classes filles, comme sur cette application :p ).
            • Partager sur Facebook
            • Partager sur Twitter
              3 mai 2011 à 23:10:05

              Citation : Nisnor

              Erf...Encore de l'exploration de trame :ninja: . J'testerais ça à l'occaz'.


              C'est assez instructif, ne tarde pas trop pour y jeter un oeil :)

              Citation : Nisnor

              Mais si ça venait d'une histoire cache navigateur? Personne n'aurait déjà entendu parler de ce genre de problèmes? Ce qui me fait penser à ça, c'est le fait que, quand je lance mon appli, ça marche une fois...Il me suffit de quitter l'appli puis de la relancer pour que ça ne remarche qu'une fois encore >_< .


              C'est possible que ça provienne du cache. A vrai dire ça me rappelle les problèmes qu'Advance avait rencontrés il y a quelques semaines pour son système de chat par webcam, et j'ai moi aussi observé un comportement bizarre en répétant certains appels avec les mêmes paramètres (le serveur était sollicité uniquement si la réponse n'était pas en cache). J'ai tendance à me méfier du binding PollingHttpDuplex depuis, et je préfère travaille en NetTcpBinding (bien qu'il soit plus ch*ant à faire marcher).

              Ce qui m'étonne ici c'est que ton serveur est sollicité à chaque appel, donc l'appel passe au moins à travers le cache... :euh:

              Citation : Nisnor

              En jonglant avec les KnownTypeAttribute et les ServiceKnownTypeAttribute, on peut faire un système capable de prendre en charge n'importe quel objet. Dans mon cas, j'ai une librairie à part qui contient les contrats de service + tous mes POCO (organisés autour d'une classe de base centrale).

              Une des surcharges des attributs Known[xxx]Attribute demande un nom de méthode qui retourne la liste des types connus par le (dé)sérialiseur WCF. Un petit coup de reflexion pour rechercher tous les types qui héritent de la classe de base, une mise en cache dans un dictionnaire et ça donne un système qui me semble flexible (pas besoin de spécifier à la main les types connus dans la classe de base) et rapide (après une première passe uniquement. Seul le démarrage de l'application -et donc la recherche des types par reflexion- est un poil plus long. Après, ça se joue surement à quelques ms tant qu'on recherche pas des milliers de types...Ca reste largement jouable pour une application qui contient une quarantaine de classes filles, comme sur cette application :p ).


              Ah, c'était donc ça le truc :D Je n'ai pas encore eu l'occasion de jouer avec, ça m'a l'air pas mal :)
              • Partager sur Facebook
              • Partager sur Twitter
                9 mai 2011 à 10:42:50

                Je reviens aux nouvelles pour ce [passionant] topic >_< .

                Je n'ai pas regardé les trames avec Fiddler (j'm'en étais déjà servit pour voir si le polling était bien effectif) parce que c'est lourd à mettre en place. Je travaille en connexion HTTP over SSL et pour que Fiddler puisse afficher les trames, il faut qu'il puisse les déchiffrer. Pour ça, il se charge d'installer une flopée de certificat à la noix, qui ne sont naturellement pas certifiés, avant de s'intercaler entre le client et le serveur. J'avais pas envies de me reprendre la tête avec ça => La désinstallation automatique des certificats est vraiment bancale...Fiddler laisse des morceaux de machins à moitié certifiés dans tout le magasin de l'ordinateur, pas cool :pirate: .

                Cela dit, il y a une bonne nouvelle => le problème a "disparu".

                :magicien:

                Ah! Si ça pouvait être ça, ce serait chic. Ce que j'ai fais, c'est que j'ai mis les temps d'expiration côtés client et serveur sur la même base de temps. En utilisant pollingHttpDuplex, on doit spécifier, en plus des timeout de réception, d'émission, d'ouverture et de fermeture, un timeout définissant le délai entre chaque "ping" (configuré côté client) et un délai, côté serveur, que je ne saurais pas vous expliquer de suite, n'ayant pas compris à quoi il servait (nom du paramètre : serverPollTimeout. Peu explicite comme nom je trouve...Un clientPollTimeout se comprend tout à fait...Mais un "serverPollTimeout"...Je vois pas). J'ai juste mis les temps d'émissions (côté client et serveur) à 20s, le délai d'envoit des ping à 10s et le serverPollTimeout à 20s également. Depuis, plus de problèmes.

                J'éditerais ce message si je trouve à quoi correspond le serverPollTimeout.

                @bientôt
                • Partager sur Facebook
                • Partager sur Twitter
                  9 mai 2011 à 12:42:19

                  Bonne nouvelle que tu sois débloqué :)

                  Et en effet si tu trouves des infos pertinentes à ce sujet, ça m'intéresse ;)
                  • Partager sur Facebook
                  • Partager sur Twitter
                    9 mai 2011 à 15:43:14

                    J'ai pas encore lu l'article en entier...Mais j'ai trouvé le lien "miracle" concernant pollingDuplex :D .

                    Dans cet article, il y est dis :

                    Citation

                    The value of serverPollTimeout controls the maximum time the server will hold onto client’s long poll HTTP request before responding. If that time has elapsed without the server having an application message to push back to the client, the server will send back an empty HTTP OK response (causing the client to re-issue a new long poll).



                    Ce serait donc le temps d'attente maximal avant renvoi des messages publiés à destination d'un client connecté. Sachant ça, je ne vois pas du tout le rapport entre ce délai et le fait que mes méthodes ne furent appelable qu'une seule fois o_O .

                    Je continue dans les "TimeoutException" => Cette fois-ci, j'ai une TimeoutException lorsque j'appelle une méthode qui est censée me retourner un tableau d'élément.

                    De galère en galère, je suis sur que je vais finir par réussir à utiliser ce binding correctement... :(

                    Edit : serverPollTimeout est assez "multi-fonction". Il affecte bien plus que ce qui est cité dans le message. Par ailleurs, le pollingDuplex a des bugs connus qui sont présent dans Silverlight 4 :

                    Citation

                    Problem: If ServerPollTimeout is larger than ClientPollTimeout, messages sent from the server to the client may be lost. Workaround: Always set ClientPollTimeout larger than ServerPollTimeout.



                    Ce qu'il y a de fun, c'est que c'est même pas "messages sent [...] are lost" mais "may be lost", histoire de rajouter un peu d'aléatoire dans les bugs. De plus, ayant connaissance de ce bug, je me demande bien comment faire pour avoir un système de service réactif (donc avec des délais court) :colere: . Moralité : Même en SL4, ne pas utiliser pollingDuplex, c'est pas encore assez fiable pour une application à but lucratif!!
                    • Partager sur Facebook
                    • Partager sur Twitter
                      11 mai 2011 à 23:36:05

                      Au fait, je ne sais pas si ça pourra t'aider d'une façon ou d'une autre mais :

                      J'ai appris récemment que depuis SL3, Silverlight embarquait son propre stack HTTP permettant de gérer les requêtes émises depuis le client. Par défaut, SL utilise l'API du navigateur et donc le stack HTTP du browser - c'est notamment pour celà que le cache du browser est sollicité à chaque requête, ce qui peut être un avantage ou un inconvénient.

                      Utiliser le stack HTTP de Silverlight (appelé "Client Stack" par opposition au "Browser Stack") ne permet donc plus de bénéficier du cache, mais apporte d'autres avantages avec entre autres:
                      - Support complet des headers customisés dans les requêtes. (<- utile pour mon projet)
                      - Utilisation de méthodes HTTP autres que GET et POST.
                      - Support complet des codes de retour HTTP, en particulier les erreurs 500 (fini les messages "Page not found" en cas d'exception côté serveur :D )

                      Il serait intéressant de voir si tu observes un comportement différent avec le stack Client. Après tout, ça ne coute rien d'essayer :)

                      Plus d'infos ici: Comment : spécifier une gestion HTTP via un navigateur ou un client
                      • Partager sur Facebook
                      • Partager sur Twitter
                        13 mai 2011 à 11:03:57

                        :D

                        Ca concorde exactement avec les résultats de mes recherches.

                        Voici, en vrac, les différents liens qui m'ont été [très] utiles pour résoudre les multiples problèmes liés à tout ce petit monde (stack HTTP, timeouts notamment) :


                        Tous les problèmes que j'avais ne se présentent plus désormais. J'ai "perdu" beaucoup de temps à rechercher les solutions mais le résultat est enfin là!!

                        Edit : Au cours de mes lectures, j'ai également lu que, à changer de pile HTTP côté client, on se supprime l'accès aux cookies [gérés par le navigateur]. Ca me semble logique... Gaffe à ces solutions si on a besoin des cookies web...Sinon, il faudra surement se refaire une implémentation de la gestion des cookies.
                        • Partager sur Facebook
                        • Partager sur Twitter
                          13 mai 2011 à 12:56:14

                          Content que tu aies pu trouver une solution à ton problème :)

                          Citation : Nisnor

                          Edit : Au cours de mes lectures, j'ai également lu que, à changer de pile HTTP côté client, on se supprime l'accès aux cookies [gérés par le navigateur]. Ca me semble logique... Gaffe à ces solutions si on a besoin des cookies web...Sinon, il faudra surement se refaire une implémentation de la gestion des cookies.


                          Perso je cherchais justement une alternative aux cookies : je travaille sur une appli facebook en iframe et les cookies dans les iframes cay le mal :-° Les headers customisés font assez bien l'affaire ^^
                          • Partager sur Facebook
                          • Partager sur Twitter

                          [WCF/Silverlight] TimeoutException inexplicable.

                          × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                          × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                          • Editeur
                          • Markdown