Introduction

A l'heure de WCF il pourrait paraitre désuet de parler de Web Services mais détrompez vous ces derniers toujours bel et bien là, plus que jamais. Pour faire communiquer facilement des plateformes hétérogènes on n'a encore rien inventé de mieux que les Web Services, qu'ils soient intégrés ou non dans WCF. Au cours de cet article nous allons nous attarder sur une problématique récurrente avec les Web Services XML/SOAP, comment transférer efficacement des grandes quantités de données comme des fichiers par exemple. En effet, les quantités de données échangées sur les systèmes informatiques ne cessent de croire et les fichiers deviennent de plus en plus volumineux. Les Web Services ont des avantages innombrables mais ils en sont pas pour autant parfaits ils ont un inconvénient majeur, leurs performances moyennes. Voyons sans plus tarder comment il est possible de transférer des quantités de données relativement importante aux travers de Web Service XML/SOAP.

Microsoft .Net

2. Les Web Services, qu'est-ce que c'est ?

Un peu d'histoire pour commencer, les web services ont faits leur apparition pour solutionner les problèmes hyper-complexes d'interopérabilité des systèmes. Recommandés par le W3C ils sont supposés permettre d'échanger des données entre plateformes hétérogènes de façon transparente en s'appuyant sur le langage XML/SOAP en utilisant des messages standardisé. Même si cela n'est pas parfait il faut reconnaitre que cela fonctionne quand même relativement bien et qu'à l'heure actuelle nous n'avons rien trouvé de mieux et d'aussi largement adopté.

Le principe de communication des Web Services est relativement simple, il s'agit de sérialiser et de désérialiser des objets à l'aide d'XML et du protocole SOAP. Je pense qu'il n'est plus nécessaire de présenter XML je passerai donc directement à SOAP (pour Simple Object Access Protocol). SOAP permet de représenter des objets à l'aide du langage XML, ce protocole à été recommandé par le W3C en 2003. SOAP à vu le jour en grande partie sous l'impulsion de Microsoft.

Les Web Services utilise le très répandu protocole HTTP pour communiquer, il est bien évidemment également possible d'utiliser HTTPS. L'avantage d'utiliser HTTP comme protocole de communication est de pouvoir passer les firewalls qui posent tant de problèmes lorsqu'il faut communiquer au delà du périmètre interne des entreprises. De fait, les Web Services comme leur nom l'indique peuvent être publiés sur le Web.

Un Web Service est l'exposition par le protocole HTTP de méthodes regroupées à l'intérieur d'un service. Ce service étant définit à l'aide de protocoles et de langages standards, il est alors possible pour pratiquement n'importe quelle plateforme de venir s'y connecter. Concrètement cela signifie que grâce aux Web Services nous pouvons faire communiquer très facilement une application .Net sous Windows avec une application Java tournant sous Linux. Et j'insiste sur le facilement, sans les Web Services je souhaite beaucoup de courage à ceux qui devront faire communiquer ces deux mondes. Certes même avec les Web Services il y a toujours des situations complexes, mais ils permettent d'éliminer 90% des problèmes d'interopérabilité.

