Nesta postagem do blog, descobriremos como armazenar dados em cache facilmente usando Spring Cache e Couchbase como gerente de loja de apoio.
Índice
- Uma palavra de apresentação
- O
CacheAbstração - A implementação do Couchbase
- Obtendo
couchbase-spring-cachee colocando-o em prática - Conclusão
Uma palavra de apresentação
Houve muito trabalho relacionado a Primavera ultimamente! Temos estado ocupados trabalhando no Dados do Spring Couchbase para atualizá-lo para a geração 2.x do Java SDK, trazendo consigo uma série de novos recursos e aprimoramentos (mas falaremos mais sobre isso em uma postagem posterior no blog)...
Ao longo do caminho, percebemos que há algumas classes no projeto que não estão realmente relacionadas diretamente a Dados do Spring e, como tal, não precisou aderir ao seu ciclo formal de lançamento "Release Train": o cache pacote.
Portanto, iniciamos um novo projeto de exemplo simples do Spring Boot Cache para hospedar o 2.x geração do Couchbase Spring Cache implementação no github (couchbaselabs/couchbase-spring-cache).
Vamos dar uma olhada em como ele pode ser aproveitado para introduzir facilmente o cache com base no Couchbase em um projeto Spring!
O Cache Abstração
O Spring Framework vem com uma abstração leve de um Cache que os desenvolvedores podem usar automaticamente ao anotar métodos em suas classes (por exemplo, em um @Repositório estereótipo).
Para usar os mecanismos de cache de inicialização do Spring, basta anotar seus métodos com um punhado de anotações relacionadas ao cache:
@Cacheablepermitirá que a primeira invocação do método anotado com um determinado conjunto de parâmetros de entrada seja executada, mas o resultado será armazenado em cache e as invocações subsequentes (com o mesmo conjunto) serão atendidas de forma transparente a partir do cache.@CachePutsempre invocará o método e armazenará em cache seu resultado (ao contrário de@Cacheablenão otimiza o fluxo de invocação).@CacheEvictcalculará uma chave de cache a partir dos parâmetros do método anotado e a removerá do cache quando o método for executado (por exemplo, porque a invocação desse método torna uma entrada obsoleta).@Cachingpermite reagrupar o comportamento da anotação anterior em uma única anotação.@CacheConfigpermite que tenhamos parâmetros de cache comuns definidos em uma classe inteira.
A implementação do Couchbase
O mecanismo abstrato é implementado pela estrutura, mas é necessário escolher uma implementação de apoio real. O Spring vem com alguns deles (na memória Mapa, EhCache, Gemfire...), mas é absolutamente possível definir personalizados, simplesmente implementando um Cache e um Gerenciador de cache e torná-lo visível no contexto do Spring.
Esse é o foco de couchbase-spring-cache.
Cada cache tem um nome, e cada valor no cache tem uma chave de cache. A implementação do Couchbase traduzirá isso em uma chave de documento que reflete esse...
- este é um
Cachedocumento relacionado (por padrão, usando o prefixo "cache:“) - está relacionado a um determinado
Cache(adicionando oNOME DO CACHEpara o prefixo acima) - ele armazena um determinado valor de cache em
CACHE_KEY(portanto, no final das contas "cache:CACHE_NAME:CACHE_KEY“).
Por enquanto, os valores devem ser Serializável e são armazenados como um Documento serializável do 2.x Java SDK, mas a transcodificação alternativa poderá ser oferecida no futuro, por exemplo. JSON/JsonDocument...
Obtendo couchbase-spring-cache e colocando-o em prática
Observação:
No momento em que este texto foi escrito, o projeto estava em
1.0-SNAPSHOTportanto, não está disponível na versãoCentral Mavenainda. Você terá que compilar manualmente o jar e adicioná-lo ao seu repositório Maven local.
Download e criação do couchbase-spring-cache projeto
Comece clonando o projeto do github:
|
1 2 |
git clone https://github.com/couchbaselabs/couchbase-spring-cache.git cd couchbase-spring-cache |
Em seguida, crie e instale-o localmente usando o Maven:
|
1 |
mvn clean install |
Você deverá ver uma mensagem de sucesso indicando a versão criada e onde ela foi instalada:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[INFO] Installing /path/to/couchbase-spring-cache/target/couchbase-spring-cache-<span style="background-color: yellow">1.0-SNAPSHOT.jar</span> to <span style="background-color: orange">/path/to/.m2/repository/</span><span style="background-color: lightgrey">com/couchbase/client/couchbase-spring-cache/1.0-SNAPSHOT/couchbase-spring-cache-1.0-SNAPSHOT.jar</span> [INFO] Installing /path/to/couchbase-spring-cache/pom.xml to /path/to/.m2/repository/com/couchbase/client/couchbase-spring-cache/1.0-SNAPSHOT/couchbase-spring-cache-1.0-SNAPSHOT.pom [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ |
Iniciando um projeto de tutorial
Criaremos um projeto de exemplo do Spring Cache usando o Maven e o Inicialização do Spring como base.
Comece criando a seguinte estrutura de diretórios para o projeto em um diretório raiz de sua escolha (usaremos cbcache aqui):
|
1 2 3 4 5 6 7 |
cbcache └── src └── main └── java └── com └── couchbase └── demo |
Por exemplo, use o seguinte comando:
|
1 2 |
mkdir -p cbcache/src/main/java/com/couchbase/demo/ cd cbcache |
Em seguida, inicie o POM na raiz do cbcache em pom.xml com o seguinte conteúdo:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
<!--?xml version="1.0" encoding="UTF-8"?--> 4.0.0 com.couchbase.demo cbcache 0.1.0 org.springframework.boot spring-boot-starter-parent 1.3.0.RELEASE 1.8 org.springframework.boot spring-boot-starter org.springframework spring-context <!--the couchbase cache artifact we built earlier--> com.couchbase.client couchbase-spring-cache 1.0-SNAPSHOT org.springframework.boot spring-boot-maven-plugin |
Adicionar entidade e repositório de gerenciamento de livros
Usaremos o exemplo de um Livro (conforme encontrado no repositório "COMEÇANDO - Armazenamento de dados em cache com o Spring" guia oficial do Spring.io).
Criar o Livro classe de entidade em src/main/java/com/couchbase/demo/Book.java:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
import java.io.Serializable; public class Book implements Serializable { private static final long serialVersionUID = -7674163614777124381L; private String isbn; private String title; public Book(String isbn, String title) { this.isbn = isbn; this.title = title; } public String getIsbn() { return isbn; } public void setIsbn(String isbn) { this.isbn = isbn; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } @Override public String toString() { return "Book{" + "isbn='" + isbn + ''' + ", title='" + title + ''' + '}'; } } |
Observe o
Livroclasse éSerializávelPor enquanto, isso é importante para o armazenamento do Couchbase.
Crie uma interface de repositório simples e uma implementação ingênua que simule um atraso:
em src/main/java/com/couchbase/demo/BookRepository.java:
|
1 2 3 4 5 6 7 |
package com.couchbase.demo; public interface BookRepository { Book getByIsbn(String isbn); } |
em src/main/java/com/couchbase/demo/SimpleBookRepository.java:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
package com.couchbase.demo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class SimpleBookRepository implements BookRepository { private static final Logger log = LoggerFactory.getLogger(SimpleBookRepository.class); @Override public Book getByIsbn(String isbn) { simulateSlowService(); Book result = new Book(isbn, "Some book"); log.info("Actual fetch of " + isbn); return result; } // Don't do this at home private void simulateSlowService() { try { long time = 5000L; Thread.sleep(time); } catch (InterruptedException e) { throw new IllegalStateException(e); } } } |
O principal Inicialização do Spring Aplicativo
Por fim, crie um aplicativo que use o repositório em src/main/java/com/couchbase/demo/Application.java:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
package com.couchbase.demo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.stereotype.Component; @SpringBootApplication public class Application { private static final Logger log = LoggerFactory.getLogger(Application.class); @Component static class Runner implements CommandLineRunner { @Autowired private BookRepository bookRepository; @Override public void run(String... args) throws Exception { long start; log.info(".... Fetching books"); fetchAndLog("isbn-1234"); fetchAndLog("isbn-1234"); fetchAndLog("isbn-1234"); fetchAndLog("isbn-8888"); fetchAndLog("isbn-8888"); } private void fetchAndLog(String isbn) { long start = System.currentTimeMillis(); Book book = bookRepository.getByIsbn(isbn); long time = System.currentTimeMillis() - start; log.info(isbn + " --> " + book + " in " + time + "ms"); } } public static void main(String[] args) { SpringApplication.run(Application.class, args); } } |
Se você executar o Aplicativo's principal em seu IDE (ou se você invocar o método "mvn spring-boot:run" na linha de comando), você perceberá que todas as chamadas são de fato muito lentas:
|
1 2 3 4 5 6 7 8 9 10 11 |
[...] .... Fetching books [...] Actual fetch of isbn-1234 [...] isbn-1234 --> Book{isbn='isbn-1234', title='Some book'} in 5001ms [...] Actual fetch of isbn-1234 [...] isbn-1234 --> Book{isbn='isbn-1234', title='Some book'} in 5004ms [...] Actual fetch of isbn-1234 [...] isbn-1234 --> Book{isbn='isbn-1234', title='Some book'} in 5004ms [...] Actual fetch of isbn-8888 [...] isbn-8888 --> Book{isbn='isbn-8888', title='Some book'} in 5003ms [...] Actual fetch of isbn-8888 [...] isbn-8888 --> Book{isbn='isbn-8888', title='Some book'} in 5003ms |
Adição de recursos de cache do Spring Boot
Para ativar o armazenamento em cache, você deve seguir algumas etapas: primeiro, você deve oferecer ao Spring um Gerenciador de cache @Bean...
Como estaremos usando o CouchbaseCacheManager (é claro), precisaremos nos conectar a um Aglomerado e usar um Balde (a unidade de armazenamento onde o couchbase armazenará os documentos do cache).
Portanto, o CouchbaseCacheManager precisa de um mapeamento entre Cache e os correspondentes nomes Balde para usar, passado como um Mapa.
Em src/main/java/com/couchbase/demo/Application.javaadicione as seguintes declarações de bean:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public static final String BOOK_CACHE = "books"; @Bean(destroyMethod = "disconnect") public Cluster cluster() { //this connects to a Couchbase instance running on localhost return CouchbaseCluster.create(); } @Bean(destroyMethod = "close") public Bucket bucket() { //this will be the bucket where every cache-related data will be stored //note that the bucket "default" must exist return cluster().openBucket("default", ""); } @Bean public CacheManager cacheManager() { Map<String, Bucket> mapping = new HashMap<String, Bucket>(); //we'll make this cache manager recognize a single cache named "books" mapping.put(BOOK_CACHE, bucket()); return new CouchbaseCacheManager(mapping); } |
Em seguida, você deseja ativar a varredura de anotações relacionadas ao cache e o proxy associado, colocando a opção @EnableCaching na anotação Aplicativo classe.
Ativar o cache em SimpleBookRepository
Vamos ver como ativar o cache real em nosso SimpleBookRepository e verifique como o aplicativo se comporta depois disso.
Para fazer getByIsbn automaticamente no cache na primeira chamada e servir as chamadas subsequentes com dados do cache, basta anotá-lo dessa forma:
|
1 2 3 4 5 6 7 8 |
@Override @Cacheable(Application.BOOK_CACHE) //using the name of the cache we declared earlier public Book getByIsbn(String isbn) { simulateSlowService(); Book result = new Book(isbn, "Some book"); log.info("Actual fetch of " + isbn); return result; } |
Vamos executar o aplicativo novamente e ver como ele se comporta agora:
|
1 2 3 4 5 6 7 8 |
[...] .... Fetching books [...] Actual fetch of isbn-1234 [...] isbn-1234 --> Book{isbn='isbn-1234', title='Some book'} in 5022ms [...] isbn-1234 --> Book{isbn='isbn-1234', title='Some book'} in 3ms [...] isbn-1234 --> Book{isbn='isbn-1234', title='Some book'} in 1ms [...] Actual fetch of isbn-8888 [...] isbn-8888 --> Book{isbn='isbn-8888', title='Some book'} in 5007ms [...] isbn-8888 --> Book{isbn='isbn-8888', title='Some book'} in 1ms |
Uau! Isso é muito melhor para invocações após a primeira, parece que está de fato em cache :-)
Como ver os dados no Couchbase
Vamos dar uma olhada rápida no console da Web para verificar se esses ótimos tempos podem ser atribuídos ao Couchbase:
- abra uma nova guia em seu navegador e navegue até
https://localhost:8091. - Conecte-se ao console da Web.
- ir para o
Compartimentos de dadose clique na guiaDocumentospara o balde que você escolheu usar ("padrão").
O que você vê nessa tela (link rápido para os preguiçosos) deve ser semelhante a este:

Podemos ver que ambos os livros foram armazenados em cache no couchbase, usando o cache:CACHE_NAME:CACHE_KEY para os IDs de documentos.
Conclusão
O armazenamento em cache fácil usando o Couchbase agora está ao seu alcance!
Há muito mais que Spring Cache pode fazer por você (por exemplo, escolher como criar a chave de cache, cache condicional, despejo de cache, etc.), e há especificidades para couchbase-spring-cache (por exemplo, para a limpeza do cache, você pode escolher entre usar uma exibição que removerá apenas o documento relevante ou, se o seu bucket for dedicado a um único cache, use o descarga mecanismo...).
Espero que este tutorial introdutório tenha despertado seu apetite para usar o cache facilmente usando Spring Cache e Couchbase!
As próximas etapas provavelmente serão a introdução de formatos de armazenamento alternativos (como JSON) e a oferta do artefato no Maven Central ou em um repositório Maven semelhante publicamente acessível (Bintray alguém?)...
Fique atento às notícias relacionadas à primavera em um futuro próximo!
Enquanto isso, boa codificação :)