Criando daemons em Ruby on Rails (com script para Gentoo)
ruby on rails+daemon gem+daemon plugin+capistrano+gentoo linux
Eu estava utilizando o RailsCron para envio de email em batch e segundo plano, porém tive muitos problemas com este plugin. Ele não reiniciava bem, era necessário reiniciá-lo manualmente toda vez que o servidor web fosse reiniciado. Isso era um grande problema!
Quando estava buscando uma solução, li que o RailsCron está depreciado e que no lugar poderíamos uilizar o Daemons Generator com o Daemons Gem.
Resolvi então aproveitar meu trabalho e disponibilizar um passo-a-passo de instalaçao e utilização do daemons generator.
Primeiro Passo: Instalação do gem e do plugin [c] sudo gem install daemons cd /path/da/sua/aplicacao ./script/plugin install http://svn.kylemaxwell.com/rails_plugins/daemon_generator/trunk mv vendor/plugins/trunk vendor/plugins/daemon_generator [/c]
Segundo Passo: Criação de um daemon Em meu caso utilizei um daemon para enviar e-mails pendente em série a cada minuto. [c] ./script/generate daemon email_queue [/c]
Rodando a linha de comando acima, serão criados os seguintes arquivos:
script/daemons - este arquivo é criado apenas no primeiro daemon, ele server para iniciar e parar todos os daemons do projeto. lib/daemons/email_queue.rb - este arquivo que será o daemon em si. lib/daemons/email_queue_ctl - este script serve para iniciar e parar este daemon especificamente. config/daemons.yml - Este arquivo também será gerado apenas na primeiraz vez, ele contém algumas configurações do daemon generator.
Terceiro Passo: Programação do seu daemon Você precisa editar o arquivo do seu daemon para colocar a programação desejada. No meu caso editei o lib/daemons/email_queue.rb e coloquei o seguinte conteúdo:
[ruby] #!/usr/bin/env ruby
You might want to change this
ENV[“RAILS_ENV”] ||= “production”
require File.dirname(FILE) + “/../../config/environment”
$running = true; Signal.trap(“TERM”) do $running = false end
while($running) do
ActiveRecord::Base.logger < < “#{Time.now} - DAEMON: Processing invite queue.\n” QueueInvite.find(:all, :conditions => [‘sent_at is ? and send_at < = ?’,nil,Time.now.utc]).each { |e| e.deliver }
sleep 60 end [/ruby]
As linhas que serão executadas pelo daemon ficam dentro do while($running).
Atenção: o daemon fica rodando direto em um loop infinito então cuidado com o que você coloca aí dentro!
No meu caso a primeira linha server para criar uma entrada no log dizendo a data e hora que a rotina foi executada a segunda linha é a rotina em si e a última é um sleep que fará com que a rotina só seja executada a cada minuto.
Não vou explicar o QueueInvite, por que já faz parte da minha aplicação, mas resumidamente a cada 60 segundos este daemon busca no banco de dados os convites pendentes e manda enviá-los pelo método deliver.
Quarto Passo: Atualização da receita do Capistrano (se aplicável) Se você usa o capistrano para automatizar o processo de deploy de sua aplicação (e deveria usar!), é necessário alterar o arquivo de receita para ele inciar e parar nosso daemon a cada deploy.
Para tal, coloque o seguinte código no arquivo config/deploy.rb
[ruby] desc “Stop daemons before deploying” task :before_deploy do run “#{current_path}/script/daemons stop” end
desc “Start daemons after deploying” task :after_deploy do run “#{current_path}/script/daemons start” end [/ruby] Atenção: Verifique antes se as tarefas before_deploy e after_deploy já existem no seu arquivo. Caso existam, copie apenas o conteúdo da terefa.
Quinto Passo: Criar um daemon do sistema operacional A princípio tudo está funcionando, mas e quando reiniciarmos o servidor? Bem o daemon tem que voltar a rodar! Eu utilizo o Gentoo Linux e disponibilizei aqui o script que eu criei para cuidar deste problema.
script/gentoo_daemons [c] #!/sbin/runscript
Gentoo users: add this script to ‘default’ run level.
==================================================
#
rails_daemons This shell script takes care of starting and stopping
the daemons plugin of one rails project.
#
Copyright (c) by Rafael Lima (http://rafael.adm.br)
project_name=”Your project name” project_path=”/path/of/your/project”
depend() { need localmount cron }
checkconfig() { if [ ! -d ${project_path} ] ; then eerror “The project directory does not exists! Please, check your configuration.” fi }
start() { checkconfig || return 1 ebegin “Starting ${project_name} daemons” ${project_path}/script/daemons start eend $? }
stop () { ebegin “Stopping ${project_name} daemons” ${project_path}/script/daemons stop eend $? } [/c]
É isso pessoal, espero que este artigo seja útil para muita gente! Em caso de dúvidas sintam-se a vontade para deixar um comentário!
Abraços.