As aplicações que desenvolvemos hoje são cada vez mais complexas. Essa complexidade se deve a muitos fatores que vão desde a globalização até a evolução da forma como as empresas fazem B2B e B2C.
Muitas vezes, o banco de dados acaba sendo um gargalo. Uma vez que a sua rede está 100% ok, uma forma viável de reduzir a carga no banco de dados é utilizar alguma forma de cache. Se esse cache tiver *hit*, ou seja, acertos em uma taxa considerável, isso pode trazer muitas vantagens para a sua aplicação.
Cache com EHCache
EHCache é um solução open source de cache para Java. O EHCache lhe permite fazer vários tipos de cache, como por exemplo aplicar cache junto ao Hibernate/JPA, cache de páginas web usando GZIP e até mesmo cache distribuído, que é o tema deste post.
EHCache – Arquitetura Modular
O EHCache tem uma arquitetura modular, logo, você pode escolher os módulos que deseja utilizar. Neste post vou mostrar como trabalhar com o Cache Server.
REST EHCache Server
Quando precisamos trabalhar com cache distribuído, existem duas opções com o EHCache. A primeira é utilizar o cluster do próprio Terracota (solução de cache distribuído da Terracota, empresa que comprou o EHCache 🙂 ); a outra opção é usar o cache Server.
O Cache Server tem uma API REST muito bacana. O mais legal disso é que como REST funciona em cima de http, você pode consumir o Cache Server com qualquer linguagem/tecnologia. Por exemplo, você poderia ter cache distribuído para várias aplicações escritas em linguagens diferentes e rodando em diverentes sistemas operacionais distribuídos pelo mundo.
Outro ponto a favor de o Cache Server ser em REST é que com isso podemos usar as soluções de loadbalance e até mesmo DNS, isso porque ele vai ficar rodando em um servidor de aplicação parão JEE. Por default, o REST Server vem empacotado em uma distribuição standalone que roda com um Glassfish embutido. Neste post, vou mostrar como adaptar o REST EHCache Server para rodar no JBoss AS 5.
Ajustes para Rodar no JBoss AS 5.x
Primeiro você precisa baixar o ehcache server, depois disso vamos deletar algumas bibliotecas e adicionar outras. Essa é a unica modificação que precisamos fazer para rodá-lo no JBoss AS. Neste post, vou utilizar a versão 1.0.0.
Entre na pasta onde você extraiu o cache server – vou me referir a essa pasta como $Download. Depois entre na seguinte pasta: $Download/war/WEB-INF/lib e delete os seguintes jars:
- activation-1.1.jar
- jaxb-api-2.0.jar
- jaxb-impl-2.1.9.jar
- stax-api-1.0.jar
- resolver-20050927.jar
Agora gere um war com todo o conteúdo das pastas WEB-INF e META-INF, pode ser usando as ferramentas do Java, pode ser com o seu IDE, eu fiz isso usando o winrar. 😀
Faça o deploy no JBoss AS 5.x e se divirta muito!
Manipulando o Servidor de Cache com REST
Agora vou mostrar como criar aréas de cache no REST Cache Server do EHCache e como adicionar e recuperar objetos desse cache. Para isso, vou utilizar dois frameworks que são o XStream e o HttpClient. Então vamos à classe genérica que criei para manipular o cache server, que chamei carinhosamente de HttpClientRESTBaby.
/**
*
* @author Diego Pacheco
*
*/
public class MainRESTBabyRules {
public static void main(String[] args) throws Throwable {
HttpClientRESTBaby restBaby = new HttpClientRESTBaby();
restBaby.createCache("cacheName1");
restBaby.getCache("cacheName1");
People p1 = new People();
p1.setId(1l);
p1.setName("Diego Pacheco");
restBaby.addEntry("cacheName1", p1.getId().toString(), p1);
String result = restBaby.getEntry("cacheName1", p1.getId().toString());
People p2 = (People) new XStream().fromXML(result);
System.out.println("P2: " + p2.toString());
System.out.println("p1 is equals p2 ? " + p1.equals(p2) );
}
}
Nessa classe, estou disponibilizando os seguintes métodos:
- createCache(String cacheName)
- getCache(String cacheName)
- addEntry(String cacheName,String key,Serializable value)
- getEntry(String cacheName,String key)
createCache: Cria uma área de cache dentro do cache server, você chama uma vez e ele cria a área. Se você chamar de novo, ele vai retornar um conflito, porque o cache já foi criado no server.
getCache: Retorna um XML no padrão EHCache com a configuração de cache que está associada a essa área de cache no server.
addEntry: Adiciona um elemento ao cache. Perceba que é uma combinação de chave/valor, onde a chave deve ser única. A chave vai acabar sendo parte do URL REST, e o valor deve ser Serializable, logo, você pode, sim, passar um Objeto do seu domínio da sua aplicação como um pojo de pessoa.
getEntry: Retorna uma entrada do cache a partir de uma chave. Essa chave deve existir no servidor de cache, do contrário você não terá o valor desejado. Neste caso, estou retornando o xml do Xstream que define o objeto, seria possível configurar o XStream para gerar JSon. A vantagem disso é que o EHCache server entende o content type de JSon. 😀
Algumas considerações sobre o código
A constante REST_CACHE_SERVER define onde está o servidor de cache. Perceba que o nome da aplicação web(.war) faz parte do url. Caso você tenha empacotado com outro nome, não se esqueça de modificar esse valor, bem como se você estiver usando-o em outra porta.
Um teste básico
Agora podemos subir o JBoss AS com o servidor de cache dentro dele. Vamos a um simples código de testes para verificar se tudo está funcionando como deveria estar. Confira o código abaixo:
/**
*
* @author Diego Pacheco
*
*/
public class MainRESTBabyRules {
public static void main(String[] args) throws Throwable {
HttpClientRESTBaby restBaby = new HttpClientRESTBaby();
restBaby.createCache("cacheName1");
restBaby.getCache("cacheName1");
People p1 = new People();
p1.setId(1l);
p1.setName("Diego Pacheco");
restBaby.addEntry("cacheName1", p1.getId().toString(), p1);
String result = restBaby.getEntry("cacheName1", p1.getId().toString());
People p2 = (People) new XStream().fromXML(result);
System.out.println("P2: " + p2.toString());
System.out.println("p1 is equals p2 ? " + p1.equals(p2) );
}
}
Ao rodar esse código, você deve ver a seguinte saída no console:
++http://localhost:8080/ehcache-server-jboss-1.0.0/rest/cacheName1
+Result[
+]
+201
+Created
+HTTP/1.1 201 Created
++http://localhost:8080/ehcache-server-jboss-1.0.0/rest/cacheName1
+Result[
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><cache><cacheConfiguration><clearOnFlush>true</clearOnFlush><diskAccessStripes>1</diskAccessStripes><diskExpiryThreadIntervalSeconds>120</diskExpiryThreadIntervalSeconds><diskPersistent>false</diskPersistent><diskSpoolBufferSizeMB>30</diskSpoolBufferSizeMB><diskStorePath>C:\Users\DIEGOP~1\AppData\Local\Temp\</diskStorePath><eternal>false</eternal><loggingEnabled>false</loggingEnabled><maxElementsInMemory>10000</maxElementsInMemory><maxElementsOnDisk>10000000</maxElementsOnDisk><name>cacheName1</name><overflowToDisk>true</overflowToDisk><statistics>true</statistics><timeToIdleSeconds>120</timeToIdleSeconds><timeToLiveSeconds>120</timeToLiveSeconds></cacheConfiguration><description>[ name = cacheName1 status = STATUS_ALIVE eternal = false overflowToDisk = true maxElementsInMemory = 10000 maxElementsOnDisk = 10000000 memoryStoreEvictionPolicy = LRU timeToLiveSeconds = 120 timeToIdleSeconds = 120 diskPersistent = false diskExpiryThreadIntervalSeconds = 120 cacheEventListeners: net.sf.ehcache.statistics.LiveCacheStatisticsWrapper hitCount = 0 memoryStoreHitCount = 0 diskStoreHitCount = 0 missCountNotFound = 0 missCountExpired = 0 size = 0 ]</description><name>cacheName1</name><statistics><averageGetTime>0.0</averageGetTime><cacheHits>0</cacheHits><diskStoreSize>0</diskStoreSize><evictionCount>0</evictionCount><inMemoryHits>0</inMemoryHits><memoryStoreSize>0</memoryStoreSize><misses>0</misses><onDiskHits>0</onDiskHits><size>0</size><statisticsAccuracy>STATISTICS_ACCURACY_BEST_EFFORT</statisticsAccuracy></statistics><uri>http://localhost:8080/ehcache-server-jboss-1.0.0/rest/cacheName1</uri></cache>
+]
+200
+OK
+HTTP/1.1 200 OK
++http://localhost:8080/ehcache-server-jboss-1.0.0/rest/cacheName1/1
+Result[
+]
+201
+Created
+HTTP/1.1 201 Created
++http://localhost:8080/ehcache-server-jboss-1.0.0/rest/cacheName1/1
+Result[
<com.blogspot.diegopacheco.cache.playground.cacheserver.rest.domain.People>
<id>1</id>
<name>Diego Pacheco</name>
</com.blogspot.diegopacheco.cache.playground.cacheserver.rest.domain.People>
+]
+200
+OK
+HTTP/1.1 200 OK
P2: ID: 1, Name: Diego Pacheco
p1 is equals p2 ? true
Se você quiser, pode pegar os fontes completos, bem como o projeto do eclipse no meu repositório do subversion na web. Espero que você tenha gostado e que também tire proveito do cache server em suas aplicações.
Abraços.