Alura > Cursos de Programação > Cursos de Kotlin > Conteúdos de Kotlin > Primeiras aulas do curso Kotlin e Spring: recursos avançados

Kotlin e Spring: recursos avançados

Cache com Redis - Apresentação

Boas-vindas ao curso de Kotlin e Spring: recursos avançados. Eu sou o João Victor e serei o seu instrutor.

Começaremos com uma pergunta: quais são esses novos recursos que estamos adicionando nessa aplicação Kotlin? Para quem tiver interesse em trabalhar com aplicações de microsserviços que usam recursos de alta performance, como Redis, o MySQL, entre outras ferramentas, você está no curso certo.

Neste curso, aprenderemos como que utilizamos o cache com a ferramenta Redis. O Redis é um repositório Chave-Valor que possui diversas funcionalidades, podemos utilizá-lo como banco de dados, como um recurso, como um streaming ou usá-lo como um cache.

No caso, vamos usar a ferramenta Redis como cache. Para cachear as nossas informações de tópicos, para não precisarmos ficar batendo no nosso banco de dados. Ao longo do curso, vamos aprender como realizar o envio de e-mails com Spring Boot.

Na nossa situação, a aplicação Kotlin usa o Spring Boot como framework e faremos algumas tentativas para o envio desse e-mail. Logo, conseguiremos fazer o teste de como fazemos o envio, por exemplo, de um e-mail configurando servidor de e-mail, configurando usuário e senha.

Não vamos utilizar no nosso caso um e-mail real, não chegaremos nesse nível em uma aplicação em desenvolvimento. Usaremos um recurso bem interessante, chamado de Mailtrap - realiza a simulação do envio do e-mail e do servidor de e-mail.

Veremos como fazer um relatório com Thymeleaf. O Thymeleaf é uma biblioteca que se integra com o Spring Boot, que podemos usar para retornar algo visual, como um HTML para o usuário através do servidor.

Ao invés de termos duas aplicações desacopladas - uma no Front-end e outra no Back-end - e termos que fazer a conexão entre elas para exibir um relatório, podemos usar o Thymeleaf. Isso usando um HTML que vai estar na aplicação Back-end e ao ser requisitada devolvemos esse HTML para o usuário ter um retorno mais visual.

Além disso, vamos ver como fazer Pipelines de CI e CD. Desta forma conseguimos obter com a Integração Contínua feedbacks constantes, tanto da nossa equipe quanto do código, com testes automatizados, de unidades ou de integração.

Nós também criaremos uma pipeline de delivery para fazer uma entrega em produção de forma automatizada, sem a necessidade de fazer deploy com linha de comando.

Neste curso vamos aprender vários recursos bem interessantes, espero que fique satisfeito e aprenda bastante. Obrigado pela sua atenção e te espero no próximo vídeo.

Cache com Redis - Configurando o Redis

Durante o desenvolvimento da nossa aplicação, foi possível incluirmos várias features. Um dos recursos adicionados foi o cache, cujo objetivo é salvar informações no banco de dados em uma camada anterior ao banco de dados.

Ao executarmos um GetMapping na aplicação buscando por tópico, cacheamos essa informação e passamos o seguinte: se não houver mudança no banco de dados, os dados existentes nele podem ser guardados em uma camada anterior ao banco de dados. Assim, quando o usuário chamar o endpoint, não vai ser preciso chegar até o banco de dados para retornar essa informação.

Se ocorrer alguma alteração no banco de dados, temos como apagar esse cache, que está sendo controlado pela aplicação, e na próxima requisição do get ir ao banco de dados, recuperar as informações que teve uma alteração e, uma vez que a primeira requisição é feita, salvar no cache de novo e assim por diante.

É um recurso interessante, visto que conforme a aplicação vai sendo escalada, a quantidade de usuários que a utilizam também aumenta. Isso implica que o cache faz com que realizemos menos requisições para o banco de dados.

Para fazer o cache da aplicação usamos uma dependência do próprio Spring. No arquivo pom.xml o spring-boot-starter-cache e com o conjunto de anotações da Spring API, no arquivo TopicoController temos o cacheable e o cacheEvict.

