Exporter (0) Imprimer
Développer tout

Méthodes Web asynchrones côté serveur

Matt Powell
Microsoft Corporation

Résumé : Matt Powell explique comment utiliser des méthodes Web asynchrones côté serveur afin de mettre en oeuvre des services Web Microsoft ASP.NET extrêmement performants.


Introduction

Dans mon article du 3 septembre (leave-msdn france Site en anglais), j'ai expliqué comment appeler des services Web en mode asynchrone sur HTTP en utilisant les fonctionnalités côté client de Microsoft® .NET Framework. Cette manière de procéder se révèle très utile pour appeler un service Web sans verrouiller l'application ni engendrer une multitude de threads en arrière-plan. Aujourd'hui, nous allons examiner les méthodes Web asynchrones qui offrent des fonctionnalités similaires côté serveur. Les méthodes Web asynchrones offrent des performances élevées semblables à celles qu'autorise la méthode HSE_STATUS_PENDING pour écrire des extensions ISAPI, sans qu'il soit nécessaire d'écrire du code pour piloter votre pool de threads, tout en bénéficiant des avantages de l'exécution de code géré.

Pour commencer, examinons les méthodes Web standard synchrones de Microsoft® ASP.NET. La réponse à une méthode Web synchrone est transmise lors du retour de la méthode. Si le temps d'exécution d'une requête est relativement long, la thread qui traite la requête sera en cours d'utilisation jusqu'à ce que l'appel aboutisse. Malheureusement, la plupart des appels de longue durée sont dus à des événements tels qu'une requête de base de données longue à traiter ou un appel à un autre service Web. Par exemple, si vous exécutez un appel à une base de données, la thread actuelle attend que cet appel soit terminé. La thread doit simplement attendre sans rien faire, jusqu'à ce qu'elle reçoive un signal de retour de la requête. Des situations semblables se produisent lorsqu'une thread attend l'exécution d'un appel adressé à un socket TCP ou à un service Web terminal.

Or, l'existence de threads en attente n'est pas souhaitable, notamment lorsque le serveur est soumis à une utilisation intensive. Les threads en attente ne sont guère productives, puisqu'elles ne répondent pas à d'autres requêtes. Nous devons en fait trouver un moyen de lancer un processus long d'arrière-plan sur le serveur tout en réintégrant la thread dans le pool de traitement ASP.NET. Puis, une fois le processus terminé, nous chercherons à exécuter une fonction de rappel (callback) afin de terminer le traitement de la requête et signaler son achèvement à ASP.NET. Actuellement, cette fonctionnalité est possible grâce à ASP.NET avec les méthodes Web asynchrones.

Fonctionnement des méthodes Web asynchrones

Lorsque vous écrivez un service Web ASP.NET standard en utilisant des méthodes Web, Microsoft® Visual Studio® .NET compile votre code afin de créer un assembly qui sera appelé à la réception de requêtes relatives à ses méthodes Web. L'assembly en lui-même ne sait rien du format SOAP. Par conséquent, lorsque vous lancez votre application pour la première fois, le gestionnaire ASMX doit se « refléter » sur l'assembly pour déterminer les méthodes Web qui sont exposées. Pour des requêtes standard synchrones, cela consiste à trouver les méthodes auxquelles est associé un attribut WebMethod et à créer la logique nécessaire pour appeler la bonne méthode sur la base de l'en-tête HTTP de SOAPAction.

Pour les requêtes asynchrones, pendant la réflexion, le gestionnaire ASMX recherche les méthodes Web qui ont un certain type de signature reconnue pour être asynchrone. Notamment, il cherche deux méthodes pour lesquelles s'appliquent les règles suivantes :

  • Il existe une méthode Web BeginXXX et EndXXX, où XXX est une chaîne représentant le nom de la méthode à exposer.
  • La fonction BeginXXX renvoie une interface IAsyncResult et accepte respectivement en paramètres d'entrée l'interface AsyncCallback et un objet.
  • La fonction EndXXX reçoit l'interface IAsyncResult comme unique paramètre.
  • L'une et l'autre doivent être marquées avec l'attribut WebMethod.

Si le gestionnaire ASMX trouve deux méthodes qui répondent à ces conditions, il expose la méthode XXX dans son WSDL comme s'il s'agissait d'une méthode Web standard. La méthode accepte en entrée les paramètres définis avant le paramètre AsyncCallback dans la signature de BeginXXX et renvoie le résultat de la fonction EndXXX. Ainsi, si nous avions une méthode Web dont la déclaration synchrone ressemble à ceci :

