Zadanie cron dla szyn: najlepsze praktyki?

295

Jaki jest najlepszy sposób uruchamiania zaplanowanych zadań w środowisku Rails? Skrypt / biegacz? Grabie? Chciałbym uruchomić zadanie co kilka minut.

jes5199
źródło
149
W przypadku osób przybywających z Google poszukaj lepszych rozwiązań poza przyjętą odpowiedzią.
jrdioko
4
Za każdym razem, gdy odpowiedź wydaje się bardziej rozsądna niż odpowiedź zaakceptowana, czyli stary hack.
Rob
2
Uwaga: przynajmniej jedna odpowiedź zakłada, że ​​masz zainstalowany jakiś klejnot.
Tass
Kilka dobrych praktyk (jak się okazało) zostało streszczonych tutaj wisecashhq.com/blog/writing-reliable-cron-jobs
Thibaut
W wielu przypadkach zadania crona mają nieprzyjemny zapach. Lepiej zapisz harmonogram poprzez sidekiq / resque (lub inny proces roboczy w tle) lub napisz demona (mniej funkcjonalny i monitorowany). Zadania Crona mają co najmniej kilka złych rzeczy: 1) blokowanie w jednym przypadku jest uciążliwe; 2) monitorowanie nie może być łatwo wykonane; 3) obsługa wyjątków powinna być ponownie napisana ręcznie; 4) niełatwy do ponownego uruchomienia; 5) wszystkie powyższe problemy łatwo rozwiązane przez pracowników w tle.
Dmitry Polushkin

Odpowiedzi:

110

Korzystam z metody prowizji (obsługiwanej przez heroku )

Z plikiem o nazwie lib / zadania / cron.rake ..

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

Aby wykonać z wiersza poleceń, jest to po prostu „rake cron”. To polecenie można następnie umieścić w systemie operacyjnym cron / harmonogram zadań zgodnie z potrzebami.

Zaktualizuj to dość stare pytanie i odpowiedź! Niektóre nowe informacje:

  • usługa cron heroku, o której mówiłem, została zastąpiona przez Heroku Scheduler
  • w przypadku częstych zadań (szczególnie tam, gdzie chcesz uniknąć kosztów uruchomienia środowiska Rails) moim preferowanym podejściem jest użycie crona systemowego do wywołania skryptu, który albo (a) włączy bezpieczne / prywatne API webhook, aby wywołać wymagane zadanie w tle lub (b) bezpośrednio kolejkować zadanie w wybranym systemie kolejkowania
