Fala galera! Vou escrever um pouquinho sobre um recurso muito bacana e ao mesmo tempo muito importante, que é o de trabalhar com tarefas em background.
Cenário
Como exemplo para nosso post, iremos adotar o envio de e-mail em massa, ok? Onde o usuário ao clicar em “disparar e-mails” será disparado cerca de 1000 e-mails.
NOTA: Não irei abordar a aplicação como um todo, irei focar somente no método do envio de e-mail no modelo de mensagem, até porque é o que mais nos interessa.
Problema
Imagine o momento que o usuário clicar em “disparar e-mails”… Dependendo da quantidade disparada, o usuário pode não ter uma experiência muito boa em seu site, pois o mesmo terá de esperar até o final da execução da tarefa. E como todos nós sabemos que alguns usuário são um pouco impacientes(Eu), o que pode acontecer para piorar? Isso mesmo! Ele irá tentar 1000.times { puts “novamente…” } achando que a ação não esta acontecendo.
Solução
Existem outras ferramentas para nos ajudar a resolver este problema, mas neste post irei abordar o resque.
A implementação do resque é bem simples, vamos lá?
Implementação
A primeira coisa a ser feita é a instalação o banco utilizado pelo resque para armazenar tarefas a serem executadas. Então vamos instalar o redis:
#No Mac
brew install redis
#No Ubuntu/Debian
apt-get install redis-server
Após a instalação, vamos instalar a gem do resque, então basta adicionar a gem ao seu arquivo Gemfile e rodar o bundle install.
gem 'resque'
bundle install
Precisamos criar o arquivo lib/tasks/resque.rake:
require 'resque/tasks'
task "resque:setup" => :environment
Vamos criar agora o arquivo de conexão com o redis. Então crie o arquivo config/resque.yml:
development: localhost:6379
test: localhost:6379
staging: redis1.se.github.com:6379
fi: localhost:6379
production: redis1.ae.github.com:6379
ATENÇÃO: Não se esqueça de mudar a configuração no ambiente de produção.
Agora iremos criar o nosso initializer em config/initializers/resque.rb:
rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..'
rails_env = ENV['RAILS_ENV'] || 'development'
resque_config = YAML.load_file(rails_root + '/config/resque.yml')
Resque.redis = resque_config[rails_env]
Resque.inline = ENV['RAILS_ENV'] == "test"
Pronto! Vamos ao nosso código agora!
Vamos imaginar que temos um modelo message.rb desta forma:
NOTA: Não irei abordar a configuração do envio do e-mail em si, estou supondo que seu envio de e-mail já esteja funcionando.
class Message < ActiveRecord::Base
after_create :send_message
def send_message
MessageMailer.deliver_message(self)
end
end
Vamos alterar nossa classe para:
class Message < ActiveRecord::Base
after_create :async_send_message
def async_send_message
Resque.enqueue(AsyncSendMessage, self.id)
end
def send_message
MessageMailer.deliver_message(self)
end
end
Repare que o método Resque.enqueue(AsyncSendMessage, self.id) irá enfileirar o envio do e-mail, passando como parâmetro a classe do nosso Worker(que iremos criar no próximo passo) e o id da nossa mensagem(que acabou de ser salva).
Vamos ao nosso worker agora! Crie um arquivo em app/workers/async_send_message.rb:
class AsyncSendMessage
@queue = :async_send_message
def self.perform(message_id)
message = Message.find(message_id)
message.send_message
end
end
Agora tudo faz sentido! Quando nossa mensagem for criada, será chamado o método async_send_message, que irá enfileirar o e-mail na lista de tarefas a serem executadas. Quando chegar a vez do e-mail ser enviado, nosso worker entra em ação, invocando o método send_message.
NOTA: Poderíamos também ao invés de criar o método async_send_message, ter enfileirado o e-mail diretamente no método send_message e ter chamado o método deliver_message diramente do nosso worker.
Testando
Antes de testar, precisamos iniciar o servidor do redis(caso ainda não esteja iniciado). No Ubuntu/Debian ele será automaticamente iniciado na instalação, no caso do Mac, basta executarmos:
sudo redis-server /usr/local/etc/redis.conf
Dessa forma, quase tudo irá funcionar, o usuário irá achar que esta enviando o e-mail naquele momento, mas, o e-mail será enfileirado para o envio. Para executar o que esta na lista de tarefas, temos que deixar rodando:
QUEUE=* rake environment resque:work
Veja que usei *, então todos os workers irão rodar(caso você tenha mais de um), porém se você quiser especificar um somente, basta executar o comando dessa forma:
QUEUE=async_send_message rake environment resque:work
NOTA: Veja, o async_send_message está especificado em @queue dentro de nosso worker AsyncSendMessage.
É isso ai pessoal, espero que tenha ajudado!
Abraços!
-
diegoalvareznogueira posted this