Ein Cron Job für Schienen: Best Practices?

295

Was ist der beste Weg, um geplante Aufgaben in einer Rails-Umgebung auszuführen? Skript / Läufer? Rechen? Ich möchte die Aufgabe alle paar Minuten ausführen.

jes5199
quelle
149
Wer von Google hierher kommt, sucht über die akzeptierte Antwort hinaus nach besseren Ansätzen.
Jrdioko
4
Die Wann-Antwort scheint vernünftiger zu sein als die akzeptierte Antwort, die ein alter Hack ist.
Rob
2
Bitte beachten Sie auch, dass mindestens eine Antwort davon ausgeht, dass Sie ein bestimmtes Juwel installiert haben.
Tass
Ein paar (was ich herausgefunden habe) gute Praktiken sind hier zusammengefasst. Wisecashhq.com/blog/writing-reliable-cron-jobs
Thibaut Barrère
In vielen Fällen riechen Cron-Jobs schlecht. Schreiben Sie den Scheduler besser über sidekiq / resque (oder einen anderen Hintergrund-Worker) oder schreiben Sie einen Daemon (weniger funktionsfähig und überwachbar). Cron-Jobs haben mindestens einige schlechte Dinge: 1) Das Sperren für die eine Instanz ist ein Schmerz; 2) Überwachung kann nicht einfach durchgeführt werden; 3) Die Ausnahmebehandlung sollte erneut manuell geschrieben werden. 4) nicht einfach neu zu starten; 5) Alle oben genannten Probleme können von Hintergrundarbeitern leicht gelöst werden.
Dmitry Polushkin

Antworten:

110

Ich verwende den Rake-Ansatz (wie von Heroku unterstützt )

Mit einer Datei namens lib / task / cron.rake ..

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

Für die Ausführung über die Befehlszeile ist dies nur "Rake Cron". Dieser Befehl kann dann wie gewünscht auf dem Cron / Task-Scheduler des Betriebssystems abgelegt werden.

Update das ist eine ziemlich alte Frage und Antwort! Einige neue Infos:

  • Der Heroku-Cron-Service, auf den ich verwiesen habe, wurde inzwischen durch Heroku Scheduler ersetzt
  • Für häufige Aufgaben (insbesondere, wenn Sie die Startkosten der Rails-Umgebung vermeiden möchten) ist mein bevorzugter Ansatz, System Cron zum Aufrufen eines Skripts zu verwenden, das entweder (a) eine sichere / private Webhook-API aufruft, um die erforderliche Aufgabe im Hintergrund aufzurufen oder (b) eine Aufgabe direkt in das Warteschlangensystem Ihrer Wahl einreihen
