Introdução ao Ehcache

Introdução ao Ehcache

*1. Visão geral *

Neste artigo, apresentaremos o Ehcache, um cache baseado em Java de código aberto amplamente utilizado. Possui armazenamento de memória e disco, ouvintes, carregadores de cache, API RESTful e SOAP e outros recursos muito úteis.

Para mostrar como o cache pode otimizar nosso aplicativo, criaremos um método simples que calculará valores quadrados dos números fornecidos. Em cada chamada, o método chama o método _calculateSquareOfNumber (número int) e imprime a mensagem informativa no console.

Com este exemplo simples, queremos mostrar que o cálculo dos valores ao quadrado é feito apenas uma vez e todas as outras chamadas com o mesmo valor de entrada estão retornando resultado do cache.

É importante notar que estamos totalmente focados no Ehcache (sem Spring); se você quiser ver como o Ehcache funciona com o Spring, consulte o link de leitura:/spring-cache-tutorial [este artigo].

===* 2. Dependências do Maven *

Para usar o Ehcache, precisamos adicionar esta dependência do Maven:

<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.1.3</version>
</dependency>

A versão mais recente do artefato Ehcache pode ser encontrada em aqui.

===* 3. Configuração de cache *

O Ehcache pode ser configurado de duas maneiras:

  • A primeira maneira é através do Java POJO, onde todos os parâmetros de configuração são configurados pela API Ehcache *A segunda maneira é a configuração através de um arquivo XML, onde podemos configurar o Ehcache de acordo com definição de esquema fornecida

Neste artigo, mostraremos as duas abordagens - Java e configuração XML.

====* 3.1 Configuração Java *

Esta subseção mostrará como é fácil configurar o Ehcache com POJOs. Além disso, criaremos uma classe auxiliar para facilitar a configuração e a disponibilidade do cache:

public class CacheHelper {

    private CacheManager cacheManager;
    private Cache<Integer, Integer> squareNumberCache;

    public CacheHelper() {
        cacheManager = CacheManagerBuilder
          .newCacheManagerBuilder().build();
        cacheManager.init();

        squareNumberCache = cacheManager
          .createCache("squaredNumber", CacheConfigurationBuilder
            .newCacheConfigurationBuilder(
              Integer.class, Integer.class,
              ResourcePoolsBuilder.heap(10)));
    }

    public Cache<Integer, Integer> getSquareNumberCacheFromCacheManager() {
        return cacheManager.getCache("squaredNumber", Integer.class, Integer.class);
    }

   //standard getters and setters
}

Para inicializar nosso cache, primeiro, precisamos definir o objeto Ehcache CacheManager. Neste exemplo, estamos criando um cache padrão squaredNumber ” com o _newCacheManagerBuilder () _ API .

O cache simplesmente mapeará as chaves Integer para os valores Integer.

Observe como, antes de começarmos a usar o cache definido, precisamos inicializar o objeto CacheManager com o método _init () _.

Finalmente, para obter nosso cache, podemos apenas usar a API _getCache () _ com o nome fornecido, a chave e os tipos de valor do nosso cache.

Com essas poucas linhas, criamos nosso primeiro cache, que agora está disponível para nosso aplicativo.

====* 3.2 Configuração XML *

O objeto de configuração da subseção 3.1. é igual a usar esta configuração XML:

<cache-template name="squaredNumber">
    <key-type>java.lang.Integer</key-type>
    <value-type>java.lang.Integer</value-type>
    <heap unit="entries">10</heap>
</cache-template>

E para incluir esse cache em nosso aplicativo Java, precisamos ler o arquivo de configuração XML em Java:

URL myUrl = getClass().getResource(xmlFile);
XmlConfiguration xmlConfig = new XmlConfiguration(myUrl);
CacheManager myCacheManager = CacheManagerBuilder
  .newCacheManager(xmlConfig);

===* 4. Teste Ehcache *

Na seção 3. mostramos como você pode definir um cache simples para seus propósitos. Para mostrar que o cache realmente funciona, criaremos a classe SquaredCalculator que calculará o valor ao quadrado da entrada fornecida e armazenaremos o valor calculado em um cache.

Obviamente, se o cache já contiver valor calculado, retornaremos o valor em cache e evitaremos cálculos desnecessários:

public class SquaredCalculator {
    private CacheHelper cache;

