Un lavoro cron per rotaie: buone pratiche?


295

Qual è il modo migliore per eseguire attività pianificate in un ambiente Rails? Script / runner? Rastrello? Vorrei eseguire l'attività ogni pochi minuti.


149
Per quelli che vengono qui da Google, guarda oltre la risposta accettata per approcci migliori.
jrdioko,

4
La risposta ogni volta sembra più ragionevole della risposta accettata, che è un vecchio trucco.
Rob

2
Si noti inoltre che almeno una risposta presuppone che sia installata una determinata gemma.
Tass

Un paio di (che ho scoperto essere) buone pratiche sono riassunte qui wisecashhq.com/blog/writing-reliable-cron-jobs
Thibaut Barrère,

In molti casi i lavori cron hanno un cattivo odore. Meglio scrivere scheduler tramite sidekiq / resque (o altri operatori in background) o scrivere un demone (meno funzionale e monitorabile). I lavori Cron hanno almeno alcune cose negative: 1) bloccare per un caso è un dolore; 2) il monitoraggio non può essere effettuato facilmente; 3) la gestione delle eccezioni dovrebbe essere scritta di nuovo manualmente; 4) non è facile riavviare; 5) tutti i problemi di cui sopra facilmente risolvibili dai lavoratori in background.
Dmitry Polushkin,

Risposte:


110

Sto usando l'approccio rake (come supportato da heroku )

Con un file chiamato lib / task / cron.rake ..

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

Per eseguire dalla riga di comando, questo è solo "rake cron". Questo comando può quindi essere inserito nel programma di pianificazione cron / task del sistema operativo come desiderato.

