loop_dance — фоновый планировщик быстрого развертывания

В последнее время в проектах часто приходится создавать демона, который периодически фоном что-то проверяет или отсылает.

Обычно подобную задачу решают с помощью таких пакетов как whevenever, daemon_controller, daemon_generator и т.д. и все казалось бы просто и понятно, но надоело каждый раз городить огород и писать одно и тоже. Мне нужно всего-лишь чтобы раз в час выполнялось какое-нибудь User.notify_all

Презентую loop_dance — gem для быстрого развертывания управляемого демона в рельсовом окружении.

Условия


  1. Минимум лишнего кода. Только то, что нужно запустить и с какой периодичностью.
  2. Код выполняется в рельсовском окружении.
  3. Демон должен автоматически запускаться/перезапускаться при деплоинге проекта.
  4. Возможность управлять демоном через rake-задачи или прямо из приложения (приятно видеть на админке его статус)

Танцуем


Вставляем пакет в наш Gemfile, незабываем затем обновить bundle

gem "loop_dance"


Создаем файлик lib/loop_dance.rb с заданиями и указанной периодичностью.

Например: Каждые 3 часа оповещать всех пользователей. Каждые 60 секунд проверять рапорты.

 class Dancer1 < LoopDance::Dancer

   every 3.hours do
      User.notify_all
   end

   every 60.seconds do
      Report.checks
   end

 end


Вот и все.

Мы создали первого танцора, который запустится автоматически при следующем рестарте рельсовского сервера, будет висеть в системе независимым демоном и с указанной периодичностью выполнять задания.

Можно им управлять и вручную:

rake loop_dance:start_all
rake loop_dance:stop_all
rake loop_dance:status

rake loop_dance:dancer1:start
rake loop_dance:dancer1:stop
rake loop_dance:dancer1:status


А можно и из самого приложения:

Dancer1.start  unless  Dancer1.running?


Адрес проекта: github.com/dapi/loop_dance

Ссылки по теме:
ruby-toolbox.com/categories/daemon_management.html
ruby-toolbox.com/categories/daemonizing.html
ruby-toolbox.com/categories/scheduling.html
+30
11 января 2011, 19:47
24
dapi 22,0

комментарии (21)

0
goshakkk #
Большое спасибо. Часто приходится сталкиваться с такими задачами, и писать каждый раз демонов — не лучший выход.
+2
bibendi #
А не лучше ли использовать старый добрый cron, который запускает rake задачи. И лишнюю память не будет занимать, и жутко стабилен, плюс время запуска заданий можно поинтереснее задавать =)
0
dapi #
В каких то случаях лучше.

Но это вечный вопрос — что лучше иметь запускаемые по крону скрипты, постоянно отжирающие ресурсы во время запуска или иметь один запущенный демон съевший часть памяти на совсем постоянно. Если таймаут между задачами не большой — то проще запустить единожды и не париться, а в данном случае демоном еще и проще управлять.
0
shortcaster #
В данном случае плюс в том, что регулярные задачи описываются в коде приложения, всегда на виду, никуда не потеряются, не забудутся. Но если хочется именно cron, есть еще Whenever
0
bibendi #
Ну так rake задачи тоже же в коде приложения. И Расписание для крона тоже храниться в текстовом файле и при деплое через капистрано новое расписание автоматом заливается в крон.

И заодно, отвечу здесь на ответ выше: полностью согласен, если время запуска менее пяти минут, то лучше писать демона. Мы используем вот этот gem.
0
dapi #
cron это очень хороший вариант для редких задач. Есть два недостатка:

1) по крону не будешь запускать частые задачи, такие, например, как ежеминутный опрос твиттера, иначе большая часть ресурсов процессора будет уходить на запуск задач, а не на их выполнение;
2) если кроновские задачи вдруг начали подвисать (сеть подводит или просто стали сложнее и длительнее) и время их выполнение превышает частоту запуска. Например задача запускается каждые 10 минут, а длительность выполнения начала превышать 11 минут, то через некоторое время вы получите память забитую зависшими тасками со всеми вытекающими последствиями.

