1) O documento apresenta um workshop sobre Amazon ElastiCache Avançado, com foco em Redis e Memcached.
2) A agenda inclui revisão rápida dos serviços, lançamento, conexão, distribuição de chaves, monitoramento e casos de uso.
3) O workshop é apresentado por especialistas da AWS e da Kanui e aborda melhores práticas e lições aprendidas na utilização do ElastiCache.
2. Amazon ElastiCache Avançado
Fábio Aragão da Silva, Solutions Architect at AWS
Willy Barro, Chief Technology Officer at Kanui
Fernando Cabral, Lead SysAdmin at Kanui
28 de Maio de 2015
3. Agenda
• Revisão Rápida
• Lançando
• Conectando
• Distribuindo as Chaves (Sharding)
• Monitorando
• Kanui
• Casos de Uso
• Juntando tudo
• Melhores Práticas e Lições Aprendidas
7. Redis
Similar a um banco no
NoSQL
http://redis.io/commands
Cache em memória
do tipo chave,valor
Suporte a tipos de dados
strings, listas, hashes, sets, sets ordenados,
bitmaps & HyperLogLogs
Single-threaded
Operações Atômicas
Suporte a transações
com propriedades ACID
Ridiculamente rápido!
Réplicas de Leitura
Persistência
Snapshots ou log append-only
Funcionalidade de
publicação/subscrição
8. Memcached
Alocação Slab
Cache em memória do tipo
chave,valor
Suporte a String e
Objetos
Multi-threaded
Absurdamente rápido!
Consolidado
Sem persistência
Padrões de Sharding
10. Lançando
1
Escolha:
• Versão
• Porta
• Parametros
• Multi-AZ & replicação*
• Nome do cluster
• Tipo de nó
• # de Nós (ou réplicas)
• Local de backup no S3*
2
Choose:
• Grupo de subnet
• Zonas de Disponibilidade
• Security group
• Habilite backups*
• Janela de Manutenção
• Tópico do SNS
3
+ +
*opção no Redis
Escolha o motor:
A partir do console da AWS:
12. Mas ao final das contas, o que interessa
mesmo são os endpoints
Endpoint: Refere-se a um nó em particular do cluster
mycache-002.hnou5c.0001.usw2.cache.amazonaws.com:6379 (redis)
mycache.hnou5c.0002.usw2.cache.amazonaws.com:11211 (memcached)
Endpoint de Configuração: Para o memcached, é um alias de DNS para
consultar a lista atual de nós participantes de um cluster
mycache.hnou5c.cfg.usw2.cache.amazonaws.com:11211
Endpoint: Primário: Pra grupos de replicação no redis, é um alias de DNS que
se refere ao nó onde devem ser feitas as escritas
mycacherepgroup.hnou5c.ng.0001.usw2.cache.amazonaws.com:6379
13. Redis Multi-AZ Auto-Failover
Escolhe a réplica com
menor atraso
Não muda o DNS
Availability Zone #1 Availability Zone #2
escritas
Use o endpoint
primário
leituras
Use os endpoints de
réplica (ou o primário
também)
15. Conectando ao Redis
Language Library
Ruby Redis-rb, Redis objects
Python Redis-py
Node.js node-redis
C#/.NET ServiceStack.Redis
PHP phpredis
Java Jedis
Bibliotecas Cliente:
Suporta os mesmos comandos
+ histórico de comandos
+ teste de latência
+ backups
+ vários outros
$telnet {primary-endpoint} 6379
>HSET hash mykey "mydata"
:1
>HGET hash mykey
$6
mydata
#from redis.io download:
$redis-cli -h {primary-endpoint}
16. // Exempoo em Java – requer http://aws.amazon.com/sdk-for-java
AmazonElastiCache ec = new AmazonElastiCacheClient();
String replicationGroupName = "mycache”; // mude para o nome do seu grupo de replicação
String metadataURL = "http://169.254.169.254/latest/meta-data/placement/availability-zone";
String myAZ = new Scanner(new URL(metadataURL).openStream(), "UTF-8").
useDelimiter("A").next();
ec.setRegion(Region.getRegion(Regions.US_WEST_2));
DescribeReplicationGroupsRequest rgrequest = new
DescribeReplicationGroupsRequest().withReplicationGroupId(replicationGroupName);
DescribeReplicationGroupsResult rgresult = ec.describeReplicationGroups(rgrequest);
for (ReplicationGroup rg : rgresult.getReplicationGroups()) {
for (NodeGroup ng : rg.getNodeGroups()) {
for (NodeGroupMember ngm : ng.getNodeGroupMembers()) {
if (ngm.getCurrentRole().equals("replica") &&
ngm.getPreferredAvailabilityZone().equalsIgnoreCase(myAZ)) {
System.out.println(ngm.getReadEndpoint().getAddress() + ":" +
ngm.getReadEndpoint().getPort());
}
}
}
}
Quais réplicas estão na minha AZ?
EncontrarAZ
ChamadaaAPIdo
ElastiCache
17. Conectando no Memcached
Útil para algumas informações &
manutenção, mas normalmente vamos nos
conectar usando alguma biblioteca cliente
Language Library
Ruby Dalli, Dalli:ElastiCache
Python Memcache Ring, django-elasticache
Node.js node-memcached
C#/.NET ElastiCache Auto Discovery Client
PHP ElastiCache Auto Discovery Client
Java ElastiCache Auto Discovery Client
(based on spymemcached)
Bibliotecas Cliente:
As bibliotecas oficiais de ElastiCache
para PHP, Java e .NET suportam Auto
Discovery se nós de Memcached
forem adicionados ou removidos
$telnet {cfg-endpoint} 11211
>config get cluster
$telnet {node1} 11211
>set mykey 0 60 6
>mydata
STORED
>get mykey
VALUE mykey 0 6
mydata
END
18. Clientes para o Amazon ElastiCache
• Bibliotecas oficiais para download:
– Java: baseada em spymemcached
– PHP: (várias versões)
– .NET
• Benefícios:
– Provê hashing consistente
– Realiza Auto Discovery (a cada 60s) para detectar nós adicionados
ou removidos
– Não requer reconfiguração ou dar HUP em instâncias em execução
19. # PHP
$server_endpoint =
"mycache.z2vq55.cfg.usw2.cache.amazonaws.com";
$server_port = 11211;
$cache = new Memcached();
$cache->setOption(
Memcached::OPT_CLIENT_MODE, Memcached::DYNAMIC_CLIENT_MODE);
# Use o endpoint de configuração como o único servidor
$cache->addServer($server_endpoint, $server_port);
# A biblioteca localiza os nós automaticamente
$cache->set("key", "value");
Descoberta automática de nós
Sempre use o endpoint
de configuração
21. Quem tem as chaves?
Com múltiplos servidores de Memcached, onde armazenar as chaves?
Primeira abordagem:
Baseada no módulo do número de
servidores:
Desvantagem:
Ao adicionar novo cluster,
grande número de chaves
precisa ser reassociado:
Se você for de
3 servidores 4
servidores,
¾ = 75% das chaves
serão impactadas
num_nós_antigos
num_nós novos= ( )server_list = [
'mycache.0001.usw2.cache.amazonaws.com:11211',
'mycache.0002.usw2.cache.amazonaws.com:11211'
]
server_index = hash(key) % server_list.length
server = server_list[server_index]
22. Melhor ainda: hashing consistente
Uma explicação bem simplificada de algo que pode gerar algumas horas de discussão
# número de chaves impactadas com +/-
nós :
aproximadamente ( 1 – método via módulo)
Ex: se for de 3 servidores 4 servidores,
(1 - ¾) ~ 25% das chaves serão impactadas
①Imagine o anel
as vezes chamado de ‘continuum’
②Divida-o em partições
(um número fixo “N”)
Nesse caso, 24, em geral 2(32 or 160)
③Servidores são “mapeados” nas partições
spread throughout the ring, not even like this
1
2
3
4
5
6
789
10
11
12
13
14
15
16
Nó-A
Nó-B
Nó-C
Nó-C
Nó-B
Nó-A
④ Bibliotecas cliente fazem o hash da chave e
usam % N para determinar onde elas devem
a maior partição mais próxima no anel
“uma estratégia de sharding
para a todos governar
24. Memcached internamente
SEM
MEMÓRIA
Slab Classe 42
Tam. Chunk: 1MB
Chunks/Pg: 1
> stats slabs
Slab Classe 27
Tam. Chunk: 42KB
Chunks/Pg: 24
Slab Classe 15
Tam. Chunk: 1800B
Chunks/Pg: 582
Slab Classe 1
Tam. Chunk: 96B
Chunks/Pg: 10922
Memory pool
25. >stats cachedump 1 100
ITEM mykey3 [4 b; 1414372065 s]
>stats slabs
STAT 1:used_chunks 1
>get mykey3
END
>stats cachedump 1 100
END
>stats slabs
STAT 1:used_chunks 0
Gerenciamento da memória
• Não existe processo ‘reaper’ (anjo
da morte) pra limpeza da memória
• Eviction no Memcached limpa a
memória com base em LRU
• LRU = Menos recentemente usado
• Tempo de expiração não é
‘mantenha até’, mas sim ‘não
válido após’
• Por padrão, páginas não se
movem
• Use ‘parameter groups’ para
alterar comportamento padrão
Exemplo
28. “Os serviços da AWS nos ajudaram a fazer da Black
Friday um dia comum, ao invés de um pesadelo para o
time de engenharia”
• A Kanui é um e-commerce
especializado em artigos
esportivos e um dos líderes em
seu segmento
• Como o site é a base de todo o
nosso negócio, alta
disponibilidade é imprescindível.
“Com alta
escalabilidade,
interoperabilidade com
ferramentas open source,
suporte eficaz e visibilidade de
custos detalhada, a AWS se
tornou uma escolha natural”
- Willy Barro, CTO
29. • Disponibilidade - Zero Downtime. Um
minuto fora do ar é um minuto sem
vendas
• Escalabilidade - Suportar a Black Friday,
mobile push notifications e promoções
relâmpago
• Custos - Visibilidade detalhada de todos
os custos
• Tools - Ferramentas e bibliotecas para
gerenciar e monitorar a infraestrutura
O Desafio
33. Revisando o cenário tipico de web 2.0
ELB Aplicação
APIs Externas
fácil de adicionar
34. Começe com “cache preguiçoso” (‘lazy’)
• Muito benéfico para padrões
de acesso de intensa leitura
– Informação de perfil de usuário
– Dados sumarizados
• Muitas bibliotecas encapsulam
esse padrão
# Pseudocódigo em Python:
def get_user(user_id):
# Verifica o Cache
record = cache.get(user_id)
if record is None:
# Consulta a Base de Dados
record = db.query(
”select * from users where id = ?”,
user_id)
# Popula o Cache
cache.set(user_id, record)
return record
# Código da Aplicação
user = get_user(17)
35. E como ficam as escritas e leituras?
• Cache atualizado em
tempo real
• Trata atualizações no perfil
dos usuários
• Pode também escolher
remover a chave e deixar o
cache “preguiçoso” agir
# Pseudocódigo em Python:
def save_user(user_id, values):
# Salva na Base de Dados
record = db.query(
"update users ... where id = ?",
user_id, values)
# Grava no Cache
cache.set(user_id, record)
return record
# Código da Aplicação
user = save_user(17, {"name": ”Sauron"})
36. Persistência de Sessão
1) Instale o“memcache”
‘yum install php-pecl-memcache’
2) Configure o “php.ini”
session.save_handler = memcache
session.save_path =
"tcp://node1:11211, tcp://node2:11211"
3) Configure o “php.d/memcache.ini”
memcache.hash_strategy = consistent
memcache.allow_failover = 1
memcache.session_redundancy = 3
4) Re-inicie o httpd
5) Comece a usar dados da sessão:
<?php
session_start();
$_SESSION[”REQUEST_TIME"] = time()];
?>
• Para situações onde você
precisa armazenar a sessão
externamente
– Em especial quando se usa
ASG (auto scaling groups)
– Cache é otimizado para
altos volumes de leitura
Reference:
http://php.net/manual/en/book.memcache.php *strange memcache bug needs n+1
Exemplo em PHP
37. Fonte em https://github.com/martinrusev/django-redis-sessions
1) Instale o django-redis-sessions:
’pip install django-redis-sessions’
2) Altere o ‘settings.py’:
SESSION_ENGINE = 'redis_sessions.session'
SESSION_REDIS_HOST = 'mycache.hnou5c.ng.0001.usw2.cache.amazonaws.com'
SESSION_REDIS_PREFIX = 'djangosession’
3) Confirme que as sessões estão sendo persistidas no Redis:
mycache.hnou5c.ng.0001.usw2.cache.amazonaws.com:6379> keys "djangosession*"
1) "djangosession:rm6az4eesd7ruc5sibbmf6rlhrwinevs"
Exemplo com Django
Persistência de Sessão
38. Taxa Limite
• Caso ideal caso você queira por
exemplo limitar a quantidade de
request por segundo a uma API
• Usa o comando INCR
ELB
API
Externa Referência: http://redis.io/commands/INCR
FUNCTION LIMIT_API_CALL(APIaccesskey)
limit = HGET(APIaccesskey, “limit”)
time = CURRENT_UNIX_TIME()
keyname = APIaccesskey + ":”+time
count = GET(keyname)
IF current != NULL && count > limit
THEN
ERROR ”API request limit
exceeded"
ELSE
MULTI
INCR(keyname)
EXPIRE(keyname,10)
EXEC
PERFORM_API_CALL()
END
39. Fila de Tarefas
• Basicamente, qualquer coisa pode
ser feita de forma assíncrona for a
da experiência imediata do
usuário:
– Envio de email
– Processamento de imagem ou
video
– Conversão de documentos
– Geração de relatórios
– Limpeza de cache
– Interação com API’s externas
– search indexing
Baseada em Ruby Baseada em Python
http://python-rq.orghttp://github.com/resque
Redis-Queue
40. Publicação/Subscrição
• Casos de Uso:
• Mensagens dentro do
aplicativo
• Janelas de web chat
• Chat/invite para jogos
online
• Não é persistente
• Mais detalhes
http://www.rediscookbook.org
• Usando Pub/Sub para
comunicação assíncrona
SUBSCRIBE “mordor:chat”
SUBSCRIBE “mordor:chat”
SUBSCRIBE
“mordor:chat”
SUBSCRIBE
“mordor:chat”
PUBLISH “mordor:chat” “Estou de olho em você!”
Estou de olho em você!
Estou de olho em você!
Estou de olho em você!
Estou de olho em você!
(integer) 4
>
>
>
>
>
41. var clients = [];
var echo = sockjs.createServer();
echo.on('connection', function(conn) {
clients.push(conn);
conn.on('data', function(message) {
for (var i=0; i<clients.length; i++) {
clients[i].write(message);
}
});
});
WebSockets
http://goldfirestudios.com/blog/136/Horizontally-Scaling-Node.js-and-WebSockets-with-Redis
1
4
3
2
// configure o redis e pub/sub, subscreva ao tópico
// ao receber uma msg, publique pra todos os clientes
// para enviar uma mensagem, publique no redis
Comece com um esqueleto:
Exemplo com Node.js
sub.on('message', function(channel, msg) {
for (var i=0; i<clients.length; i++) {
clients[i].write(msg);
}
});
pub.publish('websocket', message);
var redis = require('node-redid');
var pub = redis.createClient(port, host);
var sub = redis.createClient(port, host);
sub.subscribe('websocket');
npm install node-redis
42. E o favorito de todos: leaderboard!
Não se eu
destruir primeiroÉ meu!
• Fácil de implementar usando Sorted
Sets
• Garante simultaneamente:
– unicidade and ordenação
ZADD "leaderboard" 1201 "Gollum”
ZADD "leaderboard" 963 "Sauron"
ZADD "leaderboard" 1092 "Bilbo"
ZADD "leaderboard" 1383 "Frodo”
ZREVRANGE "leaderboard" 0 -1
1) "Frodo"
2) "Gollum"
3) "Bilbo"
4) "Sauron”
ZREVRANK "leaderboard" "Sauron"
(integer) 3
Example
def save_score(user, score):
redis.zadd("leaderboard", score,
user)
def get_rank(user)
return redis.zrevrank(user) + 1
43. Integração com NGINX
/foo
http {
...
include includes/memc-backend.conf
...
server {
# GET /foo?cmd=get&key=bar
location /foo {
set $memc_cmd $arg_cmd;
set $memc_key $arg_key;
memc_cmds_allowed get;
memc_connect_timeout 5s;
memc_pass backend;
}
}
- includes/memc-backend.conf
upstream backend {
server 172.16.1.1:11211;
server 172.16.1.2:11211;
}
1 Comece com nginx
2 Compile no
memc-nginx-module
3 Configure o segmento
de URI para usar o Memcached
45. auto discovery com Serf
ElastiCache
NODE
+
−
SNS
TOPICO
SQS
MENSAGEM
API de Auto
Scaling
AutoScalingGroup
Protocolo Gossip
S S S S S
S
S
S
S
S
S
S
S
S
S
S
?) Mudança no Cache
?) Mudança no ASG
> Notifica Agente Serf
Script Python:
46. auto discovery com Serf
Usage: percolator <region-name> <app-autoscale-group> <elasticache-cluster> <sqs-queue> <serf-role>
# Reconfigure o serf se ocorrer qualquer mudança no grupo de autoscaling
appautoscalegroup_ips = getAppAutoscalegroupIps(arg_region, arg_appautoscalegroup)
if len(appautoscalegroup_ips) > 0:
...
if ((hashlib.md5(open(serfconfig_file, 'rb').read()).hexdigest()) !=
(hashlib.md5(serfconfig).hexdigest())):
...
sysLog(”Grupo de autoscaling "%s" mudou – atualizando o cluster serf" % arg_appautoscalegroup)
text_file.write(serfconfig)
os.system(serfdaemon_reload)
# Mande uma mensagem de atualização pelo serf caso encontre uma mensagem de atualização do cluster
de elasticache na fila do SQS
if checkSqsQueue(arg_region, arg_sqsqueue, debug):
elasticachenode_addresses = getElasticacheClusterAddresses(arg_region, arg_elasticachecluster)
...
os.system(serfdaemon_event % nodelist[1:])
sysLog(”Despachando evento de atualização de aplicação no serf: %s" % nodelist[1:])
47. E por fim
Lembre-se:
• Nenhum dos dois motores de
cache tem qualquer noção
significativa sobre autenticação ou
criptografia
• Inicie seus clusters de cache
dentro de subnets privadas da sua
VPC
• Utilize regras apropriadas de
security group para controlar o
acesso aos seus nós de cache
E o que fazer a partir daqui?
• Automatize a atividade do seu
cluster
• Tire vantagem da infra de Multi-AZ
• Identifique novos casos de uso que
se beneficiariam com cache
• Use o Amazon SNS e
monitoramento
Segurança
49. Considerações gerais de planejamento
ElastiCache para Memcached
• Tamanho dos nós
– http://docs.aws.amazon.com/AmazonElastiC
ache/latest/UserGuide/CacheNode.Memcach
ed.html
• Otimização
– http://docs.aws.amazon.com/AmazonElastiC
ache/latest/UserGuide/CacheParameterGrou
ps.Memcached.html -
CacheParameterGroups.Memcached.Connec
tionOverhead
• Distribuição em Multi-AZs
– Distribua os nós do Memcached entre
múltiplas AZ’s
ElastiCache para Redis
• Tamanho dos nós
– http://docs.aws.amazon.com/AmazonElastiCa
che/latest/UserGuide/CacheNode.Redis.html
• Otimização
– Redis é single threaded, então escolher um
nó com processador mais rápido vai permitir
melhor performance / maior throughput
• Distribuição em Multi-AZs
– ElastiCache para Redis suporta cluster de nó
único apenas
– Certifique-se de que a(s) replica(s) de leitura
está(ão) em uma AZ diferente do cluster
primário
Mudar o tamanho do nó causa impacto significativo na
aplicação, então escolha o tamanho do nó cuidadosamente
50. Melhores Práticas e Lições Aprendidas
• ElastiCache para Memcached
– Encapsule o acesso ao Memcached em comandos Hystrix para
permitir controle de timeouts e atuar como “circuit breaker”
• Para saber mais sobre o Hystrix, veja https://github.com/Netflix/Hystrix
– Use chamadas de API assíncronas no lugar de chamadas
síncronas
• No passado, tivemos situações de time out de operações
– Diagnosticamos a causa no SDK de ElastiCache para Java e realizamos
a correção
• Mudamos a implementação para executar chamadas assíncronas
com timeout pequeno e re-tentativas
– Estamos trabalhando para reduzir ainda mais o limite de timeout
51. Melhores Práticas e Lições Aprendidas
• ElastiCache para Memcached (continuação)
– Métricas do Amazon CloudWatch:
• CPUUtilization, GetHills, GetMisses, IncrementHits, IncrementMisses,
DeleteHits, NewItems, UnusedMemory, FreeableMemory
52. Melhores Práticas e Lições Aprendidas
• ElastiCache para Redis
– Failover automático do cluster primário*
• AWS cuida da recuperação automática do nó principal, porém essa
operação pode levar até 10 minutos
• A promoção de uma réplica de leitura qualquer a nó principal é uma
operação manual
– Considere implementar um cache de 2 camadas com múltiplas
réplicas de leitura
• Requer implementação em código para gerenciar nó
“principal/preferencial” versus replicas de leitura “alternativas”
• Provê grau adicional de proteção no caso de falha de AZ e
indisponibilidade do cluster principal.
** Baseado em testes feitos antes do anúncio da funcionalidade. Vejam o artigo do blog da AWS chamado
“Multi-AZ Support/Auto Failover for ElastiCache for Redis”.
53. Melhores Práticas e Lições Aprendidas
• ElastiCache para Redis (continuação)
– Métricas do Amazon CloudWatch
• Métricas de nível do nó: CPUUtilization, SwapUsage,
FreeableMemory, NetworkBytesIn, NetworkBytesOut
• Métricas de nível do ElastiCache: Replication Lag,
CurrConnections, CacheHits, Cachemisses