Aggiorna questa è piuttosto una vecchia domanda e risposta! Alcune nuove informazioni:

  • il servizio cronologico di heroku a cui ho fatto riferimento è stato sostituito da Heroku Scheduler
  • per attività frequenti (specialmente dove si desidera evitare i costi di avvio dell'ambiente Rails) il mio approccio preferito è quello di utilizzare il cron di sistema per chiamare uno script che (a) attiverà un'API webhook sicura / privata per richiamare l'attività richiesta in background oppure (b) accodare direttamente un'attività sul sistema di accodamento scelto

Quale dovrebbe essere la voce cron per questo caso, quindi il sistema operativo conosce il percorso corretto per l'attività di rake?
jrdioko,

13
NB: in questi giorni lo sto usando ogni volta (vedi la risposta di Jim Garvin), ma una voce cron non elaborata per eseguire il rake task sarebbe qualcosa del tipo: 30 4 * * * / bin / bash -l -c 'cd / opt / railsapp && RAILS_ENV = rake di produzione cron --silent '
tardate

1
Come si chiama dalla console? L'ho fatto load "#{Rails.root}/lib/tasks/cron.rake"e rake cron, ma ho ricevuto NameError: variabile locale non definita o metodo `cron 'per main: Object
B Seven

3
Il problema con questo approccio è la :environmentdipendenza. Abbiamo un'applicazione Rails molto pesante che impiega molto tempo ad avviarsi, il nostro Rake viene chiamato ogni minuto e consuma più risorse all'avvio dell'ambiente Rails rispetto all'esecuzione dell'attività . Mi piacerebbe avere un ambiente Rails già avviato per essere chiamato tramite cron, deve essere qualcosa tra l' approccio controller e quello rake .
fguillen,

Qual è la durata di questa attività? Sto usando una condizione if. Voglio sapere con che frequenza viene eseguito. Non riesco a trovare alcuna informazione al riguardo nel sito Web di Heroku.
Shubham Chaudhary,

254

Ho usato il estremamente popolare Whenever su progetti che dipendono fortemente da attività pianificate, ed è fantastico. Ti dà un bel DSL per definire le tue attività pianificate invece di dover gestire il formato crontab. Dal README:

Ogni volta che è una gemma di Ruby che fornisce una chiara sintassi per la scrittura e la distribuzione di cron job.

Esempio dal 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
Se viene eseguito ogni minuto, l'ambiente verrà riavviato ogni volta, il che può essere costoso. Sembra che github.com/ssoroka/scheduler_daemon eviti questo.
Lulalala,

3
+1 per mantenere la configurazione cron con il tuo sistema di controllo della versione
brittohalloran,

3
Penso che questa sia la soluzione migliore. Se stai usando le rotaie, penso che sia meglio scrivere tutto nelle rotaie. Con questo approccio puoi anche dimenticare l'attività cron quando cambi server, si sposta con l'app.
Adrian Matteo,

C'è un ottimo Railscast su Ogni volta che è davvero utile (è disponibile anche una versione gratuita più vecchia).
aceofbassgreg,

@Tony, Ogni volta che è fondamentalmente una lingua specifica del dominio per la scrittura di lavori cron. Si compila nella normale sintassi cron sul server rails e cron è ciò che esegue i lavori specificati (di solito tramite rails runner).
Greg,

19

Nel nostro progetto abbiamo usato per la prima volta ogni gemma, ma abbiamo affrontato alcuni problemi.

Siamo quindi passati a RUFUS SCHEDULER gioiello , che si è rivelato molto semplice e affidabile per le attività di pianificazione in Rails.

Lo abbiamo usato per l'invio di mail settimanali e giornaliere e anche per l'esecuzione di alcune attività periodiche di rake o di qualsiasi metodo.

Il codice usato in questo è come:

    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

Per saperne di più: https://github.com/jmettraux/rufus-scheduler


1
Per rufus, come l'ho usato sia per semplici progetti ruby ​​che per app full rails.
Paulo Fidalgo,

8
Potresti essere un po 'più specifico sui problemi che hai incontrato con Whenever?
Duke,

la risposta più grande che mai
Darlan Dieterich,

17

Supponendo che le attività non richiedano troppo tempo per il completamento, basta creare un nuovo controller con un'azione per ogni attività. Implementare la logica dell'attività come codice controller, quindi impostare un cronjob a livello di sistema operativo che utilizza wget per richiamare l'URL di questo controller e agire agli intervalli di tempo appropriati. I vantaggi di questo metodo sei:

  1. Avere accesso completo a tutti gli oggetti Rails come in un normale controller.
  2. Può svilupparsi e testare proprio come fai le normali azioni.
  3. Può anche invocare le tue attività ad hoc da una semplice pagina Web.
  4. Non consumare più memoria attivando ulteriori processi rubino / rotaie.

12
Come impedire ad altri di accedere a questa attività? Se l'attività che prende cpu e l'ha chiamato frequentemente causerà problemi.
Sarunw,

44
So che è successo un po 'di tempo fa, ma questo non è sicuramente il modo migliore per fare più lavori cron. Perché passare attraverso l'interfaccia Web, violando ciò che l'interfaccia rappresenta realmente, quando ci sono molti altri modi per accedere all'ambiente Rails?
Matchu,

6
La qualifica "supponendo che i tuoi compiti non richiedano troppo tempo per il completamento" sembra ENORME. Non sarebbe meglio usare un approccio più generalmente utile, e non solo nei casi in cui le attività sono molto veloci? In questo modo non stai costantemente rivalutando se questo o quell'attività debbano essere riscritti usando un approccio diverso.
iconoclasta il

77
Questa vecchia domanda è il principale risultato di Google per "rails cron". Questa risposta è tutt'altro che l'approccio migliore. Si prega di consultare le altre risposte per suggerimenti più sani.
Jim Garvin,

2
Non è il modo migliore. Esistono molti altri modi per accedere a Rails env tramite un processo cron senza chiamare un servizio REST. L'approccio al rastrello è sicuramente migliore
Shine,

10

le attività di script / runner e rake vanno perfettamente bene come cron job.

Ecco una cosa molto importante che devi ricordare quando esegui lavori cron. Probabilmente non verranno chiamati dalla directory principale della tua app. Ciò significa che tutti i tuoi requisiti per i file (al contrario delle librerie) devono essere eseguiti con il percorso esplicito: ad esempio File.dirname (__ FILE__) + "/ other_file". Questo significa anche che devi sapere come chiamarli esplicitamente da un'altra directory :-)

Controlla se il tuo codice supporta l'esecuzione da un'altra directory con

# 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

Inoltre, i lavori cron probabilmente non vengono eseguiti come te, quindi non dipendono da alcun collegamento inserito in .bashrc. Ma questo è solo un suggerimento cron standard ;-)


