Rails 的 cron 作业:最佳实践?

发布于 2024-07-08 15:22:00 字数 63 浏览 14 评论 0 原文

在 Rails 环境中运行计划任务的最佳方式是什么? 脚本/运行程序? 耙? 我想每隔几分钟运行一次任务。

What's the best way to run scheduled tasks in a Rails environment? Script/runner? Rake? I would like to run the task every few minutes.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(21

停顿的约定 2024-07-15 15:22:00

我在严重依赖计划任务的项目上使用了非常流行的 Whenever,它非常棒。 它为您提供了一个很好的 DSL 来定义您的计划任务,而不必处理 crontab 格式。 来自自述文件:

每当 Ruby gem 提供
清晰的编写和部署语法
计划任务。

自述文件中的示例:

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

I've used the extremely popular Whenever on projects that rely heavily on scheduled tasks, and it's great. It gives you a nice DSL to define your scheduled tasks instead of having to deal with crontab format. From the README:

Whenever is a Ruby gem that provides a
clear syntax for writing and deploying
cron jobs.

Example from the 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
遗失的美好 2024-07-15 15:22:00

我正在使用 rake 方法(由 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) 调用安全/私有 Webhook API 以在后台调用所需的任务,或者 (b)直接将任务放入您选择的排队系统中

I'm using the rake approach (as supported by heroku)

With a file called lib/tasks/cron.rake ..

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

To execute from the command line, this is just "rake cron". This command can then be put on the operating system cron/task scheduler as desired.

Update this is quite an old question and answer! Some new info:

  • the heroku cron service I referenced has since been replaced by Heroku Scheduler
  • for frequent tasks (esp. where you want to avoid the Rails environment startup cost) my preferred approach is to use system cron to call a script that will either (a) poke a secure/private webhook API to invoke the required task in the background or (b) directly enqueue a task on your queuing system of choice
苏别ゝ 2024-07-15 15:22:00

在我们的项目中,我们第一次使用了 gem,但遇到了一些问题。

然后我们切换到 RUFUS SCHEDULER gem,事实证明它非常有用在 Rails 中调度任务既简单又可靠。

我们用它来发送每周和每周一次的邮件。 每日邮件,甚至运行一些定期的 rake 任务或任何方法。

其中使用的代码如下:

    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

In our project we first used whenever gem, but confronted some problems.

We then switched to RUFUS SCHEDULER gem, which turned out to be very easy and reliable for scheduling tasks in Rails.

We have used it for sending weekly & daily mails, and even for running some periodic rake tasks or any method.

The code used in this is like:

    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

To learn more: https://github.com/jmettraux/rufus-scheduler

冰葑 2024-07-15 15:22:00

假设您的任务不需要太长时间才能完成,只需为每个任务创建一个带有操作的新控制器即可。 将任务的逻辑实现为控制器代码,然后在操作系统级别设置一个 cronjob,使用 wget 在适当的时间间隔调用该控制器的 URL 和操作。 此方法的优点是:

  1. 可以完全访问所有 Rails 对象,就像在普通控制器中一样。
  2. 可以像正常操作一样进行开发和测试。
  3. 还可以从简单的网页临时调用您的任务。
  4. 不要通过启动额外的 ruby​​/rails 进程来消耗更多内存。

Assuming your tasks don't take too long to complete, just create a new controller with an action for each task. Implement the logic of the task as controller code, Then set up a cronjob at the OS level that uses wget to invoke the URL of this controller and action at the appropriate time intervals. The advantages of this method are you:

  1. Have full access to all your Rails objects just as in a normal controller.
  2. Can develop and test just as you do normal actions.
  3. Can also invoke your tasks adhoc from a simple web page.
  4. Don't consume any more memory by firing up additional ruby/rails processes.
太阳男子 2024-07-15 15:22:00

每当(和 cron)的问题是它每次执行时都会重新加载 Rails 环境,当您的任务频繁或有大量初始化工作要做时,这是一个真正的问题。 因此,我在生产中遇到了问题,必须警告您。

