Cron робота для рейок: найкраща практика?


295

Який найкращий спосіб виконувати заплановані завдання в середовищі Rails? Сценарій / бігун? Граблі? Я хотів би виконувати завдання кожні кілька хвилин.


149
Для тих, хто приїжджає сюди від Google, шукайте кращі підходи.
jrdioko

4
Щоразу, коли відповідь здається більш розумною, ніж прийнята відповідь, що є старим хаком.
Роб

2
Також пам’ятайте, що принаймні одна відповідь передбачає, що у вас встановлено певний дорогоцінний камінь.
Тасс

Пару (що я виявив) передового досвіду зведено тут на веб-сайті mudcashhq.com/blog/writing-reliable-cron-jobs
Thibaut Barrère

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

Відповіді:


110

Я використовую рейковий підхід (як підтримується heroku )

З файлом, який називається lib / task / cron.rake ..

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

Для виконання з командного рядка це просто "рейка cron". Потім ця команда може бути поміщена в планувальник операцій cron / task за бажанням.

Оновіть це досить старе питання та відповідь! Нова інформація:

  • сервіс heroku cron, на який я посилався, був замінений програмістом Heroku
  • для частих завдань (особливо, коли ви хочете уникати вартості запуску середовища Rails), моїм переважним підходом є використання системного крона для виклику сценарію, який (a) натикатиме API безпечного / приватного веб-кукса, щоб викликати необхідне завдання у фоновому режимі або (b) безпосередньо задати завдання в системі вибору черги

Яким повинен бути запис крона для цього випадку, щоб ОС знала правильний шлях до завдання граблі?
jrdioko

13
NB: Цими днями я використовую щоразу (див. Відповідь Джима Гарвіна), але необроблений запис крона для виконання граблі-завдання буде чимось на зразок: 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, повинно бути щось середнє між підходом контролера та режимом рейку .
fguillen

Яка тривалість цього завдання? Я використовую умову if. Хочу знати, наскільки регулярно це відбувається. Я не можу знайти інформацію про це на веб-сайті heroku.
Shubham Chaudhary

254

Я користувався надзвичайно популярним Коли на проекти , які в значній мірі залежать від запланованих завдань, і це здорово. Це дає вам приємний DSL для визначення запланованих завдань замість того, щоб мати справу з форматом crontab. З ПРОЧИТАННЯ:

Щоразу, як це дорогоцінний камінь 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 цього уникає.
lulalala

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

3
Я думаю, що це найкраще рішення. Якщо ви використовуєте рейки, я думаю, що краще написати все в рейки. При такому підході ви також можете забути про завдання cron при зміні серверів, воно рухається разом із додатком.
Адріан Маттео

Існує чудовий Railscast про те, коли це дуже корисно (старша безкоштовна версія також працює).
aceofbassgreg

@ Тоні, Кожен раз, коли це в основному специфічна мова для написання завдань на Cron. Він компілюється в звичайний синтаксис cron на вашому сервері рейлів, а cron - це те, що виконує задані вами завдання (як правило, через бігу rails).
Грег

19

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

Потім ми перейшли на дорогоцінний камінь 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, як я використовував його як для простих рубінових проектів, так і для повних додатків рейлів.
Пауло Фідальго

8
Не могли б ви бути трохи більш конкретними щодо проблем, з якими стикалися з кожним разом?
Герцог

найвірогідніша відповідь
Дарлан

17

Припустимо, що для виконання завдань не потрібно занадто багато часу, просто створіть новий контролер з дією для кожного завдання. Реалізуйте логіку завдання як код контролера. Потім встановіть cronjob на рівні ОС, який використовує wget для виклику URL-адреси цього контролера та дії у відповідні інтервали часу. Перевагами цього методу є:

  1. Майте повний доступ до всіх ваших об'єктів Rails так само, як у звичайному контролері.
  2. Може розвиватися і перевірятися так само, як ви робите звичайні дії.
  3. Можна також викликати ваші завдання adhoc з простої веб-сторінки.
  4. Не витрачайте більше пам’яті, запускаючи додаткові процеси рубіну / рейки.

12
Як не допустити доступу інших до цього завдання? Якщо завдання, яке приймає процесор і часто викликає його, спричинить проблеми.
sarunw

