Exporter (0) Imprimer
Développer tout

Appels de services Web asynchrones sur HTTP avec .NET Framework

Matt Powell
Microsoft Corporation

Résumé : Matt Powell énumère les différentes options fournies par Microsoft .NET Framework pour appeler un service Web de manière asynchrone sur HTTP. Cette technique optimise les appels efficaces au service Web sans bloquer les applications en cas de trafic réseau important.

Lors de l'écriture d'applications qui utilisent des services Web, vous devez obligatoirement prendre en compte la période d'attente imposée lors d'appels sur un réseau. Dans certains cas, en particulier sur un réseau privé disposant d'une bande passante conséquente, les appels peuvent aboutir en moins d'une demi-seconde et l'attente n'est pas très longue. Cependant, si vous envoyez des requêtes à distance via Internet ou si vous effectuez des appels nécessitant un temps de traitement important, vous devez alors commencer à réfléchir sur l'impact de ces délais sur votre application. Les applications Microsoft® Windows® Forms, par exemple, peuvent sembler bloquées en cas d'attente de réponse d'un appel vers un service Web. Si vous appelez un service Web depuis une page ASP.NET Microsoft®, plusieurs appels au service Web peuvent ralentir considérablement l'affichage de vos pages. Si votre application effectue de nombreux appels au service Web, il est alors important de réfléchir au moyen de les effectuer le plus efficacement possible.

La solution à un grand nombre de ces problèmes est d'interroger votre service Web de manière asynchrone. Les appels asynchrones génèrent une réponse immédiate, puis utilisent un autre mécanisme pour indiquer la fin de l'appel. Ceci permet à votre application d'effectuer toutes les tâches d'arrière-plan requises, telles que les dialogues avec l'interface, les informations sur l'état courant de la requête ou même le lancement d'autres appels au service Web. Nous étudierons la méthode de prise en charge par Microsoft® .NET Framework des appels au service Web sur HTTP et la manière dont nous devons les utiliser dans deux ou trois scénarios classiques.

Les blocs de construction

Lorsque vous choisissez l'option Ajouter une référence Web dans Microsoft Visual Studio® .NET, une classe est créée héritant de System.Web.Services.Protocols.SoapHttpClientProtocol. La classe SoapHttpClientProtocol dispose d'une fonction protégée appelée Invoke qui est en fait utilisée lorsque vous effectuez un appel vers l'une des méthodes exposées par un service Web. Pour chaque méthode Web définie dans l'utilitaire WSDL du service Web, l'assistant crée une fonction avec les paramètres de nom et les valeurs renvoyées appropriés. Chacune de ces fonctions appelle alors la fonction Invoke sur la classe SoapHttpClientProtocol en passant les informations au paramètre et ainsi de suite. Pour cet article, j'ai créé un service Web avec une méthode dont le résultat peut s'avérer résolument plus long. Cette méthode Web s'appelle DelayedResponse. Elle nécessite un entier comme unique paramètre et renvoie une chaîne. L'option Ajouter une référence Web génère le code proxy suivant pour cette méthode :

Public Function DelayedResponse(ByVal waitPeriod As Integer) _
 As String
 Dim results() As Object _
 = Me.Invoke("DelayedResponse", _
 New Object() {waitPeriod})
 Return CType(results(0),String)
End Function

La méthode Invoke requiert deux paramètres : le nom de la fonction et un tableau d'objets qui conserve les paramètres à passer à la fonction. La méthode Invoke renvoie un tableau d'objets, qui, dans notre cas, contient seulement un élément—la chaîne renvoyée par notre fonction. Il s'agit du mécanisme permettant d'effectuer des appels synchrones qui attendent une réponse avant de renvoyer des éléments.

La classe SoapHttpClientProtocol dispose également d'une méthode appelée BeginInvoke, qui est le mécanisme utilisé pour lancer une requête asynchrone. La classe créée par l'option Ajouter une référence Web crée également une fonction publique appelée BeginDelayedResponse qui accompagne la fonction de blocage DelayedResponse étudiée précédemment. Le code de BeginDelayedResponse est affiché ci-dessous.

