I. Comment faisions-nous pour vivre sans Routing Service ?▲
Une application qui repose sur WCF va la plupart du temps utiliser des connexions point-à-point. Ces connexions vont créer un couplage fort entre le client et les services eux-mêmes. Le fait de procéder de la sorte va imposer de devoir modifier une application en cas de changement d'une adresse, d'un protocole ou autre. L'avantage principal de WCF est sa souplesse et ses facultés d'adaptation à des protocoles ou des modes de communication différents. En effet, tout se passe quasiment dans les fichiers de configuration.
Les limitations de WCF sont sa rigidité face à des configurations d'infrastructures changeantes ou des besoins de haute disponibilité. Avec la version 4 de WCF, Microsoft a introduit une nouveauté, le Routing Service qui n'est ni plus ni moins qu'un service WCF qui va permettre de router des messages et de rendre l'architecture de nos applications plus robuste et flexible à la fois. Je m'explique…
Le WCF Routing Service est un nouveau type de services qui permet de faire les choses suivantes :
- Router des messages vers une URL donnée (typiquement un autre service)
- Assurer une haute disponibilité en routant des messages vers autre service si le service initial n'est pas disponible
- Router des messages vers des services différents en fonction de leur contenu
- Répliquer des messages vers plusieurs services simultanément (multicast)
- Masquer la complexité d'une infrastructure à une application cliente en ne créant qu'un seul point d'entrée
- Utiliser des protocoles différents, par exemple HTTP entre le client et le routeur, et TCP entre le routeur et le service…
Et bien d'autres possibilités encore… Le Routing Service de WCF 4 offre un grand nombre de fonctionnalités qui permettent de développer des applications d'entreprise très souples et d'avancer encore un peu plus vers de vraies architectures SOA.
Dans cet article, nous allons voir deux choses, premièrement comment utiliser le Routing Service de WCF pour masquer la complexité de l'architecture physique des services, puis nous verrons comment gérer une modification de protocole entre une application cliente et un service, sans pour autant toucher une seule ligne de cette même application. Les autres fonctionnalités offertes par le Routing Service de WCF 4 seront abordées dans de futurs articles.
Beaucoup d'entreprises ont développé leurs propres services de routage custom, en effet, ils deviennent nécessaires dès que l'architecture devient un rien complexe, comme peuvent l'être 90% des architectures existantes. Microsoft nous fait gagner beaucoup de temps en l'intégrant directement à WCF 4 !
II. Masquer la complexité d'une architecture▲
Une des fonctionnalités de base du Routing Service de WCF 4 est de créer un service virtuel qui va en fait créer un point d'entrée unique et indépendant de l'architecture existante en centralisant par exemple les services nécessaires à une application donnée.
Par exemple votre super application de " pricing " a besoin d'accéder à des services exposés par l'application de gestion des stocks, le système de comptabilité, le service de gestion clients, le service d'approvisionnement, etc… Bien évidemment tous ces services se trouvent sur des machines et des réseaux différents, et vous n'avez aucunement la main sur ceux-ci, ils peuvent donc déménager, changer de nom ou même totalement être réécris sans que vous n'ayez quoi que ce soit à dire !
Une solution permettant de palier à tous ces soucis en une seule fois consiste à créer un service virtuel entre votre application qui consomme ces services et les services eux-mêmes. Un service virtuel qui va faire du routage de messages entre le client et les services physiques sans que votre application cliente n'ait la moindre information sur l'implémentation et l'organisation de ceux-ci.
Vous allez me dire avec WCF c'est simple, il suffit de changer les endpoints dans le fichier de configuration... Certes, la plupart du temps ce sera le seul impact, mais dans la vraie vie ce n'est pas si simple. Imaginez que cette application soit utilisée par des dizaines de personnes, que la criticité de cette dernière fasse qu'il ne soit pas possible de la stopper plus de quelques minutes durant la nuit… Ou pire encore, que vous n'ayez pas la main sur la gestion de celle-ci ? De plus, votre application se connecte à des dizaines de services qui ont la fâcheuse tendance à déménager ou à changer de nom toutes les semaines… Bref, refaire un MSI et le déployer, a un cout énorme que ce soit en termes de temps ou de ressources, ce n'est tout simplement pas acceptable.
II-A. Création d'un client et de quatre services de base▲
Imaginons une application (nommée A) qui se connecte à quatre services S1 à S4 se trouvant pour le premier chez un prestataire et pour le second sur un autre site géré par une autre équipe que la vôtre, nous allons créer un service de routage qui va s'intercaler entre A et S1-S4, son rôle sera de recevoir les messages de A et de les envoyer vers S1, S2, S3 et S4.
Pour cela nous allons tout d'abord créer quatre services WCF (S1 à S4) exposant une méthode SendMessage qui prendra en paramètre une chaine de caractères. Je reste volontairement simpliste pour ne pas brouiller la compréhension.
Ensuite nous allons créer une application WinForms qui sera cliente de ces quatre services. Le fichier de configuration de l'application cliente va donc ressembler à cela :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding
name
=
"BasicHttpBinding_IServiceS1"
closeTimeout
=
"00:01:00"
openTimeout
=
"00:01:00"
receiveTimeout
=
"00:10:00"
sendTimeout
=
"00:01:00"
allowCookies
=
"false"
bypassProxyOnLocal
=
"false"
hostNameComparisonMode
=
"StrongWildcard"
maxBufferSize
=
"65536"
maxBufferPoolSize
=
"524288"
maxReceivedMessageSize
=
"65536"
messageEncoding
=
"Text"
textEncoding
=
"utf-8"
transferMode
=
"Buffered"
useDefaultWebProxy
=
"true"
>
<readerQuotas
maxDepth
=
"32"
maxStringContentLength
=
"8192"
maxArrayLength
=
"16384"
maxBytesPerRead
=
"4096"
maxNameTableCharCount
=
"16384"
/>
<security
mode
=
"None"
>
<transport
clientCredentialType
=
"None"
proxyCredentialType
=
"None"
realm
=
""
/>
<message
clientCredentialType
=
"UserName"
algorithmSuite
=
"Default"
/>
</security>
</binding>
<binding
name
=
"BasicHttpBinding_IServiceS2"
closeTimeout
=
"00:01:00"
openTimeout
=
"00:01:00"
receiveTimeout
=
"00:10:00"
sendTimeout
=
"00:01:00"
allowCookies
=
"false"
bypassProxyOnLocal
=
"false"
hostNameComparisonMode
=
"StrongWildcard"
maxBufferSize
=
"65536"
maxBufferPoolSize
=
"524288"
maxReceivedMessageSize
=
"65536"
messageEncoding
=
"Text"
textEncoding
=
"utf-8"
transferMode
=
"Buffered"
useDefaultWebProxy
=
"true"
>
<readerQuotas
maxDepth
=
"32"
maxStringContentLength
=
"8192"
maxArrayLength
=
"16384"
maxBytesPerRead
=
"4096"
maxNameTableCharCount
=
"16384"
/>
<security
mode
=
"None"
>
<transport
clientCredentialType
=
"None"
proxyCredentialType
=
"None"
realm
=
""
/>
<message
clientCredentialType
=
"UserName"
algorithmSuite
=
"Default"
/>
</security>
</binding>
<binding
name
=
"BasicHttpBinding_IServiceS3"
closeTimeout
=
"00:01:00"
openTimeout
=
"00:01:00"
receiveTimeout
=
"00:10:00"
sendTimeout
=
"00:01:00"
allowCookies
=
"false"
bypassProxyOnLocal
=
"false"
hostNameComparisonMode
=
"StrongWildcard"
maxBufferSize
=
"65536"
maxBufferPoolSize
=
"524288"
maxReceivedMessageSize
=
"65536"
messageEncoding
=
"Text"
textEncoding
=
"utf-8"
transferMode
=
"Buffered"
useDefaultWebProxy
=
"true"
>
<readerQuotas
maxDepth
=
"32"
maxStringContentLength
=
"8192"
maxArrayLength
=
"16384"
maxBytesPerRead
=
"4096"
maxNameTableCharCount
=
"16384"
/>
<security
mode
=
"None"
>
<transport
clientCredentialType
=
"None"
proxyCredentialType
=
"None"
realm
=
""
/>
<message
clientCredentialType
=
"UserName"
algorithmSuite
=
"Default"
/>
</security>
</binding>
<binding
name
=
"BasicHttpBinding_IServiceS4"
closeTimeout
=
"00:01:00"
openTimeout
=
"00:01:00"
receiveTimeout
=
"00:10:00"
sendTimeout
=
"00:01:00"
allowCookies
=
"false"
bypassProxyOnLocal
=
"false"
hostNameComparisonMode
=
"StrongWildcard"
maxBufferSize
=
"65536"
maxBufferPoolSize
=
"524288"
maxReceivedMessageSize
=
"65536"
messageEncoding
=
"Text"
textEncoding
=
"utf-8"
transferMode
=
"Buffered"
useDefaultWebProxy
=
"true"
>
<readerQuotas
maxDepth
=
"32"
maxStringContentLength
=
"8192"
maxArrayLength
=
"16384"
maxBytesPerRead
=
"4096"
maxNameTableCharCount
=
"16384"
/>
<security
mode
=
"None"
>
<transport
clientCredentialType
=
"None"
proxyCredentialType
=
"None"
realm
=
""
/>
<message
clientCredentialType
=
"UserName"
algorithmSuite
=
"Default"
/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint
address
=
"http://localhost:8001/ServiceS1"
binding
=
"basicHttpBinding"
bindingConfiguration
=
"BasicHttpBinding_IServiceS1"
contract
=
"ServiceS1Reference.IServiceS1"
name
=
"BasicHttpBinding_IServiceS1"
/>
<endpoint
address
=
"http://localhost:8002/ServiceS2"
binding
=
"basicHttpBinding"
bindingConfiguration
=
"BasicHttpBinding_IServiceS2"
contract
=
"ServiceS2Reference.IServiceS2"
name
=
"BasicHttpBinding_IServiceS2"
/>
<endpoint
address
=
"http://localhost:8003/ServiceS3"
binding
=
"basicHttpBinding"
bindingConfiguration
=
"BasicHttpBinding_IServiceS3"
contract
=
"ServiceS3Reference.IServiceS3"
name
=
"BasicHttpBinding_IServiceS3"
/>
<endpoint
address
=
"http://localhost:8004/ServiceS4"
binding
=
"basicHttpBinding"
contract
=
"ServiceS4Reference.IServiceS4"
name
=
"BasicHttpBinding_IServiceS4"
/>
</client>
</system.serviceModel>
</configuration>
Le fichier de configuration du Service S1 ressemblera à cela :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service
name
=
"ServiceS1.ServiceS1"
>
<endpoint
address
=
""
binding
=
"basicHttpBinding"
contract
=
"ServiceS1.IServiceS1"
>
<identity>
<dns
value
=
"localhost"
/>
</identity>
</endpoint>
<endpoint
address
=
"mex"
binding
=
"mexHttpBinding"
contract
=
"IMetadataExchange"
/>
<host>
<baseAddresses>
<add
baseAddress
=
"http://localhost:8001/ServiceS1/"
/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata
httpGetEnabled
=
"True"
/>
<serviceDebug
includeExceptionDetailInFaults
=
"False"
/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Les fichiers de configuration des autres services (S2 à S4) seront identiques, seuls les numéros de port et noms changerons.
II-B. Ajout d'un service de routage▲
A ce point nous avons donc une application tout ce qu'il y a de plus normal. Un client et quatre services WCF auxquels le client envoie des informations. Nous allons maintenant tirer parti de WCF 4 en ajoutant un service de routage par lequel vont transiter tous les messages avant d'être dispatchés vers les quatre services.
Pour cela nous allons ajouter un projet console de plus à notre solution (RoutingService). Ce projet va être très simple, il ne va contenir que quatre lignes de code et un fichier de configuration… Difficile de faire plus simple, d'autant que les quatre lignes de code sont utilisées pour lancer le service de routage dans une application console. Si vous hébergez ce service dans IIS alors vous n'aurez aucune ligne de code à écrire.
using
System;
using
System.
Collections.
Generic;
using
System.
Linq;
using
System.
Text;
using
System.
ServiceModel;
namespace
RoutingService
{
class
Program
{
static
void
Main
(
string
[]
args)
{
ServiceHost svcRoutingHost =
new
ServiceHost
(
typeof
(
System.
ServiceModel.
Routing.
RoutingService));
svcRoutingHost.
Open
(
);
Console.
WriteLine
(
"Routing Service is running..."
);
Console.
ReadLine
(
);
}
}
}
Passons maintenant à l'analyse du fichier de configuration, c'est là que réside la clé du routage de messages intégrés à WCF 4.
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<services>
<service
name
=
"System.ServiceModel.Routing.RoutingService"
behaviorConfiguration
=
"MyRoutingServiceBehavior"
>
<host>
<baseAddresses>
<add
baseAddress
=
"http://localhost:8000/RoutingService"
/>
</baseAddresses>
</host>
<endpoint
address
=
"applicationA"
binding
=
"basicHttpBinding"
name
=
"MyRoutingServiceEndpoint"
contract
=
"System.ServiceModel.Routing.ISimplexDatagramRouter"
/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior
name
=
"MyRoutingServiceBehavior"
>
<serviceMetadata
httpGetEnabled
=
"True"
/>
<serviceDebug
includeExceptionDetailInFaults
=
"true"
/>
<routing
filterTableName
=
"RoutingServiceFilterTable"
/>
</behavior>
</serviceBehaviors>
</behaviors>
<client>
<endpoint
address
=
"http://localhost:8001/ServiceS1"
binding
=
"basicHttpBinding"
contract
=
"*"
name
=
"ServiceS1"
>
</endpoint>
<endpoint
address
=
"http://localhost:8002/ServiceS2"
binding
=
"basicHttpBinding"
contract
=
"*"
name
=
"ServiceS2"
>
</endpoint>
<endpoint
address
=
"http://localhost:8003/ServiceS3"
binding
=
"basicHttpBinding"
contract
=
"*"
name
=
"ServiceS3"
>
</endpoint>
<endpoint
address
=
"http://localhost:8004/ServiceS4"
binding
=
"basicHttpBinding"
contract
=
"*"
name
=
"ServiceS4"
>
</endpoint>
</client>
<routing>
<filters>
<filter
name
=
"MyFilter"
filterType
=
"MatchAll"
/>
</filters>
<filterTables>
<filterTable
name
=
"RoutingServiceFilterTable"
>
<add
filterName
=
"MyFilter"
endpointName
=
"ServiceS1"
/>
<add
filterName
=
"MyFilter"
endpointName
=
"ServiceS2"
/>
<add
filterName
=
"MyFilter"
endpointName
=
"ServiceS3"
/>
<add
filterName
=
"MyFilter"
endpointName
=
"ServiceS4"
/>
</filterTable>
</filterTables>
</routing>
</system.serviceModel>
</configuration>
La première partie déclare le service de routage comme n'importe quel service WCF, c'est-à-dire on définit le A, le et le BC et c'est tout :
<services>
<service
name
=
"System.ServiceModel.Routing.RoutingService"
behaviorConfiguration
=
"MyRoutingServiceBehavior"
>
<host>
<baseAddresses>
<add
baseAddress
=
"http://localhost:8000/RoutingService"
/>
</baseAddresses>
</host>
<endpoint
address
=
"applicationA"
binding
=
"basicHttpBinding"
name
=
"MyRoutingServiceEndpoint"
contract
=
"System.ServiceModel.Routing.ISimplexDatagramRouter"
/>
</service>
</services>
Remarquez une petite différence, le contrat définit est une nouveauté de WCF 4 : ISimplexDatagramRouter. C'est ce nouveau contrat qui va gérer le routage des messages. Avec la dernière version de WCF, ce sont quatre nouveaux contrats qui ont été créés spécifiquement pour le routage, voici ce que nous dit le MSDN :
- IDuplexSessionRouter Defines the interface required to process messages from duplex session channels.
- IRequestReplyRouter Defines the interface required to process messages from request-reply channels.
- ISimplexDatagramRouter Defines the interface required for processing messages from simplex datagram.
- ISimplexSessionRouter Defines the interface required to process messages from simplex session channels.
Comme vous pouvez le voir j'ai choisi le contrat de base qui permet de router des messages simples, sans session, sans call-back… " Keep it simple ", mais sachez que si vous avez des sessions, des call-back ou tout autre petite complexité vous pourrez quand même le gérer avec le service de routage de WCF.
<behaviors>
<serviceBehaviors>
<behavior
name
=
"MyRoutingServiceBehavior"
>
<serviceMetadata
httpGetEnabled
=
"True"
/>
<serviceDebug
includeExceptionDetailInFaults
=
"true"
/>
<routing
filterTableName
=
"RoutingServiceFilterTable"
/>
</behavior>
</serviceBehaviors>
</behaviors>
Ici rien de particulier si ce n'est l'ajout de la ligne qui définit le nom de la filterTable qui vous allez le voir bientôt nous permet d'établir des règles de filtrages des messages.
<client>
<endpoint
address
=
"http://localhost:8001/ServiceS1"
binding
=
"basicHttpBinding"
contract
=
"*"
name
=
"ServiceS1"
>
</endpoint>
<endpoint
address
=
"http://localhost:8002/ServiceS2"
binding
=
"basicHttpBinding"
contract
=
"*"
name
=
"ServiceS2"
>
</endpoint>
<endpoint
address
=
"http://localhost:8003/ServiceS3"
binding
=
"basicHttpBinding"
contract
=
"*"
name
=
"ServiceS3"
>
</endpoint>
<endpoint
address
=
"http://localhost:8004/ServiceS4"
binding
=
"basicHttpBinding"
contract
=
"*"
name
=
"ServiceS4"
>
</endpoint>
</client>
Ici il s'agit juste de lister tous les endpoints des services vers lesquels le service de routage va devoir rediriger les messages.
<routing>
<filters>
<filter
name
=
"MyFilter"
filterType
=
"MatchAll"
/>
</filters>
<filterTables>
<filterTable
name
=
"RoutingServiceFilterTable"
>
<add
filterName
=
"MyFilter"
endpointName
=
"ServiceS1"
/>
<add
filterName
=
"MyFilter"
endpointName
=
"ServiceS2"
/>
<add
filterName
=
"MyFilter"
endpointName
=
"ServiceS3"
/>
<add
filterName
=
"MyFilter"
endpointName
=
"ServiceS4"
/>
</filterTable>
</filterTables>
</routing>
C'est ici que se situe le cœur du routage. Nous créons un ou plusieurs filtres que nous allons appliquer aux services qui vont recevoir les messages routés. C'est ici que l'on fait le trie sur ce que l'on veut passer ou pas. Pour le moment nous laissons tout passer, ce qui explique le MatchAll dans le type de filtre. Vous verrez dans les articles suivants que l'on peut filtrer à peu près tout ce que l'on souhaite !
Voilà, notre service de routage est terminé ! Il ne reste plus qu'à faire pointer le client vers ce service unique au lieu de le faire directement pointer vers les services S1 à S4. Pour cela il faut modifier comme suit le fichier de configuration du client (ApplicationA) :
<client>
<endpoint
address
=
"http://localhost:8000/RoutingService/applicationA"
binding
=
"basicHttpBinding"
bindingConfiguration
=
"BasicHttpBinding_IServiceS1"
contract
=
"ServiceS1Reference.IServiceS1"
name
=
"BasicHttpBinding_IServiceS1"
/>
<endpoint
address
=
"http://localhost:8000/RoutingService/applicationA"
binding
=
"basicHttpBinding"
bindingConfiguration
=
"BasicHttpBinding_IServiceS2"
contract
=
"ServiceS2Reference.IServiceS2"
name
=
"BasicHttpBinding_IServiceS2"
/>
<endpoint
address
=
"http://localhost:8000/RoutingService/applicationA"
binding
=
"basicHttpBinding"
bindingConfiguration
=
"BasicHttpBinding_IServiceS3"
contract
=
"ServiceS3Reference.IServiceS3"
name
=
"BasicHttpBinding_IServiceS3"
/>
<endpoint
address
=
"http://localhost:8000/RoutingService/applicationA"
binding
=
"basicHttpBinding"
contract
=
"ServiceS4Reference.IServiceS4"
name
=
"BasicHttpBinding_IServiceS4"
/>
</client>
Vous voyez, maintenant tous les services WCF sont identifiés par la même adresse, par contre ils conservent le même nom et le même contrat. Maintenant quand vous envoyez un message par le client, celui-ci est d'abord intercepté par le Routing Service avant d'être redirigé vers le service désiré (Service S1, S2, S3 ou S4).
Les avantages qui découlent de l'ajout d'un service de routage sont simples mais très intéressants en terme de souplesse de votre architecture :
- Découplage entre l'application et l'architecture et la composition des services (principe SOA)
- Comme nous le verrons dans la deuxième partie, il est possible de changer le protocole de communication entre le service de routage et le service (S1 à S4) sans que le client ne soit impacté.
- La configuration est centralisée dans un seul et même service, pas la peine de modifier des fichiers de config sur des dizaines de machines clientes en cas par exemple d'un simple changement d'URL ou de port d'un service existant.
Je viens donc de vous montrer la fonction de base du service de routage des messages intégrés à WCF, bien que fort utile et très puissant, c'est l'arbre qui cache la forêt. Les fonctionnalités sont nombreuses et permettent une gestion avancée de ses services. Dans la deuxième partie de cet article je vais vous expliquer comment faire du " Protocol Bridging " qui consiste à utiliser un protocole différent entre le client et le service de routage de celui utilisé entre le service de routage et le service (S1 à S4).
III. Protocol bridging : changer de protocole en toute transparence▲
Horreur, malheur, catastrophe ! Votre administrateur réseau, vient de vous annoncer que dès demain matin, c'est terminé, vous n'allez plus pouvoir utiliser TCP pour communiquer avec le service du prestataire qui gère l'expédition de vos marchandises, cela doit désormais être http… Evidemment le prestataire est au courant depuis 6 mois et a eu le temps de modifier son service, par contre comme d'habitude, en tant qu'interne vous êtes prévenus la veille ! Ne vous inquiétez pas, puisque vous avez fait le choix judicieux d'utiliser le service de routage vous allez pouvoir gérer le problème en deux minutes.
Petit rappel, si jamais vous n'avez pas de service de routage vous allez devoir modifier le endpoint du service concerné dans le fichier de configuration. Et cela sur chaque machine, sur laquelle est installé l'application… Bon courage ! Surtout, si vous ne gérez pas en personne, le déploiement de cette application.
Revenons à la solution qui comprend un client, le service de routage et nos quatre services WCF (S1 à S4). Pour simuler cette situation nous allons modifier le service S1 pour qu'il communique avec le binding NetTcpBinding, et le service S3 pour qu'il communique avec le binding WebHttpBinding. Voici les impacts sur les fichiers de configuration des services S1 et S3.
Service S1 :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service
name
=
"ServiceS1.ServiceS1"
>
<endpoint
address
=
"net.tcp://localhost:8021/ServiceS1/"
binding
=
"netTcpBinding"
contract
=
"ServiceS1.IServiceS1"
>
<identity>
<dns
value
=
"localhost"
/>
</identity>
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata
httpGetEnabled
=
"False"
/>
<serviceDebug
includeExceptionDetailInFaults
=
"False"
/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Service S3 :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service
name
=
"ServiceS3.ServiceS3"
>
<endpoint
address
=
"net.tcp://localhost:8023/ServiceS3/"
binding
=
"netTcpBinding"
contract
=
"ServiceS3.IServiceS3"
>
<identity>
<dns
value
=
"localhost"
/>
</identity>
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata
httpGetEnabled
=
"False"
/>
<serviceDebug
includeExceptionDetailInFaults
=
"False"
/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
En fait comme vous vous en doutiez certainement les impacts sur les services S1 et S3 sont minimes, il suffit juste de changer le binding, changer l'url pour qu'elle corresponde au protocole et changer le port, et c'est tout. Faites attention à ce que ces ports soient autorisés par votre Firewall, nous sommes ici en TCP, il y a de grandes chances que votre Firewall vous demande des comptes au lancement du service.
Maintenant que nos services S1 et S3 sont à jour, voyons comment dire à notre service de routage qu'il va devoir recevoir ses messages en http et les renvoyer vers S1 et S3 en TCP. Accrochez-vous car cela va aller très vite, il suffit simplement de modifier la configuration des endpoints de S1 et S3, le service de routage va tout gérer tout seul. Il va automatiquement récupérer les messages sur http, les reconditionner et hop les renvoyer par TCP sans que nous n'ayez rien à faire.
Voici à quoi doit ressembler votre fichier de configuration du service de routage des messages :
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<services>
<service
name
=
"System.ServiceModel.Routing.RoutingService"
behaviorConfiguration
=
"MyRoutingServiceBehavior"
>
<host>
<baseAddresses>
<add
baseAddress
=
"http://localhost:8000/RoutingService"
/>
</baseAddresses>
</host>
<endpoint
address
=
"applicationA"
binding
=
"basicHttpBinding"
name
=
"MyRoutingServiceEndpoint"
contract
=
"System.ServiceModel.Routing.ISimplexDatagramRouter"
/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior
name
=
"MyRoutingServiceBehavior"
>
<serviceMetadata
httpGetEnabled
=
"True"
/>
<serviceDebug
includeExceptionDetailInFaults
=
"true"
/>
<routing
filterTableName
=
"RoutingServiceFilterTable"
/>
</behavior>
</serviceBehaviors>
</behaviors>
<client>
<endpoint
address
=
"net.tcp://localhost:8021/ServiceS1"
binding
=
"netTcpBinding"
contract
=
"*"
name
=
"ServiceS1"
>
</endpoint>
<endpoint
address
=
"http://localhost:8002/ServiceS2"
binding
=
"basicHttpBinding"
contract
=
"*"
name
=
"ServiceS2"
>
</endpoint>
<endpoint
address
=
"net.tcp://localhost:8023/ServiceS3"
binding
=
"netTcpBinding"
contract
=
"*"
name
=
"ServiceS3"
>
</endpoint>
<endpoint
address
=
"http://localhost:8004/ServiceS4"
binding
=
"basicHttpBinding"
contract
=
"*"
name
=
"ServiceS4"
>
</endpoint>
</client>
<routing>
<filters>
<filter
name
=
"MyFilter"
filterType
=
"MatchAll"
/>
</filters>
<filterTables>
<filterTable
name
=
"RoutingServiceFilterTable"
>
<add
filterName
=
"MyFilter"
endpointName
=
"ServiceS1"
/>
<add
filterName
=
"MyFilter"
endpointName
=
"ServiceS2"
/>
<add
filterName
=
"MyFilter"
endpointName
=
"ServiceS3"
/>
<add
filterName
=
"MyFilter"
endpointName
=
"ServiceS4"
/>
</filterTable>
</filterTables>
</routing>
</system.serviceModel>
</configuration>
Comme vous le voyez, seuls, les endpoints client de S1 et S3 ont été modifiés, pour le reste, rien à changer.
Voilà pour ce premier article sur le service de routage de WCF 4. J'espère vous avoir donné une première idée de tout ce qu'il est possible de faire avec cette nouveauté intégrée à .Net 4.0. D'autres articles viendront rapidement pour couvrir les aspects de multicast, de haute disponibilité… Ces sujets sont trop vastes pour tous être condensés en un seul et même article.
J'espère vous avoir convaincu d'utiliser le Routing Service de WCF 4, il présente de forts avantages qui pourront vous aider à gérer des architectures complexes et/ou sur lesquelles vous n'avez pas la main. Evidemment une étape supplémentaire dans un envoi de messages a un cout, en ressources matérielles, en temps de développement et en maintenance, mais vu les gains et les avantages procurés par cette solution, je pense qu'il s'agit d'un investissement très rentable dans une grande majorité des cas.