2009-09-10 5 views
3

J'écris un service Web ASP.NET en utilisant C# qui a une fonction DoLookup(). Pour chaque appel à la fonction DoLookup(), j'ai besoin de mon code pour exécuter deux requêtes distinctes: l'une vers un autre service Web sur un site distant et l'autre vers une base de données locale. Les deux requêtes doivent se terminer avant que je puisse compiler les résultats et les renvoyer comme réponse à la méthode DoLookup. Le problème auquel je suis confronté est que je veux rendre ceci aussi efficace que possible, à la fois en termes de temps de réponse et d'utilisation des ressources sur le serveur web. Nous attendons jusqu'à plusieurs milliers de requêtes par heure. Voici un aperçu C# -comme approximative de ce que j'ai jusqu'à présent:Optimisation d'un service Web ASMX avec plusieurs opérations de longue durée

public class SomeService : System.Web.Services.WebService 
{ 
    public SomeResponse DoLookup() 
    { 
     // Do the lookup at the remote web service and get the response 
     WebResponse wr = RemoteProvider.DoRemoteLookup(); 

     // Do the lookup at the local database and get the response 
     DBResponse dbr = DoDatabaseLookup(); 

     SomeResponse resp = new SomeResponse(wr, dbr); 

     return resp; 
    } 
} 

Le code ci-dessus fait tout de manière séquentielle et fonctionne très bien, mais maintenant je veux le rendre plus évolutif. Je sais que je peux appeler la fonction DoRemoteLookup() de manière asynchrone (RemoteProvider a des méthodes BeginRemoteLookup/EndRemoteLookup) et que je peux aussi effectuer la recherche de base de données de manière asynchrone en utilisant les méthodes BeginExecuteNonQuery/EndExecuteNonQuery.

Ma question (enfin) est la suivante: comment puis-je déclencher à la fois la recherche de service web ET la recherche de base de données simultanément sur des threads séparés et s'assurer qu'ils ont tous deux terminé avant de renvoyer la réponse? La raison pour laquelle je veux exécuter les deux requêtes sur des threads séparés est qu'elles ont toutes les deux potentiellement de longs temps de réponse (1 ou 2 secondes) et que je voudrais libérer les ressources du serveur web pour gérer d'autres requêtes pendant qu'il attend des réponses. Une note supplémentaire - J'ai la recherche de service Web à distance fonctionnant de manière asynchrone actuellement, je ne voulais juste pas rendre l'exemple ci-dessus trop confus. Ce que je lutte avec est d'obtenir à la fois la recherche de service à distance et la recherche de base de données a commencé en même temps et de déterminer quand ils ont terminé à la fois.

Merci pour vos suggestions.

+0

Avez-vous des threads fonctionnant dans un service Web? Threading dans le web quoi que ce soit est très limité sur la plate-forme. Net .. –

+0

@Charles Conway: Avez-vous lu l'une des réponses ci-dessous? De quoi parlez-vous, "limité"? –

Répondre

2

Vous pouvez utiliser une paire de AutoResetEvents, un pour chaque fil. À la fin de l'exécution du thread, vous appelez AutoResetEvents.Set() pour déclencher l'événement. Après la création des unités d'exécution, vous utilisez WaitAll() avec les deux événements AutoResetEvents. Cela entraînera le thread à bloquer jusqu'à ce que les deux événements soient définis. L'inconvénient de cette approche est que vous devez vous assurer que Set() est garanti d'être appelé, sinon vous allez bloquer pour toujours. De plus, assurez-vous qu'avec les threads, vous gérez correctement les exceptions, ou que vous provoquerez par inadvertance d'autres problèmes de performances lorsque des exceptions non gérées provoquent le redémarrage de votre application Web.

MSDN Has sample code regarding AutoResetEvent usage.

+1