È possibile eseguire il lavoro come qualsiasi utente (è sufficiente impostare la voce crontab per l'utente desiderato), ma si è corretti sul fatto che il profilo e gli script di accesso non verranno eseguiti e non si inizierà nella directory principale. Quindi è comune avviare il comando con un "cd" come mostrato nel commento di @ luke-franci
Tom Wilson

10

Il problema di quando (e cron) è che ricarica l'ambiente delle rotaie ogni volta che viene eseguito, il che è un vero problema quando le tue attività sono frequenti o hanno molto lavoro di inizializzazione da fare. Ho avuto problemi di produzione a causa di questo e devo avvisarti.

Rufus Scheduler lo fa per me ( https://github.com/jmettraux/rufus-scheduler )

Quando ho lunghi lavori da eseguire, lo uso con delayed_job ( https://github.com/collectiveidea/delayed_job )

Spero che aiuti!


10

Sono un grande fan dello scheduler resque / resque . Puoi non solo eseguire attività simili a cron ripetute ma anche attività in momenti specifici. L'aspetto negativo è che richiede un server Redis.


10

Questo è interessante, nessuno ha menzionato il Sidetiq . È una bella aggiunta se stai già utilizzando Sidekiq.

Sidetiq fornisce una semplice API per la definizione di lavoratori ricorrenti per Sidekiq.

Il lavoro sarà simile al seguente:

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

  recurrence { hourly.minute_of_hour(15, 45) }

  def perform
    # do stuff ...
  end
end

8

Entrambi funzioneranno bene. Di solito uso script / runner.

Ecco un esempio:

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

Puoi anche scrivere uno script pure-Ruby per farlo se carichi i file di configurazione giusti per connetterti al tuo database.

Una cosa da tenere a mente se la memoria è preziosa è che lo script / runner (o un'attività Rake che dipende dall '"ambiente") caricherà l'intero ambiente Rails. Se hai solo bisogno di inserire alcuni record nel database, questo utilizzerà la memoria che non è necessario. Se scrivi la tua sceneggiatura, puoi evitarlo. In realtà non ho ancora avuto bisogno di farlo, ma lo sto prendendo in considerazione.


8

Usa Craken (processi cron rake centric)


1
scrivere cron job è così difficile, meglio scaricare una gemma per questo
f0ster

1
non è difficile, ma averli archiviati in git e sempre aggiornati sulla distribuzione è un grande vantaggio quando si lavora in una squadra.
Thibaut Barrère


3

Ecco come ho impostato i miei compiti cron. Ne ho uno per fare backup giornalieri del database SQL (usando rake) e un altro per far scadere la cache una volta al mese. Qualsiasi output viene registrato in un file log / cron_log. Il mio crontab è simile al seguente:

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

La prima attività cron esegue backup giornalieri del db. I contenuti di cron_tasks sono i seguenti:

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

La seconda attività è stata configurata in un secondo momento e utilizza script / runner per far scadere la cache una volta al mese (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}"

Immagino di poter fare il backup del database in qualche altro modo, ma finora funziona per me :)

I percorsi per rastrellare e ruby ​​possono variare su server diversi. Puoi vedere dove sono usando:

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

3

Usare qualcosa come Sidekiq o Resque è una soluzione molto più solida. Supportano entrambi i tentativi di lavoro, l'esclusività con un blocco REDIS, il monitoraggio e la pianificazione.

Tieni presente che Resque è un progetto morto (non attivamente mantenuto), quindi Sidekiq è un'alternativa decisamente migliore. È anche più performante: Sidekiq esegue diversi lavoratori su un singolo processo multithread mentre Resque esegue ciascun lavoratore in un processo separato.


Questa è una risposta corretta. Molti possono dimenticare le belle funzionalità fornite da sidekiq o resque, come l'interfaccia web per il monitoraggio di ciò che sta accadendo: numero di lavori in esecuzione, non riusciti o pianificati, riavviali facilmente, blocco per lavoratori unici, limitazione e limitazione, ecc.
Dmitry Polushkin il

3

Di recente ho creato alcuni lavori cron per i progetti a cui ho lavorato.

Ho scoperto che la gemma Clockwork è molto utile.

require 'clockwork'

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

Puoi anche pianificare il tuo lavoro in background usando questo gioiello. Per la documentazione e ulteriore assistenza, consultare https://github.com/Rykian/clockwork



2

Una volta ho dovuto prendere la stessa decisione e sono davvero contento di quella decisione oggi. Usa resque scheduler perché non solo un redis separato eliminerà il carico dal tuo db, ma avrai anche accesso a molti plugin come resque-web che fornisce una grande interfaccia utente. Man mano che il tuo sistema si sviluppa, avrai sempre più attività da pianificare in modo da poterle controllare da un'unica posizione.


1

Probabilmente il modo migliore per farlo è usare rake per scrivere le attività che ti servono e semplicemente eseguirlo tramite riga di comando.

Puoi vedere un molto utile video sui railscast

Dai un'occhiata anche a queste altre risorse:


Ho provato senza successo a utilizzare la sintassi in questo tutorial. L'attività non è stata eseguita.
Tass

1

Ho usato la gemma dell'orologio e funziona abbastanza bene per me. C'è anche una clockworkdgemma che consente a uno script di essere eseguito come demone.


0

Non ne sono davvero sicuro, immagino che dipenda dal compito: quanto spesso eseguire, quanto complicato e quanta comunicazione diretta con il progetto Rails è necessario ecc. Immagino che ci fosse solo "One Best Way" per fare qualcosa , non ci sarebbero tanti modi diversi per farlo.

Nel mio ultimo lavoro in un progetto Rails, dovevamo creare un mailer di inviti batch (inviti al sondaggio, non spamming) che dovrebbe inviare i messaggi programmati ogni volta che il server ha avuto tempo. Penso che avremmo usato gli strumenti daemon per eseguire le attività di rake che avevo creato.

Sfortunatamente, la nostra azienda ha avuto alcuni problemi di denaro ed è stata "acquistata" dal principale rivale, quindi il progetto non è mai stato completato, quindi non so cosa alla fine avremmo usato.


0

Uso lo script per eseguire cron, questo è il modo migliore per eseguire un cron. Ecco qualche esempio di cron,

Apri CronTab -> sudo crontab -e

E incolla le linee di soffietto:

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

Ecco un formato cron, ti aiuterà

::CRON FORMAT::

tabella di formato 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. 

Spero che questo ti possa aiutare :)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.