Está funcional. Mas qual é a questão dessa dependência que estamos usando? O primeiro ponto é que essa solução usa o cache em memória. Isto é, toda vez que a aplicação for realizar o cache das informações, não temos um repositório específico para armazená-lo. Isso implica que quando a aplicação for restartada, esse cache é apagado.

O segundo ponto é sobre a escalabilidade, em que a ideia é escalarmos a aplicação sem dificuldades. Como, por exemplo, se estivermos trabalhando com containers, que seja possível ter vários containers conforme a necessidade de escalar essa aplicação.

Essa é uma problemática para esse tipo de cache que estamos usando, não é escalável. Portanto, se não escala junto com a aplicação podemos ter sérios contratempos mais para frente. Esses são os dois pontos que limitam a nossa execução. Mas como faremos para aplicar um cache efetivo? É nesse momento que usamos o Redis.

O Redis é um repositório que pode ser usado como um data store, como uma database - nesta questão é importante destacar que é um banco de dados Chave-Valor. Como um cache, como um streaming engine (em português, "Mecanismo de streaming") ou message broker (em português, "Corretor de Mensagens").

Com essa introdução, conseguimos ter uma noção do recurso que o Redis oferece para usarmos na aplicação para melhorar o cache. Agora, faremos algumas alterações no código: A primeira é que no arquivo pom.xml vamos remover a dependência do spring-boot-starter-cache.

Essa dependência nos serviu até agora, porém conforme vamos desenvolvendo a nossa aplicação, precisamos remover e incluir determinadas features, e chegou o momento de evoluirmos o cache.

Dependência para remover do pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

No final, aproximadamente na linha 138, incluiremos uma nova dependência. Para pegar essa dependência, vamos ao site do Maven Repository e digitaremos "spring boot redis" no campo de busca na parte superior central da tela. Na primeira opção temos o "Spring Boot Starter Data Redis", clicaremos nela.

Vai aparecer uma página com uma tabela com as seguintes colunas: Version, Vulnerabilities, Repository, Usages e Date. Selecionaremos a versão 2.6.7 da coluna "Version". Lembrando que, por estarmos usando o Spring Boot, não deixamos a versão especificada no pom.xml.

Após selecionar a versão 2.6.7, vai aparecer uma parte em que temos as tags de dependência, vamos copiá-la e colá-la no nosso código antes de fechar as tags de dependências.

Código para copiar do site MVN Repository:

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.6.7</version>
</dependency>

Vai ser exibido um ícone de lâmpada na linha da versão, podemos removê-lo, dado que estamos usando o Spring Boot, logo ele vai usar a mesma versão do Spring Boot para as versões das dependências que usamos, como o Redis. Faremos o reloading da aplicação usando as teclas "Ctrl + Shift + O".

pom.xml:

//código omitido

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

//código omitido

Vamos agora configurar o application properties incluindo o caminho, o host e a porta. Ou seja, a forma em que a aplicação vai encontrar o Redis.

Isso porque diferente do cache em memória, o Redis vai ser um repository que ao subirmos a aplicação, teremos um repository e precisaremos apenas informar para a aplicação que ao cachear as informações, é necessário fazer isso no repositório que está no local estabelecido.

Para isso, vamos em "src > main > resources > application-dev.yml" e após o format_sql explicitaremos o cache: com o type, redis, host e port. O redis não fica no mesmo nível do cache, pois queremos configurá-lo e teremos para ele o host e o port.

application-dev.yml:

//código omitido

cache:
    type: redis
redis:
    host: localhost
    port: 6379

//código omitido

Após a configuração do cache e do Redis, podemos nos perguntar "Em que momento o Redis entra na nossa aplicação?". Conforme estamos utilizando alguns recursos dockerizados, como o MySQL, seguiremos a mesma lógica com o Redis.

Logo, teremos um container do Redis rodando na nossa máquina, que vai servir como um repositório. E toda vez que alguma informação da aplicação for cacheada nesse repositório, ele vai usar o Docker para acessar esse Redis e salvar o cache.