Public Function BeginDelayedResponse( _
 ByVal waitPeriod As Integer, _
 ByVal callback As System.AsyncCallback, _
 ByVal asyncState As Object) As System.IAsyncResult
 Return Me.BeginInvoke("DelayedResponse", _
 New Object() {waitPeriod}, _
 callback, _
 asyncState)
End Function

BeginDelayedResponse utilise la méthode BeginInvoke qui est similaire à la méthode Invoke utilisée précédemment. Les deux premiers paramètres sont identiques à ceux utilisés pour la méthode Invoke. Cependant, il existe deux autres paramètres pour BeginInvoke et elle ne renvoie plus de tableau d'objets. La différence essentielle, cependant, est que la méthode BeginInvoke permet une réponse immédiate et n'attend pas l'aboutissement de l'appel au service Web.

Si nous observons les deux premiers paramètres ajoutés à BeginInvoke, nous remarquons un élément appelé System.AsyncCallback. C'est ce que l'on appelle un délégué, qui est un mécanisme de base permettant de déclarer un pointeur fonction en tant que code managé. Dans ce cas, la fonction est appelée une fois que l'appel de la méthode Web aboutit et que nous avons reçu la réponse.

Le dernier paramètre BeginInvoke est appelé asyncState et est simplement déclaré comme un type d'objet. Il peut s'agir de tout élément utilisé pour suivre cette requête. Vous pouvez recourir à la même fonction de rappel pour de nombreuses requêtes asynchrones différentes, afin de distinguer la réponse à un appel de la réponse à un autre appel. Vous classez alors les informations concernant l'appel dans le paramètre asyncState et elles seront à votre disposition dans votre fonction de rappel.

La méthode BeginInvoke se différencie par le résultat obtenu. Évidemment, si l'appel n'est pas terminé, les données de réponse ne peuvent pas être renvoyées à la méthode Web. C'est un pointeur d'interface System.IAsyncResult qui est renvoyé. Vous pouvez utiliser le pointeur d'interface IAsyncResult pour obtenir des informations sur la requête. IAsyncResult expose quatre propriétés publiques répertoriées ci-dessous :

PropriétéDescription
AsyncState Il s'agit simplement des données passées dans le quatrième paramètre de la méthode BeginInvoke.
AsyncWaitHandle Un objet WaitHandle qui peut être utilisé pour bloquer l'exécution en cours du thread jusqu'à l'aboutissement d'un ou de plusieurs appels au service Web.
CompletedSynchronously Ce paramètre ne s'applique pas aux appels au service Web. L'interface IAsyncResult est utilisée pour diverses opérations d'E/S et cette propriété vous permet de savoir si la requête d'opération d'E/S asynchrone a été si rapide qu'elle s'est terminée avant même le renvoi de la fonction Begin.
IsCompleted Il s'agit simplement d'un indicateur que vous pouvez utiliser pour déterminer si l'appel est terminé ou non.

Le pointeur IAsyncResult est ce qui, en fin de compte, vous permet et permet au système de différencier une fin asynchrone d'une autre. Il vous fournit également les différentes options permettant de déterminer la fin d'un appel. Nous étudierons bientôt comment utiliser ces options.

Comme indiqué précédemment, l'option Ajouter une référence Web crée une fonction pour chaque méthode Web fournie par votre service utilisant BeginInvoke. Dans notre cas, la fonction générée est appelée BeginDelayedResponse. Il s'agit d'un wrapper simplifié de la méthode BeginInvoke qui expose les paramètres de l'appel de la méthode Web, ainsi que la fonction de rappel et les paramètres asyncState fournis par BeginInvoke. Nous étudierons ultérieurement les trois techniques à notre disposition pour effectuer une requête asynchrone et savoir lorsque celle-ci est terminée.

Trois options pour effectuer des appels asynchrones

Chaque application est différente et certains scénarios qui permettent d'effectuer des appels asynchrones fonctionnent pour certaines applications et pas pour d'autres. Le .NET Framework apporte beaucoup de souplesse lors de la mise en place d'appels asynchrones. Vous pouvez vérifier le résultat d'une requête, bloquer une exécution sur l'objet WaitHandle ou attendre la fonction de rappel. Observons de plus près chacune de ces approches.