[WebMethod]
 public string LengthyProcedure(int milliseconds) {...}

la déclaration asynchrone serait :

[WebMethod]
 public IAsyncResult BeginLengthyProcedure(
    int milliseconds, 
    AsyncCallback cb, 
    object s) {...}

 [WebMethod]
 public string EndLengthyProcedure(IAsyncResult call) {...}

La forme WSDL correspondante serait identique.

Lorsque le gestionnaire ASMX se « reflète » sur un assembly et détecte une méthode Web asynchrone, il doit gérer les requêtes pour cette méthode différemment des requêtes synchrones. Au lieu d'appeler une méthode simple, il appelle BeginXXX. Ainsi, il désérialise la requête entrante en paramètres passés à la fonction (comme c'est le cas pour des requêtes synchrones) et transmet également le pointeur vers une fonction de rappel interne comme paramètre supplémentaire AsyncCallback pour la méthode BeginXXX.

Cette façon de procéder est similaire au modèle de programmation asynchrone de .NET Framework pour les applications clientes de service Web. En cas d'appels de service Web asynchrones côté client, vous libérez les threads bloquées qui deviennent utilisables par la machine cliente, tout en libérant, côté serveur, les threads bloquées sur la machine serveur. Toutefois, deux différences sont à noter. Tout d'abord, les fonctions BeginXXX et EndXXX ne sont pas appelées par votre code serveur, mais par le gestionnaire ASMX. Ensuite, il vous appartient d'écrire le code pour les fonctions BeginXXX et EndXXX au lieu d'utiliser le code généré par WSDL.EXE ou par l'assistant « Ajouter une référence Web » de Visual Studio .NET. Pourtant, le résultat, à savoir la libération de threads afin qu'elles effectuent d'autres opérations, est le même.

Une fois que le gestionnaire ASMX a appelé la fonction BeginXXX du serveur, il renvoie la thread dans le pool des threads de traitement afin de pouvoir traiter d'autres requêtes. Le contexte HTTP (HttpContext) pour la requête n'est pas encore libéré. Pour terminer de traiter la requête, le gestionnaire ASMX attend que la fonction de rappel transmise à la fonction BeginXXX soit appelée.

Une fois celle-ci appelée, le gestionnaire ASMX appelle la fonction EndXXX afin que votre méthode Web puisse terminer les traitements requis et que les données renvoyées soient disponibles pour être sérialisées en réponse SOAP. Ce n'est qu'au moment où la réponse est envoyée, après le retour de la fonction EndXXX, que l'instance HttpContext pour la requête est libérée.

Une méthode Web asynchrone simple

Comme exemple de méthode Web asynchrone, je commencerai par une méthode Web synchrone simple appelée LengthyProcedure, dont le code est fourni ci-après. Nous étudierons ensuite comment faire de même d'une manière asynchrone. LengthyProcedure se bloque pendant le nombre de millisecondes spécifié.

[WebService]
public class SyncWebService : System.Web.Services.WebService
{
 [WebMethod]
 public string LengthyProcedure(int milliseconds) 
 { 
 System.Threading.Thread.Sleep(milliseconds);
 return "Réussite"; 
 }
}

Nous allons ensuite convertir LengthyProcedure en méthode Web asynchrone. Nous devons créer une fonction BeginLengthyProcedure ainsi qu'une fonction EndLengthyProcedure, comme indiqué plus haut. N'oubliez pas que notre appel à BeginLengthyProcedure va devoir renvoyer une interface IAsyncResult. Dans ce cas, je vais faire en sorte que l'appel à BeginLengthyProcedure invoque une méthode asynchrone en utilisant un délégué et que la méthode BeginInvoke soit appelée sur ce délégué. La fonction de rappel passée à BeginLengthyProcedure est transmise à la méthode BeginInvoke sur notre délégué et l'interface IAsyncResult renvoyée par BeginInvoke est renvoyée par la méthode BeginLengthyProcedure.

La méthode EndLengthyProcedure est appelée une fois que le délégué est terminé. Nous appelons la méthode EndInvoke sur le délégué en passant l'interface IAsyncResult que nous avons reçue en entrée à l'appel de EndLengthyProcedure. La chaîne résultante est la chaîne renvoyée par notre méthode Web. Voici le code :

