Les performances d’Hibernate (intégré via GORM) dans Grails peuvent être améliorées par le cache de second niveau et le cache de requête. Nous allons voir comment gérer ce cache pour une architecture contenant des serveurs en cluster.
La problématique : haute disponibilité
La mise en place d’un cluster de plusieurs instances Grails est indispensable dans le cadre d’une architecture haute disponibilité.
Dans le cas où le cache Hibernate est activé, si le cache n’est pas partagé entre les différentes instances, des problèmes apparaissent :
- Problème de synchronisation : les données modifiées par une instance, ne sont pas connues des autres instances ;
- Problème de performance au niveau de la base de données : chaque instance créée son cache indépendamment des autres. Plus le nombre d’instances est élevé, plus la charge de la base augmente.
La mise en commun du cache Hibernate est une nécessité dès qu’une telle architecture est à prévoir.
Une solution : serveur Terracotta
Le cache Hibernate utilise par défaut Ehcache qui dispose d’un système de partage de cache : Terracotta server.
Dans les grandes lignes, Terracotta server se décline en une solution open-source et commerciale.
La solution open-source permet une architecture maître/esclave entre différents serveurs Terracotta.
La solution commerciale ajoute le support et le Terracotta Server Array permettant une souplesse dans la scalabilité.
Une architecture : maître/esclave
Dans notre cas, nous allons opter pour la solution la plus simple, une architecture maître/esclave entre les instances Terracotta.
Dans cette architecture à 2 serveurs physiques + 1 base de données, chaque front dispose de son serveur Terracotta.
Mise en place des serveurs Terracotta
La configuration d’un serveur Terracotta est relativement facile.
Il suffit de déclarer la liste des serveurs via le nœud et activer le mode
networked-active-passive
.
%(user.home)/terracotta/server-data
%(user.home)/terracotta/server-logs
9530
%(user.home)/terracotta/server-data
%(user.home)/terracotta/server-logs
9530
networked-active-passive
5
/var/log/terracotta/client-logs
Le premier serveur est le maître, le 2ème l’esclave. Le fonctionnement est classique, si le maître tombe, l’esclave prend le relais et devient maître.
Grails 2 : étape 1 – dépendances
Avant toute chose, pour utiliser les librairies Terracotta, il faut
ajouter le dépôt et les dépendances dans le BuildConfig.groovy
:
repositories {
// ...
mavenRepo 'http://www.terracotta.org/download/reflector/releases'
// ...
}
dependencies {
// ...
runtime 'net.sf.ehcache:ehcache-core:2.4.6'
runtime 'net.sf.ehcache:ehcache-terracotta:2.4.6'
runtime 'org.terracotta:terracotta-toolkit-1.3-runtime:3.3.0'
// ...
}
Les versions utilisées ici sont celles compatibles avec Grails 2 et Hibernate.
Grails 2 : étape 2 – Serializable
Tous les objets du domaine qui seront en cache devront implémenter l’interface Serializable
, afin de pouvoir transiter entre les différents nœuds du cluster :
class DomainClass implements Serializable {
// ...
static mappings = { cache true }
// ...
}
Grails 2 : étape 3 – ehcache.xml
Il suffit de déclarer le fichier ehcache.xml
dans src/java
.
Petite subtilité, l’emplacement des serveurs Terracotta ${terracotta.config.location}
est configurable via une variable d’environnement.
Un outil : dev-console
Un client lourd existe afin de contrôler l’activité sur les serveurs Terracotta.
Il suffit de lancer de bin/dev-console.sh
à partir du répertoire d’installation de Terracotta :
Cet outil peut être utilisé en production afin de consulter les statistiques sur les objets du cache, en utilisant par exemple un tunnel SSH.
Des alternatives
Ehcache n’est pas la seule solution utilisable en tant que cache de second niveau pour Hibernate, à choisir en fonction du contexte et de l’utilisation :
- JBoss Infinispan successeur de JBoss Cache,
- Memcached,
- Hazelcast,
- …