Redis III: Encerrando a serie

iundarigun
Dev Cave
Published in
9 min readFeb 25, 2020

Vamos para a última parte da serie sobre Redis. Se não leu as duas anteriores, pode dar uma olhada na primeira parte aqui e na segunda parte aqui.

Schemas e segurança

No primeiro post comentei que não precisamos especificar senha nem database para conectar no Redis, embora os dois estejam disponíveis. Vamos começar o post de hoje por aqui.

Por padrão, o Redis nos oferece 16 schema database nomeadas por números, e quando conectamos numa instância sem especificar nada, estamos conectando no schema 0. A quantidade total de database schemas pode ser modificado no arquivo de configuração do Redis que veremos a frente. Para usar os schemas vamos conectar via CLI, usando aquela imagem de docker que criamos no primeiro post da serie.

$ docker start local-redis$ docker exec -it local-redis redis-cli

Agora criamos uma chave no database schema padrão, vamos trocar de database schema e listar as chaves:

A instrução select # nos permite transitar entre os diferentes database schemas. Uma vez mudado, aparece um número no prompt indicando qual estamos usando no momento.

No Spring boot é relativamente fácil de usar, só configurar mais uma property no arquivo application.yml:

spring:
redis:
host: localhost
port: 6379
database: 1

Auth

Pensando em segurança, podemos configurar nosso Redis para exigir uma senha. Novamente, podemos fazer isso via arquivo de configuração, ou podemos fazer essa configuração via redis-cli:

127.0.0.1:6379> config set requirepass devcave
OK
127.0.0.1:6379> keys *
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth devcave
OK

E, de novo, no Spring Boot é simples de usar:

spring:
redis:
host: localhost
port: 6379
password: devcave

O password é o mesmo para todos os database schemas. Como comentamos varias vezes, o Redis busca ser brutalmente simples, então a segurança é também simples (e menos confiável), pois foi pensado para ser usado em ambientes seguros sem acesso externo. É possível também configurar os ips aceitos aumentando a segurança (dar uma olhada no arquivo de configuração, secção Network)

Arquivo de configuração

O Redis pode ser configurado a través do arquivo de configuração. Deixei uma versão “crua” no meu repositório. O arquivo contém várias secções que podemos configurar e trouxe só uns exemplos aqui:

Secção Network, onde podemos configurar os binds por ip
Secção de replicação master-slave
Secção de segurança, onde podemos configurar por arquivo a senha

Todas as alterações feitas no arquivo podem ser feitas também via redis-cli, como vimos no caso da configuração da senha.

Para usá-lo, podemos criar um container especificando o arquivo. Para isso precisamos compartilhar o volume e depois especificar “redis-server <path>/redis.conf” como parâmetro de execução:

$ docker stop local-redis
$ docker run -v ~/workspace/learning-redis/redis-compose:/usr/local/etc/redis -p 6379:6379 --name local-redis2 redis redis-server /usr/local/etc/redis/redis.conf

Master Slave

Uma das seções que vimos é que da para configurar um master-slave. Com essa configuração conseguimos ter replicação dos dados criados no Redis master para o Redis slave, evitando perder dados caso o master morrer, ou usar o master para escritura e o slave para leitura. No repositório, criei uma pasta com um docker compose criando dois Redis, um na porta 6379 (master) e outro na porta 6380 (slave). As configurações foram feitas nos arquivos de configuração.

Master

No redis1.conf (master), alteramos só a linha 507, para settar uma senha para o server de Redis.

Slave

No redis2.conf (slave), alteramos:

  • Linha 92, para settar a porta 6380
  • Linha 286, para dizer que o redis será um slave, indicando o master
  • Linha 293, para configurar a senha do master
  • Linha 507, para settar a senha do server do slave

Para “levantar” os serviços, usamos o docker compose da mesma pasta (lembre de dar stop no container local-redis e local-redis2 que criamos e iniciamos anteriormente):

$ docker-compose up -d

Se queremos conectar no Redis master, podemos fazer:

$ docker exec -it masterslave_redis1_1 redis-cli
127.0.0.1:6379> auth devcave
OK
127.0.0.1:6379> set "123" "1213"
OK

Agora podemos verificar se a chave foi replicada no slave:

$ docker exec -it masterslave_redis2_1 redis-cli -p 6380
127.0.0.1:6380> auth devcave
OK
127.0.0.1:6380> keys *
1) "123"

Mas, como configuramos isso no Spring Boot? Vamos precisar criar algumas coisas mais manuais. No meu repositório, há uma pasta master-slave com um projeto simples que usa o master para escrever e o slave para ler.

No application.yml especificamos as informações necessárias para conectar nos dois Redis:

spring:
redis:
port: 6379
password: devcave
host: localhost
redis-slave:
port: 6380
password: devcave
host: localhost

Já no código, temos uma classe de configuração com os ConnectionFactory e o SpringRedisTemplate para cada Redis:

E temos a classe RedisRepository usando o StringRedisTemplate adequado em função da operação:

Usando os endpoints da aplicação, vamos criar uma chave, consulta-la e deleta-la. O comportamento que observamos nos dois Redis ligando a opção do monitor é a seguinte:

A direita temos o master, e a esquerda o slave

Sentinel

O Sentinel é uma ferramenta usada para ter resiliência e alta disponibilidade de forma automática. É responsável para verificar a saúde das instâncias, sejam master ou slave, e caso acontecer alguma coisa com a instância master, promover um dos slaves para master.

Uma vez conectados no Sentinel, ele nos fornece o endereço da instância master para poder conectar nela. Na pasta redis-compose/sentinel do meu repositório deixei um docker-compose e os arquivos config necessários para subir um sentinel com um master e um slave. A continuação vamos detalhar as mudanças realizadas nos arquivos de configuração padrão:

