Envio de e-mails assincronamente com Backgroundjob (Bj)

Durante o desenvolvimento do Zest, nos deparamos com uma situação padrão: enviar e-mails assincronamente. Já havíamos usado no VoteBolsa o ARMailer e ele vem funcionando bem. Dois motivos nos fizeram pensar em fazer algo diferente desta vez:

1) No Zest, já usávamos o Backgroundjob (Bj) para os outros processos assíncronos;

2) Usar o ARMailer implica, principalmente, em termos que configurar uma tarefa no cron do servidor de aplicações para efetivamente enviar os emails. O cron é excelente, mas ficar dependendo de configurar o cron em cada novo servidor é algo que estamos tentando evitar.

Por isso, optamos por criar um job para o Bj que tratasse de chamar o enviador de e-mails corretamente (classe mailer que herda de ActionMailer::Base). O nosso job ficou assim:

Criando o Job:

def self.create(args={})
raise RailsJobArgumentError, "arguments must be a Hash" unless args.is_a?(Hash)
raise RailsJobArgumentError, "hash should contain a symbol key called mailer_class" unless args.has_key?(:mailer_class)
raise RailsJobArgumentError, "mailer_class should ends with _mailer" unless args[:mailer_class] =~ /(_mailer)$/
raise RailsJobArgumentError, "hash should contain a symbol key called method" unless args.has_key?(:method)
Bj.submit("./script/runner '#{self}.run(#{args.inspect})'")
end

Para executar o Job, fazemos:

def self.execute(args)
params = {}
mailer_class = args.delete(:mailer_class)
deliver_method = args.delete(:method)
raise RailsJobArgumentError, ":mailer_class not specified" unless mailer_class
raise RailsJobArgumentError, ":method not specified" unless deliver_method
args.each do |key, value|
unless (key.to_s =~ /_id\z/).blank?
obj = key.to_s.gsub("_id","").camelize.constantize.find(value)
params.store obj.class.to_s.underscore.to_sym, obj
else
params.store key.to_sym, value
end
end
mailer_obj = mailer_class.camelize.constantize
# check if mailer_class constantizes do send email
raise RailsJobArgumentError, "mailer_class should have ActionMailer::Base as it superclass" unless mailer_obj.superclass.to_s == "ActionMailer::Base"
mailer_obj.send("deliver_#{ deliver_method }", params)
end

Para usar esse Job, vamos supor que queremos enviar um email para o usuário que acaba de se cadastrar. Então, criaríamos um observer como:

class UserObserver < ActiveRecord::Observer
after_create(user)
SendMailJob.create(
:mailer_class => "user_mailer",
:method => "activation",
:user_id => user.id
)
end
end

Uma  diferença importante é que o ArMailer o email é apagado do banco de dados tão logo é enviado, enquanto no Bj o email permanece na tarefas já executadas (veja na tabela bj_job_archive o campo stdout). Portanto, não armazene dados sensíveis (como senha) em seu job.

2 opiniões sobre “Envio de e-mails assincronamente com Backgroundjob (Bj)”

  1. Na verdade, essa solução pode ser facilmente modificada para o starling. Para falar a verdade, no momento estamos usando o Workling para o Zist (o sistema de invoices). O que ele fornece é uma camada de abstração para usar com o Starling ou o BJ. Mas o BJ cumpre muito bem sua função. A limitação dele é apenas 1 processo por máquina. Mas para nos está perfeito por enquanto (nada no cron, processos assíncronos iniciando o consumo imediatamente, …).

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *