Skip to content

ffx64/optimizing-backend-applications-2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 

Repository files navigation

🔧 Otimizando Aplicações Back-End - Spring Boot + RedisDB #2

Introdução

Dando continuidade à série sobre otimização de back-end, chegou a hora de falar sobre cache. Enquanto o Load Balancer ajuda a distribuir requisições entre múltiplas instâncias, o Redis entra em cena para reduzir o tempo de resposta e desafogar o banco de dados relacional, servindo como memória de acesso ultrarrápido.

Conceito rápido

Redis é um banco de dados em memória com operações extremamente rápidas. Em cenários de alta concorrência, você não quer que cada requisição vá até o PostgreSQL. A sacada é: consultas repetitivas vão para o Redis, e só quando o dado não estiver lá é que se consulta o banco relacional.

Arquitetura com Spring Boot + RedisDB

                                                            +-------------------------+
                                                            |                         |
                                 +----[não tem cache]-----> |  PostgreSQL (em disco)  |
                                 |                          |                         |
                                 |                          +-------------------------+
   +-------------------+         |                                       |
   |                   |         |                                       |
   |    Spring Boot    |---------+                              [atualiza o cache]
   |                   |         |                                       |
   +-------------------+         |                                       v
                                 |                          +--------------------------+
                                 |                          |                          |
                                 +------[tem cache]------>  |   RedisDB (em memória)   |
                                                            |                          |
                                                            +--------------------------+
  • Spring Boot pergunta pro Redis primeiro.
  • Se Redis tem -> resposta rápida na memória.
  • Se Redis não tem -> vai para o Postgres, pega, devolve a resposta e ainda atualiza o Redis pro próximo acesso.

Configuração (docker-compose.yml)

services:
  postgres:
    image: bitnami/postgresql:17
    environment:
      POSTGRES_USER: ${POSTGRESQL_USERNAME}
      POSTGRES_PASSWORD: ${POSTGRESQL_PASSWORD}
      POSTGRES_DB: ${POSTGRESQL_DATABASE}
    ports:
      - "5432:5432"
    volumes:
      - ./.database/persistent:/bitnami/postgresql/data
    restart: always
    networks:
      - znet

  redis:
    volumes:
      - ./.database/cache/:/data
    networks:
      - znet

  api_server_a:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      DB_URL: jdbc:postgresql://postgres:5432/${POSTGRESQL_DATABASE}
      DB_USER: ${POSTGRESQL_USERNAME}
      DB_PASSWORD: ${POSTGRESQL_PASSWORD}
      REDIS_HOST: redis
      REDIS_PORT: ${REDIS_PORT}
      REDIS_CACHE_EXPIRATION: ${REDIS_CACHE_EXPIRATION}
    depends_on:
      - postgres
      - redis
    networks:
      - znet

networks:
  - znet

Obs: este docker-compose.yml está abstraído. O restante da aplicação segue o padrão mostrado no post anterior.

Spring Boot + Redis

O Spring já tem suporte nativo a cache, mas se usarmos apenas o sistema interno, cada instância teria o seu próprio cache isolado. Isso não escala em ambientes distribuídos. Por isso, utilizamos o Redis: um cache compartilhado entre todas as instâncias da API.

1. Habilitando cache no projeto

DemostrationApplication.java

package com.ffx64.demostration;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching         // Ativa o mecanismo de cache do Spring
public class SasApplication {
	public static void main(String[] args) {
		SpringApplication.run(SasApplication.class, args);
	}
}

2. Configurando Redis no Spring

Para que o Spring Boot use o Redis como cache distribuído, precisamos configurar alguns parâmetros.

application.properties

# Adicione isso no seu application.properties
spring.cache.type=redis
spring.redis.host=${REDIS_HOST}
spring.redis.port=${REDIS_PORT}
spring.data.redis.repositories.enabled=false
app.cache.redis.expiration=${REDIS_CACHE_EXPIRATION}

3. Configurando conexão do Redis no Spring

Para que o Spring Boot consiga usar o Redis como cache distribuído, precisamos configurar:

  • Conexão com o Redis;
  • Cache Manager com serialização adequada e tempo de expiração.

RedisConfig.java

package com.ffx64.demostration.infra.redis;

import java.time.Duration;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;

@Configuration
public class RedisConfig {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Value("${app.cache.redis.expiration}")
    private int expiration;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName(host);
        config.setPort(port);
        return new LettuceConnectionFactory(config);
    }

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        JdkSerializationRedisSerializer serializer = new JdkSerializationRedisSerializer();

        RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(expiration))
            .disableCachingNullValues()
            .serializeValuesWith(
                RedisSerializationContext.SerializationPair.fromSerializer(serializer)
            );

        return RedisCacheManager.builder(redisConnectionFactory)
            .cacheDefaults(cacheConfig)
            .build();
    }
}

4. Serializando os DTOs

Quando usamos o JdkSerializationRedisSerializer no Spring, todos os objetos que forem pro cache precisam ser serializáveis. Isso significa que eles têm que poder ser transformados em bytes para o Redis guardar, e depois reconstruídos quando a aplicação pedir de volta.

No caso dos records, a boa notícia é que não precisamos de boilerplate nenhum: basta declarar implements Serializable.

PostsResponseDTO.java

package com.ffx64.demostration.dto;

import java.io.Serializable;
import java.time.OffsetDateTime;

public record PostsResponseDTO(
    Long id,
    String name,
    String description,
    String slug,
    String author,
    String text,
    OffsetDateTime createdAt,
    OffsetDateTime updatedAt,
) implements Serializable {}

5. Usando cache nos Services

  • @Cacheable(value="posts", key="#slug") -> primeira vez busca no Postgres, salva no Redis, próximas vezes pega direto do cache;
  • Isso reduz latência, carga no banco e melhora a escalabilidade horizontal da aplicação.

PostsServices.java

    @Cacheable(value="posts", key="#slug")
    public PostsResponseDTO get(String slug) {
        PostsEntity post = repository.findByGuid(guid).orElseThrow(PostsNotFoundException::new);
        return toResponseDTO(post);
    }

Obs: Este é um post introdutório, portanto as configurações apresentadas foram simplificadas para fins de compreensão.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Contributors