Temps de lecture : 8 min.

Microsoft AppFabric, anciennement nommé Velocity dans ses versions beta, est un cache distribué. Pour schématiser, il s’agit d’une HashTable partagée via une connectivité réseau.

Dans son produit, Microsoft propose une utilisation sous la forme d’un cluster d’hôtes permettant ainsi une montée en charge facile d’installation. Parmi les fonctionnalités proposées :

  • Support d’un nombre de serveurs allant de 2 à plus d’une centaine
  • Redimensionnement dynamique du cluster (ajout/suppression de noeuds) sans interruption de service
  • Load-balancing automatique : le service distribue de manière autonome les travaux aux différents noeuds du cluster
  • Haute disponibilité : les données sont répliquées sur les hôtes du cluster
  • Intégration des sessions ASP.NET

L’API permettant la consommation du cache AppFabric se veut extrêmement épurée. Il en résulte une prise en main rapide et une mise en place peu coûteuse en temps.

Enfin, le serveur ainsi que l’API client sont disponibles gratuitement au téléchargement. Vous pouvez suivre les liens présents dans la suite de l’article.

Installation

Serveur

Pour commencer, il faudra télécharger l’installer disponible sur le site de téléchargement de Microsoft à cette adresse : http://www.microsoft.com/fr-fr/download/details.aspx?id=27115

L’installer fait environ 30 Mo, et la version utilisé dans cet article est la 1.1.0.0

Voici le premier écran d’installation :

Passez les étapes et effectuez une installation complète, afin d’éviter de devoir relancer l’installer dans le futur.

Une fois installé, l’outil de configuration de AppFabric Server se lance. Passez les étapes jusqu’à cet écran:

Sans oublier de configurer la base de données en cliquant sur le bouton “Configure”. Veillez à ne pas donner un nom de base de données déjà existant, l’outil se charge de la créer et de la remplir.

Notez également qu’il vous est offert la possibilité de sauvegarder la configuration du serveur dans un fichier XML,  selon vos préférences.

Laissez les pots de communication par défaut, sauf nécessité réseau particulière.

Une fois l’installation terminée, vous pourrez constater la présence de 3 nouveaux services Windows dans le gestionnaire de services :

Ainsi qu’une nouvelle section dans le IIS Manager permettant de monitorer le serveur AppFabric:

Outil d’administration du cache