    public int getSquareValueOfNumber(int input) {
        if (cache.getSquareNumberCache().containsKey(input)) {
            return cache.getSquareNumberCache().get(input);
        }

        System.out.println("Calculating square value of " + input +
          " and caching result.");

        int squaredValue = (int) Math.pow(input, 2);
        cache.getSquareNumberCache().put(input, squaredValue);

        return squaredValue;
    }

   //standard getters and setters;
}

Para concluir nosso cenário de teste, também precisaremos do código que calculará os valores quadrados:

@Test
public void whenCalculatingSquareValueAgain_thenCacheHasAllValues() {
    for (int i = 10; i < 15; i++) {
        assertFalse(cacheHelper.getSquareNumberCache().containsKey(i));
        System.out.println("Square value of " + i + " is: "
          + squaredCalculator.getSquareValueOfNumber(i) + "\n");
    }

    for (int i = 10; i < 15; i++) {
        assertTrue(cacheHelper.getSquareNumberCache().containsKey(i));
        System.out.println("Square value of " + i + " is: "
          + squaredCalculator.getSquareValueOfNumber(i) + "\n");
    }
}

Se executarmos nosso teste, obteremos esse resultado em nosso console:

Calculating square value of 10 and caching result.
Square value of 10 is: 100

Calculating square value of 11 and caching result.
Square value of 11 is: 121

Calculating square value of 12 and caching result.
Square value of 12 is: 144

Calculating square value of 13 and caching result.
Square value of 13 is: 169

Calculating square value of 14 and caching result.
Square value of 14 is: 196

Square value of 10 is: 100
Square value of 11 is: 121
Square value of 12 is: 144
Square value of 13 is: 169
Square value of 14 is: 196

Como você pode notar, o método _calculate () _ estava fazendo cálculos apenas na primeira chamada. Na segunda chamada, todos os valores foram encontrados no cache e retornados a partir dele.

===* 5. Outras opções de configuração do Ehcache *

Quando criamos nosso cache no exemplo anterior, era um cache simples sem opções especiais. Esta seção mostrará outras opções que são úteis na criação de cache.

====* 5.1. Persistência do disco *

Se houver muitos valores para armazenar no cache, podemos armazenar alguns desses valores no disco rígido.

PersistentCacheManager persistentCacheManager =
  CacheManagerBuilder.newCacheManagerBuilder()
    .with(CacheManagerBuilder.persistence(getStoragePath()
      + File.separator
      + "squaredValue"))
    .withCache("persistent-cache", CacheConfigurationBuilder
      .newCacheConfigurationBuilder(Integer.class, Integer.class,
        ResourcePoolsBuilder.newResourcePoolsBuilder()
          .heap(10, EntryUnit.ENTRIES)
          .disk(10, MemoryUnit.MB, true))
      )
  .build(true);

persistentCacheManager.close();

Em vez do CacheManager padrão, agora usamos PersistentCacheManager, que persistirá em todos os valores que não podem ser salvos na memória.

Na configuração, podemos ver que o cache salvará 10 elementos na memória e alocará 10 MB no disco rígido para persistência.

====* 5.2 Expiração de dados *

Se armazenarmos muitos dados em cache, é natural que salvemos os dados armazenados em cache por um período de tempo para evitar o grande uso de memória.

O Ehcache controla a atualização dos dados através da interface Expiry:

CacheConfiguration<Integer, Integer> cacheConfiguration
  = CacheConfigurationBuilder
    .newCacheConfigurationBuilder(Integer.class, Integer.class,
      ResourcePoolsBuilder.heap(100))
    .withExpiry(Expirations.timeToLiveExpiration(Duration.of(60,
      TimeUnit.SECONDS))).build();

Nesse cache, todos os dados permanecerão ativos por 60 segundos e, após esse período, serão excluídos da memória.

===* 6. Conclusão*

Neste artigo, mostramos como usar o cache Ehcache simples em um aplicativo Java.

Em nosso exemplo, vimos que mesmo um cache configurado simplesmente pode salvar muitas operações desnecessárias. Além disso, mostramos que podemos configurar caches através de POJOs e XML e que o Ehcache possui alguns recursos interessantes - como persistência e expiração de dados.

Como sempre, o código deste artigo pode ser encontrado no GitHub.