Redis III: Encerrando a serie
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:
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:
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:
- 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!