No site do Docker Hub , que é um repositório - como o GitHub - mas de imagens em Docker. Na caixa de busca digitaremos "Redis" e selecionaremos a primeira opção "Docker Official Image" . Nessa página temos as "Tags" no menu após o título, clicaremos nessa aba tags.

Descendo um pouco a página, copiaremos o pull command do docker pull redis:latest do lado direito da tela, selecionando o ícone de folha com uma ponta dobrada do lado direito do comando. Em seguida, abriremos o terminal, colaremos o comando e selecionaremos "Enter" para rodar.

Comando para rodar no Prompt:

docker pull redis:latest

Após aguardar baixar a imagem, e no "Docker Desktop" - caso não tenha baixado ainda - ou no próprio terminal, criar o container. Caso optemos pela segunda opção, usaremos o seguinte comando:

docker run --name redis-local redis:latest

Note que ele já startar o Redis, como podemos visualizar na primeira linha. Agora poderíamos começar a usar a aplicação fazendo o cache com Redis, porém não é o suficiente para realizar o cache.

Precisamos fazer algumas configurações no arquivo TopicoController.kt, para fazer com que o cache seja mais eficiente. Logo, além de ser escalável e não ser mais em memória, ainda vamos deixá-lo mais efetivo do lado da aplicação.

Muito obrigado por me acompanhar nesse vídeo e te espero no próximo.

Cache com Redis - Uso do Redis

Já configuramos no application properties as informações do Redis, para a aplicação conseguir gerar o cache e salvar no repositório do Redis. Chegou o momento de usarmos e vermos esse cache funcionando.

Só que antes vou apontar um destaque. No vídeo anterior, quando criamos o nosso Redis, vimos que o container subiu, se rodarmos no terminal o comando docker ps, podemos visualizar que está sendo executado. Porém, esquecemos de informar a porta do container, para que a aplicação consiga se comunicar nesse container.

No arquivo application-dev.yml passamos a porta 6379, mas em nenhum momento explicitamos para expor uma porta no container para a aplicação conseguir acessar. No terminal, na coluna "ports" até temos a padrão "6379/tcp", sendo a do Redis.

Mas não temos a porta do container, logo a aplicação não consegue chegar no container e, consequentemente, não gera o cache no Redis. Agora executaremos o comando docker stop redis-local e recriar outro container incluindo a porta para que a aplicação consiga encontrá-lo.

docker stop redis-local

Agora vamos rodar o comando docker run, para exibir a porta incluiremos o parâmetro -p e a porta do container, que vai ser a mesma do Redis. Sendo antes dos dois pontos (":") a porta do container e depois a do Redis, que vai ser hospedado nesse container.

Ainda no comando colocaremos um --name e chamar de redis com a imagem redis:latest. Para executar clicaremos na tecla "Enter".

docker run -p 6379:6379 --name redis redis:latest

Note que na primeira linha aparece a mensagem "Redis is starting", isso significa que vai startar um novo container. Depois, podemos executar o docker ps para visualizarmos. Perceba que na coluna "ports" está mais semelhante ao SQL.

Coluna "ports":

0.0.0.0.6379->6379/tcp
0.0.0.0.3306->3306/tcp

Temos a porta, um localhost que é o container com a porta 6379 e, após a seta, o recurso do Redis com a 6379. Isso é apenas para alcançarmos, de fato, o container.

Eu informei anteriormente que além de melhorarmos a escalabilidade do cache com o Redis e do cache em memória, faríamos melhorias no projeto. Mais especificamente nas classes que usam o cache.

Voltando para o arquivo TopicoController, pode ter ficado alguma dúvida quando mencionei que íamos continuar usando o cache de Redis com as mesmas anotações. Isso acontece por essas anotações serem um conjunto do Spring API que podemos ver as anotações da documentação .

Todas essas anotações do site são da API do Spring Cache. Ou seja, todos que implementam essa API podem usar esses recursos, com as interfaces e anotações que têm em comum. A implementação por trás difere, antes usávamos uma mais básica, e agora estamos usando a do Redis.