44
Я знаю, що це було деякий час тому, але це, безумовно, вже не найкращий спосіб робити роботу з крон. Навіщо переходити через веб-інтерфейс, порушуючи те, що насправді представляє інтерфейс, коли існує маса інших способів доступу до середовища Rails?
Матчу

6
Кваліфікація "припускаючи, що ваші завдання не потребують занадто довгого часу", здається ВЕЛИЧЕЗНА. Чи не було б краще використовувати підхід, який загалом корисніший, і не тільки в тих випадках, коли завдання дуже швидкі? Таким чином, ви не постійно переоцінюєте, чи потрібно переписати ту чи іншу задачу, використовуючи інший підхід.
іконоборство

77
Це старе запитання - найкращий результат google для "rails cron". Ця відповідь далеко не найкращий підхід. Будь ласка, дивіться інші відповіді для отримання більш здорових пропозицій.
Джим Гарвін

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

10

Завдання скрипт / бігун та граблі прекрасно виконуються як 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. Але це просто стандартний наконечник;


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

10

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

Rufus планувальник робить це для мене ( https://github.com/jmettraux/rufus-scheduler )

Коли у мене є тривалі роботи, я використовую її із відкладеною роботою ( https://github.com/collectiveidea/delayed_job )

Я сподіваюся, що це допомагає!



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

І те, і інше буде добре працювати. Зазвичай я використовую сценарій / runner.

Ось приклад:

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 (робочі місця, орієнтовані на граблі)


1
писати завдання на крон настільки важко, краще скачайте дорогоцінний камінь для цього
f0ster

1
це не важко - але зберігання їх у git та завжди в курсі розгортання - великий плюс, коли одна робота в команді.
Thibaut Barrère

5

Я використовую backgroundrb.

http://backgroundrb.rubyforge.org/

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


3

Ось, як я налаштував свої завдання на Cron. Я маю щоденно робити резервні копії бази даних SQL (використовуючи граблі), а інший - закінчувати кеш-пам'ять раз на місяць. Будь-який вихід записується у файл файлу / 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 робить щоденні резервні копії db. Вміст cron_tasks такий:

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

Друге завдання було встановлено пізніше і використовує скрипт / бігун, щоб закінчувати кеш-пам'ять раз на місяць (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

Нещодавно я створив кілька робочих місць для проектів, над якими працював.

Я виявив, що дорогоцінний камінь Clockwork дуже корисний.

require 'clockwork'

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

Ви навіть можете запланувати своє фонове завдання, використовуючи цей дорогоцінний камінь. Для отримання документації та подальшої допомоги зверніться до https://github.com/Rykian/ clockwork



2

Колись мені довелося прийняти те саме рішення, і сьогодні я дуже задоволений цим рішенням. Використовуйте програму- планувальник, оскільки не тільки окремий редис зніме навантаження з вашого db, ви також матимете доступ до багатьох плагінів, як resque-web, який забезпечує чудовий користувальницький інтерфейс. У міру розвитку вашої системи у вас буде все більше завдань для планування, так що ви зможете керувати ними з одного місця.


1

Мабуть, найкращий спосіб це зробити - використовувати рейку для написання потрібних завдань, а також просто виконати їх за допомогою командного рядка.

Ви можете побачити дуже корисне відео на рейлі

Погляньте також на інші ресурси:


Я безуспішно намагався використовувати синтаксис у цьому підручнику. Завдання не було виконано.
Тасс

1

Я заводний камінь , і вона працює дуже добре для мене. Існує також clockworkdдорогоцінний камінь, який дозволяє запускати сценарій як демон.


0

Я не дуже впевнений, я думаю, це залежить від завдання: як часто бігати, наскільки складний і скільки потрібне пряме спілкування з рельсовим проектом і т. Д. Я здогадуюсь, чи був просто "Один найкращий шлях" , щоб зробити що - то , не було б так багато різних способів зробити це.

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

На жаль, у нашої компанії були деякі проблеми з грошима, і її "купив" головний конкурент, тому проект так і не був завершений, тому я не знаю, чим би ми врешті використали.


0

Я використовую скрипт для запуску cron, це найкращий спосіб запустити cron. Ось приклад для cron,

Відкрийте CronTab -> sudo crontab -e

І вставити рядки нижче:

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

Ось якийсь формат кронів, вам допоможе

::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.