10
votes

Service Windows WCF - Opérations longues / rappel au module d'appel

J'ai un service Windows qui prend le nom d'un tas de fichiers et effectue des opérations sur eux (Zip / Unzip, Mise à jour de DB, etc.). Les opérations peuvent prendre du temps en fonction de la taille et du nombre de fichiers envoyés au service.

(1) Le module qui envoie une demande à ce service attend jusqu'à ce que les fichiers soient traités. Je veux savoir s'il existe un moyen de fournir un rappel dans le service qui informera le module d'appel lors de la fin du traitement des fichiers. Veuillez noter que plusieurs modules peuvent appeler le service à la fois pour traiter les fichiers afin que le service devra fournir une sorte de tâche que je suppose.

(2) Si une méthode de service est appelée et est en cours d'exécution et qu'un autre appel est effectué sur le même service, comment cet appel va-t-il être traité (je pense qu'il n'y a qu'un seul thread associé au service). J'ai vu que lorsque le service prend du temps dans le traitement d'une méthode, les threads associés au service commencent à augmenter.


0 commentaires

3 Réponses :


14
votes

WCF propose en effet des liaisons en duplex qui vous permettent de spécifier un contrat de rappel, de sorte que le service puisse rappeler au client appelant pour notifier.

Cependant, à mon avis, ce mécanisme est plutôt flakeux et pas vraiment recommandé.

Dans un tel cas, lorsque l'appel provoque une opération de course assez longue, je ferais quelque chose comme ceci:

Si vous voulez coller aux liaisons HTTP / NETTCP, je voudrais:

  • dépose la demande avec le service, puis "Laissez Go" - ce serait un appel à sens unique, vous déposez simplement ce que vous voulez avoir fait, puis votre client est effectué
  • Demandez à un appel de statut que le client pourrait appeler après un délai donné pour savoir si les résultats de la demande sont prêts ou non d'ici maintenant
  • S'ils sont, il devrait y avoir un appel de trois services pour récupérer les résultats

    Donc, dans votre cas, vous pouvez déposer la demande pour zip certains fichiers. Le service ira et ferait son travail et stockerait la fermeture à glissière résultante dans un emplacement temporaire. Ensuite, plus tard, le client peut vérifier si le zip est prêt, et si oui, récupérez-le.

    Cela fonctionne encore mieux sur une file d'attente de messagerie (MSMQ) qui est présent dans chaque machine de serveur Windows (mais pas beaucoup de gens semblent le savoir ou l'utiliser):

    • Votre client dépose la demande sur une file d'attente de demande
    • Le service écoute sur cette demande de file d'attente et récupère la demande après la demande et fonctionne-t-il
    • Le service peut alors poster les résultats à une file d'attente de résultat sur laquelle vos appelants à leur tour écoutent

      Vérifiez comment effectuer tout cela efficacement en lisant l'excellent article MSDN Foudnations : Construisez un service de réponse WCF de la file d'attente - hautement recommandé!

      Un système basé sur la file d'attente de messages a tendance à être beaucoup plus stable et moins tendu d'erreur d'un système basé sur le contrat en duplex / callback, à mon avis.


4 commentaires

Je ne peux pas utiliser MSMQ car ce service Windows est exécuté sur le système de l'utilisateur (XP, Vista, 7) et fait partie du produit à télécharger par l'utilisateur. Donc, un getStatus () est la voie à suivre, je suppose. Vous vous demandez comment complexez-vous avec plusieurs commandes exécutant en même temps.


@ A9S6: WCF crée une nouvelle instance de service pour chaque appel - il n'y a donc vraiment aucun problème avec plusieurs demandes en même temps - chacune a sa propre instance de classe de service isolée.


Voulez-vous dire qu'une nouvelle instance de la classe de services (service de service héritée) est créée sur chaque appel? Je pensais que cela n'était arrivé qu'avec des services Web et non des services Windows. Donc, il ne faut donc pas mettre l'initialisation dans cette classe sauf si nécessaire sur chaque appel de méthode?


@ A9S6: Oui - Chaque appel à un service WCF (par défaut) entraîne la création d'une nouvelle instance de classe de service pour gérer la demande. Mais c'est une bonne chose !!



