SeamFramework.orgCommunity Documentation
Dans presque toutes les applications d'entreprise, la base de données est le premier goulet d'étranglement et le moins scalable tierce-partie de l'environement d'exécution. Les gens venant des environements PHP/Ruby vont essayer de vous dire que les architectures surnommées "ne partage rien" se dimensionnent bien. Et bien c'est peut être littérallement vrai, je ne sait pas combien d'application intéréssantes multi-utilisateurs peuvent être implémentées avec aucun partage de ressources entre les différentes noeuds du cluster. Que pensent vraiment ces personnes loufoques d'une architecture "ne rien partager sauf la base de données". Bien sur, le partage de la base de données est un problème principal quand on dimensionne une application multi utilisateur donc réclammer que cette architecture soit hautement dimensionnable est absurde et dites vous que c'est pas mal de ce type d'application sur les quelles ces gens passent la plus part du temps à travailler dessus.
Le moins que nous puissions rendre faisable et de partager la base de données le moins souvent est d'une grande valeur.
Cela demande un cache. Et bien , pas juste un seul cache. Une application Seam bien désignée va fonctionner avec une stratégie de cache multi couche riche qui va impacter chaque couche de l'application:
La base de données a , bien sûr, son propre cache. C'est super-important, mais ne peut pas être dimensionné comme un cache dans une application tierce.
Votre solution ORM (Hibernate ou une autre implémentation JPA) a un cache de second niveau de données de la base de données. C'est une fonctionnalité très puissante mais elle est souvant mal utilisée. Dans une environement en cluster, la conservation des données dans le cache transactionnel consistant au travers du cluster entier et avec la base de données est assez couteux. Cela gagne en plus de sens s'il est partagé entre plusieurs utilisateurs et s'il est mis à jours rarement. Dans les architectures transactionnelles sans état, les gens essaye souvent d'utiliser le cache de second niveau pour les états conversationnel. C'est toujours très mauvais et c'est spécialement faux dans Seam.
Le contexte de conversation est un cache de l'état conversationnel. Les composant que vous mettez dans le contexte conversationnel peuvent conserver et mettre en cache l'état relatif à l'intéraction de l'utilisateur courrant.
En particulier, le contexte de persistance géré par Seam (ou un contexte géré par container EJB étendue s'associant avec un bean de session avec état d'étendue conversationnel) agit comme un cache de données qui a été lu pendant la conversation courrante. Ce cache tends à avoir une fréquence très élevé de solicitation! Seam optimise la réplication des contextes de persistance géré par Seam dans un environement en cluster et il n'y a pas de prérequis pour la consistance transactionnelle avec la base de données (un véroullage optimiste est suffisant) donc vous n'avez pas besoin de trop vous inquiéter au sujet de l'implication en terme de performations de ce cache, à moins que ne lisiez des milliers d'objets dans un seul contexte de persistance.
L'application peu mettre en cache l'état non-transactionnel dans le contexte d'application de Seam. Convervez l'état dans le contexte applicatif est bien sur invisible pour les autres noeuds dans le cluster.
L'application peut mettre en cache l'état transactionnel en utilisant le composant cacheProvider
de Seam qui intègre JBossCache, Jboss POJO Cache ou EHCache dans l'environement de Seam. Cet état sera visible pour les autres noeuds si votre cache est capable de s'exécuter dans un mode en cluster.
Enfin, Seam vous permet de mettre en cache les fragments rendus d'une page JSF. A la différence du cache de second niveau de l'ORM, ce cache n'est pas automatiquement invalidé quand les données changent, donc vous avez besoin d'écrire le code de l'application pour réaliser une invalidation explicite ou de mettre des politiques d'expirations appropriées.
Pour plus d'informations à propos du cache de second niveau, vous allez avoir besoin de vous référer à la document de votre solution ORM, car c'est un sujet extrèmement complexe. Dans cette section, nous allons parler de l'utilisation du chache directemnet via le composant cacheProvider
, ou comme le cache de fragment de page, via le controle <s:cache>
.
Le composant livré cacheProvider
gère une instance de:
org.jboss.cache.TreeCache
org.jboss.cache.Cache
org.jboss.cache.aop.PojoCache
net.sf.ehcache.CacheManager
Vous pouvez de manière sure mettre tout objet Java immuable dans le cache, et il sera stocké dans le cache et répliqué au travers du cluster (en supposant que la réplication est disponible et activée). Si vous voulez conserver les objets mutables dans le cache, vous allez avoir besoin de lire la documentation nécéssaire à l'outil de cache pour comprendre commen lui notifier du changement dans la mise en cache auprès de l'outil du cache.
Pour utiliser cacheProvider
, vous aller devoir inclure les fichiers jars de l'implémentation de ce cache dans votre projet:
jboss-cache.jar
- JBoss Cache 1.4.1
jgroups.jar
- JGroups 2.4.1
jboss-cache.jar
- JBoss Cache 2.2.0
jgroups.jar
- JGroups 2.6.2
jboss-cache.jar
- JBoss Cache 1.4.1
jgroups.jar
- JGroups 2.4.1
jboss-aop.jar
- JBoss AOP 1.5.0
ehcache.jar
- EHCache 1.2.3
Si vous utilisez JBoss Cache dans un containeur autre que le JBoss Application Server, allez voir la page sur JBoss Cache wiki pour les dépendances.
Pour un déploiement EAR de Seam, nous vous recommandaons que les fichiers jars de la mise en cache et la configuration soient directement mis dans l'EAR.
Vous allez avoir aussi besoinde fournir un fichier de configuration pour JBossCache. Placez treecache.xml
avec une configuration de la mise en cache approprié dans le classpath (par exemple le fichier jar ejb ou WEB-INF/classes
). JBossCache a de nombreuses horreurs et des réglages de configurations confus, donc nous n'allons pas en parler ici. Merci de vous référer à la document de JBossCache pour plus d'information.
Vous trouverez un exemple de treecache.xml
dans examples/blog/resources/treecache.xml
.
EHCache sera exécuté dans sa configuration par défaut sans fichier de configuration
Pour modifier le fichier de configuration en cours d'utilisation, configurez votre cache dans components.xml
:
<components xmlns="http://jboss.com/products/seam/components"
xmlns:cache="http://jboss.com/products/seam/cache">
<cache:jboss-cache-provider configuration="META-INF/cache/treecache.xml" />
</components
>
Maintenant, vous pouvez injecter votre cache dans tout composant de Seam:
@Name("chatroomUsers")
@Scope(ScopeType.STATELESS)
public class ChatroomUsers
{
@In CacheProvider cacheProvider;
@Unwrap
public Set<String
> getUsers() throws CacheException {
Set<String
> userList = (Set<String
>) cacheProvider.get("chatroom", "userList");
if (userList==null) {
userList = new HashSet<String
>();
cacheProvider.put("chatroom", "userList", userList);
}
return userList;
}
}
Si vous voulez avoir de multplies configurations de mises en cache dans votre application, utilisez components.xml
pour configurer vos différents fournisseur de mise en cache:
<components xmlns="http://jboss.com/products/seam/components"
xmlns:cache="http://jboss.com/products/seam/cache">
<cache:jboss-cache-provider name="myCache" configuration="myown/cache.xml"/>
<cache:jboss-cache-provider name="myOtherCache" configuration="myother/cache.xml"/>
</components
>
L'utlisation la plus intéressante de JBossCache est le tag <s:cache>
, la solution de Seam pour le problème de mettre en cache les fragments de pages.<s:cache>
utilise pojoCache
en interne, donc nous avez besoin de suivre les étapes listées ci dessous avant de pouvoir l'utiliser. (Mettez les jars dans le EAR, barboter dans les horribles options de configurations , etc.)
<s:cache>
est utilisé pour mettre en cache quelques contenues rendues rarement. par exemple, la page bienvenue de votre blog qui affiche les entrées récentes du blog:
<s:cache key="recentEntries-#{blog.id}" region="welcomePageFragments">
<h:dataTable value="#{blog.recentEntries}" var="blogEntry">
<h:column>
<h3
>#{blogEntry.title}</h3>
<div>
<s:formattedText value="#{blogEntry.body}"/>
</div>
</h:column>
</h:dataTable>
</s:cache
>
La key
permet d'avoir de multiples versions en cache de chaque fragment de page. Dans ce cas, il n'y a qu'une version mise en cache par blog. La region
détermine le cache ou le noeud local pour toutes les versions qui seront à stocker. Les noeuds différents peuvent avoir des politiques d'expirations différentes. (C'est du boulot que vous configuriez tout en utilisant les horribles options de configurations susmentionnées.)
Bien sur, le gros problème avec <s:cache>
est qu'il est trop stupide pour connaitre quand les données sousjacentes changent (par exemple, quand les billets d'un bloggeur ont une nouvelle entrée). Donc vous avez besoin de virer les fragment en cache manuellement:
public void post() {
...
entityManager.persist(blogEntry);
cacheProvider.remove("welcomePageFragments", "recentEntries-" + blog.getId() );
}
Autre alternative, si ce n'est pas critique que les modifications ne soient pas immédiatement visitble pour l'utilisateur, vous pouvez définir une periode d'expiration courte dans le noeud de JbossCache.