DevSecOps

29 mar, 2018

Crie aplicações mais rápidas utilizando Redis

Publicidade

Aprender. Esse foi o verbo que mais conjuguei desde que entrei no iCasei. Muito se deve por ter trocado Microsoft por Ruby on Rails, que é uma mudança “pesada”, vamos concordar. Resumindo, hoje me encontro melhor em um terminal cheio de comandos do que em um Wizard de instalação repetitivo e chato!

Voltando ao assunto. Outra coisa valiosa que aprendi lá, foi em como saber trabalhar com cache é extremamente importante.

Mas por que, cara?

  • Só assim para aguentar um volume alto de acessos
  • Você ganha performance (teu site fica muito rápido)
  • A conta da AW fica mais barata no final do mês

Como a mágica funciona?

É como se você criasse um atalho para uma informação específica, entende? Ao invés de procurar uma conexão com o banco, executar uma consulta e esperar a resposta, você guarda esse dado em uma chave no cache que pode estar salvo em um arquivo físico na própria aplicação (In-Process) ou até em um Redis ou Memcached (Distributed Cache).

In-Process

Acho que todas as linguagens tem um provider de FileSystem nativo para armazenar informações em cache e que pode ajudar muito dependendo da situação. A ideia é gerar um arquivo físico em uma pasta temporária da aplicação para facilitar o acesso via IO, mas essa opção pode gerar um aumento significativo de processamento da máquina.

Distributed Cache

Em algumas situações você vai querer compartilhar esse atalho entre aplicações e é ai que o Cache Distribuído entra na história.

Imagine o seguinte: no iCasei, o casal de noivos vai criar todo o site do casamento em poucos dias. Eles vão subir imagens, textos, informar o local da festa, estilizar cores e depois os convidados que vão entrar, admirar o site e muitas das vezes comprar um presente.

Preciso ficar sempre perguntando lá no banco de dados como é o site do casamento?

Não! Vamos criar um atalho para que a aplicação que exibe o site não precise ficar consumindo tanto hardware assim. Mas e se o casal resolver mudar alguma informação?

Tudo tranquilo. Como meu cache está armazenado de forma distribuída, quando o casal alterar a informação na aplicação Painel, vamos limpar essa chave e a outra aplicação que renderiza o site customizado vai recria-lá com os dados atualizados e vai seguir assim até que a aplicação Painel limpe a informação novamente.

Opções do mercado

Vamos falar sobre Redis nesse artigo, mas não existe só ele no mercado, e dependendo do que você pretende fazer, talvez nem seja a melhor opção. Resumindo, não existe bala de prata; para cada situação uma tecnologia vai se comportar melhor do que outra e por isso resolvi abrir um tópico para “comparar” os dois mais conhecidos do mercado.

Memcached

O Memcached foi desenvolvido inicialmente por Brad Fitzpatrick para seu site em 2003. O LiveJournal tem como princípio a arquitetura cliente-servidor onde o cliente sempre vai buscar a chave que procura. Para ficar rápido, ele resolveu salvar essas informações em memória RAM, ou seja, pouco custo de hardware para encontra-las.

Redis

O Redis foi criado por Salvatore Sanfilippo, em 2009, e em muitos momentos o consideram um banco NoSQL. Tem suporte para diversos Clients e também se beneficia da memória RAM para buscar as informações de forma rápida, mas pode facilmente ser configurado para trabalhar com dados persistentes.

Comparando

Redis Memcached
Modelo chave/valor Sim Sim
Salvam em memória Sim Sim
Pouca latência Sim Sim
Compatível com n/linguagens Sim Sim

Coisas legais do Redis

Aqui estão listados alguns dos motivos pelos quais muitos desenvolvedores vem escolhendo o Redis para trabalhar:

  • Dados estruturados (lists, sets, sorted sets, bitmaps, etc)
  • Master/Slave e Sentinel (crescimento horizontal)
  • Binary safe (muito mais espaço)
  • Single-threaded (um de cada vez)
  • Push/Pop (integração com Sidekiq, Resque, RestMQ…)
  • Pubsub nativo (Action Cable do Rails 5 se beneficia disso)

Operações atômicas

Sem dúvidas essa é uma das características mais importantes do Redis, e entender ela pode te ajudar bastante na hora de decidir como escalar sua aplicação.

Por ser single-threaded, o Redis executa uma operação de cada vez, ou seja, nada é executado paralelamente. Isso faz com que seu core seja mais simples por não precisar lidar com concorrências e sync de informações.