Rufus 调度程序为我做这件事( https://github.com/jmettraux/rufus-scheduler

当我有很长的工作时要运行,我将其与delayed_job(https://github.com/collectiveidea/delayed_job)一起使用,

我希望这有帮助!

The problem with whenever (and cron) is that it reloads the rails environment every time it's executed, which is a real problem when your tasks are frequent or have a lot of initialization work to do. I have had issues in production because of this and must warn you.

Rufus scheduler does it for me ( https://github.com/jmettraux/rufus-scheduler )

When I have long jobs to run, I use it with delayed_job ( https://github.com/collectiveidea/delayed_job )

I hope this helps!

不语却知心 2024-07-15 15:22:00

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 提示;-)

script/runner and rake tasks are perfectly fine to run as cron jobs.

Here's one very important thing you must remember when running cron jobs. They probably won't be called from the root directory of your app. This means all your requires for files (as opposed to libraries) should be done with the explicit path: e.g. File.dirname(__FILE__) + "/other_file". This also means you have to know how to explicitly call them from another directory :-)

Check if your code supports being run from another directory with

# 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

Also, cron jobs probably don't run as you, so don't depend on any shortcut you put in .bashrc. But that's just a standard cron tip ;-)

も让我眼熟你 2024-07-15 15:22:00

我是 resque/resque 调度程序。 您不仅可以运行重复的类似 cron 的任务,还可以在特定时间运行任务。 缺点是,它需要 Redis 服务器。

I'm a big fan of resque/resque scheduler. You can not only run repeating cron-like tasks but also tasks at specific times. The downside is, it requires a Redis server.

萌面超妹 2024-07-15 15:22:00

有趣的是,没有人提到 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

That is interesting no one mentioned the Sidetiq.
It is nice addition if you already using Sidekiq.

Sidetiq provides a simple API for defining recurring workers for
Sidekiq.

Job will look like this:

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

  recurrence { hourly.minute_of_hour(15, 45) }

  def perform
    # do stuff ...
  end
end
半城柳色半声笛 2024-07-15 15:22:00

两者都会工作得很好。 我通常使用脚本/运行器。

下面是一个示例:

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

如果加载正确的配置文件来连接到数据库,您还可以编写纯 Ruby 脚本来执行此操作。

如果内存很宝贵,需要记住的一件事是脚本/运行程序(或依赖于“环境”的 Rake 任务)将加载整个 Rails 环境。 如果您只需要向数据库中插入一些记录,这将使用您实际上不需要的内存。 如果您编写自己的脚本,则可以避免这种情况。 我实际上还不需要这样做,但我正在考虑。

Both will work fine. I usually use script/runner.

Here's an example:

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

You can also write a pure-Ruby script to do this if you load the right config files to connect to your database.

One thing to keep in mind if memory is precious is that script/runner (or a Rake task that depends on 'environment') will load the entire Rails environment. If you only need to insert some records into the database, this will use memory you don't really have to. If you write your own script, you can avoid this. I haven't actually needed to do this yet, but I am considering it.

情绪少女 2024-07-15 15:22:00

使用 Craken (以 rake 为中心的 cron 作业)

Use Craken (rake centric cron jobs)

暮倦 2024-07-15 15:22:00

我使用背景rb。

http://backgroundrb.rubyforge.org/

我用它来运行计划任务以及需要太长时间的任务渴望正常的客户端/服务器关系。

I use backgroundrb.

http://backgroundrb.rubyforge.org/

I use it to run scheduled tasks as well as tasks that take too long for the normal client/server relationship.

腻橙味 2024-07-15 15:22:00

使用 Sidekiq 或 Resque 是一个更强大的解决方案。 它们都支持重试作业、REDIS 锁的排他性、监控和调度。

请记住,Resque 是一个死项目(没有积极维护),因此 Sidekiq 是一个更好的选择。 它还具有更高的性能:Sidekiq 在单个多线程进程上运行多个工作线程,而 Resque 在单独的进程中运行每个工作线程。

Using something Sidekiq or Resque is a far more robust solution. They both support retrying jobs, exclusivity with a REDIS lock, monitoring, and scheduling.

Keep in mind that Resque is a dead project (not actively maintained), so Sidekiq is a way better alternative. It also is more performant: Sidekiq runs several workers on a single, multithread process while Resque runs each worker in a separate process.

寂寞陪衬 2024-07-15 15:22:00

这是我设置 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 ----";

第二个任务是稍后设置的,并使用 script/runner 每月使缓存过期一次(lib/monthly_cron.rb):

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

我想我可以通过其他方式备份数据库,但到目前为止它对我有用: )

rake 和 ruby​​ 的路径在不同的服务器上可能会有所不同。 您可以使用以下命令查看它们的位置:

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

Here's how I have setup my cron tasks. I have one to make daily backups of SQL database (using rake) and another to expire cache once a month. Any output is logged in a file log/cron_log. My crontab looks like this:

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

The first cron task makes daily db backups. The contents of cron_tasks are the following:

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

The second task was setup later and uses script/runner to expire cache once a month (lib/monthly_cron.rb):

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

I guess I could backup database some other way but so far it works for me :)

The paths to rake and ruby can vary on different servers. You can see where they are by using:

whereis ruby # -> ruby: /usr/local/bin/ruby
whereis rake # -> rake: /usr/local/bin/rake
断桥再见 2024-07-15 15:22:00

我最近为我一直在做的项目创建了一些 cron 作业。

我发现宝石发条非常有用。

require 'clockwork'

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

您甚至可以使用这个 gem 安排您的后台工作。
有关文档和进一步帮助,请参阅 https://github.com/Rykian/clockwork

I have recently created some cron jobs for the projects I have been working on.

I found that the gem Clockwork very useful.

require 'clockwork'

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

You can even schedule your background job using this gem.
For documentation and further help refer https://github.com/Rykian/clockwork

眼角的笑意。 2024-07-15 15:22:00

您可以使用 resqueresque-schedular gem 来创建 cron,这非常容易做到。

https://github.com/resque/resque

https://github.com/resque/resque-scheduler

you can use resque and resque-schedular gem for creating cron, this is very easy to do.

https://github.com/resque/resque

https://github.com/resque/resque-scheduler

情愿 2024-07-15 15:22:00