Pour vous assurer de ne pas bloquer définitivement, utilisez la surcharge de WaitAll() qui prend un paramètre de délai (spécifiant un délai d'attente acceptable pour les opérations à effectuer). Vous pouvez ensuite vérifier la valeur de retour de WaitAll() pour déterminer si elle a été renvoyée car toutes les poignées ont été signalées ou parce que le délai d'expiration a expiré. –

+0

Super point. Je n'avais pas prévu de temps mort, car il voulait s'assurer que les deux avaient été terminés avant de revenir. – Alan

0

En supposant que vous pouvez avoir une fonction de rappel pour les deux la demande Web et la recherche de base de données alors quelque chose le long de ces lignes peuvent travailler

bool webLookupDone = false; 
bool databaseLookupDone = false; 

private void FinishedDBLookupCallBack() 
{ 
    databaseLookupDone = true; 
    if(webLookupDone) 
    { 
     FinishMethod(); 
    } 
} 

private void FinishedWebLookupCallBack() 
{ 
    webLookupDone = true; 
    if(databaseLookupDone) 
    { 
     FinishMethod(); 
    } 
} 
+0

Ce code a une condition de concurrence, si les deux threads définissent simultanément l'indicateur done à true avant de vérifier si l'autre thread a terminé, FinishMethod sera appelé deux fois. – DSO

+0

Cette pensée m'avait traversé l'esprit, mais je ne savais pas trop comment l'empêcher. Je ne suis pas trop familier avec le multithreading. – Bela

+0

Pas difficile à réparer, il suffit de mettre un verrou autour de chaque méthode, de sorte que le réglage de l'indicateur done et la vérification de l'autre drapeau fait est fait atomiquement. – DSO

1

Voir Asynchronous XML Web Service Methods, How to: Create Asynchronous Web Service Methods et How to: Chain Asynchronous Calls with a Web Service Method.

Mais notez le premier alinéa de ces articles:

Cette rubrique est spécifique à une technologie héritée. Les services Web XML et les clients du service Web XML doivent désormais être créés à l'aide de Windows Communication Foundation (WCF).


BTW, faire les choses comme ces articles disent est important car il libère le thread de travail ASP.NET alors que la tâche de longue durée fonctionne. Dans le cas contraire, vous risquez de bloquer le thread de travail, ce qui l'empêche de traiter d'autres demandes et d'affecter l'évolutivité.

+0

Dans ce cas, les appels doivent être exécutés simultanément. L'appel WebsService lui-même n'est pas asynch - il doit terminer la recherche avant de renvoyer le résultat à l'appelant. – Alan

+1

Je ne vois pas pourquoi vous ne pourriez pas faire fonctionner ces techniques. Si vous considérez que vos deux méthodes asynchrones sont une seule méthode asynchrone composée, vous devriez pouvoir utiliser http://msdn.microsoft.com/en-us/library/bb559019.aspx –

+0

Merci pour les liens John. Je prévois de rendre l'appel DoLookup asynchrone une fois qu'il l'appelle, il appelle ses méthodes internes de manière asynchrone. Je vais regarder les liens que vous avez fournis. – TLiebe

0

Je suppose que je n'ai pas suffisamment de rep pour upvote ni pour commenter. Voici donc un commentaire sur la réponse de John Saunders et le commentaire d'Alan à ce sujet.

Vous voulez certainement aller avec la réponse de John si vous êtes préoccupé par l'évolutivité et la consommation de ressources.

Il y a deux considérations ici: Accélérer une demande individuelle, et rendre votre système gérer de nombreuses demandes simultanées efficacement. Les réponses d'Alan et de John sont obtenues en effectuant les appels externes en parallèle. Ce dernier, et il semble que ce soit votre principale préoccupation, est atteint en ne bloquant aucun fil, c'est-à-dire la réponse de John.

Ne pas générer vos propres threads. Les threads sont chers, et il y a déjà beaucoup de threads dans le Threadpool IO qui gérera vos appels externes si vous utilisez les méthodes asynch fournies par le framework .net.

Votre webmethod doit également être asynch. Dans le cas contraire, un thread de travail sera bloqué jusqu'à ce que vos appels externes soient terminés (il reste encore 1-2 secondes même s'ils s'exécutent en parallèle). Et vous n'avez que 12 unités d'exécution par UC qui traitent les demandes entrantes (si votre machine.config est définie selon recommendation.) I.e. vous seriez au maximum capable de gérer 12 requêtes simultanées (multiplié par le nombre de processeurs). D'un autre côté, si votre méthode web est asynch, Begin retournera presque instantanément et le thread retourné au pool de threads de travail sera prêt à gérer une autre requête entrante, tandis que vos appels externes seront attendus par le port de complétion des E/S. être traité par les threads du pool de threads IO, une fois qu'ils reviennent.

+0

Merci Turnhose.Je prévois d'avoir un événement BeginLookup() et EndLookup() une fois que tout le reste fonctionnera. J'ai fait une version Begin/End Lookup() pour les appels séquentiels au service web et à la base de données distants - maintenant je dois juste faire une version asynchrone qui à son tour appelle les appels internes de manière asynchrone. – TLiebe

Questions connexes