Redis Master

No arquivo redis1.conf tem as configurações do master:

  • Linha 69: Comentamos o bind para permitir conexões desde outras ips.
  • Linha 293: Settamos a senha do master. Embora isso é feito com os slaves como vimos na configuração anterior, caso o master cair e seja escolhido um dos slaves para master, se o server voltar, ao existir um master, será configurado como slave pelo sentinel, então essa configuração será necessária.
  • Linha 507: Senha do Redis. É importante, pelo mesmo motivo que no ponto anterior, que a senha seja a mesma no master e no slave.

Redis Slave

No arquivo redis2.conf tem as configurações do slave:

  • Linha 69: Comentamos o bind para permitir conexões desde outras ips.
  • Linha 92: Como usamos o mesmo host, trocamos a porta para a 6380
  • Linha 286: Indicamos que é uma réplica do master, indicando o endereço e a porta do mesmo.
  • Linha 293: Settamos a senha do master.
  • Linha 507: Senha do Redis.

Sentinel

Para rodar o sentinel, só precisamos executar o container com o comando redis-sentinel passando o arquivo de configuração sentinel.conf e não com redis-server como fazemos no master e slave (veja o docker-compose.yml para mais detalhes). As alterações que fizemos são:

  • Linha 84: Especificamos a monitoração do sentinel, indicando um nome identificativo, o endereço do Redis master com a porta, e quorum para realizar ações de promoção, remoção, etc, usado com um valor maior que 1 quando temos mais de um Sentinel.
  • Linha 86: Especificamos o password usado para aquele grupo de monitoração gerenciado, identificado pelo nome indicado anteriormente.
  • Linha 113: Indicamos o timeout para considerar que um master está indisponível e é preciso promover um slave.
  • Lihna 121: Indicamos o número de réplicas reconfiguradas em paralelo quando o master estiver down. Usamos o padrão, só trocamos o identificador do monitor.
  • Linha 146: Indicamos o timeout do failover. Deixamos padrão, trocando unicamente o identificador do monitor.

Nota: a ordem das instruções no arquivo pode (e vai variar) após iniciar o docker-compose!

A continuação vou mostrar o resultado de iniciar os três serviços, escrever no Redis master, intentar escrever no Redis slave (até aqui é como o exemplo do master-slave), parar o Redis master, escrever de fato no Redis slave que foi promovido a master, e levantar o antigo master e ver que agora é slave:

Se olhar nos logs do sentinel veremos exatamente o que aconteceu:

Para usar no Spring Boot, podemos simplesmente configurar o sentinel da seguinte forma:

spring:
redis:
sentinel:
nodes: localhost:26379
master: sentinel-redis
password: devcave

Nota: Até onde eu vi, é normal configurar um sentinel por cada server do Redis, seja slave ou master, mas para simplificar os exemplos, deixei só um sentinel. No Spring, basta com passar a lista de sentinel (endereço e porta) separados por vírgula.

Redis Cluster

Se até aqui sua cabeça ainda não explodiu, tem mais um assunto que talvez faça você entender as possibilidades do Redis. Vimos como ter resiliência com o Sentinel, e vimos também como separar escrita e leitura com o master slave anteriormente. Mas se nosso Redis precisar escalar mais parece que temos uma limitação na escala horizontal. Ou não?

Sharding

O Sharding é um método para distribuir dados a través de diferentes máquinas, possibilitando a escala horizontal. No Redis o sharding é feito distribuindo as chaves entre uma quantidade finita de slots, concretamente 16.384. Esses slots, quando o Redis é configurado dessa forma, são distribuídos entre as diferentes instâncias do cluster. Quando uma chave for persistida ou consultada, é feito um hash da chave que determina em que slot, e por tanto em que “máquina”, deve persistida ou consultada.

Para ver um exemplo, deixei um docker-compose na pasta redis-compose/cluster do meu repositório. Dando o start, configuramos 6 nós do Redis, três masters e três slaves, então os slots são distribuídos entre 3 grupos de master-slave.

Executando o docker-compose, o cluster ficou configurado da seguinte forma:

Isso pode variar de execução para execução.
  • Master na porta 6381 com slave na porta 6385
  • Master na porta 6382 com slave na porta 6386
  • Master na porta 6383 com slave na porta 6384

Conectamos em qualquer nó e inserimos umas chaves:

No exemplo acima, conectamos numa réplica (o parâmetro -c indica que conectamos no modo cluster) e na hora de criar as chaves, ele redireciona para o nó onde o slot correspondente está alocado.

Passando para o Spring Boot, conseguimos configurar de foma fácil novamente:

spring:
redis:
cluster:
nodes: localhost:6381,localhost:6382,localhost:6383

Se levantar a aplicação more-than-cache que vimos no post anterior e brincamos inserindo alguns dados, veremos que o driver para Redis do Spring já é inteligente suficiente para lidar com o cluster:

Encerrando

A intenção desta série foi mostrar alguma aplicações e configurações (um pouco) avançadas sobre Redis. Tentei ir além do clássico cache e espero ter atingido o objetivo, embora acho que este último post seja um pouco denso.

Ficou para atrás o uso do Redis como fila que não achei o jeito de encaixar no terceiro post, mas se tiver curiosidade na pasta redis-queue do meu repositório tem um projeto simples em Kotlin e Spring Boot com um producer e um consumer. A ideia é a mesma que nas outras funcionalidades do Redis: Ser brutalmente simples.

O que achou da série? Gostou? Achou superficial? Ficou com dúvidas? Comenta ai!

Referências

--

--

iundarigun
Dev Cave

Java and Kotlin software engineer at Clearpay