Interrogation de fin de requête

L'interface IAsyncResult renvoyée par notre fonction BeginDelayedResponse dispose d'une propriété IsCompleted qui peut être vérifiée pour déterminer la fin ou non de la requête. Vous pouvez alors lancer des interrogations jusqu'au retour d'une valeur True. Le code utilisé pour cette approche est indiqué ci-dessous :

' Interrogation du code qui peut immobiliser votre processeur
Dim proxy as New localhost.Service1()
Dim result as IAsyncResult
Result = proxy.BeginDelayedResponse(2000, _
  Nothing, _
  Nothing)
While (result.IsCompleted = False)
 ' Effectuer un traitement
 ...
Wend
Dim response as String
response = proxy.EndDelayedResponse(result)

Cette méthode qui permet de vérifier si l'appel a abouti est une approche plutôt simple, mais elle entraîne quelques inconvénients. Si on étudie le code, nous notons d'abord l'appel initial vers le proxy BeginDelayedResponse, avec 2000 comme paramètre à passer à la méthode Web, puis le rappel et la propriété AsyncState définis sur Nothing. Nous avons ensuite une boucle 'while' qui interroge la propriété IsCompleted jusqu'à obtenir la valeur True. À la fin de l'appel et lorsque la propriété IsCompleted est réglée sur True, nous sortons de la boucle 'while' et nous obtenons la réponse à l'aide d'une autre fonction appelée EndDelayedResponse qui correspond à une classe générée par l'option Ajouter une référence Web. EndDelayedResponse correspond au wrapper de la méthode EndInvoke de la classe SoapHttpClientProtocol. Il s'agit du mécanisme qui permet de récupérer les données renvoyées de l'appel de la méthode Web. Ce wrapper doit être utilisé lorsque vous savez que l'appel au service Web est terminé ; il renvoie simplement les mêmes informations que la méthode Invoke pour les appels de blocage. Nous utiliserons la méthode EndDelayedResponse dans les trois scénarios asynchrones pour obtenir les résultats de notre appel au service Web. Il est à noter que si EndDelayedResponse est appelé avant la fin de la requête, il est simplement bloqué jusqu'à la fin de la requête.

Un des problèmes à surveiller si vous recourez à cette méthode d'interrogation pour déterminer si votre appel est terminé est que vous risquez d'utiliser un grand nombre de cycles processeur de votre ordinateur si vous n'êtes pas attentif. Par exemple, si notre boucle 'while' ne comporte aucun code, le thread d'exécution peut alors absorber la plupart des ressources de traitement de votre machine. Il peut s'avérer finalement si gourmand en temps de traitement que le code d'envoi de la requête au service Web et de réception des données risque d'être ralenti. Il est donc important d'utiliser cette méthode d'interrogation avec précaution.

Si vous souhaitez réellement attendre la fin de la requête au service Web, vous devrez probablement utiliser l'approche WaitHandle développée dans la suite du document. Cependant, si le traitement est important et si vous souhaitez seulement vérifier de temps en temps que l'appel au service Web est terminé, l'interrogation n'est pas, dans ce cas, une mauvaise solution. Les applications utilisent souvent une combinaison d'interrogations avec l'une des autres approches asynchrones. Par exemple, vous pouvez choisir d'effectuer des appels au service Web de manière asynchrone car vous avez un traitement en arrière-plan à effectuer, mais une fois que vous avez terminé, vous pouvez souhaiter seulement bloquer l'exécution jusqu'à la fin du service Web. Dans ce cas, vous pouvez lancer des interrogations ponctuelles tout en effectuant le traitement, puis recourir à un WaitHandle pour attendre le résultat une fois que le traitement en arrière-plan est terminé.

Utilisation des WaitHandles

Les objets WaitHandle sont pratiques pour des scénarios dans lesquels vous devez effectuer des appels asynchrones, mais pour lesquels vous ne souhaitez pas libérer le thread en cours d'exécution. Par exemple, si vous interrogez un service Web asynchrone depuis une application ASP.NET et que vous quittez l'événement en cours de traitement dans ASP.NET, vous risquez de ne pas pouvoir inclure les données en provenance de l'appel Web dans les informations renvoyées à l'utilisateur. Les objets WaitHandle permettent d'effectuer un traitement après interrogation du service Web, puis de bloquer l'exécution jusqu'à la fin de l'appel. Cette méthode est particulièrement utile en cas de nombreux appels au service Web à partir d'une page ASP.NET.