opóźnienie
źródło
Jaka powinna być pozycja cron w tym przypadku, aby system operacyjny znał prawidłową ścieżkę do zadania prowizji?
jrdioko
13
Uwaga: obecnie używam za każdym razem (patrz odpowiedź Jima Garvina), ale surowy wpis crona do uruchomienia zadania rake byłby mniej więcej taki: 30 4 * * * / bin / bash -l -c 'cd / opt / railsapp && RAILS_ENV = prowizja produkcyjna cron --silent '
tardate
1
Jak to nazwać z poziomu konsoli? Zrobiłem load "#{Rails.root}/lib/tasks/cron.rake"i rake cron, ale otrzymałem NameError: nieokreślona zmienna lokalna lub metoda `cron 'dla main: Object
B Seven
3
Problemem w tym podejściu jest :environmentzależność. Mamy bardzo ciężką aplikację Rails, która uruchamia się długo, nasza Rake jest wywoływana co minutę i zużywa więcej zasobów, uruchamiając środowisko Rails, które wykonuje zadanie . Chciałbym mieć już uruchomione środowisko Rails, które będzie wywoływane przez crona, musi być czymś pomiędzy podejściem kontrolera a środowiskiem rake .
fguillen
Jaki jest czas trwania tego zadania? Używam warunku if. Chcę wiedzieć, jak regularnie jest to uruchamiane. Nie mogę znaleźć żadnych informacji na ten temat na stronie heroku.
Shubham Chaudhary
254

Korzystałem z niezwykle popularnej aplikacji Ever Ever przy projektach, które w dużej mierze polegają na zaplanowanych zadaniach i jest świetna. Daje ci fajne DSL do definiowania zaplanowanych zadań zamiast konieczności radzenia sobie z formatem crontab. Z README:

Ilekroć jest klejnot Ruby, który zapewnia jasną składnię do pisania i wdrażania zadań cron.

Przykład z pliku 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
Jim Garvin
źródło
22
Jeśli jest uruchamiany co minutę, środowisko będzie restartowane za każdym razem, co może być kosztowne. Wydaje się, że github.com/ssoroka/scheduler_daemon unika tego.
lulalala,
3
+1 za utrzymanie konfiguracji crona w systemie kontroli wersji
brittohalloran,
3
Myślę, że to najlepsze rozwiązanie. Jeśli używasz szyn, myślę, że lepiej jest pisać wszystko w szynach. Dzięki takiemu podejściu możesz również zapomnieć o zadaniu cron podczas zmiany serwerów, które porusza się wraz z aplikacją.
Adrian Matteo
Jest świetny Railscast o Zawsze, gdy jest to naprawdę pomocne (działa również starsza darmowa wersja).
aceofbassgreg 11.04.13
@ Tony, Kiedykolwiek jest to w zasadzie język specyficzny dla domeny do pisania zadań cron. Kompiluje się w regularną składnię cron na serwerze Rails, a cron wykonuje określone przez ciebie zadania (zwykle za pośrednictwem programu runner).
Greg
19

W naszym projekcie po raz pierwszy używaliśmy klejnotów, ale napotykaliśmy pewne problemy.

Następnie przełączyliśmy się na RUFUS SCHEDULER klejnot , który okazał się bardzo łatwy i niezawodny do planowania zadań w Railsach.

Użyliśmy go do wysyłania cotygodniowych i codziennych wiadomości e-mail, a nawet do uruchamiania okresowych zadań rake lub dowolnej metody.

Kod użyty w tym jest jak:

    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

Aby dowiedzieć się więcej: https://github.com/jmettraux/rufus-scheduler

Pankhuri
źródło
1
Za rufus, ponieważ użyłem go zarówno do prostych projektów rubinowych, jak i do pełnych aplikacji railsowych.
Paulo Fidalgo
8
Czy mógłbyś być bardziej konkretny na temat problemów, z którymi się spotkałeś Kiedykolwiek?
Duke
najdoskonalsza odpowiedź
Darlan Dieterich
17

Zakładając, że twoje zadania nie potrwają zbyt długo, po prostu utwórz nowy kontroler z akcją dla każdego zadania. Zaimplementuj logikę zadania jako kod kontrolera, a następnie skonfiguruj cronjob na poziomie systemu operacyjnego, który używa wget do wywoływania adresu URL tego kontrolera i działania w odpowiednich odstępach czasu. Zaletami tej metody są:

  1. Masz pełny dostęp do wszystkich obiektów Railsów, tak jak w normalnym kontrolerze.
  2. Może się rozwijać i testować tak jak normalne działania.
  3. Może również wywoływać zadania doraźnie z prostej strony internetowej.
  4. Nie zużywaj więcej pamięci, uruchamiając dodatkowe procesy ruby ​​/ rails.
Dziwak
źródło
12
Jak uniemożliwić innym dostęp do tego zadania? Jeśli zadanie podejmujące procesor i wywoływane go często spowoduje problemy.
sarunw,
44
Wiem, że to było dawno temu, ale zdecydowanie nie jest to już najlepszy sposób na wykonywanie zadań cron. Po co przechodzić przez interfejs sieciowy, naruszając to, co interfejs naprawdę reprezentuje, skoro istnieje wiele innych sposobów dostępu do środowiska Rails?
Matchu
6
Kwalifikacja „zakładając, że twoje zadania nie zajmują zbyt długo” wydaje się OGROMNA. Czy nie lepiej byłoby zastosować podejście bardziej ogólnie przydatne, i to nie tylko w przypadkach, gdy zadania są bardzo szybkie? W ten sposób nie będziesz stale oceniać, czy to czy inne zadanie wymaga przepisania przy użyciu innego podejścia.
iconoclast 15.04.11
77
To stare pytanie jest najlepszym wynikiem Google dla „szyn cron”. Ta odpowiedź jest daleka od najlepszego podejścia. Zobacz inne odpowiedzi, aby uzyskać bardziej rozsądne sugestie.
Jim Garvin
2
Nie najlepszy sposób. Masz wiele innych sposobów dostępu do Rails env poprzez zadanie cron bez wywoływania usługi REST. Podejście z prowizją jest z pewnością lepsze
Shine
10

zadania skryptu / runnera i rake'u są całkowicie odpowiednie do uruchamiania jako zadania crona.

Oto jedna bardzo ważna rzecz, o której musisz pamiętać podczas uruchamiania zadań CRON. Prawdopodobnie nie zostaną wywołane z katalogu głównego aplikacji. Oznacza to, że wszystkie wymagania dotyczące plików (w przeciwieństwie do bibliotek) powinny być wykonane z wyraźną ścieżką: np. Nazwa_pliku_pliku (__ PLIK___) + „/ inny_plik”. Oznacza to również, że musisz wiedzieć, jak jawnie wywoływać je z innego katalogu :-)

Sprawdź, czy Twój kod obsługuje uruchamianie z innego katalogu za pomocą

# 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

Ponadto zadania cron prawdopodobnie nie działają tak jak Ty, więc nie polegaj na żadnym skrócie, który umieściłeś w .bashrc. Ale to tylko standardowa wskazówka crona ;-)

webmat
źródło
Możesz uruchomić zadanie jak każdy użytkownik (wystarczy ustawić wpis crontab dla użytkownika, którego chcesz), ale masz rację, że profile i skrypty logowania nie będą działać i nie uruchomisz się w katalogu domowym. Dlatego powszechne jest uruchamianie polecenia za pomocą „cd”, jak pokazano w komentarzu @ luke-franci
Tom Wilson
10

Problem z kiedykolwiek (i cronem) polega na tym, że przeładowuje środowisko szyny za każdym razem, gdy jest wykonywane, co jest prawdziwym problemem, gdy twoje zadania są częste lub masz dużo pracy inicjalizacyjnej. Z tego powodu miałem problemy z produkcją i muszę was ostrzec.

Rufus harmonogram robi to dla mnie ( https://github.com/jmettraux/rufus-scheduler )

Kiedy mam długie zadania do uruchomienia, używam go z opóźnieniem_job ( https://github.com/collectiveidea/delayed_job )

Mam nadzieję, że to pomoże!

Abdo
źródło
10

Jestem wielkim fanem resque / resque planer . Możesz uruchamiać nie tylko powtarzające się zadania podobne do crona, ale także zadania w określonych momentach. Minusem jest to, że wymaga serwera Redis.

Tyler Morgan
źródło
10

To ciekawe, że nikt nie wspomniał o Sidetiqu . Jest to miły dodatek, jeśli już używasz Sidekiq.

Sidetiq zapewnia prosty interfejs API do definiowania powtarzających się pracowników dla Sidekiq.

Job będzie wyglądał następująco:

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

  recurrence { hourly.minute_of_hour(15, 45) }

  def perform
    # do stuff ...
  end
end
Alexander Paramonov
źródło
8

Oba będą działać dobrze. Zwykle używam skryptu / runnera.

Oto przykład:

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

Możesz również napisać skrypt czysto Ruby, aby to zrobić, jeśli załadujesz odpowiednie pliki konfiguracyjne do połączenia z bazą danych.

Pamiętaj, że jeśli pamięć jest cenna, skrypt / runner (lub zadanie Rake zależne od „środowiska”) załaduje całe środowisko Rails. Jeśli musisz wstawić tylko niektóre rekordy do bazy danych, zajmie to pamięć, której tak naprawdę nie musisz. Jeśli napiszesz własny skrypt, możesz tego uniknąć. Właściwie to jeszcze nie musiałem tego robić, ale rozważam to.

Luke Francl
źródło
8

Użyj Craken (zadania cron rake centric)

Thibaut Barrère
źródło
1
pisanie zadań crona jest tak trudne, lepiej pobierz klejnot
f0ster
1
to nie jest trudne - ale posiadanie ich w git i zawsze na bieżąco przy wdrażaniu to duży plus, gdy ktoś pracuje w zespole.
Thibaut Barrère
5

Używam backgroundrb.

http://backgroundrb.rubyforge.org/

Używam go do uruchamiania zaplanowanych zadań, a także zadań, które trwają zbyt długo w stosunku do normalnej relacji klient / serwer.

salt.racer
źródło
3

Oto jak skonfigurowałem swoje zadania cron. Mam jeden do codziennego tworzenia kopii zapasowych bazy danych SQL (przy użyciu prowizji), a drugi do wygasania pamięci podręcznej raz w miesiącu. Każde wyjście jest rejestrowane w pliku log / cron_log. Mój crontab wygląda następująco:

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

Pierwsze zadanie cron wykonuje codzienne kopie zapasowe bazy danych. Zawartość cron_tasks jest następująca:

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

Drugie zadanie zostało skonfigurowane później i używa skryptu / programu uruchamiającego do wygaśnięcia pamięci podręcznej raz w miesiącu (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}"

Chyba mógłbym wykonać kopię zapasową bazy danych w inny sposób, ale jak dotąd działa dla mnie :)

Te ścieżki do natarcia i rubin może być różna na różnych serwerach. Możesz zobaczyć, gdzie się znajdują, używając:

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

źródło
3

Używanie czegoś Sidekiq lub Resque jest znacznie bardziej niezawodnym rozwiązaniem. Obie obsługują ponawianie zadań, wyłączność z blokadą REDIS, monitorowanie i planowanie.

Pamiętaj, że Resque to martwy projekt (nie aktywnie utrzymywany), więc Sidekiq jest lepszą alternatywą. Jest również wydajniejszy: Sidekiq obsługuje kilku pracowników w jednym, wielowątkowym procesie, a Resque uruchamia każdego pracownika w osobnym procesie.

jaysqrd
źródło
To poprawna odpowiedź. Wielu może zapomnieć o fajnych funkcjach, które zapewniają sidekiq lub resque, takich jak interfejs internetowy do monitorowania tego, co się dzieje: liczba zadań uruchomionych, nieudanych lub zaplanowanych, łatwo je zrestartuj, zablokuj dla unikalnych pracowników, dławienie i ograniczanie itp.
Dmitry Polushkin,
3

Ostatnio stworzyłem kilka miejsc pracy dla projektów, nad którymi pracuję.

Odkryłem, że klejnot Clockwork jest bardzo przydatny.

require 'clockwork'

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

Za pomocą tego klejnotu możesz nawet zaplanować pracę w tle. Aby uzyskać dokumentację i dalszą pomoc, odwiedź https://github.com/Rykian/clockwork

Vipul Lawande
źródło
2

Kiedyś musiałem podjąć tę samą decyzję i dziś jestem bardzo zadowolony z tej decyzji. Użyj harmonogramu resque, ponieważ nie tylko oddzielne redis usunie obciążenie z bazy danych, będziesz mieć również dostęp do wielu wtyczek, takich jak resque-web, które zapewniają świetny interfejs użytkownika. W miarę rozwoju systemu będziesz mieć coraz więcej zadań do zaplanowania, dzięki czemu będziesz w stanie kontrolować je z jednego miejsca.

Caner Çakmak
źródło
1

Prawdopodobnie najlepszym sposobem na to jest użycie rake do napisania potrzebnych zadań i po prostu wykonanie ich za pomocą wiersza poleceń.

Bardzo przydatny film można zobaczyć podczas railscastów

Zobacz także inne zasoby:

Adrià Cidre
źródło
Próbowałem bezskutecznie użyć składni w tym samouczku. Zadanie nie zostało wykonane.
Tass
1

Użyłem klejnotu zegarkowego i działa całkiem dobrze dla mnie. Istnieje również clockworkdklejnot, który pozwala skryptowi działać jako demon.

nnattawat
źródło
0

Nie jestem do końca pewny, myślę, że zależy to od zadania: jak często uruchamiać, ile skomplikować i ile potrzebna jest bezpośrednia komunikacja z projektem szyn itp. Sądzę, że istnieje tylko „jeden najlepszy sposób”, aby coś zrobić , nie byłoby tak wielu różnych sposobów, aby to zrobić.

Podczas mojej ostatniej pracy w projekcie Rails musieliśmy stworzyć pakiet mailingowy z zaproszeniem (zaproszenia do ankiety, nie spamowanie), który powinien wysyłać planowane wiadomości e-mail, ilekroć serwer ma czas. Myślę, że zamierzaliśmy użyć narzędzi demona do uruchomienia utworzonych przeze mnie zadań prowizji.

Niestety, nasza firma miała problemy z pieniędzmi i została „kupiona” przez głównego rywala, więc projekt nigdy nie został ukończony, więc nie wiem, co byśmy w końcu wykorzystali.

Stein G. Strindhaug
źródło
0

Używam skryptu do uruchamiania crona, to najlepszy sposób na uruchomienie crona. Oto przykład dla crona,

Otwórz CronTab -> sudo crontab -e

I wklej poniżej linie:

00 00 * * * wget https: // twój_host / some_API_end_point

Oto jakiś format cron, który ci pomoże

::CRON FORMAT::

tabela formatu 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. 

Mam nadzieję, że to ci pomoże :)

Jestem
źródło