Dans les faits les Web Services exposent des contrats WSDL, il s'agit là encore d'un langage reposant sur XML qui va permettre de définir un service et ses méthodes. WSDL signifiant d'ailleurs Web Service Definition Language. Ces contrats WSDL vont lister toutes les opérations exposées par un service, quels sont les paramètres en entrées et ce que retournent les méthodes. WSDL est bien évidemment un standard multiplateforme. Avec WSDL il est possible de définir n'importe quel objet et d'en avoir une représentation qui sera multiplateforme. Concrètement un objet dans mon application .Net pourra être transféré dans une application Java, pourtant ces deux technologies ne sont pas directement interopérables. La magie opère par le biais de la sérialisation XML. Prenons l'exemple d'un Web Service exposant une méthode GetTemperature() qui prend en paramètre un code postal et retourne la température actuelle dans la ville correspondante. Cette température pourrait par exemple être représentée sous forme d'un entier (oui oui je sais ce n'est vraiment pas le meilleur type pour cela...) dans l'application .Net. Transférer directement un entier .Net dans une application Java n'étant pas possible le Web Service va le sérialiser (le convertir) en SOAP/XML, c'est à dire le décrire à l'aide du langage SOAP/XML (qui lui est standard). L'application Java va alors se connecter à ce Web Service, récupérer cet entier sérialisé puis le désérialiser depuis SOAP/XML vers un objet entier Java. Et voilà, la boucle est bouclée, nous venons de faire communiquer .Net et Java sans problème.

Tout ceci est simplifié à l'extrême mais vous allez voir que la réalité n'est pas forcément plus complexe, surtout avec .Net ! Le but de l'article n'étant pas de présenter les Web Services je vous renvois vers d'autres ressources si vous avez d'autres interrogations sur le fonctionnement des Web Services.

3. La solution MTOM

Maintenant que nous avons présenté dans les grandes lignes ce qu'est un Web Service, revenons à l'objet de cet article, le transfert de grandes quantités de données par Web Service.

Vous l'aurez compris, ce procédé de sérialisation et de désérialisation SOAP/XML est couteux et cela à tous les niveaux. Tout d'abord en temps, une conversion d'une image en sa représentation XML nécessite du temps et de plus par essence la description d'un objet est toujours plus longue que l'objet lui même, SOAP/XML n'échappe pas à cette règle. XML étant un langage très verbeux il va générer de gros volumes d'informations (texte) pour représenter des objets. Avec les débits et la réactivité des réseaux il n'y a aucune difficulté pour transférer quelques kilos octets dans des messages SOAP mais quand il s'agit par exemple de transférer un fichier de 5 méga-octets cela est une toute autre histoire. Imaginons un fichier image, celui ci est composé de données binaires, le représenter sous forme SOAP/XML est tout à fait possible mais c'est long et va occuper une place considérable. Pour vous donner un ordre d'idée une imagine JPEG d'un méga-octet sérialisée pèsera entre 5 à 10Mo en SOAP/XML... De plus, de par sa nature un objet sérialisé en XML devra être intégralement placé en mémoire pour pouvoir être désérialisé, imaginez un peu l'état d'un serveur transférant des milliers de photos haute-résolution par jour grâce à des Web Services... la charge est considérable !

Il existait jusqu'à lors quelques solutions pour transférer ces volumes de données binaires plus efficacement mais rien de totalement standard et d'interopérable jusqu'à l'arrivée de MTOM. Ce sigle de quatre lettres qui signifient Message Transmission Optimization Mechanism, est un ensemble de procédés qui vont permettre dans une certaine mesure d'éviter la sérialisation XML des fichiers binaires. Certes il est parfois fort agréable de lire des messages en SOAP/XML plutôt qu'une suite d'octets mais cela est terriblement lent et volumineux à générer et à transférer. MTOM va permettre de transférer des fichiers binaires par Web Service sans pour autant devoir les sérialiser/désérialiser en XML.

Le monde des développeurs .Net ne disposez pas jusqu'à WSE 3.0 d'outils lui permettant d'utiliser MTOM. WSE pour Web Service Enhancements en version 3.0 apporte de nombreuses nouveautés par rapport aux versions précédentes (notamment dans la sécurité des Web Services), mais celle qui nous intéresse aujourd'hui est le support de MTOM. Pour ceux qui ne connaissent pas WSE, il s'agit d'un ensemble d'outils et de librairies permettant d'étendre les Web Servcies que l'on connait.

Pour faire simple MTOM est un mécanisme automatique (quasi transparent pour le développeur) qui va optimiser les messages SOAP qui contiennent des données binaires. Concrètement, au lieu de transporter la sérialisation SOAP/XML d'une image JPEG nous allons transférer directement les octets qui la composent attachés au message SOAP. Vous l'aurez compris, ici nous ne transportons plus une représentation SOAP/XML standard, il faut donc avoir en tête que l'on ne peut pas tout transporter de cette manière, sans quoi les Web Services n'auraient plus lieu d'exister et l'on retomberait dans des problèmes d'interopérabilité. Mais une image JPEG, une vidéo AVI par exemple sont exactement les mêmes sur Linux, Windows ou n'importe quelle plateforme... Nous pouvons donc les transporter en l'état sans mettre en danger l'interopérabilité de la solution.

Pour commencer, voyons concrètement à quoi ressemble un message SOAP envoyé par un Web Service employant MTOM :

Un message SOAP avec MTOM activé
Sélectionnez

Content-Type: multipart/related; boundary=MIMEBoundary4A7AE22984E7738034;
                         type="application/xop+xml"; start="<0.09BC7F4BE2E4L7EF1B@example.org>";
                         start-info="text/xml; charset=utf-8"

--MIMEBoundary4A7AE55984E7438034
content-type: application/xop+xml; charset=utf-8; type="application/soap+xml;"
content-transfer-encoding: binary
content-id: <0.09BC7F4BE2E4D3EF1B@apache.org>

<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="...."....>
  ........
         <xop:Include href="cid:1.A91D6D2E3D7AC4D580@apache.org" 
                        xmlns:xop="http://www.w3.org/2004/08/xop/include">
         </xop:Include>
  ........
</soapenv:Envelope>
--MIMEBoundary4A7AE55984E7438034
content-type: application/octet-stream
content-transfer-encoding: binary
content-id: <1.A91L7D2E3D7AC4D577@example.org>

Binary Data.....
--MIMEBoundary4A7AE55984E7438034--

Comme vous pouvez le voir c'est un peu différent d'un message SOAP habituel :).