L'accès à un objet WaitHandle est fourni par la variable IAsyncResult renvoyée par la fonction BeginDelayedResponse. Le code ci-dessous indique un scénario simple utilisant l'approche WaitHandle.

' Code WaitHandle simple 
Dim proxy As New localhost.Service1()
Dim result As IAsyncResult
result = proxy.BeginDelayedResponse(2000, Nothing, Nothing)
' Effectuer un traitement.
' ...
' Traitement effectué. Attendre la fin. 
result.AsyncWaitHandle.WaitOne()
Dim response As String
response = proxy.EndDelayedResponse(result)

Dans le code ci-dessus, nous avons utilisé la méthode WaitOne de l'objet WaitHandle pour attendre ce handle précis. Il existe également des méthodes statiques appelées WaitAll et WaitAny dans la classe WaitHandle. Ces deux méthodes statiques utilisent des tableaux de WaitHandle comme paramètres et renvoient les informations lorsque tous les appels sont terminés ou dès que l'un des appels est terminé, selon la fonction appelée. Dans le cas où vous appelez trois services Web séparés, vous pouvez appeler chacun de ces services de manière asynchrone, placez le WaitHandle correspondant à chacun des services dans un tableau, puis appelez la méthode WaitAll jusqu'à la fin des appels. Les appels aux services Web peuvent alors s'exécuter en même temps. Si vous faites ceci de manière synchrone, vous ne pourrez pas les lancer en parallèle, ce qui aura pour résultat d'approximativement tripler le temps d'exécution.

Il est à noter que les méthodes WaitOne, WaitAll et WaitAny acceptent toutes l'ajout d'un paramètre de délai d'attente. Ceci vous permet d'imposer un délai de réponse à rappel précis. Si le délai d'attente de ces méthodes expire, une valeur False est renvoyée. Ceci vous permet de traiter d'autres données avant de relancer l'appel et de devoir attendre, ou bien cela vous offre l'opportunité d'annuler les requêtes.

Utilisation des rappels

La troisième approche pour interroger un service Web de manière asynchrone est d'utiliser les rappels. L'utilisation de rappels peut être très efficace en cas d'appels au service Web simultanés. Cette approche se prête également bien au traitement en arrière-plan des résultats des appels au service Web. Cependant, cette approche est plus complexe que les deux autres méthodes. L'utilisation de rappels dans une application Microsoft Windows® est une approche particulièrement intéressante, car elle évite le blocage du thread de messages dans votre fenêtre.

Le résultat est simplement que votre fonction de rappel est exécutée à la fin de l'appel au service Web. Cependant, la fonction de rappel peut ne pas être appelée dans le cas du thread qui a initié l'appel BeginInvoke d'origine. Ceci peut provoquer des problèmes d'envoi de commandes aux éléments de contrôle dans une application Windows Form, car ces commandes doivent être appelées depuis le thread spécifique qui gère le traitement des messages pour cette fenêtre particulière. Il existe heureusement une méthode pour résoudre ce problème.

Le code ci-dessous indique un scénario simple où un rappel est utilisé et où les résultats sont affichés dans un contrôle label.

Dim proxy as localhost.Service1
Private Delegate Sub MyDelegate(ByVal response As String)

Private Sub Button1_Click(ByVal sender As System.Object, _
 ByVal e As System.EventArgs) Handles Button1.Click
 proxy = New localhost.Service1()
 proxy.BeginDelayedResponse(2000, _
 New AsyncCallback(AddressOf Me.ServiceCallback), _
 Nothing)
End Sub

Private Sub ServiceCallback(ByVal result As IAsyncResult)
 Dim response As String
 response = proxy.EndDelayedResponse(result)
 Label1.Invoke( _
 New MyDelegate(AddressOf Me.DisplayResponse), _
 New Object() {response})
End Sub

Private Sub DisplayResponse(ByVal response As String)
 Label1.Text = response
End Sub