Porém, as anotações e as classes abstratas que possuem uma API são as mesmas. Por isso, é possível mudar a implementação sem alterar muito o código. Agora, gostaria de destacar outro ponto.

Ao colocarmos o cache no nível de controller, sempre pensamos em uma requisição externa. Visto que o usuário realiza uma requisição HTTP para a nossa controller, que possui o cache salvo e, ao invés da aplicação chamar o service que aciona o repository, ali mesmo já devolvemos as informações para o usuário.

Há diversas aplicações que colocam o cache no nível de controller. Porém, há momentos em que vamos precisar de alguns serviços internos, como uma rota ou job., que vão precisar de alguma informação do banco de dados e não vão realizar nenhuma requisição HTTP para o controller. Geralmente chamamos no service.

Caso tenhamos essa possibilidade de ter algo interno que chama o service, que pode ter que chegar no banco de dados: por qual motivo não fazemos o cache no mesmo nível de serviço? Ficaria mais eficiente, visto que além de atender a requisição externa, conseguiria atender também serviços internos da aplicação. Por exemplo, um job.

O que faremos para melhorar o cache é levar essas anotações do arquivo TopicoController para TopicoService. Podemos copiar anotação por anotação mesmo e ir colando. A primeira vai ser a cacheable do listar, que vamos colar no listar do arquivo TopicoService.

TopicoService:

@Cacheable(cacheNames = ["Topicos"], key = "#root.method.name")

Não vamos alterar nada, o nome do cache vai permanecer "topicos" e a chave (key), que é a identificação do cache no Redis.

No método cadastrar, no atualizar e no deletar, colaremos o CacheEvict. Isso para que quando tiver um novo cadastro, atualização ou remoção de informação, o cache que já temos salvo no Redis seja apagado, visto que não vai contemplar as novas informações.

Logo, ele é apagado e ao fazermos uma requisição get, ele memoriza de novo com os dados enviados e continua com o cache até outra operação de cadastro ou atualização.

@CacheEvict(cacheNames = ["Topicos"], allEntries = true)

Precisamos subir a aplicação para analisarmos se vamos conseguir realizar uma requisição. Dessa forma, teremos que ver como vamos visualizar essas informações. Para rodarmos a aplicação, selecionaremos o botão "▶", a primeira opção da esquerda para a direita do menu na parte superior da tela.

Ao iniciar o build, perceba que ela já está mostrando o Spring Data Redis, essa é a primeira alteração que acontece ao subirmos a aplicação com as modificações que fizemos. Deu tudo certo.

No Postman, já temos o endpoint de busca de token do lado esquerdo em "My Workspace" em "Alura > Buscar token". Nele clicaremos no botão "Send" do lado direito da tela, com o método post. Isso para copiarmos um Bearer Token e fazermos uma busca autenticada e autorizada na API. Vamos expandir a aba Headers e copiar o campo "Bearer".

Em "Alura > Buscar tópico" buscaremos um tópico. Na aba "Authorization" vai aparecer um type para escolhermos, vamos clicar em "Bearer Token" e colaremos em "Token" no campo do lado direito da tela. Em seguida, vamos clicar no botão "Send" para visualizar quais informações temos no banco de dados.

Em "Body" vai ser exibido alguns campos como "id" e o "titulo". Temos o ID 1 e 2 e os títulos "Duvida sobre kotlin" e "Aprendendo java", respectivamente. Fizemos uma requisição usando o método get, mas não conseguimos ver o Redis operando. O que podemos fazer para visualizar se essas informações estão sendo armazenadas?

Note que a primeira requisição feita durou 1857 ms, se efetuarmos uma nova requisição esse tempo já diminui para 135 ms. Se fizermos outro envio, esse tempo vai para 42 ms. Isso pode significar que realmente está sendo memorizada (cacheada). Mas como podemos visualizar isso?

Podemos entrar no Redis e analisar essas informações sendo armazenadas. Para isso, vamos no terminal para executar o comando docker exec -it redis sh.