曾经我不得不做出同样的决定,我对今天的决定感到非常高兴。 使用resque调度程序,因为不仅单独的redis会减轻数据库的负载,您还可以访问许多插件,例如resque-web,它提供了出色的用户界面。 随着系统的发展,您将需要安排越来越多的任务,因此您将能够从一个地方控制它们。

Once I had to make the same decision and I'm really happy with that decision today. Use resque scheduler because not only a seperate redis will take out the load from your db, you will also have access to many plugins like resque-web which provides a great user interface. As your system develops you will have more and more tasks to schedule so you will be able to control them from a single place.

一念一轮回 2024-07-15 15:22:00

也许最好的方法是使用 rake 编写您需要的任务,然后通过命令行执行它。

您可以在railscasts 上观看非常有用的视频

另请查看其他资源:

Probably the best way to do it is using rake to write the tasks you need and the just execute it via command line.

You can see a very helpful video at railscasts

Also take a look at this other resources:

ぇ气 2024-07-15 15:22:00

我使用了 clockwork gem,它对我来说效果很好。 还有 clockworkd gem 允许脚本作为守护进程运行。

I used clockwork gem and it works pretty well for me. There is also clockworkd gem that allows a script to run as a daemon.

北陌 2024-07-15 15:22:00

我不太确定,我想这取决于任务:运行频率、复杂程度以及需要与 Rails 项目进行多少直接通信等。我想是否只有“一个最好的方法” 要做某事,不会有那么多不同的方法。

在我在 Rails 项目中的上一份工作中,我们需要制作一个批量邀请邮件程序(调查邀请,而不是垃圾邮件),只要服务器有时间,它就应该发送计划的邮件。 我认为我们将使用 守护进程工具 来运行我创建的 rake 任务。

不幸的是,我们公司遇到了一些资金问题,被主要竞争对手“收购”,因此该项目从未完成,所以我不知道我们最终会使用什么。

I'm not really sure, I guess it depends on the task: how often to run, how much complicated and how much direct communication with the rails project is needed etc. I guess if there was just "One Best Way" to do something, there wouldn't be so many different ways to do it.

At my last job in a Rails project, we needed to make a batch invitation mailer (survey invitations, not spamming) which should send the planned mails whenever the server had time. I think we were going to use daemon tools to run the rake tasks I had created.

Unfortunately, our company had some money problems and was "bought" by the main rival so the project was never completed, so I don't know what we would eventually have used.

旧人 2024-07-15 15:22:00

我使用脚本来运行 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. 

希望这对您有帮助:)

I Use script to run cron, that is the best way to run a cron.
Here is some example for cron,

Open CronTab —> sudo crontab -e

And Paste Bellow lines:

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

Here is some cron format, will help you

::CRON FORMAT::

cron format table

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. 

Hope this will help you :)

野鹿林 2024-07-15 15:22:00

good_job 作为其他 ActiveJob 后端的替代方案越来越受到关注,并且它还支持 cron 式重复作业。 基本上是这样完成的:

# Configure cron with a hash that has a unique key for each recurring job
config.good_job.cron = {
  # Every 15 minutes, enqueue `ExampleJob.set(priority: -10).perform_later(42, name: "Alice")`
  frequent_task: { # each recurring job must have a unique key
    cron: "*/15 * * * *", # cron-style scheduling format by fugit gem
    class: "ExampleJob", # reference the Job class with a string
    args: [42, "life"], # positional arguments to pass; can also be a proc e.g. `-> { [Time.now] }`
    kwargs: { name: "Alice" }, # keyword arguments to pass; can also be a proc e.g. `-> { { name: NAMES.sample } }`
    set: { priority: -10 }, # additional ActiveJob properties; can also be a lambda/proc e.g. `-> { { priority: [1,2].sample } }`
    description: "Something helpful", # optional description that appears in Dashboard
  },
  another_task: {
    cron: "0 0,12 * * *",
    class: "AnotherJob",
  },
  # etc.
}

您可以在此处阅读更多相关信息。

good_job is gaining more traction as an alternative to the other ActiveJob backends and it also supports cron-style recurring jobs. Here is how it's done basically:

# Configure cron with a hash that has a unique key for each recurring job
config.good_job.cron = {
  # Every 15 minutes, enqueue `ExampleJob.set(priority: -10).perform_later(42, name: "Alice")`
  frequent_task: { # each recurring job must have a unique key
    cron: "*/15 * * * *", # cron-style scheduling format by fugit gem
    class: "ExampleJob", # reference the Job class with a string
    args: [42, "life"], # positional arguments to pass; can also be a proc e.g. `-> { [Time.now] }`
    kwargs: { name: "Alice" }, # keyword arguments to pass; can also be a proc e.g. `-> { { name: NAMES.sample } }`
    set: { priority: -10 }, # additional ActiveJob properties; can also be a lambda/proc e.g. `-> { { priority: [1,2].sample } }`
    description: "Something helpful", # optional description that appears in Dashboard
  },
  another_task: {
    cron: "0 0,12 * * *",
    class: "AnotherJob",
  },
  # etc.
}

You can reade more about it here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文