Dans ce code, nous appelons BeginDelayedResponse dans l'événement Button1_Click, puis formons la sous-routine. Notez que, dans ce cas, plutôt que d'envoyer Nothing comme deuxième paramètre, nous passons un objet AsyncCallback. Fondamentalement, il s'agit seulement d'une manière d'effectuer une boucle sur l'adresse de notre fonction de rappel, qui est appelée ServiceCallback. La fonction ServiceCallback doit être une sous-routine avec un seul paramètre de type IAsyncResult. La classe SoapHttpClientProtocol appelle cette fonction et fournit le pointeur d'interface IAsyncResult correspondant à l'appel au service Web terminé. Nous utilisons ensuite à nouveau la méthode EndDelayedResponse pour obtenir les résultats, mais à ce moment un léger problème surgit.

Étant donné que notre rappel ne sera probablement pas dans le thread principal de notre fenêtre, nous devons utiliser une autre méthode Invoke pour pouvoir définir le texte pour un label dans notre fenêtre. Tous les contrôles disposent d'une méthode Invoke que vous pouvez utiliser afin d'appeler une fonction qui sera exécutée dans leur thread de message principal. L'utilisation de Invoke sur le contrôle label équivaut aux autres méthodes étudiées précédemment. Nous devons fournir à Invoke l'adresse de la fonction à appeler en incluant tous les paramètres à passer vers cette fonction dans un tableau d'objets qui sert de deuxième paramètre. Nous devons donc déclarer un type de délégué que nous appelons MyDelegate afin d'informer la méthode Invoke de la syntaxe de la fonction à appeler. Dans ce cas, la fonction est appelée DisplayResponse et ne dispose que d'un seul paramètre—la chaîne renvoyée de l'appel au service Web. Cette fonction DisplayResponse est appelée dans le propre thread du contrôle label1 et le paramétrage du texte du contrôle de votre application se fait sans aucun problème.

Erreurs potentielles

Jusqu'ici, les exemples étudiés étaient plutôt simples. Il a été supposé un grand nombre d'hypothèses, telles que l'aboutissement de nos appels au service Web et, de plus, ces exemples sont simplifiés car un seul appel est effectué à la fois. Nous allons maintenant étudier un scénario incluant plusieurs appels au service Web. Nous verrons comment gérer les erreurs pouvant être générées par ces appels et nous étudierons la méthode d'annulation des appels, qui peut s'avérer très utile.

Utilisation du paramètre asyncState

Étudions à présent un exemple où nous ajoutons à Nothing un élément, tel que le dernier paramètre de notre appel BeginDelayedResponse. Si vous vous souvenez, il s'agit du paramètre asyncState qui est simplement déclaré en tant qu'objet. Nous utilisons désormais ce paramètre pour nous permettre d'effectuer plusieurs appels au service Web et d'associer les réponses aux requêtes correspondantes.

Le scénario qui suit est un scénario où je veux effectuer trois appels différents vers la méthode Web DelayedResponse. Je souhaite ensuite afficher les résultats de ces appels dans trois différents contrôles labels de mon application Windows. Les résultats de mon premier appel devront s'afficher dans le premier label, les résultats de mon deuxième appel devront s'afficher dans mon deuxième label et les résultats de mon troisième appel devront s'afficher dans mon troisième label. Je vais utiliser la même fonction de rappel pour chaque appel au service Web et, afin de déterminer quel label correspond à quel appel, je passerai l'objet du label dans le paramètre asyncState. Pour compliquer le tout, j'ai défini au hasard la durée de chaque appel. Le code à utiliser pour cette opération est affiché ci-dessous.

Dim proxy As localhost.Service1
Private Delegate Sub LabelDelegate( _
 ByVal responseLabel As Label, _
 ByVal response As String)

Private Sub Button1_Click(ByVal sender As System.Object, _
 ByVal e As System.EventArgs) Handles Button7.Click
 proxy = New localhost.Service1()
 ClearForm()
 Dim r As New Random()

 proxy.BeginDelayedResponse(r.Next(1, 10000), _
 New AsyncCallback(AddressOf Me.ServiceCallback), _
 Label1)
 proxy.BeginDelayedResponse(r.Next(1, 10000), _
 New AsyncCallback(AddressOf Me.ServiceCallback), _
 Label2)
 proxy.BeginDelayedResponse(r.Next(1, 10000), _
 New AsyncCallback(AddressOf Me.ServiceCallback), _
 Label3)
