Introdução ao BTRFS
junho 30, 2017

Cache usando Spring Cache e EhCache

Recentemente tivemos que desenvolver um webservice para uma operadora de telefonia colombiana para atender a uma demanda urgente da presidencia. O webservice precisaria consumir um serviço REST (que trafega XML), fazer o parsing do XML, aplicar as regras de negócio definidas pela operadora e entregar a resposta em JSON que seria por sua vez usada em um aplicativo móvel.

Nosso webservice seria usado para exibir diversos dados para o cliente final da companhia telefônica. O aplicativo móvel segrega os dados em diversas telas, porém, o serviço do qual buscamos os dados “brutos” nos entrega as informações todas misturados: não há como buscar da fonte apenas os registros que são usados para a tela 1, depois buscar os dados para a tela 2 e assim por diante.

Nesse cenário, para preencher a tela 1 ou a tela N, precisamos tirar do serviço “fonte” o mesmo fluxo de dados.

Um outro complicador é que esse serviço REST é bastante lento para responder. Para evitar que o aplicativo móvel tenha que “pagar o mesmo preço” sempre que o usuário final navegue em diferentes telas resolvemos, entre outras providências e otimizações, implementar uma solução de caching do XML que o REST retorna.

Exemplo de uso do Spring Cache + EhCache

Usamos o Spring Cache e o EhCache para guardar em memória o resultado do serviço REST. O snippet abaixo mostra o código da classe de configuração do Spring  similar à que usamos na solução final.

package br.com.omniatech.examples.caching.configuration;


import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleCacheErrorHandler;
import org.springframework.cache.interceptor.SimpleCacheResolver;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import net.sf.ehcache.config.CacheConfiguration;

@Configuration
@EnableCaching
@ComponentScan(basePackages = { "br.com.omniatech" })
public class SpringConfiguration implements CachingConfigurer {

	@Bean(destroyMethod="shutdown")
	public net.sf.ehcache.CacheManager ehCacheManager() {
		net.sf.ehcache.config.Configuration config = new net.sf.ehcache.config.Configuration();

		CacheConfiguration cacheConfiguration = new CacheConfiguration();
		cacheConfiguration.setName("ExampleCache");
		cacheConfiguration.setTimeToLiveSeconds(120);
		cacheConfiguration.setMemoryStoreEvictionPolicy("LRU");
		cacheConfiguration.setMaxEntriesLocalHeap(1000);

		config.addCache(cacheConfiguration);

		return net.sf.ehcache.CacheManager.newInstance(config);
	}

	@Bean
	@Override
	public CacheManager cacheManager() {
		net.sf.ehcache.CacheManager ehCacheManager = ehCacheManager();
		EhCacheCacheManager ehCacheCacheManager = new EhCacheCacheManager(ehCacheManager);
		return ehCacheCacheManager;
	}

	@Bean
	@Override
	public KeyGenerator keyGenerator() {
		return new SimpleKeyGenerator();
	}

	@Bean
	@Override
	public CacheResolver cacheResolver()    {
		return new SimpleCacheResolver(cacheManager());
	}

	@Bean
	@Override
	public CacheErrorHandler errorHandler() {
		return new SimpleCacheErrorHandler();
	}

}

O código acima configura todo o framework de cache e cria um cache de nome “ExampleCache” que vai manter os resultados do serviço REST por 120 segundos. Após esse tempo, caso o usuário no aplicativo móvel solicite mais dados a requisição ao REST será feita novamente.

A seguir podemos ver um exemplo de método cujo resultado vai ser “cacheado” pelo Spring:

package br.com.omniatech.examples.caching.business;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

@Component
public class ExampleBusinessLogic {

	@Cacheable("ExampleCache")
	public String expensiveStringFunction(String string) {
		//Sleeping to simulate the expensiveness of the function
		try {
			Thread.sleep(1000);
		} catch (InterruptedException ie) {}
		
		String now = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(new Date());
		System.out.println(String.format("CACHE MISS for key '%1$s' at %2$s", string, now));
		
		return String.format("%1$s was cached at %2$s", string , now);
	}
}

Na primeira chamada do método para um determinado valor de “string” o corpo do método é executado porém, qualquer outra chamada dentro do TTL do cache (2 minutos), retornará o valor dessa primeira chamada. O corpo do método não é chamado nesses casos por isso é importante que ele não possua outras responsabilidades que devam ser satisfeitas a cada chamada.

Download

O código fonte completo do projeto de exemplo pode ser baixado no GitHub em:

https://github.com/ederrf/springcache-ehcache-example

Comments are closed.