1
votes

(1) Le moyen le plus simple d'y parvenir est avec un TaskID comme vous le souhaitez, puis une autre méthode appelée IStaskComplete avec laquelle le client peut vérifier si la tâche est terminée.

(2) Les appels supplémentaires apportés au service démarreront de nouveaux threads.

Edit: Le comportement du service par défaut consiste à démarrer de nouveaux threads par appel. La propriété configurable est Mode contextuel d'instance , et peut être réglé sur percall, la perspective ou partageable.


1 commentaires

Êtes-vous sûr de (2)? Existe-t-il un article qui explique ce comportement ou un moyen de fermer ces threads de service?



1
votes

La question a une solution, mais j'utilise un service Duplex WCF pour obtenir le résultat d'une opération longue et même si j'ai trouvé un problème qui m'a coûté plusieurs heures à résoudre (et c'est pourquoi j'ai cherché cette question Plus tôt), maintenant cela fonctionne parfaitement et je pense que c'est une solution simple dans le cadre de service duplex du WCF.

Quel est le problème avec une opération longue? Le problème principal consiste à bloquer l'interface client pendant que le serveur effectue l'opération et avec le service Duplex WCF, nous pouvons utiliser un rappel sur le client pour éviter le blocage (il s'agit d'une méthode ancienne pour éviter de bloquer mais il peut être facilement transformé en Le cadre ASYNC / AWAIT à l'aide d'un TaskCompletSource).

En bref, la solution utilise une méthode pour démarrer l'opération de manière asynchrone sur le serveur et retourne immédiatement. Lorsque les résultats sont prêts, le serveur les renvoie à l'aide du rappel du client.

Premièrement, vous devez suivre un guide standard pour créer des services et des clients duplex de WCF, et j'ai trouvé ces deux utiles: < / p>

Service Duplex MSDN

CodeProject Article WCF Duplex Service

Suivez ensuite ces étapes en ajoutant votre propre code:

  1. Définissez l'interface de rappel avec une méthode de gestionnaire d'événements pour envoyer des résultats du serveur et les recevoir dans le client. XXX

  2. Définissez l'interface de service avec une méthode permettant de transmettre les paramètres nécessaires à la volée longue (reportez-vous à l'interface précédente IlongOrationCallback dans la CallbackContractattribute) XXX

  3. dans la classe de service qui implémente l'interface de service, Obtenir d'abord le proxy du client Rappelez-le et enregistrez-le dans un champ de classe , puis démarrez la longue opération fonctionnant de manière asynchrone et renvoyez le Valeur de Bool immédiatement. Lorsque le fonctionnement de l'opération longue est terminé, envoyez les résultats au client à l'aide du champ de proxy de rappel du client. XXX

  4. dans le client Créez une classe qui implémente ilongoperérationCallback pour recevoir des résultats et ajouter une méthode pour démarrer la longue opération sur le serveur (la méthode de démarrage et le gestionnaire d'événements n'ont pas besoin d'être dans la même classe) < / p> XXX

    Notes:

    1. J'utilise le retour BOOL dans la méthode Startlongoperation pour indiquer que le serveur est occupé par opposition à la baisse, mais il n'est nécessaire que lorsque la longue opération ne peut pas être concomitante comme dans mon application actuelle, et peut-être sont les meilleures façons de la WCF de réaliser la non-concurrence (à découvrir si le serveur est en panne, ajoutez un bloc d'essai / attraper comme d'habitude).

    2. La citation importante que je n'ai pas vue documentée est la nécessité de mettre en cache le proxy du client de rappel de rappel dans la méthode StartLongoperation. Mon problème était que j'essayais d'obtenir le proxy dans la méthode de travail (oui, tous les exemples utilisent le proxy du client de rappel à la méthode de service, mais ce n'est pas indiqué dans la documentation et dans le cas d'une Fonctionnement longue Nous devons retarder le rappel jusqu'à la fin de l'opération).

    3. ne pas obtenir et ne pas mettre en cache deux fois le proxy de rappel après une méthode de service renvoyée et avant la suivante.

      Disclaimer: Je n'ai pas ajouté de code pour contrôler les erreurs, etc.


0 commentaires