End Sub

Private Sub ServiceCallback(ByVal result As IAsyncResult)
 Dim response As String
 response = proxy.EndDelayedResponse(result)
 Dim responseLabel As Label = result.AsyncState
 responseLabel.Invoke( _
 New LabelDelegate(AddressOf Me.DisplayResponses), _
 New Object() {responseLabel, response})
End Sub

Private Sub DisplayResponses(ByVal responseLabel As Label, _
 ByVal response As String)
 responseLabel.Text = response
 Form1.ActiveForm.Refresh()
End Sub

L'objet label passé dans le paramètre asyncState est disponible dans notre fonction de rappel via le paramètre IAsyncResult passé dans le rappel. Nous avons également modifié le paramètre du délégué de l'exemple précédent afin d'ajouter un autre paramètre. Ce paramètre supplémentaire correspond encore au label, de sorte que le délégué repère le contrôle pour lequel le texte doit être défini.

Vous aurez souvent besoin de passer plus d'informations qu'un seul objet via asyncState. Étant donné que asyncState est déclaré uniquement en tant qu'objet, il peut aussi être utilisé pour passer des données complexes, telles qu'un tableau d'objets ou une structure plus compliquée que vous souhaiteriez utiliser. Dans notre cas, nous n'avons utilisé qu'un objet proxy mais, dans certains scénarios, vous pouvez avoir un objet proxy différent pour chaque appel au service Web. Dans ce cas, vous risquez alors de vouloir également inclure l'objet proxy dans les données asyncState. Toute autre donnée spécifique à votre appel au service Web peut aussi être un candidat probable pour l'inclusion.

Interception des erreurs

Si vous avez l'habitude d'interroger les services Web de manière synchrone, vous avez alors probablement l'habitude d'intercepter des erreurs par l'intermédiaire d'un bloc catch de l'instruction try qui enveloppe votre appel distant. Ce type d'opération sur des appels asynchrones peut sembler curieux. Est-ce que je devrais inclure la fonction BeginDelayedResponse ou EndDelayedResponse dans un bloc catch de l'instruction try ? Peut-être même, serait-il nécessaire de les inclure toutes les deux !

En fait, pour des erreurs SOAP normales et pour d'autres erreurs liées au transport, seul l'appel EndDelayedResponse doit être inclus dans un bloc catch de l'instruction try. Il faut comprendre que l'appel BeginDelayedResponse ne va pas générer d'erreur car il est immédiatement renvoyé, et cela avant l'apparition de problèmes. Les erreurs ne vont pas se produire au hasard alors que vous êtes en train d'attendre des rappels ou le déblocage des appels en attente. Ce qui se produit plutôt, c'est que lorsqu'une erreur survient, cela provoque la fin de l'appel quel que soit le mécanisme utilisé, c'est-à-dire le paramétrage de la propriété IsCompleted sur True, le déclenchement d'un WaitHandle ou l'exécution d'un rappel. Ce n'est que lorsque vous effectuez l'appel vers EndDelayedResponse qu'une erreur est déclenchée et vous informe des raisons de la défaillance.

Pour prendre en compte l'éventualité d'un dysfonctionnement dans mon application, j'ai ajouté un bloc catch de type instruction try à ma fonction de rappel qui entoure la fonctionEndDelayedResponse. J'ai ensuite modifié le texte de la réponse selon l'échec.

Private Sub ServiceCallback(ByVal result As IAsyncResult)
 Dim response As String
 Try
 response = proxy.EndDelayedResponse(result)
 Catch e As Exception
 Response = "Échec"
 End Try
 Dim responseLabel As Label = result.AsyncState
 responseLabel.Invoke( _
 New LabelDelegate(AddressOf Me.DisplayResponses), _
 New Object() {responseLabel, response})
End Sub

Abandon de requêtes