Assez discuté, passons maintenant à la pratique, comment utiliser MTOM dans un Web Service réalisé en .Net / C#.

Tout d'abord WSE 3.0 n'est pas intégré à Visual Studio et encore moins à Windows, il faut donc le télécharger sur le site de Microsoft à cette adresse. Il s'agit d'un fichier exécutable pesant un peu plus de 11 Mo.

Il faut installer WSE sur les machines serveurs et clientes qui devront utiliser MTOM, le setup propose d'ailleurs à cet effet divers profiles d'installation en fonction de ce que l'on souhaite faire :

  • Runtime
  • Administrator
  • Developer
  • Visual Studio Developer
  • Custom
Templates d'installation de WSE 3.0



Nous souhaitons l'utiliser directement dans Visual Studio, nous choisissons donc naturellement le profile Visual Studio Developer. Après il suffit juste de cliquer sur " suivant " et " installer " et voilà c'est aussi simple que cela. En cas de souci post installation vous pouvez consulter le fichier readme.htm à cet emplacement : " C:\Program Files\Microsoft WSE\v3.0\readme.htm ".

Une fois l'installation terminée vous verrez qu'une nouvelle entrée est apparue dans le menu de Visual Studio lorsque vous faites un clic droit sur un projet dans l'explorateur de solution de Visual Studio : "WSE Settings 3.0". (Attention, l'add-in de WSE 3.0 ne s'installe pas sous Visual Studio 2008).

WSE Settings 3.0

C'est par le biais de cet outil que nous allons très facilement activer MTOM sur nos Web Services ou sur nos clients de Web Service. Il n'y aura rien à redévelopper, l'opération est quasi transparente. Passons à une petite démonstration.

Tout d'abord voici un web service simpliste qui possède une seule et unique méthode GetImage() qui retourne un tableau d'octets contenant une fichier gif.

Web Service de base
Sélectionnez

using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.IO;