[WebService]
public class AsyncWebService : System.Web.Services.WebService
{
 public delegate string LengthyProcedureAsyncStub(
 int milliseconds);

 public string LengthyProcedure(int milliseconds) 
 { 
 System.Threading.Thread.Sleep(milliseconds);
 return "Réussite"; 
 }

 public class MyState 
 { 
 public object previousState; 
 public LengthyProcedureAsyncStub asyncStub; 
 }

 [ System.Web.Services.WebMethod ]
 public IAsyncResult BeginLengthyProcedure(int milliseconds, 
 AsyncCallback cb, object s)
 {
 LengthyProcedureAsyncStub stub 
  = new LengthyProcedureAsyncStub(LengthyProcedure);
 MyState ms = new MyState();
 ms.previousState = s; 
 ms.asyncStub = stub;
 return stub.BeginInvoke(milliseconds, cb, ms);
 }
 
 [ System.Web.Services.WebMethod ]
 public string EndLengthyProcedure(IAsyncResult call)
 {
 MyState ms = (MyState)call.AsyncState;
 return ms.asyncStub.EndInvoke(call);
 }
}

Quand utiliser les méthodes Web asynchrones ?

Avant de déterminer s'il est utile de recourir à des méthodes Web asynchrones dans une application, vous devez examiner plusieurs points. Premièrement, la fonction BeginXXX concernant votre appel doit renvoyer une interface IAsyncResult. Un certain nombre d'opérations d'E/S asynchrones renvoient des interfaces IAsyncResult pour accéder à des flux de données, exécuter des appels de type Microsoft® Windows® Sockets, effectuer des E/S de fichier, interagir avec d'autres dispositifs matériels, appeler des méthodes asynchrones et, naturellement, appeler d'autres services Web. Le plus souvent, vous cherchez, par le biais d'une de ces opérations, à obtenir l'interface IAsyncResult afin de pouvoir la renvoyer à partir de votre fonction BeginXXX . L'autre possibilité est de créer votre propre classe qui implémente l'interface IAsyncResult, mais dans ce cas, vous intégrerez probablement une des implémentations E/S mentionnées plus haut.

Pour quasiment toutes les opérations asynchrones citées, l'utilisation de méthodes Web asynchrones en vue d'intégrer l'appel asynchrone en arrière-guichet se justifie et améliore l'efficacité du code des services Web. Une exception toutefois est à relever concernant les appels de méthodes asynchrones avec des délégués. En effet, les délégués entraînent l'exécution de ce type d'appels sur une thread du pool de threads de traitement. Malheureusement, ces mêmes threads sont utilisées par le gestionnaire ASMX pour traiter les requêtes entrantes. Ainsi, contrairement aux appels qui exécutent des opérations d'E/S réelles sur des ressources matérielles ou de réseau, un appel de méthode asynchrone utilisant des délégués va bloquer une des threads de processus pendant la durée de son exécution. Vous pourriez tout aussi bien bloquer la thread d'origine et exécuter votre méthode Web en mode synchrone.

L'exemple suivant concerne une méthode Web asynchrone qui appelle un service Web d'arrière-guichet. Les méthodes BeginGetAge et EndGetAge sont marquées avec l'attribut WebMethod afin de s'exécuter en mode asynchrone. Le code de cette méthode Web asynchrone appelle la méthode Web d'arrière-guichet UserInfoQuery afin d'obtenir les informations à renvoyer. L'appel à UserInfoQuery est exécuté de manière asynchrone et passé à la fonction AsyncCallback, qui elle-même a été transmise à la méthode BeginGetAge. Ceci permet que la fonction de rappel interne soit appelée lorsque la requête d'arrière-guichet est exécutée. La fonction de rappel appelle ensuite notre méthode EndGetAge pour terminer la requête. Dans ce cas, le code est bien plus simple que dans le précédent exemple et il présente l'avantage supplémentaire de ne pas lancer le traitement d'arrière-guichet dans le même pool de threads de traitement que celui qui traite nos requêtes de méthodes Web de niveau intermédiaire.