L'un des avantages de l'utilisation des requêtes asynchrones dans une application est que vous n'avez pas à verrouiller votre interface utilisateur à la fin de l'appel. Évidemment, si vous permettez aux utilisateurs de votre application de continuer à interagir avec celle-ci, une fonctionnalité très courante que vous risquez alors de vouloir inclure est une touche d'annulation des requêtes en cours. Si, pour une raison ou une autre, l'appel au service Web est long à aboutir, c'est une bonne idée d'inclure une option qui permettra à l'utilisateur de déterminer le temps d'attente souhaité.

L'abandon d'une requête est simple à réaliser. Utilisez la méthode Abort disponible sur la classe proxy créée lorsque vous avez utilisé l'option Ajouter une référence Web. Vous devriez garder à l'esprit deux ou trois choses concernant l'abandon des requêtes. Lorsque vous appelez la méthode Abort, toute requête en cours continuera à s'exécuter jusqu'au bout, mais comportera une erreur. Ceci signifie que si vous recourez aux rappels, votre fonction sera toujours appelée pour chaque requête en cours. En cas d'appel de la méthode EndInvoke, ou dans notre cas, de la fonction wrapper EndDelayedResponse, une erreur est alors générée et indique que la connexion sous-jacente a été interrompue.

Génération dynamique de threads pour effectuer des appels synchrones

Une autre possibilité existe pour résoudre un grand nombre de problèmes liés aux appels asynchrones. Il s'agit simplement de générer un thread qui puisse effectuer des appels synchrones à codage simplifié. Cela peut s'avérer une bonne idée pour certains scénarios, mais vous devriez prendre en compte deux ou trois points liés à cette approche.

Tout d'abord, cette approche devrait simplifier votre code d'appel au service Web, mais il est probable que vous aurez à mettre en place une logique au moins aussi compliquée qu'une partie du code utilisé pour gérer vos threads et communiquer entre eux. De plus, si vous effectuez de nombreux appels au service Web, vous risquez de surcharger votre système avec des threads à gérer. En cas d'application cliente normale, ceci peut ne pas être dramatique, mais si vous effectuez des appels depuis une page ASP.NET, vous devez prendre en compte le nombre d'utilisateurs qui peuvent accéder simultanément à votre système. L'ajout de deux threads pour les appels au service Web depuis une page ASP.NET peut sembler ne pas représenter une surcharge excessive, mais que se passe-t-il si 200 autres personnes utilisent simultanément la même page ? Cela fait alors environ 400 nouveaux threads, ce qui est sans aucun doute significatif. Bien que le mécanisme de rappel pour effectuer des appels asynchrones utilise des threads supplémentaires pour les rappels, un pool de threads est réutilisé afin que les rappels soient aussi efficaces que possible—et que les problèmes liés à l'exécution simultanée de centaines de threads soient évités.

Si la génération dynamique de threads en arrière-plan pour effectuer vos appels au service Web a toujours un sens pour vous, vous devriez alors envisager la génération dynamique de ces threads à l'aide de délégués et du pool de threads de processus. Vous pouvez alors appeler ces méthodes à l'aide de paradigmes asynchrones similaires à ceux utilisés pour nos rappels au service Web et invoquer les appels de fonction pour interagir avec les contrôles d'un thread à l'autre. Pour obtenir davantage d'informations sur l'utilisation de délégués pour les appels asynchrones généraux, reportez-vous à l'article de Richard Grimes, .NET Delegates: Making Asynchronous Method Calls in the .NET Environment (leave-msdn france Site en anglais).

Conclusion

La technique qui permet d'interroger des services Web de manière asynchrone est certainement l'une des fonctionnalités les plus utiles parmi celles proposées pour utiliser des services Web sur HTTP à partir d'applications .NET Framework. La plupart des applications du monde réel pourront bénéficier de cette fonctionnalité pour optimiser les appels au service Web sans pour autant bloquer les ressources en cas d'encombrement du trafic réseau. Le .NET Framework apporte une réelle souplesse dans la gestion des appels asynchrones au service Web sur HTTP et fournit aux développeurs un niveau de contrôle important pour déterminer la méthode de traitement des fins d'appels.

À 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 lundi 25 novembre 2002



Pour en savoir plus
Afficher:
© 2014 Microsoft