【问题标题】:A cron job for rails: best practices?rails 的 cron 工作:最佳实践?
【发布时间】:2010-09-22 02:12:45
【问题描述】:

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

【问题讨论】:

  • 对于那些从 Google 来到这里的人,请在接受的答案之外寻找更好的方法。
  • 任何时候的答案似乎比公认的答案更合理,这是一个老套路。
  • 另请注意,至少有一个答案假定您安装了某个 gem。
  • 这里总结了几个(我发现的)好的做法wisecashhq.com/blog/writing-reliable-cron-jobs
  • 在许多情况下,cron 作业是一种难闻的气味。最好通过 sidekiq/resque(或其他后台工作程序)编写调度程序,或者编写一个守护进程(功能较少且可监控)。 Cron 作业至少有一些不好的地方:1)锁定一个实例是一种痛苦; 2) 监控不容易; 3) 异常处理应再次手动编写; 4)不易重启; 5) 后台工作人员轻松解决上述所有问题。

标签: ruby-on-rails rake daemon cron runner


【解决方案1】:

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

只要 Ruby gem 提供 用于编写和部署的清晰语法 cron 作业。

自述文件中的示例:

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

【讨论】:

  • 如果每分钟运行一次,则每次都会重新启动环境,这可能代价高昂。 github.com/ssoroka/scheduler_daemon 似乎避免了这种情况。
  • +1 用于将 cron 配置与您的版本控制系统保持一致
  • 我认为这是最好的解决方案。如果您使用的是rails,我认为最好在rails中编写所有内容。使用这种方法,您还可以在更改服务器时忘记 cron 任务,它会随着应用程序移动。
  • 有一个很棒的 Railscast 关于每当这真的很有帮助(旧的免费版本也是如此)。
  • @Tony,Whenever 基本上是一种用于编写 cron 作业的领域特定语言。它在你的 Rails 服务器上编译成常规的 cron 语法,而 cron 是执行你指定的作业(通常通过 rails runner)。
【解决方案2】:

我正在使用 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) 直接在您选择的排队系统上排队

