I. Qu'est-ce que le multicasting ?

Vous avez surement entendu ce terme lors d'une discussion entre deux administrateurs réseaux, mais entre développeurs, c'est déjà beaucoup moins probable. Dans la langue de Molière je devrais plutôt utiliser le mot multidiffusion, mais si l'on commence à tout traduire on ne va plus rien y comprendre ! Pour faire simple, le multicasting, c'est envoyer une information simultanément à plusieurs destinataires. Que ce soit en réseau ou en développement la signification reste la même, nous allons diffuser un message (un paquet pour nos amis administrateurs réseaux) simultanément vers plusieurs destinataires.

Microsoft .Net
Microsoft .Net



II. WCF 4 et le multicasting

WCF, depuis sa version 4 intègre le " Routing Service " qui permet, entre autre, de faire du multicasting de message, c'est-à-dire qu'il est capable de recevoir un message venant d'un client et de l'envoyer à plusieurs services en même temps, sur des ports et avec des bindings qui peuvent être différents. Voici un schéma qui montre une utilisation possible du multicasting avec WCF 4.

WCF : multicasting de messages
WCF : multicasting de messages


Ce multicasting peut être pratique dans un scénario d'archivage de messages, mais pas seulement. Un autre scénario possible pourrait être le suivant : vous travaillez sur un site de e-commerce et vous devez développer un module qui va indiquer qu'une commande est prête à être expédiée. Cette dernière information intéresse plusieurs personnes/services bien différents :

  • Le service comptabilité qui doit prélever le montant de la commande sur le compte du client
  • Le service logistique qui doit prévenir le transporteur qu'il a un colis à expédier
  • Le client a qui vous devez envoyer un email pour l'informer de l'expédition imminente de ses achats

Il est très probable que ces trois actions différentes imposent d'envoyer un message à des modules distincts basés sur des technologies distinctes et qui, potentiellement se trouvent sur des réseaux différents. Dans un tel scénario le multicasting de message à la sauce WCF 4 peut se révéler très utile et évite de coder des usines à gaz. D'autant plus que si demain ce message doit être envoyé à un quatrième destinataire, avec le " Routing Service vous n'aurez rien à recoder, seulement un fichier de configuration à modifier !

III. Implémentation du multicasting avec WCF 4

Implémenter le multicasting avec WCF 4 est simple puisqu'il ne repose que sur le fichier de configuration de votre service de routage, app.config ou web.config suivant le type de l'application qui héberge le service.

Vos services doivent implémenter le même contrat puisque c'est un même message qui va être envoyé à plusieurs services, il faut donc que ces services " parlent tous le même langage ".

Contrat partagé par les quatre services
Sélectionnez

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace ServiceDeveloppez
{

    [ServiceContract]
    public interface IService
    {
        [OperationContract(IsOneWay = true)]
        void SendMessage(string message);
    }

}

Comme expliqué dans un précédent article sur le Routing Service, la magie s'opère par le biais d'un contrat spécifique qui va permettre de mettre en place le routage à proprement parlé. Ces contrats de routage sont aux nombres de quatre :

  • IDuplexSessionRouter
  • IRequestReplyRouter
  • ISimplexDatagramRouter
  • ISimplexSessionRouter

Ils permettent de couvrir des scénarios de services qui utilisent par exemple des sessions, font du callback... Pour les besoins de notre démonstration nous allons utiliser ISimplexDatagramRouter. Je vous renvoie à cette page MSDN pour prendre connaissance des spécificités de chacun de ces contrats.

III-A. Création des services

Nous allons créer quatre services utilisant différents types de bindings : basicHttpBinding, netTcpBinding et wsHttpBinding. Ces quatre services sont implémentés dans la même interface (contrat) qui se trouvera dans une dll pour plus de simplicité. Ce contrat ne comportera qu'une seule méthode pour plus de simplicité mais il n'y a aucune limitation provoquée par le " Routing Service ".

Ces quatre services exposeront donc la même méthode, contrat commun oblige. Attention aux problèmes de droit est de firewall, en effet, dès que l'on commence à s'amuser avec TCP Windows devient plus exigeant en matière de gestion des ports (et c'est bien mieux ainsi).

Voici à quoi devraient ressembler vos fichiers de configurations :

Service avec basicHttpBinding
Sélectionnez

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="ServiceS1.ServiceS1">
        <endpoint address="http://localhost:8001/ServiceS1/" binding="basicHttpBinding" contract="ServiceDeveloppez.IService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="False"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>
Service avec wsHttpBinding
Sélectionnez

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="ServiceS3.ServiceS3">
        <endpoint address="http://localhost:8003/ServiceS3/" binding="wsHttpBinding" contract="ServiceDeveloppez.IService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="False"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>
Service avec netTcpBinding
Sélectionnez

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="ServiceS2.ServiceS2">
        <endpoint address="net.tcp://localhost:8002/ServiceS2/" binding="netTcpBinding" contract="ServiceDeveloppez.IService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="False"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Comme j'héberge mes services dans des applications consoles j'ai un petit bout de code à écrire, mais rien ne vous empêche de les héberger directement dans IIS, dans ce cas, vous n'aurez que le fichier .svc à renseigner.

Service hosté dans une application console
Sélectionnez

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace ServiceS1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Démarrage du service S1");
            Console.WriteLine();
            using (ServiceHost host = new ServiceHost(typeof(ServiceS1)))
            {
                host.Open();
                Console.WriteLine();
                Console.ReadLine();
            }
        }
    }
}