[WebService]
public class GetMyInfo : System.Web.Services.WebService
{
 [WebMethod]
 public IAsyncResult BeginGetAge(AsyncCallback cb, Object state)
 {
 // Invoque un appel de service Web asynchrone.
 localhost.UserInfoQuery proxy 
  = new localhost.UserInfoQuery();
 return proxy.BeginGetUserInfo("Nom de l'utilisateur", 
     cb, 
     proxy);
 }

 [WebMethod]
 public int EndGetAge(IAsyncResult res)
 {
 localhost.UserInfoQuery proxy 
  = (localhost.UserInfoQuery)res.AsyncState;
 int age = proxy.EndGetUserInfo(res).age;
 // Exécution d'autres traitements sur les résultats 
 // renvoyés par le service Web.
 return age;
 }
}

Un des types les plus courants d'opérations d'E/S réalisées dans une méthode Web est l'appel à une base de données SQL. Malheureusement, Microsoft® ADO.NET ne propose pas actuellement de mécanisme d'appel asynchrone satisfaisant et le fait d'intégrer un appel SQL dans un appel asynchrone à un délégué n'améliore pas l'efficacité générale. La mise en cache des résultats constitue parfois une solution, mais vous devez également envisager d'utiliser le Microsoft SQL Server 2000 Web Services Toolkit (leave-msdn france Site en anglais) pour exposer vos bases de données comme service Web. Vous pourrez alors utiliser le support dans .NET Framework pour appeler des services Web de façon asynchrone et interroger ou mettre à jour vos bases de données.

L'accès à SQL via un appel de service Web doit être sérieusement envisagé pour bon nombre de vos ressources d'arrière-guichet. Si vous utilisez des sockets TCP pour communiquer avec une machine Unix ou si vous accédez à d'autres plateformes SQL existantes via des pilotes de bases de données propriétaires, ou même si vous avez une ressource à laquelle vous accédez à l'aide du modèle DCOM, vous pouvez envisager d'utiliser les nombreux kits d'outils aujourd'hui commercialisés afin d'exposer les ressources comme services Web.

Un des avantages de cette solution est que vous bénéficiez des progrès réalisés dans l'infrastructure des services Web côté client, tels que les appels de service Web asynchrones avec .NET Framework. Ainsi, vous avez la possibilité d'effectuer des appels asynchrones gratuitement et votre mécanisme d'accès client fonctionnera tout aussi efficacement avec des méthodes Web asynchrones.

Agrégation de données avec une méthode Web asynchrone

Actuellement, de nombreux services Web accèdent à de multiples ressources en arrière-guichet et agrègent les informations destinées au service Web frontal. Même si l'appel à de multiples ressources d'arrière-guichet complique le modèle des méthodes Web asynchrones, les avantages restent nombreux.

Supposons que votre méthode Web appelle deux services Web d'arrière-guichet, Service A et Service B. À partir de votre fonction BeginXXX, vous pouvez appeler le Service A et le Service B de façon asynchrone. Vous devez transmettre à chacun de ces appels asynchrones votre propre fonction de rappel. En vue de déclencher la conclusion de la méthode Web après réception des résultats des deux services, A et B, la fonction de rappel vérifie que les deux requêtes sont terminées, traite éventuellement les données renvoyées puis appelle la fonction de rappel transmise à votre fonction BeginXXX. Ceci déclenche l'appel à votre fonction EndXXX, et, lors du retour de cette dernière, la méthode Web est menée à son terme.

Conclusion

Les méthodes Web asynchrones offrent une solution efficace au sein des services Web ASP.NET pour exécuter des appels à des services d'arrière-guichet sans bloquer les précieuses threads du pool de threads de traitement. Associées à des requêtes asynchrones sur des ressources d'arrière-guichet, elles permettent au serveur d'optimiser le nombre de requêtes simultanées qu'il traite via ses méthodes Web. N'hésitez pas à étudier cette solution pour développer des applications de services Web aux performances élevées.

 

À votre service

Matt Powell est membre de l'équipe des exemples d'architecture MSDN, au sein de laquelle il a contribué au développement du très innovant SOAP Toolkit 1.0. Par ailleurs, Matt Powell a participé à la rédaction de l'ouvrage Running Microsoft Internet Information Server (en anglais) édité chez Microsoft Press ainsi que de nombreux articles de presse, et il se consacre chaque jour à sa très belle famille.



Dernière mise à jour le jeudi 5 décembre 2002



Pour en savoir plus
Afficher:
© 2014 Microsoft