verspätet
quelle
Wie sollte der Cron-Eintrag für diesen Fall lauten, damit das Betriebssystem den richtigen Pfad zur Rechenaufgabe kennt?
Jrdioko
13
NB: Heutzutage verwende ich wann immer (siehe Jim Garvins Antwort), aber ein roher Cron-Eintrag zum Ausführen einer Rechenaufgabe wäre ungefähr so: 30 4 * * * / bin / bash -l -c 'cd / opt / railsapp && RAILS_ENV = Produktionsrechen Cron - Silent '
Verzögerung
1
Wie nennt man das von der Konsole aus? Ich habe load "#{Rails.root}/lib/tasks/cron.rake"und rake cron, aber NameError: undefinierte lokale Variable oder Methode `cron 'für main: Object
B Seven
3
Das Problem bei diesem Ansatz ist die :environmentAbhängigkeit. Wir haben eine sehr schwere Rails-Anwendung, deren Start lange dauert. Unser Rake wird jede Minute aufgerufen und verbraucht mehr Ressourcen, um die Rails-Umgebung zu starten, in der die Aufgabe ausgeführt wird . Ich würde gerne eine bereits gestartete Rails-Umgebung haben, die über den Cron aufgerufen werden kann. Sie muss etwas zwischen dem Controller- Ansatz und der Rake-Umgebung sein .
Fguillen
Wie lange dauert diese Aufgabe? Ich verwende eine if-Bedingung. Ich möchte wissen, wie regelmäßig dies ausgeführt wird. Ich kann keine Informationen dazu auf der Heroku-Website finden.
Shubham Chaudhary
254

Ich habe das äußerst beliebte Wann immer für Projekte verwendet, die stark von geplanten Aufgaben abhängen, und es ist großartig. Es gibt Ihnen ein nettes DSL, um Ihre geplanten Aufgaben zu definieren, anstatt sich mit dem Crontab-Format befassen zu müssen. Aus der README:

Wann immer es sich um ein Ruby-Juwel handelt, das eine klare Syntax zum Schreiben und Bereitstellen von Cron-Jobs bietet.

Beispiel aus der 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
quelle
22
Wenn es jede Minute ausgeführt wird, wird die Umgebung jedes Mal neu gestartet, was kostspielig sein kann. Es scheint, dass github.com/ssoroka/scheduler_daemon dies vermeidet.
Lulalala
3
+1 für die Beibehaltung der Cron-Konfiguration mit Ihrem Versionskontrollsystem
Brittohalloran
3
Ich denke, das ist die beste Lösung. Wenn Sie Schienen verwenden, ist es meiner Meinung nach besser, alles in Schienen zu schreiben. Mit diesem Ansatz können Sie auch die Cron-Aufgabe beim Serverwechsel vergessen, sie bewegt sich mit der App.
Adrian Matteo
Es gibt einen großartigen Railscast über Wann immer das wirklich hilfreich ist (eine ältere kostenlose Version ist ebenfalls verfügbar).
Aceofbassgreg
@ Tony, Wann immer ist im Grunde eine domänenspezifische Sprache zum Schreiben von Cron-Jobs. Es wird in die reguläre Cron-Syntax auf Ihrem Rails-Server kompiliert, und Cron führt die von Ihnen angegebenen Jobs aus (normalerweise über Rails Runner).
Greg
19

In unserem Projekt haben wir zuerst immer Edelsteine ​​verwendet, waren aber mit einigen Problemen konfrontiert.

Wir haben dann zu RUFUS SCHEDULER gem gewechselt , was sich als sehr einfach und zuverlässig für die Planung von Aufgaben in Rails herausstellte.

Wir haben es zum Versenden von wöchentlichen und täglichen E-Mails und sogar zum Ausführen einiger periodischer Rechenaufgaben oder einer anderen Methode verwendet.

Der hier verwendete Code lautet wie folgt:

    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

Weitere Informationen : https://github.com/jmettraux/rufus-scheduler

Pankhuri
quelle
1
Up for Rufus, da ich es sowohl für einfache Ruby-Projekte als auch für Full-Rails-Apps verwendet habe.
Paulo Fidalgo
8
Könnten Sie etwas genauer auf die Probleme eingehen, auf die Sie bei Wann immer gestoßen sind?
Herzog
die beste Antwort aller Zeiten
Darlan Dieterich
17

Angenommen, die Ausführung Ihrer Aufgaben dauert nicht zu lange. Erstellen Sie einfach einen neuen Controller mit einer Aktion für jede Aufgabe. Implementieren Sie die Logik der Aufgabe als Controller-Code. Richten Sie dann einen Cronjob auf Betriebssystemebene ein, der mit wget die URL dieses Controllers und die Aktion in den entsprechenden Zeitintervallen aufruft. Die Vorteile dieser Methode sind Sie:

  1. Sie haben wie in einem normalen Controller vollen Zugriff auf alle Ihre Rails-Objekte.
  2. Kann sich genauso entwickeln und testen, wie Sie normale Aktionen ausführen.
  3. Kann Ihre Aufgaben auch adhoc von einer einfachen Webseite aus aufrufen.
  4. Verbrauchen Sie keinen Speicher mehr, indem Sie zusätzliche Ruby / Rails-Prozesse starten.
Freakent
quelle
12
Wie kann verhindert werden, dass andere auf diese Aufgabe zugreifen können? Wenn die Aufgabe, die CPU nimmt und sie häufig aufruft, Probleme verursacht.
Sarunw
44
Ich weiß, dass dies eine Weile her ist, aber dies ist definitiv nicht mehr der beste Weg, um Cron-Jobs zu erledigen. Warum sollten Sie die Weboberfläche durchgehen und gegen das verstoßen, was die Benutzeroberfläche wirklich darstellt, wenn es viele andere Möglichkeiten gibt, auf die Rails-Umgebung zuzugreifen?
Matchu
6
Die Qualifikation "vorausgesetzt, Ihre Aufgaben dauern nicht zu lange" scheint eine RIESIGE zu sein. Wäre es nicht besser, einen allgemein nützlichen Ansatz zu verwenden, und zwar nicht nur in den Fällen, in denen Aufgaben sehr schnell sind? Auf diese Weise wird nicht ständig neu bewertet, ob diese oder jene Aufgabe mit einem anderen Ansatz neu geschrieben werden muss.
Bilderstürmer
77
Diese alte Frage ist das Top-Google-Ergebnis für "Rails Cron". Diese Antwort ist alles andere als der beste Ansatz. Weitere vernünftige Vorschläge finden Sie in den anderen Antworten.
Jim Garvin
2
Nicht der beste Weg. Sie haben viele andere Möglichkeiten, über einen Cron-Job auf Rails env zuzugreifen, ohne einen REST-Service aufzurufen. Rake Ansatz ist sicherlich besser
Shine
10

Skript- / Runner- und Rake-Aufgaben können problemlos als Cron-Jobs ausgeführt werden.

Hier ist eine sehr wichtige Sache, an die Sie denken müssen, wenn Sie Cron-Jobs ausführen. Sie werden wahrscheinlich nicht aus dem Stammverzeichnis Ihrer App aufgerufen. Dies bedeutet, dass alle Ihre Anforderungen an Dateien (im Gegensatz zu Bibliotheken) mit dem expliziten Pfad ausgeführt werden sollten: z. B. File.dirname (__ FILE__) + "/ other_file". Dies bedeutet auch, dass Sie wissen müssen, wie Sie sie explizit aus einem anderen Verzeichnis aufrufen können :-)

Überprüfen Sie, ob Ihr Code die Ausführung aus einem anderen Verzeichnis mit unterstützt

# 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

Außerdem werden Cron-Jobs wahrscheinlich nicht so ausgeführt wie Sie. Sie hängen also nicht von einer Verknüpfung ab, die Sie in .bashrc eingegeben haben. Aber das ist nur eine Standard-Cron-Spitze ;-)

Webmat
quelle
Sie können den Job als beliebiger Benutzer ausführen (legen Sie einfach den crontab-Eintrag für den gewünschten Benutzer fest), aber Sie haben Recht, dass die Profil- und Anmeldeskripts nicht ausgeführt werden und Sie nicht in Ihrem Home-Verzeichnis starten. Daher ist es üblich, den Befehl mit einer "CD" zu starten, wie in @ luke-francis Kommentar gezeigt
Tom Wilson
10

Das Problem bei jedem (und jedem) Cron ist, dass die Rails-Umgebung bei jeder Ausführung neu geladen wird. Dies ist ein echtes Problem, wenn Ihre Aufgaben häufig sind oder viel Initialisierungsarbeit zu erledigen ist. Ich hatte deswegen Probleme in der Produktion und muss Sie warnen.

Rufus Scheduler erledigt das für mich ( https://github.com/jmettraux/rufus-scheduler )

Wenn ich lange Jobs ausführen muss, verwende ich sie mit verzögertem Job ( https://github.com/collectiveidea/delayed_job ).

Ich hoffe das hilft!

Abdo
quelle
10

Ich bin ein großer Fan von Resque / Resque Scheduler . Sie können nicht nur sich wiederholende Cron-ähnliche Aufgaben ausführen, sondern auch Aufgaben zu bestimmten Zeiten. Der Nachteil ist, dass ein Redis-Server erforderlich ist.

Tyler Morgan
quelle
10

Das ist interessant, niemand hat den Sidetiq erwähnt . Es ist eine schöne Ergänzung, wenn Sie bereits Sidekiq verwenden.

Sidetiq bietet eine einfache API zum Definieren wiederkehrender Worker für Sidekiq.

Job wird so aussehen:

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

  recurrence { hourly.minute_of_hour(15, 45) }

  def perform
    # do stuff ...
  end
end
Alexander Paramonov
quelle
8

Beides wird gut funktionieren. Normalerweise benutze ich Script / Runner.

Hier ist ein Beispiel:

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

Sie können dazu auch ein reines Ruby-Skript schreiben, wenn Sie die richtigen Konfigurationsdateien laden, um eine Verbindung zu Ihrer Datenbank herzustellen.

Eine Sache, die Sie beachten sollten, wenn Speicher wertvoll ist, ist, dass Skript / Runner (oder eine Rake-Aufgabe, die von der Umgebung abhängt) die gesamte Rails-Umgebung lädt. Wenn Sie nur einige Datensätze in die Datenbank einfügen müssen, wird Speicher benötigt, den Sie nicht wirklich benötigen. Wenn Sie Ihr eigenes Skript schreiben, können Sie dies vermeiden. Ich musste das noch nicht tun, aber ich denke darüber nach.

Luke Francl
quelle
8

Verwenden Sie Craken (Rake-zentrierte Cron-Jobs)

Thibaut Barrère
quelle
1
Cron Jobs zu schreiben ist so schwer, besser ein Juwel dafür herunterladen
f0ster
1
es ist nicht schwer - aber sie in git zu speichern und bei der Bereitstellung immer auf dem neuesten Stand zu sein, ist ein großes Plus, wenn man in einem Team arbeitet.
Thibaut Barrère
5

Ich benutze backgroundrb.

http://backgroundrb.rubyforge.org/

Ich verwende es, um geplante Aufgaben sowie Aufgaben auszuführen, die für die normale Client / Server-Beziehung zu lange dauern.

salt.racer
quelle
3

So habe ich meine Cron-Aufgaben eingerichtet. Ich habe eine, um tägliche Backups der SQL-Datenbank (mit Rake) zu erstellen, und eine andere, um den Cache einmal im Monat abzulaufen. Jede Ausgabe wird in einer Datei log / cron_log protokolliert. Mein Crontab sieht so aus:

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

Die erste Cron-Aufgabe erstellt tägliche DB-Backups. Der Inhalt von cron_tasks lautet wie folgt:

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

Die zweite Aufgabe wurde später eingerichtet und verwendet Script / Runner, um den Cache einmal im Monat abzulaufen (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}"

Ich denke, ich könnte die Datenbank auf andere Weise sichern, aber bisher funktioniert es für mich :)

Die Pfade zu Rake und Ruby können auf verschiedenen Servern variieren. Sie können sehen, wo sie sind, indem Sie:

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

quelle
3

Die Verwendung von Sidekiq oder Resque ist eine weitaus robustere Lösung. Beide unterstützen das Wiederholen von Jobs, die Exklusivität mit einer REDIS-Sperre, die Überwachung und die Planung.

Denken Sie daran, dass Resque ein totes Projekt ist (nicht aktiv gewartet), daher ist Sidekiq eine viel bessere Alternative. Es ist auch leistungsfähiger: Sidekiq führt mehrere Worker in einem einzigen Multithread-Prozess aus, während Resque jeden Worker in einem separaten Prozess ausführt.

jaysqrd
quelle
Das ist eine richtige Antwort. Viele können die netten Funktionen vergessen, die Sidekiq oder Resque bieten, wie die Weboberfläche zur Überwachung des Geschehens: Anzahl der ausgeführten, fehlgeschlagenen oder geplanten Jobs, einfacher Neustart, Sperren für eindeutige Mitarbeiter, Drosseln und Begrenzen usw.
Dmitry Polushkin
3

Ich habe kürzlich einige Cron-Jobs für die Projekte geschaffen, an denen ich gearbeitet habe.

Ich fand das Juwel Clockwork sehr nützlich.

require 'clockwork'

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

Mit diesem Juwel können Sie sogar Ihren Hintergrundjob planen. Dokumentation und weitere Hilfe finden Sie unter https://github.com/Rykian/clockwork

Vipul Lawande
quelle
2

Einmal musste ich die gleiche Entscheidung treffen und bin heute sehr zufrieden mit dieser Entscheidung. Verwenden Sie den Resque-Scheduler, da nicht nur ein separater Redis die Last von Ihrer Datenbank entlastet, sondern Sie auch Zugriff auf viele Plugins wie resque-web haben, die eine großartige Benutzeroberfläche bieten. Während sich Ihr System entwickelt, müssen Sie immer mehr Aufgaben planen, damit Sie sie von einem einzigen Ort aus steuern können.

Caner Çakmak
quelle
1

Der wahrscheinlich beste Weg, dies zu tun, ist die Verwendung von Rake, um die benötigten Aufgaben zu schreiben und sie einfach über die Befehlszeile auszuführen.

Sie können ein sehr hilfreiches Video bei Railscasts sehen

Schauen Sie sich auch diese anderen Ressourcen an:

Adrià Cidre
quelle
Ich habe erfolglos versucht, die Syntax in diesem Tutorial zu verwenden. Aufgabe wurde nicht ausgeführt.
Tass
1

Ich habe Uhrwerk Edelstein verwendet und es funktioniert ziemlich gut für mich. Es gibt auch ein clockworkdJuwel, mit dem ein Skript als Daemon ausgeführt werden kann.

nnattawat
quelle
0

Ich bin mir nicht sicher, ich denke, es hängt von der Aufgabe ab: wie oft ausgeführt werden soll, wie kompliziert und wie viel direkte Kommunikation mit dem Rails-Projekt erforderlich ist usw. Ich denke, wenn es nur "One Best Way" gab. , um etwas zu tun Es würde nicht so viele verschiedene Möglichkeiten geben, dies zu tun.

Bei meinem letzten Job in einem Rails-Projekt mussten wir einen Batch-Einladungs-Mailer (Umfrageeinladungen, kein Spam) erstellen, der die geplanten E-Mails senden sollte, wann immer der Server Zeit hatte. Ich denke, wir wollten Daemon-Tools verwenden , um die von mir erstellten Rake-Aufgaben auszuführen.

Leider hatte unser Unternehmen einige Geldprobleme und wurde vom Hauptkonkurrenten "gekauft", so dass das Projekt nie abgeschlossen wurde, sodass ich nicht weiß, was wir letztendlich verwendet hätten.

Stein G. Strindhaug
quelle
0

Ich benutze Skript, um Cron auszuführen, das ist der beste Weg, um Cron auszuführen. Hier ist ein Beispiel für cron,

Öffnen Sie CronTab -> sudo crontab -e

Und fügen Sie die folgenden Zeilen ein:

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

Hier ist ein Cron-Format, das Ihnen helfen wird

::CRON FORMAT::

Cron-Format-Tabelle

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. 

Hoffe das wird dir helfen :)

Ami
quelle