Есть два способа выполнения периодических задач — по крону и демоном. У каждого способа есть свои достоинства и недостатки.
+1
casper_r #
1) правильно, т.к. такие задачи засовываются в Resque github.com/defunkt/resque

2) — хрень которая лечится

require 'timeout'

begin
Timeout::timeout(10*60) {
ваши медленные и подвисающие запросы
}
rescue Timeout::Error
puts «вывалились по таймауту»
end
0
dapi #
О, кстати, насчет resque.

Правильно понимаю что для того чтобы с ним выполнять какие-то типовые задачи периодически, например каждые 5 минут, нужно запустить redis-сервер, reqsue-сервер и еще какую-то нибудь хрень (хотя бы по крону) которая будет эти задачи периодически пихать в очередь?
0
casper_r #
единственная разница между resque и вашим методом это наличиее redis'а
там все так же управляемые демоны, которые можно разбить по очередям например zip_queue, twitter_queue и запускать любое колличество ворокеров для каждой.

что же до цикличного выполнения каждые 5 минут, можно поставить плагин
github.com/bvandenbos/resque-scheduler

ваш способ хорош, но только при одной фоновой задаче, при двух уже появляются сложности контроля… кто упал, кто не выполнился и прочее… почему сразу не сделать правильно, если все равно прийдется сделать ;)

0
dapi #
loop_dance и resquire это совершенно разные вещи у них различные цели и методы их достижения.

Насчет контроля вы вообще не правы — с контролем и параллельным запуском как раз таки все в порядке, каждого демона можно контролировать отдельно и через rake task и из приложения.

Главное достоинство loop_dance в том, что вы получаете подконтрольного автоматически запускаемого демона за 5 строк кода. Сделали и забыли. Не нужно лазить на сервак, редактировать крон, прописывать пути и т.п. и т.д.
0
dapi #
А уж ко всяких очередникам типа reqsue, delayed_job, workling и тп. эта софтина не имеет ВООБЩЕ никакого отношения и не в коем случае не претендует на их замену.

Вы что-то путатете фоновое периодическое выполнение задач с выполнением заданий по запросу.
0
shortcaster #
Спасибо за статью, отличный инструмент!

PS Может стоит перенести статью в блог Rails?
0
dapi #
Перенес, а их оказывается два…
+1
benone #
Насколько я понимаю, таски обрабатываются в очереди?
А крон вроде как параллельно запускает…
0
dapi #
Верно. Если один так зависнет — остальные не выполнятся.
0
dapi #
Кстати в этом большой плюс — гарантия что через некоторое время память на забьется тормознувшими тасками, что бывает при частом запуске по крону.

А если нужно параллельное выполнение, то задачу можно кинуть во второго танцора, который будет выполняться параллельно:

 class Dancer2 < LoopDance::Dancer

   every 6.hours do
      # делать что-то очень важное параллельно первому танцору
   end

 end


вторым танцором можно также управлять отдельно
0
n0ne #
А что значит «висеть в системе независим демоном»? Т.е. на «облаке» будет постоянно потреблять память и процессор? Или я что-то недопонимаю…
0
dapi #
Я думаю на облаке будет вести себя также как и на одиноком сервере — висеть в памяти, но потреблять процессор только вовремя выполнения задач.
0
potapuff #
Можно ли с Танцором организовать отложное выполнение методов? Хочется выкинуть async-observer.
0
dapi #
Думаю не стоит — другая задача, придется самому очередь сообщений организовывать.
+1
toxicmt #
Мы используем github.com/kovyrin/loops
А вместо крона возможен даже такой вариант fourkitchens.com/blog/2010/05/09/drop-cron-use-hudson-instead

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.