Statistiques Hibernate

Elles sont très simples à mettre en oeuvre avec Spring.

Il faut d’abord indiquer dans la configuration hibernate hibernate.cfg.xml (ou dans hibernate.properties) que l’on souhaite les statistiques :

<hibernate-configuration>
<session-factory>
[…]
<property name="hibernate.generate_statistics">true</property>
[…]
</session-factory>
</hibernate-configuration>

Ensuite, au niveau Spring il suffit de détecter le serveur JMX au niveau de la plateforme

<!-- serveur JMX de la plateforme -->
<bean id="mbeanServer"
class="java.lang.management.ManagementFactory"
factory-method="getPlatformMBeanServer" />
<!—autre méthode pour récupérer le mbean server par JNDI
<bean id="mbeanServer" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jmx/runtime"/>
</bean>
-->

et d’instancier le MBean StatisticsService d’Hibernate et de faire détecter par Spring les caractéristiques du MBean

!-- hibernate statistics -->
<bean id="hibernateStatistics" class="org.hibernate.jmx.StatisticsService">
<property name="sessionFactory" ref="sessionFactoryBean" />
<property name="statisticsEnabled" value="true" />
</bean>

<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="bean:name=hibernateStatsMBean" value-ref="hibernateStatistics"/>
</map>
</property>
<property name="assembler">
<bean id="assembler" class="org.springframework.jmx.export.assembler.MethodNameBasedMBeanInfoAssembler">
<property name="methodMappings">
<props>
<prop key="bean:name=hibernateStatsMBean"/>
</props>
</property>
</bean>
</property>
</bean>
Par jconsole on peut ensuite visualiser toutes les statistiques!



Statistiques EHCache

Configuration générale EHCache

Normalement la configuration par défaut ehcache.xml est censée s'appliquer aux nouveaux caches. En réalité, elle ne s'applique qu'aux caches créés programmatiquement mais pas aux caches instanciés via Spring : il est donc conseillé de fixer les paramètres individuellement sur chaque cache (comme montré dans le § suivante). Cette méthode est d'ailleurs meilleure car elle permet de spécifier la taille des caches et leur durée de vie pour chaque type d'objet.

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd">
<diskStore path="java.io.tmpdir" />
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="600"
timeToLiveSeconds="600"
overflowToDisk="true"
diskPersistent="false"
maxElementsOnDisk="20000"
diskExpiryThreadIntervalSeconds="600"
memoryStoreEvictionPolicy="LRU"
/>
</ehcache>

Pour information voici la signification des caractéristiques par défaut du cache :

  • maxElementsInMemory : on limite à 10000 objets maximum dans le cache
  • eternal : le cache n’est pas figé et les éléments les plus vieux sont supprimés et remplacés par les nouveaux quand il n’y a plus de place dans le cache
  • timeToLiveSeconds : la durée maximum de l’objet entre sa création et son expiration (ici 10 minutes soit 600 secondes)
  • timeToIdleSeconds : la durée maximum entre deux accès avant que l’objet n’expire
  • overflowToDisk : si le cache mémoire est plein, les objets les plus anciens sont sérialisés et déchargés sur disque
  • maxElementsOnDisk : 20.000 objets maximum sur mémoire disque
  • diskPersistent : le cache peut être persisté sur disque si besoin (en cas de redémarrage le cache est restitué comme avant l’arrêt)
  • diskExpiryThreadIntervalSeconds : durée de vie de l’objet sur le disque
  • memoryStoreEvictionPolicy : méthode LRU (Least Recently Used) : le moins récemment utilisé est évincé du cache

Configuration Spring d'EHCache

Au niveau spring on instancie le cache manager avec sa configuration dans le classpath ainsi qu'un cache pour l'objet que l'on veut mettre en cache.
<!-- création du cache manager d'EHCache -->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml" />
<property name="shared" value="false" />
</bean>
<!-- création d'un cache pour chaque type d'objet à cacher -->
<bean id="cacheMyObject" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager" ref="cacheManager" />
<property name="cacheName" value="fr.openfarm.model.dto.MyObject" />
<property name="maxElementsInMemory" value="2000" />
<property name="timeToIdle" value="600" />
<property name="timeToLive" value="600" />
<property name="diskExpiryThreadIntervalSeconds" value="600" />
</bean>
En plus de l’instanciation du CacheManager et du Cache par Spring, on instancie une classe utilitaire RegisterEhCacheJmx.java qu'il faut créer spécifiquement et instancier par Spring de la façon suivante (la propriété mbeanServer est optionnelle car par défaut elle pointe sur le serveur MBean de la plateforme) :

<bean id="registerEhcacheStatistics" class="fr.openfarm.util.RegisterEhCacheJmx" init-method="init">
<!--<property name="mbeanServer" ref="mbeanServer" />-->
<property name="cacheManager" ref="cacheManager" />
</bean>

Cette classe "maison"  RegisterEhCacheJmx contient tout simplement l’enregistrement d’un MBean EHcache dans le serveur JMX de la plateforme. Tout est réalisé par la classe net.sf.ehcache.management.ManagementService par la méthode registerMBeans(...). Voici le code de la classe utilitaire :


package fr.openfarm.util;
import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.management.ManagementService;
public class RegisterEhCacheJmx {
private MBeanServer mbeanServer;
private CacheManager cacheManager;
public void init() throws Exception {
if(mbeanServer == null) {
System.out.println("mbeanServer est null => mbeanServer par défaut de la plateforme");
mbeanServer = ManagementFactory.getPlatformMBeanServer();
} else {
System.out.println("mbeanServer=" + mbeanServer.toString());
}
/**
* Enable Ehcache JMX Statistics
* Use CacheManager.getInstance() instead of new CacheManager()
* as net.sf.ehcache.hibernate.SingletonEhCacheProvider is used
* to ensure reference to the same CacheManager instance as used
* by Hibernate
*/
if(cacheManager == null) {
System.out.println("cacheManager est null => cacheManager par défaut");
cacheManager = CacheManager.getInstance();
} else {
System.out.println("cacheManager=" + cacheManager.toString());
}
ManagementService.registerMBeans(cacheManager, mbeanServer, true, true, true, true);
}
public void setMbeanServer(MBeanServer mbeanServer) {
this.mbeanServer = mbeanServer;
}
public void setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
}

Voici ce que l'on obtient avec JConsole :

e

Ceci permet d’avoir le nombre d’objets en cache (pour chaque cache) ainsi que le nombre de hits sur le cache pour pouvoir calculer le taux d’utilisation ou l'efficacité du cache.

Cachez bien!

Vous pouvez en savoir plus sur EHCache et ses nouvelles fonctionnalités sur le le blog de RESPONCIA

PS: la version d'EHCache utilisée est la 1.6.0. Je ne suis certain que les versions précédentes aient  le support JMX.