docker exec -it redis sh

Ao rodar esse comando o Redis abre um bash, em que podemos chamar o CLI Redis. Para isso digitaremos "redis-cli" e selecionaremos "Enter".

redis-cli

Agora abre o bash do Redis, que dentro podemos começar a executar comando no próprio Redis. A primeira instrução que vamos executar é o monitor, note que o retorno foi "OK". Isso significa que agora estamos monitorando as operações que acontecerão dentro do Redis.

Voltando para o Postman, enviaremos uma nova requisição get clicando no botão "Send". Retornando para o prompt é possível visualizar que ele está pegando o cache de topicos::listar. Como já realizamos outras operações, essa informação já está armazenada e vimos isso pelo tempo que foi diminuindo no time do Postman.

Novamente no Postman, enviaremos uma nova requisição e voltaremos para o terminal. Perceba que é exibido o mesmo retorno, isso implica que as informações foram realmente memorizadas (cacheadas).

Caso ainda esteja desconfiado: vimos que toda vez que postamos um novo tópico, esse cache é apagado e, ao fazermos uma requisição, é cacheado de novo. Dessa forma, garantimos que o cache está funcionando.

Vamos cadastrar um novo tópico na aplicação no arquivo TopicoController. Para enviar um tópico precisamos, primeiro, ver qual o tipo de objeto que ele precisa receber. Para tal, clicaremos em "NovoTopicoForm" e note que solicita um título, uma mensagem, o id do curso e do autor.

No Postman, criaremos uma requisição post para tópicos. Faremos isso clicando no botão "+" para abrir uma nova aba na parte superior. Na URL colocaremos "http://localhost:8080/topicos" e em "Authorization" escolheremos "Bearer Token", que colaremos o mesmo que havíamos copiado anteriormente.

No "Body" do post, selecionaremos a opção "raw" e no "text" trocaremos para "JSON", para escrevermos.

Body do post:

{
        "titulo": "O que são funções em kotlin?",
        "mensagem": "Estou com duvidas em relação a função",
        "IdCurso": 1,
        "IdAutor": 1
}

Com esses dados já conseguimos cadastrar um tópico, agora basta clicarmos no botão "Send". Note que no "body" do retorno volta com um ID 3. Agora, vamos analisar o que aconteceu no Redis pelo prompt. O interessante é que no momento inserimos um tópico, conseguimos visualizar que ele deletou o cache pela linha de retorno ""DEL" "Topicos::listar"".

No Postman enviaremos outra requisição get para ver se alteramos esse comportamento. Em "body" do retorno, temos três tópicos com o ID 1, 2 e 3 - sendo este o que acabamos de acrescentar. Voltando para o prompt, perceba que ele retornou a requisição do tipo get e setou o cache no Redis.

Conseguimos até visualizar as informações que estão sendo memorizadas (cacheadas), como a mensagem, um status, um status Topico, "O que é possível para aprender java", entre outros dados. Se fizermos uma nova requisição no Postman, de 79 ms diminuiu para 38 ms e retornando para o terminal, ele pega novamente do cache.

Com isso, é possível vermos o Redis trabalhando e as informações sendo cacheadas. Agora se cadastrarmos, atualizarmos ou deletarmos um tópico existente, ele deleta o cache criado e na próxima requisição HTTP do tipo get é inserido o cache novamente no Redis e assim por diante.

Além de termos maior visibilidade do cache funcionando, acabamos recebendo todos os benefícios do Redis. Com uma plataforma altamente escalável, visto que fica em um repositório e as informações não se perdem por estarem na memória. Temos diversas vantagens de utilizar o Redis como uma ferramenta de cache.

Neste vídeo era isso que queria mostrar para você, espero que tenha gostado e até o próximo capítulo.

Sobre o curso Kotlin e Spring: recursos avançados

O curso Kotlin e Spring: recursos avançados possui 194 minutos de vídeos, em um total de 45 atividades. Gostou? Conheça nossos outros cursos de Kotlin em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Kotlin acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas