Back-End

25 jun, 2010

Cache Distribuído com REST e EHCache Server no JBoss AS

Publicidade

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.