Работа cron для рельсов: лучшие практики?


295

Каков наилучший способ запуска запланированных задач в среде Rails? Сценарий / бегун? Грабли? Я хотел бы запускать задачу каждые несколько минут.


149
Для тех, кто приходит сюда из Google, посмотрите за принятый ответ для лучшего подхода.
Jrdioko

4
Каждый раз, когда ответ кажется более разумным, чем принятый ответ, который является старым взломом.
Роб

2
Также обратите внимание, что по крайней мере один ответ предполагает, что у вас установлен определенный драгоценный камень.
ТАСС

Пар (что я нашел, чтобы быть) хорошие практики суммированы здесь wisecashhq.com/blog/writing-reliable-cron-jobs
Тибо Barrere

Во многих случаях работа cron - это неприятный запах. Лучше написать планировщик через sidekiq / resque (или другой фоновый работник) или написать демон (менее функциональный и контролируемый). У заданий Cron есть как минимум несколько плохих вещей: 1) блокировка одного экземпляра - это боль; 2) мониторинг не может быть осуществлен легко; 3) обработка исключений должна быть снова написана вручную; 4) не легко перезапустить; 5) все вышеперечисленные вопросы легко решаются фоновыми работниками.
Дмитрий Полушкин

Ответы:


110

Я использую рейк-подход (как поддерживает heroku )

С файлом с именем lib / tasks / cron.rake ..

task :cron => :environment do
  puts "Pulling new requests..."
  EdiListener.process_new_messages
  puts "done."
end

Чтобы выполнить из командной строки, это просто "rake cron". Эта команда затем может быть помещена в cron / планировщик задач операционной системы по желанию.

Обновите это довольно старый вопрос и ответ! Некоторая новая информация:

  • сервис Heroku Cron, на который я ссылался, был заменен Heroku Scheduler
  • для частых задач (особенно там, где вы хотите избежать затрат на запуск среды Rails), я предпочитаю использовать системный cron для вызова скрипта, который либо (a) выдает API защищенного / частного webhook для вызова необходимой задачи в фоновом режиме или (б) поставить в очередь задачу в выбранной вами системе очередей

Какой должна быть запись cron для этого случая, чтобы ОС знала правильный путь к задаче rake?
Джридиоко

13
NB: в эти дни я использую всякий раз (см. Ответ Джима Гарвина), но необработанная запись cron для запуска задачи rake будет выглядеть примерно так: 30 4 * * * / bin / bash -l -c 'cd / opt / railsapp && RAILS_ENV = производственные грабли cron --silent '
tardate