Il n’y a pas d’outil d’administration fourni avec le serveur, mais vous pouvez télécharger par exemple AppFabric Caching Admin Tool (http://mdcadmintool.codeplex.com/).

L’outil est à copier dans un dossier, il suffit de lancer le .exe :

Une fois l’outil lancé, sur notre serveur AppFabric nouvellement installé, vous pourrez constater la présence de l’hôte principal (sur la machine locale) ainsi que la liste des caches qu’il contient :

Nous allons créer un cache autre que celui créé par défaut pour les besoins des exemples d’utilisation du cache qui vont suivre.

Notez bien que j’ai volontairement activé les notifications, afin de pouvoir utiliser ce mécanisme dans la dernière démonstration.

Créez également 2 régions dans ce cache : “RegionA” (sans eviction) et “RegionB” (avec éviction). Nous reviendrons plus tard sur ce paramètre.

Si vous rencontrez des difficultés lors de la création de nouveaux caches ou bien de régions, vérifiez que le service “Remote Registry” sur votre machine (celle où se trouve le serveur AppFabric) est lancé.

Définissons désormais les droits d’accès au cache pour l’utilisateur qui consommera ce dernier. Pour ce faire, cliquez sur le noeud racine “Cluster : AppFabric” puis sur le bouton “Security” en haut à droite. Dans la fenêtre de saisie, entrez le nom de l’utilisateur sous la forme : “DOMAIN\user”, c’est tout.

Client

La plus simple façon d’installer les assemblies client pour AppFabric est de passer par le gestionnaire de packages NuGet. Si vous ne l’avez pas installé, c’est simple :

Dans la capture d’écran ci-dessus, NuGet est déjà installé, le cas contraire un bouton “Install” est disponible.

Une fois cet outil installé, il suffit de taper dans la console NuGet la ligne de commande d’installation du client AppFabric (http://nuget.org/packages/ServerAppFabric.Client) :

Pour afficher cette console :

Utilisation

Nous allons pour ce faire créer une application Console. Si vous avez utilisé NuGet pour obtenir votre package AppFabric client, les références aux assemblies d’AppFabric sont déjà rajoutées à la solution, dans le cas contraire, ajoutez manuellement ces dernières, comme sur cet exemple :

Placez ensuite un fichier App.config à la racine du projet contenant au minimum ceci :

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section
        name="dataCacheClient"
        type="Microsoft.ApplicationServer.Caching.DataCacheClientSection, Microsoft.ApplicationServer.Caching.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        allowLocation="true"
        allowDefinition="Everywhere" />
  </configSections>
  <dataCacheClient>
    <hosts>
      <host name="localhost" cachePort="22233"/>
    </hosts>
    <clientNotification pollInterval="1" />
  </dataCacheClient>
  <startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup>
</configuration>

Remplacez bien entendu “localhost” par le nom d’hôte qui correspond à votre installation et le port par celui que vous avez configuré (22233 est le port par défaut). J’ai également défini le “pollInterval” (délai entre deux notifications) à 1 seconde au lieu de 300s par défaut. Attention ici il s’agit bien ici de secondes et non de millisecondes.

Premiers pas

L’exemple ci-dessous illustre la durée de rétention du cache appliquée aux objets stockés :

static void TestBasicExpiration()
{
    var factory = new DataCacheFactory();
    var cache = factory.GetDefaultCache();

    cache.Put("obj", new object(), TimeSpan.FromSeconds(5));

    var timer = new Timer(new TimerCallback(o =>
    {
        Console.WriteLine("obj : {0}", cache.Get("obj"));
    }), null, 0, 1000);
}

La sortie console est la suivante :

Une fois passées les 5 secondes définies comme durée de persistance pour l’objet portant la clé “obj”, ce dernier est détruit et l’appel la clé “obj” ne renvoie plus rien.

Les deux ligne suivantes peuvent être isolées afin de ne pas avoir à redemander ces informations statiques à chaque requête d’objets du cache :

var factory = new DataCacheFactory();
var cache = factory.GetDefaultCache();

GetDefaultCache renvoie, comme son nom l’indique, le cache par défaut de l’hôte AppFabric. Pour accéder à un cache nommé, il suffit de remplacer cet appel par GetCache(“InfineCache”) par exemple.

Utilisation des régions

Il est possible de stocker des objets dans le cache sous différentes régions :

static void TestRegion()
{
    var factory = new DataCacheFactory();
    var cache = factory.GetCache("InfineGroup");

    cache.Put("int", 5);
    cache.Put("int", 6, "RegionA");
    cache.Put("int", 7, "RegionB");

    Console.WriteLine("int(no region) : {0}", cache.Get("int"));
    Console.WriteLine("int(RegionA) : {0}", cache.Get("int", "RegionA"));
    Console.WriteLine("int(RegionB) : {0}", cache.Get("int", "RegionB"));
}

Les notifications

Le système de notification de AppFabric est basé sur l’attachement d’un delegate à un ensemble de filtres d’opérations. Il est possible de s’attacher au cache, à une région du cache ou bien à une clé d’objet précise.

Voici l’exemple :

static void CacheUpdated(string cacheName, string region, string key,
DataCacheItemVersion version, DataCacheOperations operation,
DataCacheNotificationDescriptor descriptor)
{
    //display some of the delegate parameters
    Console.WriteLine("A cache-level notification was triggered!");
    Console.WriteLine("    Cache: " + cacheName);
    Console.WriteLine("    Region: " + region);
    Console.WriteLine("    Key: " + key);
    Console.WriteLine("    Operation: " + operation);
    Console.WriteLine("======================================================");
}

static void TestNotifications()
{
    var factory = new DataCacheFactory();
    var cache = factory.GetCache("InfineGroup");

    var rnd = new Random();
    cache.Put("int", rnd.Next());

    var allCacheOperations = DataCacheOperations.AddItem | DataCacheOperations.ReplaceItem |
        DataCacheOperations.RemoveItem | DataCacheOperations.CreateRegion |
        DataCacheOperations.ClearRegion | DataCacheOperations.RemoveRegion;

    cache.AddCacheLevelCallback(allCacheOperations,
            new DataCacheNotificationCallback(CacheUpdated));

    cache.Remove("int");
    cache.Put("int", rnd.Next());
    cache.Put("int", rnd.Next());
}

Et la sortie résultante :

Opérations Bulk

Les opérations de type “Bulk” dans AppFabric se limitent à la lecture, il n’y a pour le moment aucun moyen d’effectuer des écritures de masse dans le cache.

La lecture se fait grâce à l’instruction BulkGet de l’objet DataCache :

static void TestBulkGet()
{
    var factory = new DataCacheFactory();
    var cache = factory.GetCache("InfineGroup");

    cache.CreateRegion("Desk");

    var rnd = new Random();
    for (var i = 0; i < 1000; i++)
    {
        cache.Put("int-" + i, rnd.Next(), "Desk");
    }

    var gets = cache.BulkGet(new[] { "int-6", "int-14", "int-578", "int-988" }, "Desk");
    foreach (var value in gets)
    {
        Console.WriteLine("{0} = {1}", value.Key, value.Value);
    }
}

Résultat :

Lock / Unlock

Il est possible de vérouiller une donnée du cache pour une durée spécifique au moment où on la récupère via l’instruction GetAndLock de l’objet DataCache . Attention cependant, un appel Put sur la clé de l’objet réinitialisera le verrou. De même, un appel à Get ne générera pas d’exception, car ce dernier ne demande pas d’autorisation sur la clé comme le fait GetAndLock.

L’utilisation de GetAndLock combiné avec PutAndUnlock peut s’apparenter à l’utilisation de verrou dans un système multithreading, à la différence que les verrous persistent sur le cache distribué et non sur la machine exécutant le code. Les verrous sont donc communs à tous les utilisateurs du cache.

Illustration :

static void TestLock()
{
    var factory = new DataCacheFactory();
    var cache = factory.GetCache("InfineGroup");

    cache.Put("int", 5);

    DataCacheLockHandle lockHandle;
    var value = cache.GetAndLock("int", TimeSpan.FromSeconds(5), out lockHandle);
    Console.WriteLine("GetAndLock: int = {0}", value);

    try
    {
        value = cache.GetAndLock("int", TimeSpan.FromSeconds(5), out lockHandle);
        Console.WriteLine("GetAndLock: int = {0}", value);
    }
    catch (DataCacheException ex)
    {
        if (ex.ErrorCode == DataCacheErrorCode.ObjectLocked)
            Console.WriteLine("GetAndLock failed, object already locked.");
        else
            throw ex;
    }

    var timer = new Timer(new TimerCallback(o =>
    {
        Console.WriteLine("GetAndLock(after 6s): int = {0}", cache.GetAndLock("int", TimeSpan.FromSeconds(5), out lockHandle));
    }), null, 6000, Timeout.Infinite);
}

Sortie :

Information sur un objet du cache

La fonction GetCacheItem de la classe DataCache renvoie une instance de DataCacheItem regroupant quelques informations sur l’objet mis en cache.

static void TestCacheItem()
{
    var factory = new DataCacheFactory();
    var cache = factory.GetCache("InfineGroup");

    cache.Put("int", 5, TimeSpan.FromSeconds(15));

    var item = cache.GetCacheItem("int");
    Console.WriteLine("CacheItem informations :");

    foreach (var prop in item.GetType().GetProperties())
    {
        Console.WriteLine("{0} = {1}", prop.Name, prop.GetValue(item, null));
    }
}

Sortie :

Recherche dans le cache

La recherche dans le cache peut se faire grâce aux méthodes GetObjectsByTag, GetObjectsByAllTags, GetObjectsByAnyTag et GetObjectsInRegion.

Exemple :

static void TestSearch()
{
    var factory = new DataCacheFactory();
    var cache = factory.GetCache("InfineGroup");

    cache.CreateRegion("Desk");
    var rnd = new Random();

    for (var i = 0; i < 1000; i++)
    {
        var tagParite = new DataCacheTag(i % 2 == 0 ? "Paire" : "Impaire");
        var tagRandom = new DataCacheTag(rnd.Next(100).ToString());

        cache.Put("int-" + i, i, new[] { tagParite, tagRandom }, "Desk");
    }

    foreach (var pair in cache.GetObjectsByTag(new DataCacheTag("67"), "Desk"))
    {
        var item = cache.GetCacheItem(pair.Key, "Desk");
        Console.WriteLine("{0} : {1} [Tag: {2}]", pair.Key, pair.Value, string.Join(",", item.Tags));
    }
}

Sortie :

Ressources

Examples C# : AppFabricTests.zip

Liens MSDN :