Sabendo disso, quando trabalhar com Redis, tenha em mente que para ter uma boa performance, muitas vezes você vai precisar escalar sempre de forma horizontal, ou seja, mais maquinas trabalhando para executar mais operações.

Para conseguir fazer o shared das informações entre os nós, o Redis pode ser configurado para trabalhar com Master/Slave + Sentinel, mas isso vai ficar para um próximo artigo, beleza?

Por que escolheu o Redis?

Escolhemos ele por dois motivos:

Dados estruturados: ajudam muito na hora de incrementar números, salvar listar e não ter a necessidade de realizar uma conversão implícita para isso.

O que isso quer dizer?

Nas outras opções do mercado, você vai buscar a informação salva em texto, e se isso for um array ou um inteiro antes de trabalhar com o dado, uma conversão necessariamente precisa ser realizada.

Push/pop: enfileiramos os envios de push notification e e-mails no Sidekiq, que é uma boa prática para remover a carga de ações que precisam ser rápidas, e ficar esperando um e-mail ser enviado não é tão legal.

Instalação

$ wget http://download.redis.io/releases/redis-4.0.1.tar.gz
$ tar xzf redis-4.0.1.tar.gz
$ cd redis-4.0.1
$ make

redis-cli

Já vem na instalação e é um client em linha de comando para você realizar consultas, criar e deletar chaves.

redis-cli -h 127.0.0.1

Alguns comandos:

Setando e buscando informação

$ redis> set casamento:1 ‘Evandro e Evandra’
$ redis> get casamento:1
$ redis> incr contador
$ redis> get contador

Encontrando chaves

$ redis> keys *
$ redis> keys ‘casamento:*’

Limpando informações

$ redis> flushall
redis> del ‘casamento:1’

Benchmarks

Esse é bem legal para quem quer medir a performance da instância. No exemplo abaixo, estamos realizando um teste com 1000 requisições utilizando 10 clients diferentes para isso:

$ redis-benchmark -n 1000 -c 10

Conectando aplicação Rails

Agora vou mostrar como é simples colocar o Redis para trabalhar junto com uma aplicação Rails.

Para entender o exemplo, você precisa saber o básico de Ruby on Rails como, por exemplo, o que é uma RubyGem, um pouco da estrutura de pastas e como funciona o carregamento de uma aplicação RoR.

Gemfile

Vamos instalar a gem redis-rails que vai nos fornecer os providers para realizar a comunicação com o servidor Redis.

gem ‘redis-rails’

Configurando acesso

Agora vamos criar um arquivo chamado redis.yml dentro da nossa pasta /config.
No modelo yml abaixo estamos dizendo onde encontrar o Redis dependendo do ambiente que a aplicação estiver rodando.

development:
host: 127.0.0.1
port: 6379
test:
host: 127.0.0.1
port: 6379
production:
host: sua-host
port: 6379

Instanciando uma variável global

Vamos criar um arquivo chamado redis.rb em /config/initializers/. Com isso nossa variável global $redis será instanciada no momento que a aplicação for inicializada.

Buscamos o arquivos de configuração e convertemos ele salvando na constante REDIS_CONF e depois utilizamos os dados para instanciar a classe Redis.

REDIS_CONF = YAML.load_file(File.join(Rails.root, “config/redis.yml”))[Rails.env]
$redis = Redis.new({
host: REDIS_CONF[“host”],
port: REDIS_CONF[“port”])

Pronto, agora é só utilizar.

Vamos criar um método que realiza um Request – que é uma ação bastante custosa –  para buscar o total de comentários enviados.

def countComments(url)
length = 0
begin
response = RestClient::Request.execute(method: :get, url: url)
if response.code == 200
length = JSON.parse(response.body).length
end
rescue => e
Rails.logger.error(e.backtrace)
end
length
end

Agora, na exibição dessa informação em uma View, por exemplo, vamos primeiro perguntar para o Redis se ele tem essa informação antes de executar o Request.

def index
@totalComments = $redis.get(“totalComments”)
if @totalComments.nil?
if $redis.set(“totalComments”, countComments(‘https://jsonplaceholder.typicode.com/comments’))
@totalComments = $redis.get(“totalComments”)
end
end
render layout: nil
end

O que fizemos aqui foi tentar poupar um pouco de processamento indo buscar uma informação que não vai mudar com frequência na memória ao invés de sempre ter que perguntar para a API quantos comentários existem.

Estamos preparando um curso online no Clube do Código sobre tudo isso. Fiquem ligados!

Já assistiu as últimas meetups que gravamos?