1
Как вы называете это с консоли? Я сделал load "#{Rails.root}/lib/tasks/cron.rake"и rake cron, но получил NameError: неопределенная локальная переменная или метод `cron 'для main: Object
B Seven

3
Проблема этого подхода заключается в :environmentзависимости. У нас очень тяжелое Rails-приложение, которое требует много времени для запуска, наш Rake вызывается каждую минуту и ​​потребляет больше ресурсов для запуска среды Rails, которая выполняет задачу . Я хотел бы, чтобы уже запущенная среда Rails вызывалась через cron, должна быть что-то среднее между подходом контроллера и средой rake .
fguillen

Какова продолжительность этого задания? Я использую условие if. Я хочу знать, как регулярно это выполняется. Я не могу найти никакой информации об этом на сайте heroku.
Шубхам Чаудхари

254

Я использовал чрезвычайно популярный проект всякий раз, когда он сильно зависит от запланированных задач, и это здорово. Это дает вам хороший DSL для определения ваших запланированных задач вместо того, чтобы иметь дело с форматом crontab. Из README:

Всякий раз, когда это Ruby-гем, который предоставляет четкий синтаксис для написания и развертывания заданий cron.

Пример из README:

every 3.hours do
  runner "MyModel.some_process"       
  rake "my:rake:task"                 
  command "/usr/bin/my_great_command"
end

every 1.day, :at => '4:30 am' do 
  runner "MyModel.task_to_run_at_four_thirty_in_the_morning"
end

22
Если он запускается каждую минуту, среда будет перезапускаться каждый раз, что может быть дорогостоящим. Похоже, что github.com/ssoroka/scheduler_daemon избегает этого.
Лулалала

3
+1 за сохранение конфигурации cron в вашей системе контроля версий
brittohalloran

3
Я думаю, что это лучшее решение. Если вы используете рельсы, я думаю, что лучше написать все в рельсах. При таком подходе вы также можете забыть о задаче cron при смене серверов, она перемещается вместе с приложением.
Адриан Маттео

Существует отличный Railscast о том, когда это действительно полезно (более старая бесплатная версия также доступна ).
aceofbassgreg

@ Тони, Всякий раз, когда это в основном предметно-ориентированный язык для написания заданий cron. Он компилируется в обычный синтаксис cron на вашем сервере rails, и cron выполняет заданные вами задания (обычно через rails runner).
Грег

19

В нашем проекте мы впервые использовали gem, но столкнулись с некоторыми проблемами.

Затем мы переключились на гем RUFUS SCHEDULER , который оказался очень простым и надежным для планирования задач в Rails.

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

Код, используемый в этом как:

    require 'rufus-scheduler'

    scheduler = Rufus::Scheduler.new

    scheduler.in '10d' do
      # do something in 10 days
    end

    scheduler.at '2030/12/12 23:30:00' do
      # do something at a given point in time
    end

    scheduler.every '3h' do
      # do something every 3 hours
    end

    scheduler.cron '5 0 * * *' do
      # do something every day, five minutes after midnight
      # (see "man 5 crontab" in your terminal)
    end

Чтобы узнать больше: https://github.com/jmettraux/rufus-scheduler


1
На rufus, так как я использовал его как для простых проектов ruby, так и для приложений full rails.
Пауло Фидальго

8
Не могли бы вы немного конкретнее рассказать о проблемах, с которыми вы столкнулись?
Герцог

самый великий ответ
Дарлан Дитрих

17

Предполагая, что выполнение ваших задач не занимает много времени, просто создайте новый контроллер с действием для каждой задачи. Реализуйте логику задачи в виде кода контроллера, затем настройте cronjob на уровне ОС, который использует wget для вызова URL-адреса этого контроллера и действия в соответствующие промежутки времени. Преимуществами этого метода являются вы:

  1. Иметь полный доступ ко всем вашим объектам Rails, как в обычном контроллере.
  2. Может развиваться и тестировать так же, как вы делаете обычные действия.
  3. Может также вызвать ваши задачи adhoc с простой веб-страницы.
  4. Не используйте больше памяти, запуская дополнительные процессы ruby ​​/ rails.

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

44
Я знаю, что это было некоторое время назад, но это определенно не лучший способ делать работу cron больше. Зачем идти через веб-интерфейс, нарушая то, что на самом деле представляет интерфейс, когда есть много других способов получить доступ к среде Rails?
Матчу

6
Квалификация «при условии, что ваши задачи не занимают слишком много времени» кажется огромной. Не лучше ли использовать более общий подход, и не только в тех случаях, когда задачи выполняются очень быстро? Таким образом, вы не будете постоянно переоценивать необходимость переписывания той или иной задачи с использованием другого подхода.
иконоборчество

77
Этот старый вопрос является лучшим результатом Google для "rails cron". Этот ответ далеко не лучший подход. Пожалуйста, смотрите другие ответы для более вменяемых предложений.
Джим Гарвин

2
Не лучший способ. У вас есть много других способов получить доступ к Rails env через задание cron без вызова службы REST. Подход с рейком определенно лучше
Shine

10

Задачи script / runner и rake прекрасно подходят для выполнения заданий cron.

Вот одна очень важная вещь, которую вы должны помнить при запуске заданий cron. Они, вероятно, не будут вызываться из корневого каталога вашего приложения. Это означает, что все ваши требования к файлам (в отличие от библиотек) должны выполняться с явным путем: например, File.dirname (__ FILE__) + "/ other_file". Это также означает, что вы должны знать, как явно вызывать их из другого каталога :-)

Проверьте, поддерживает ли ваш код запуск из другого каталога с

# from ~
/path/to/ruby /path/to/app/script/runner -e development "MyClass.class_method"
/path/to/ruby /path/to/rake -f /path/to/app/Rakefile rake:task RAILS_ENV=development

Кроме того, задания cron, вероятно, не выполняются как вы, поэтому не зависят от ярлыка, который вы вставляете в .bashrc. Но это просто стандартный совет cron ;-)


Вы можете запустить задание как любой пользователь (просто установите запись crontab для пользователя, которого вы хотите), но вы правы, что сценарии профиля и входа в систему не будут запускаться, и вы не запустите их в своем домашнем каталоге. Поэтому обычно команду запускают с «cd», как показано в комментарии @ luke-franci
Том Уилсон

10

Проблема с всякий раз, когда (и cron) перезагружается среда rails каждый раз, когда она выполняется, что является реальной проблемой, когда ваши задачи выполняются часто или у вас много работы по инициализации. У меня были проблемы с производством из-за этого, и я должен предупредить вас.

Планировщик Rufus делает это для меня ( https://github.com/jmettraux/rufus-scheduler )

Когда у меня есть длинные задания для выполнения, я использую его с delayed_job ( https://github.com/collectiveidea/delayed_job )

Надеюсь, это поможет!


10

Я большой поклонник Resque / Resque Scheduler . Вы можете запускать повторяющиеся задачи, похожие на cron, но и задачи в определенное время. Недостатком является то, что для этого требуется сервер Redis.


10

Это интересно, никто не упомянул Sidetiq . Это хорошее дополнение, если вы уже используете Sidekiq.

Sidetiq предоставляет простой API для определения повторяющихся рабочих для Sidekiq.

Работа будет выглядеть так:

class MyWorker
  include Sidekiq::Worker
  include Sidetiq::Schedulable

  recurrence { hourly.minute_of_hour(15, 45) }

  def perform
    # do stuff ...
  end
end

8

Оба будут работать нормально. Я обычно использую скрипт / бегун.

Вот пример:

0 6 * * * cd /var/www/apps/your_app/current; ./script/runner --environment production 'EmailSubscription.send_email_subscriptions' >> /var/www/apps/your_app/shared/log/send_email_subscriptions.log 2>&1

Вы также можете написать скрипт на чистом Ruby для этого, если загрузите нужные файлы конфигурации для подключения к вашей базе данных.

Стоит иметь в виду, что если память драгоценна, это то, что скрипт / бегун (или задача Rake, зависящая от «окружения») будет загружать всю среду Rails. Если вам нужно только вставить некоторые записи в базу данных, это будет использовать память, которая вам на самом деле не нужна. Если вы пишете свой собственный сценарий, вы можете избежать этого. На самом деле мне еще не нужно было это делать, но я обдумываю это.


8

Используйте Craken (рейк-ориентированные задания cron)


1
писать задания cron так сложно, лучше загрузите гем для этого
f0ster

1
это не сложно - но хранить их в git и всегда обновлять при развертывании - большой плюс, когда работаешь в команде.
Тибо Баррер

5

Я использую backgroundrb.

http://backgroundrb.rubyforge.org/

Я использую его для запуска запланированных задач, а также задач, которые занимают слишком много времени для нормальных отношений клиент / сервер.


3

Вот как я настроил свои задачи cron. У меня есть один, чтобы делать ежедневные резервные копии базы данных SQL (используя rake), и другой, чтобы разряжать кэш раз в месяц. Любой вывод записывается в файл log / cron_log. Мой crontab выглядит так:

crontab -l # command to print all cron tasks
crontab -e # command to edit/add cron tasks

# Contents of crontab
0 1 * * * cd /home/lenart/izziv. whiskas.si/current; /bin/sh cron_tasks >> log/cron_log 2>&1
0 0 1 * * cd /home/lenart/izziv.whiskas.si/current; /usr/bin/env /usr/local/bin/ruby script/runner -e production lib/monthly_cron.rb >> log/cron_log 2>&1

Первая задача cron делает ежедневные резервные копии БД. Содержимое cron_tasks следующее:

/usr/local/bin/rake db:backup RAILS_ENV=production; date; echo "END OF OUTPUT ----";

Вторая задача была настроена позже и использует скрипт / runner для истечения срока действия кэша один раз в месяц (lib / month_cron.rb):

#!/usr/local/bin/ruby
# Expire challenge cache
Challenge.force_expire_cache
puts "Expired cache for Challenges (Challenge.force_expire_cache) #{Time.now}"

Я думаю, я мог бы сделать резервную копию базы данных другим способом, но пока это работает для меня :)

На пути грабли и рубин может варьироваться на разных серверах. Вы можете увидеть, где они находятся, используя:

whereis ruby # -> ruby: /usr/local/bin/ruby
whereis rake # -> rake: /usr/local/bin/rake

3

Использование чего-то Sidekiq или Resque является гораздо более надежным решением. Они оба поддерживают повторные задания, эксклюзивность с блокировкой REDIS, мониторинг и планирование.

Имейте в виду, что Resque - это мертвый проект (не активно поддерживаемый), поэтому Sidekiq - лучшая альтернатива. Он также более производительный: Sidekiq запускает несколько рабочих в одном многопоточном процессе, а Resque запускает каждого рабочего в отдельном процессе.


Это правильный ответ. Многие могут забыть о приятных функциях, которые предоставляет sidekiq или resque, например, веб-интерфейс для мониторинга происходящего: количество запущенных, сбойных или запланированных заданий, простой перезапуск, блокировка для уникальных работников, регулирование и ограничение и т. Д.
Дмитрий Полушкин

3

Недавно я создал несколько рабочих мест для проектов, над которыми я работал.

Я обнаружил, что драгоценный камень Заводной очень полезен.

require 'clockwork'

module Clockwork
  every(10.seconds, 'frequent.job')
end

Вы можете даже запланировать свою фоновую работу, используя этот драгоценный камень. За документацией и дополнительной помощью обращайтесь по ссылке https://github.com/Rykian/clockwork.



2

Однажды мне пришлось принять такое же решение, и я действительно доволен этим решением сегодня. Используйте планировщик Resque, потому что не только отдельное Redis будет снимать нагрузку с вашей базы данных, вы также будете иметь доступ ко многим плагинам, таким как Resque-Web, который обеспечивает отличный пользовательский интерфейс. По мере развития вашей системы вы будете планировать все больше и больше задач, чтобы вы могли управлять ими из одного места.


1

Вероятно, лучший способ сделать это - использовать rake для написания необходимых вам задач и просто выполнить его через командную строку.

Вы можете посмотреть очень полезное видео на Railscasts

Также взгляните на эти другие ресурсы:


Я безуспешно пытался использовать синтаксис в этом уроке. Задача не была выполнена.
ТАСС

1

Я использовал заводной драгоценный камень, и он довольно хорошо работает для меня. Существует также clockworkdгем, который позволяет скрипту запускаться как демон.


0

Я не совсем уверен, думаю, это зависит от задачи: как часто бегать, насколько сложно и сколько нужно прямого общения с проектом rails и т. Д. Я думаю, был ли «Один лучший путь» что-то сделать Не было бы так много разных способов сделать это.

На моей последней работе в проекте Rails нам нужно было создать пакетное почтовое приглашение (приглашения на опрос, а не рассылку спама), которое должно отправлять запланированные письма всякий раз, когда у сервера было время. Я думаю, что мы собирались использовать инструменты демона для запуска заданий, которые я создал.

К сожалению, у нашей компании были проблемы с деньгами, и она была «куплена» основным конкурентом, поэтому проект так и не был завершен, поэтому я не знаю, что мы в конечном итоге использовали.


0

Я использую скрипт для запуска cron, это лучший способ запустить cron. Вот пример для cron,

Откройте CronTab -> sudo crontab -e

И вставьте сильфонные линии:

00 00 * * * wget https: // your_host / some_API_end_point

Вот какой-то формат cron, поможет вам

::CRON FORMAT::

таблица формата cron

Examples Of crontab Entries
15 6 2 1 * /home/melissa/backup.sh
Run the shell script /home/melissa/backup.sh on January 2 at 6:15 A.M.

15 06 02 Jan * /home/melissa/backup.sh
Same as the above entry. Zeroes can be added at the beginning of a number for legibility, without changing their value.

0 9-18 * * * /home/carl/hourly-archive.sh
Run /home/carl/hourly-archive.sh every hour, on the hour, from 9 A.M. through 6 P.M., every day.

0 9,18 * * Mon /home/wendy/script.sh
Run /home/wendy/script.sh every Monday, at 9 A.M. and 6 P.M.

30 22 * * Mon,Tue,Wed,Thu,Fri /usr/local/bin/backup
Run /usr/local/bin/backup at 10:30 P.M., every weekday. 

Надеюсь, что это поможет вам :)

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.