[WebService(Namespace = "http://monsuperwebservice.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
    public Service()
    {
        //Uncomment the following line if using designed components 
        //InitializeComponent(); 
    }

    [WebMethod]
    public byte[] GetImage()
    {
        return GetBytes(@"C:\Users\ronald.vasseur\Desktop\image.gif");
    }

    private byte[] GetBytes(string imageFilePath)
    {

        FileStream fs = File.OpenRead(imageFilePath);

        try
        {
            byte[] bytes = new byte[fs.Length];
            fs.Read(bytes, 0, Convert.ToInt32(fs.Length));
            fs.Close();
            return bytes;
        }
        finally
        {
            fs.Close();
        }
    }
}

Nous venons de développer un Web Service qui permet de downloader des images avec Visual Studio et nous souhaitons activer MTOM pour optimiser les performances de notre service. Pour cela il faut procéder de la manière suivante :

  • Clic droit sur le projet contenant notre Web Service dans Visual Studio
  • Sélectionner "WSE Settings 3.0"
  • Dans le premier onglet (General) sélectionner "Enable this project for Web Services Enhancements" et "Enable Microsoft for Web Services Enhancements" (uniquement s'il s'agit d'une application ASP.Net)
Onlget 'General'


  • Dans le dernier onglet (Messaging) paramétrer les éléments de la façon suivante : Client mode "on", Server mode "optional", MaxMimeParts = 64, Require Soap Envelope First
  • Cliquer sur OK
Onglet 'Messaging'


Et voilà notre Web Service intègre désormais MTOM et l'utilisera chaque fois que cela sera nécessaire.

Voyons un peu plus en détails ce que vient de faire cet outil. Tout d'abord il a ajouté une référence à notre projet vers la librairie Microsoft.Web.Service3. Ensuite il a ajouté les balises suivantes au fichier de configuration (web.config) :

Balises ajoutées dans le fichier Web.config
Sélectionnez

    <webServices>
      <soapExtensionImporterTypes>
        <add type="Microsoft.Web.Services3.Description.WseExtensionImporter, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      </soapExtensionImporterTypes>
      <soapServerProtocolFactory type="Microsoft.Web.Services3.WseProtocolFactory, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    </webServices>
  </system.web>
  <microsoft.web.services3>
    <messaging>
      <mtom clientMode="On" />
    </messaging>
  </microsoft.web.services3>

Il suffit de procéder exactement de la même façon pour le client pour qu'il utilise MTOM. Comme vous pouvez le voir il n'y à absolument rien à redévelopper.

MTOM, ce mécanisme d'optimisation ne sera pas déclenché à chaque requête, mais seulement quand les la quantité de données binaires est supérieure à 768 octets, en dessous de ce seuil MTOM convertira les données en une chaine de caractères encodés en Base64 et l'ajoutera en l'état dans le message SOAP. Au-delà de cette limite de 768 octets alors le message deviendra " un vrai " message MTOM avec l'apparition de section MIME attachées avec le message SOAP. Cela signifie que ce sont directement les octets " bruts " qui seront envoyés attachés au message SOAP. Cela à l'immense avantage de ne pas de voir sérialiser toutes ces données ou même de les encoder en Base64 ; les performances seront nettement meilleures et en plus la bande passante utilisée sera moindre, cela peut aller d'un rapport de 1 à 10 de qui n'est pas négligeable du tout. Au niveau des performances le gain se situera au niveau du CPU mais aussi et surtout au niveau de la mémoire RAM, en effet lorsqu'il faut sérialiser et désérialiser de tels objets la RAM utilisée est très importante.

Comme nous l'avons vu dans l'outil de configuration de WSE 3.0 on spécifie un "server mode" qui peut être positionné à :

  • always
  • never
  • optional

Je vous recommande " optional " car ainsi MTOM ne sera activé que lorsqu'il sera réellement utile, en effet envoyer certains messages avec MTOM peut s'avérer contre-productif si la quantité de données est trop faible. Le mode " always " lui va faire en sorte que quelque soit le message MTOM sera systématiquement employé.

Si jamais au court d'un échange entre le client et le serveur un message n'ayant pas le format attendu (MTOM ou non) est envoyé alors l'erreur suivante sera retournée : HTTP error 415: "Media unsupported.".

4. MTOM, comment faire avec WCF ?

A l'heure actuelle WCF prend de plus en plus d'ampleur dans les projets de développement d'applications et grignote les " Web Services ASMX traditionnels " il était donc important de signaler que WCF gère totalement MTOM et que le " MTOM WCF " est évidemment interopérable avec le " MTOM WSE " puisqu'il s'agit d'une technologie standardisée.

Activer MTOM dans WCF est encore même plus facile puisque cela tient à une petite ligne dans le fichier de configuration de votre endpoint. Je n'entre pas plus dans le détail puisque je prépare un article sur le sujet pour bientôt, donc si cela vous intéresse n'hésitez pas à revenir sur ce site pour voir l'avancement des projets.

5. Conclusion

Voilà ce que je pouvais dire sur l'intérêt d'utiliser MTOM dans vos Web Services lorsqu'il s'agit de transférer des quantités de données relativement importante par Web Services. MTOM propose un mécanisme quasi transparent et très facile à mettre en place (grâce à WSE 3.0) pour optimiser les messages SOAP.

MTOM permet réellement de grandes économies de ressources, surtout de mémoire, en effet sérialiser et désérialiser en XML des fichiers tels que des images ou mêmes des vidéos consomme des quantités démesurées de mémoires et de CPU. Soyons cependant clair, même si MTOM permet d'optimiser ces messages il faut garder à l'esprit que le Web Service n'est pas la solution au transfert de fichiers surtout quand ils sont volumineux. Le protocole FTP ou WCF avec des bindings TCP restent des solutions beaucoup plus adaptées et sont à privilégier à chaque fois que cela est possible.

Ressources