【问题标题】:rails periodic taskrails 定期任务
【发布时间】:2012-11-01 10:20:43
【问题描述】:

我有一个 ruby​​ on rails 应用程序,我正在尝试找到一种每隔几秒运行一些代码的方法。

我发现了很多使用 cron 或类似 cron 的实现的信息和想法,但这些只是精确到分钟,和/或需要外部工具。我想每 15 秒左右启动一次任务,并且我希望它完全独立地包含在应用程序中(如果应用程序停止,任务停止,并且没有外部设置)。

这用于缓存数据的后台生成。每隔几秒钟,该任务将组装一些数据,然后将其存储在缓存中,供所有客户端请求使用。这个任务很慢,所以它需要在后台运行并且不阻塞客户端请求。

我是 ruby​​ 的新手,但有很强的 perl 背景,我解决这个问题的方法是创建一个间隔计时器和处理程序,它分叉、运行代码,然后在完成后退出。 仅仅模拟一个客户端请求并让 Rails 控制器自己派生可能会更好。这样,我可以通过点击它的 URI 来启动任务(尽管由于任务将每隔几秒钟运行一次,我怀疑我是否需要这样做,但可能会在未来使用)。虽然让控制器调用周期性任务调度程序正在调用的任何方法都是微不足道的(一旦我有一个)。

【问题讨论】:

    标签: ruby-on-rails ruby asynchronous scheduled-tasks


    【解决方案1】:

    我建议使用无论何时 gem https://github.com/javan/whenever

    它允许您指定一个时间表,例如:

    every 15.minutes do
      MyClass.do_stuff
    end
    

    没有安排 cron 作业或使用外部服务进行监控。

    【讨论】:

    • 您必须小心使用Whenever,因为它必须每次都启动Rail 实例以执行任务。在计划任务开始和 Rail 完成初始化之间存在延迟。
    • 我认为只要最小的时间单位是分钟,你就不能使用15.seconds
    【解决方案2】:

    一般来说,据我所知,没有内置的方式可以在应用程序中创建周期性任务。 Rails 建立在 Rack 之上,它期望接收 http 请求,做一些事情,然后返回。所以你只需要自己在外部管理周期性任务。

    我认为考虑到您运行任务所需的频率,一个不错的解决方案可能只是为自己编写一个永远循环的简单 rake 任务,并在启动应用程序的同时启动它,使用一些东西像工头。 Foreman 经常像这样用于管理启动/关闭后台工作人员及其应用程序。在生产环境中,您可能希望使用其他工具来管理流程,例如 Monit。

    【讨论】:

    • 我最终只是创建了一个简单的工具,它在 Rails 应用程序启动时分叉并定期运行任务列表。通过它的每个循环都会检查父级是否还活着,如果没有则退出。
    【解决方案3】:

    您可以编写自己的方法,例如

    class MyWorker
      def self.work
        #do you work
        sleep 15
      end
    end
    

    使用rails runner MyWorker.work 运行它 会有一个单独的进程在后台运行

    或者您可以使用 Resque 之类的东西,但这是一种不同的方法。它的工作原理是这样的:向队列中添加一个任务,同时一个工作人员正在获取队列中的任何作业,并尝试完成它。

    所以这取决于你自己的需要。

    【讨论】:

      【解决方案4】:

      我知道这是一个老问题。但也许对于某人来说,这个答案可能会有所帮助。有一个 gem 叫crono

      Crono 是用于 Ruby on Rails 的基于时间的后台作业调度程序守护进程(就像 Cron)。

      Crono 是纯 Ruby。它不使用 Unix Cron 和其他平台相关的东西。因此,您可以在 Ruby 支持的所有平台上使用它。它使用 Active Record 将作业状态持久化到您的数据库中。您可以完全控制作业执行过程。它是 Ruby,因此您可以理解并修改它以满足您的需求。

      crono 最棒的地方在于它的代码是自我解释的。为了定期执行任务,您可以这样做:

      Crono.perform(YourJob).every 2.days
      

      也许你也可以这样做:

      Crono.perform(YourJob).every 30.seconds
      

      不管怎样,你真的可以做很多事情。另一个例子可能是:

      Crono.perform(TestJob).every 1.week, on: :monday, at: "15:30"
      

      我建议使用这个 gem 而不是 whenever,因为每当使用 Unix Cron 表时,它并不总是可用。

      【讨论】:

        【解决方案5】:

        仅仅因为它看起来有点优雅而抛出一个解决方案,并且在没有任何额外的宝石的情况下回答了这个问题。在我的场景中,我想运行一些代码,但只有在我所有的 Sidekiq 工作人员都完成了他们的工作之后。

        首先我定义了一个方法来检查是否有工人在工作......

        def workers_working?
          workers = Sidekiq::Workers.new.map do |_process_id, _thread_id, work|
            work
          end
          workers.size > 0
        end
        

        然后我们只需使用在调用之间休眠的循环来调用该方法。

        sleep 5 while workers_working?
        

        【讨论】:

          【解决方案6】:

          使用延迟作业之类的东西,并每隔一段时间重新排队?

          【讨论】:

          • 他说他希望它成为应用程序的一部分,并且只有在他的应用程序运行时才有效。看起来有点矫枉过正?
          • 关键是它需要在一个新进程中运行,这样 Web 进程才能继续为 Web 请求提供服务。因此,您可以构建自己的分叉服务器......或者只是重用现有的库。
          【解决方案7】:

          使用瘦服务器或其他使用 eventmachine 的服务器,然后只使用作为 eventmachine 一部分的计时器。示例:在 config/application.rb 中

          EM.add_periodic_timer(2) do
            do_this_every_2_sec
          end  
          

          【讨论】:

            猜你喜欢
            • 2016-05-04
            • 1970-01-01
            • 1970-01-01
            • 2010-10-12
            • 1970-01-01
            • 1970-01-01
            • 2011-10-06
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多