III-B. Création du service de routage

Le service de routage va lui, être très simple, un morceau de code pour l'héberger dans mon application console et un fichier de configuration pour lui indiquer comment router les messages.

Service de routage hosté dans une application console
Sélectionnez

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();
        }
    }
}

Voyons plus en détail son fichier de configuration. Tout d'abord ce service de routage expose en EndPoint qui va permettre au client de se connecter, en l'occurrence il s'agit d'un EndPoint utilisant le basicHttpBinding, mais une fois de plus pas de restriction à ce niveau, tous les bindings de WCF sont disponibles.

EndPoint du service de routage
Sélectionnez

    <services>
      <service name="System.ServiceModel.Routing.RoutingService" behaviorConfiguration="MyRoutingServiceBehavior">
        <endpoint address="http://localhost:8000/RoutingService/applicationA"
                  binding="basicHttpBinding"
                  contract="System.ServiceModel.Routing.ISimplexDatagramRouter"/>
      </service>
    </services>

Comme évoqué précédemment on retrouve bien notre contrat spécifique aux services de routage : System.ServiceModel.Routing.ISimplexDatagramRouter, vous voyez qu'il est contenu dans un nouveau namespace spécifique au routage WCF 4.

Définissons maintenant les EndPoints des services vers lesquels vont devoir être routés les messages reçus :

EndPoint client du service de routage
Sélectionnez

    <client>
      <endpoint address="http://localhost:8001/ServiceS1" 
                binding="basicHttpBinding" 
                contract="*"	
                name="ServiceS1">
      </endpoint>
      <endpoint	address="net.tcp://localhost:8002/ServiceS2"	
                binding="netTcpBinding" 
                contract="*"	
                name="ServiceS2">
      </endpoint>
      <endpoint address="http://localhost:8003/ServiceS3"	
                binding="wsHttpBinding" 
                contract="*"	
                name="ServiceS3">
      </endpoint>
      <endpoint	address="http://localhost:8004/ServiceS4"
                binding="basicHttpBinding" 
                contract="*"	
                name="ServiceS4">
      </endpoint>
    </client>

Nous devons aussi spécifier les filtres à utiliser et la manière de router les messages. Grâce à ce système de configuration par fichier, il est possible d'avoir un niveau de granularité élevé et de définir des scénarios complexes, notamment en faisant du filtrage au niveau du contenu des messages via des requêtes XPath, cela sera l'objet d'un prochain article!

Filtres et table de routage
Sélectionnez

    <behaviors>
      <serviceBehaviors>
        <behavior name="MyRoutingServiceBehavior">
          <serviceMetadata httpGetEnabled="True" httpGetUrl="http://localhost:8000/RoutingService/applicationA"/>
          <routing filterTableName="RoutingServiceFilterTable"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    


    <routing>
      <filterTables>
        <filterTable name="RoutingServiceFilterTable">
          <add filterName="MyMulticastFilter" endpointName="ServiceS1"/>
          <add filterName="MyMulticastFilter" endpointName="ServiceS2"/>
          <add filterName="MyMulticastFilter" endpointName="ServiceS3"/>
          <add filterName="MyMulticastFilter" endpointName="ServiceS4"/>
        </filterTable>
      </filterTables>      
      <filters>
        <filter name="MyMulticastFilter" filterType="MatchAll"/>
      </filters>
    </routing>

Voilà rien de particulier ni de différent de ce que l'on a vu dans le scénario du Message Broker. Le filtre utilisé indique MatchAll, ce qui va avoir pour effet de tout router sans ne pratiquer aucune sélection sur l'origine ou le contenu des messages.

III-C. Création du client

Pour finir, il faut bien évidemment créer le client. Il référence le End Point exposé par le service de routage sans avoir connaissance de l'architecture des services qui vont recevoir les messages qu'il va envoyer. Voici une copie du code de notre client :

Code source du client : Application A
Sélectionnez

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;

namespace ApplicationA
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btSendS1_Click(object sender, EventArgs e)
        {
            BasicHttpBinding binding = new BasicHttpBinding();
            EndpointAddress endPoint = new EndpointAddress("http://localhost:8000/RoutingService/applicationA");
            ServiceDeveloppez.IService serviceProxy = ChannelFactory<ServiceDeveloppez.IService>.CreateChannel(binding, endPoint);
            serviceProxy.SendMessage("Multicasting message from A");
        }

    }
}

Voilà, notre application utilisant le multicasting de WCF 4 est prête, il n'y a plus qu'à tester tout cela et apprécier le travail réalisé par Microsoft dans cette nouvelle version de WCF :).

IV. Conlcusion

J'espère au cours de cet article, vous avoir convaincu sur l'utilité du multicasting et montré à quel point il est facile à mettre en œuvre avec WCF 4. Vous trouverez à la suite de cet article la solution autour de laquelle j'ai construit cet article. Rendez-vous prochainement pour découvrir une autre nouveauté apportée par le " Routing Service " de " Windows Communication Foundation 4 ".

V. Ressources