【讨论】:

  • 这种情况下的 cron 条目应该是什么,以便操作系统知道 rake 任务的正确路径?
  • 注意:这些天我随时都在使用(请参阅 Jim Garvin 的回答),但是运行 rake 任务的原始 cron 条目将类似于:30 4 * * * /bin/bash -l - c 'cd /opt/railsapp && RAILS_ENV=production rake cron --silent'
  • 如何从控制台调用它?我做了load "#{Rails.root}/lib/tasks/cron.rake"rake cron,但得到了 NameError: undefined local variable or method `cron' for main:Object
  • 这种方法的问题是:environment 依赖。我们有一个非常繁重的 Rails 应用程序需要很长时间才能启动,我们的 Rake 每分钟调用一次,并且在启动执行 taskRails 环境 时消耗更多资源。我希望有一个已经 启动 Rails 环境可以通过 cron 调用,必须介于 controller 方法和 rake 环境 i> 一个。
  • 这个任务的持续时间是多少?我正在使用 if 条件。我想知道它的运行频率。我在 heroku 网站上找不到任何相关信息。
【解决方案3】:

在我们的项目中,我们第一次使用 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

【讨论】:

  • 支持 rufus,因为我已经将它用于简单的 ruby​​ 项目或完整的 rails 应用程序。
  • 您能否更具体地谈谈您在使用 When 时遇到的问题?
  • 有史以来最棒的答案
【解决方案4】:

假设您的任务不会花费太长时间来完成,只需为每个任务创建一个包含操作的新控制器。将任务的逻辑实现为控制器代码,然后在操作系统级别设置一个 cronjob,使用 wget 以适当的时间间隔调用此控制器的 URL 和操作。这种方法的优点是你:

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

【讨论】:

  • 如何防止他人访问此任务?如果任务占用cpu并频繁调用它会导致问题。
  • 我知道这是不久前的事了,但这绝对不再是执行 cron 任务的最佳方式了。当有很多其他方法可以访问 Rails 环境时,为什么要通过 Web 界面,违背界面的真正含义?
  • “假设你的任务不会花费太长时间来完成”这个资格似乎是一个巨大的问题。使用一种更普遍有用的方法不是更好吗,而不仅仅是在那些任务非常快的情况下?这样,您就不会不断地重新评估是否需要使用不同的方法重写这个或那个任务。
  • 这个老问题是“rails cron”的谷歌搜索结果。这个答案远非最佳方法。请参阅其他回复以获得更明智的建议。
  • 不是最好的方法。您还有许多其他方法可以通过 cron 作业访问 Rails env,而无需调用 REST 服务。 Rake 方法当然更好
【解决方案5】:

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

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

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

我希望这会有所帮助!

【讨论】:

    【解决方案6】:

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

    【讨论】:

    • 您可以以任何用户身份运行该作业(只需为您想要的用户设置 crontab 条目),但您是正确的,配置文件和登录脚本不会运行并且您不会在您的主目录。因此,如@luke-franci 的评论中所示,以“cd”开头的命令很常见
    【解决方案7】:

    我是resque/resque scheduler 的忠实粉丝。您不仅可以运行重复的类似 cron 的任务,还可以在特定时间运行任务。缺点是,它需要一个 Redis 服务器。

    【讨论】:

      【解决方案8】:

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

      【讨论】:

        【解决方案9】:

        两者都可以正常工作。我通常使用脚本/跑步者。

        这是一个例子:

        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 环境。如果您只需要在数据库中插入一些记录,这将使用您实际上不需要的内存。如果您编写自己的脚本,则可以避免这种情况。我实际上还不需要这样做,但我正在考虑这样做。

        【讨论】:

          【解决方案10】:

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

          【讨论】:

          • 编写 cron 作业太难了,最好下载一个 gem
          • 这并不难 - 但将它们存储在 git 中并在部署时始终保持最新状态对于团队中的一个人来说是一大优势。
          【解决方案11】:

          我使用 backgroundrb。

          http://backgroundrb.rubyforge.org/

          我使用它来运行计划任务以及对于正常的客户端/服务器关系而言耗时过长的任务。

          【讨论】:

            【解决方案12】:

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

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

            【讨论】:

            • 这是一个正确的答案。许多人可能会忘记 sidekiq 或 resque 提供的不错的功能,例如用于监控正在发生的事情的 Web 界面:正在运行、失败或计划的作业数量、轻松重启它们、锁定独特的工作人员、限制和限制等。跨度>
            【解决方案13】:

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

            第二个任务是稍后设置的,使用脚本/运行器每月一次过期缓存(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
            

            【讨论】:

              【解决方案14】:

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

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

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

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

              【讨论】:

                【解决方案15】:

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

                https://github.com/resque/resque

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

                【讨论】:

                  【解决方案16】:

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

                  【讨论】:

                    【解决方案17】:

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

                    你可以看到一个很有帮助的video at railscasts

                    还可以看看其他资源:

                    【讨论】:

                    • 我尝试使用本教程中的语法没有成功。任务未执行。
                    【解决方案18】:

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

                    【讨论】:

                      【解决方案19】:

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

                      在我在 Rails 项目中的最后一份工作中,我们需要制作一个批量邀请邮件(调查邀请,而不是垃圾邮件),它应该在服务器有时间时发送计划的邮件。我想我们将使用daemon tools 来运行我创建的 rake 任务。

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

                      【讨论】:

                        【解决方案20】:

                        我使用脚本运行 cron,这是运行 cron 的最佳方式。 这是 cron 的一些示例,

                        打开 CronTab —> sudo crontab -e

                        并粘贴波纹线:

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

                        这是一些cron格式,对你有帮助

                        ::CRON FORMAT::
                        

                        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. 
                        

                        希望对你有帮助:)

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2021-06-18
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2016-10-19
                          • 1970-01-01
                          相关资源
                          最近更新 更多