aws sao paulo summit 2015 elasti cache avancado
TRANSCRIPT
São Paulo
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
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
Revisão Rápida
Por que estamos aqui?
ms μs
ElastiCache
Memcached
• Serviço gerenciado da AWS
que permite criar, utilizar e
escalar rapidamente clusters
de cache distribuido na
nuvem
Disponível em duas opções:
Redis
Similar a um banco no
NoSQLhttp://redis.io/commands
Cache em memória
do tipo chave,valor
Suporte a tipos de dadosstrings, listas, hashes, sets, sets ordenados,
bitmaps & HyperLogLogs
Single-threaded
Operações AtômicasSuporte a transações
com propriedades ACID
Ridiculamente rápido!
Réplicas de Leitura
PersistênciaSnapshots ou log append-only
Funcionalidade de
publicação/subscrição
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
Lançando
Lançando
1Escolha:
• 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*
2Choose:
• 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:
Ou use a AWS CLI:aws elasticache create-cache-cluster
--cache-cluster-id mycache
--engine redis
--cache-node-type cache.m3.medium
--num-cache-nodes 1
ou o AWS CloudFormation:
"Resources" : {
"CacheCluster" : {
"Type": "AWS::ElastiCache::CacheCluster",
"Properties": {
"CacheNodeType" : { "Ref" : "CacheNodeType" },
"CacheSecurityGroupNames" : [ { "Ref" : "CacheSecurityGroup" } ],
"Engine" : "memcached",
"NumCacheNodes" : { "Ref" : "NumberOfCacheNodes" }
}
}
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
Redis Multi-AZ Auto-Failover
Escolhe a réplica com
menor atraso
Não muda o DNS
Availability Zone #1 Availability Zone #2
escritasUse o endpoint
primário
leiturasUse os endpoints de
réplica (ou o primário
também)
Conectando
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}
// Exempoo em Java – requer http://aws.amazon.com/sdk-for-javaAmazonElastiCache 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?
En
co
ntr
ar
AZ
Ch
am
ad
aa
AP
I d
o
Ela
sti
Ca
ch
e
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
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
# 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
Distribuindo as Chaves (Sharding)
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]
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 anelas 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çõesspread throughout the ring, not even like this
12
3
4
5
6789
1011
12
13
14
1516
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 devema maior partição mais próxima no anel
“uma estratégia de sharding
para a todos governar
Monitorando
Memcached internamente
SEM
MEMÓRIA
Slab Classe 42
Tam. Chunk: 1MB
Chunks/Pg: 1
> stats slabsSlab 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
>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
Monitoramento
Source:http://blog.elijaa.org/index.php?pages/phpMemcachedAdmin-Download
phpMemcachedAdminAmazon
CloudWatch
Alarmes
“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
• 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
A Solução
A Solução
Casos de Uso
Revisando o cenário tipico de web 2.0
ELB Aplicação
APIs Externas
fácil de adicionar
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)
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"})
Persistência de Sessão1) 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
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
Taxa Limite
• Caso ideal caso você queira porexemplo 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 + ":”+timecount = GET(keyname)IF current != NULL && count > limit THEN
ERROR ”API request limit exceeded"ELSE
MULTIINCR(keyname)EXPIRE(keyname,10)
EXECPERFORM_API_CALL()
END
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
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
>>
>
>
>
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
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
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
Juntando Tudo
auto discovery com Serf
ElastiCache
NODE+
−SNS
TOPICO
SQSMENSAGEM
API de Auto
Scaling
Au
to S
ca
lin
g G
rou
p
Protocolo Gossip
S S S S SS
S
S
S
SS
S
S
S
S
S
?) Mudança no Cache
?) Mudança no ASG> Notifica Agente Serf
Script Python:
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 clusterde 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:])
E por fim
Lembre-se:
• Nenhum dos dois motores de cache tem qualquer noçãosignificativa sobre autenticação oucriptografia
• Inicie seus clusters de cache dentro de subnets privadas da suaVPC
• 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
Melhores Práticas
Considerações gerais de planejamentoElastiCache 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
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 chamadassíncronas
• No passado, tivemos situações de time out de operações
– Diagnosticamos a causa no SDK de ElastiCache para Java e realizamosa correção
• Mudamos a implementação para executar chamadas assíncronascom timeout pequeno e re-tentativas
– Estamos trabalhando para reduzir ainda mais o limite de timeout
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
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 essaoperação pode levar até 10 minutos
• A promoção de uma réplica de leitura qualquer a nó principal é umaoperação manual
– Considere implementar um cache de 2 camadas com múltiplasré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”.
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
Leitura
• aws.amazon.com/documentation/elasticache/
Comunidade e suporte:
• awshub.com.br
• forums.aws.amazon.com
• aws.amazon.com/support
Treinamento
• https://qwiklabs.com/focuses/preview/1380
Próximos Passos
Fábio Aragão da Silva, Solutions Architect – AWS
São Paulo