0

我正在创建一个带有后台工作人员的 Rails Web 应用程序,以便在设定的时间间隔内在后台执行一些任务。我正在使用 Resque 和 Redis 来对后台作业进行排队,并使用 Resque-scheduler 以设置的间隔运行它,例如每 30 秒左右。

只有当用户访问特定页面时,后台作业才需要排队,并且它应该按计划运行,直到用户离开该页面。基本上,我想在运行时动态设置时间表。我的应用程序部署在 Cloud 中,主 Rails 应用程序和后台工作人员作为通过 Redis 通信的单独进程运行。如何动态设置时间表,从哪里开始?

resque_schedule.yml

do_my_job:
  every: 1m
  class: BackgroundJob
  description: Runs the perform method in MyJob
  queue: backgroundq

控制器内部

  def index
    Resque.enqueue(BackgroundJob)
  end

background_job.rb

class BackgroundJob
  @queue = :backgroundq

  @job_helper = BackgroundHelper.new

  def self.perform
    @job_helper.get_job_data
  end

end
4

1 回答 1

1

Resque-scheduler 可以使用动态计划执行您所描述的操作。每次用户访问您的页面时,您都会创建一个计划,Resque.set_schedule该计划每 30 秒在该用户上运行一次。当用户离开时,您使用 中止Resque.remove_schedule。我可以想象用户可能会以多种方式离开页面而您没有注意到(最坏的情况,如果他们的计算机断电怎么办?),所以我担心会留下时间表。我也不确定当您添加越来越多的计划时 resque-scheduler 是否仍然满意,因此您可能会遇到大量用户的麻烦。


您可以通过在您当前需要运行的每个用户上每 30 秒运行一个作业来最小化计划数量,例如:

class BackgroundJob
  def self.perform
    User.active.each { ... }
  end
end

如果您User#last_seen_at在用户访问您的页面时执行类似设置的操作,并且User.active加载了过去 10 分钟内看到的所有人,那么您将每 30 秒对所有活跃用户运行一次,并且用户的工作在他们之后没有机会持续很长时间离开,因为它在 10 分钟后超时。但是,如果您的用户数量超过了您在 30 秒内可以完成的工作,那么计划的作业在下一个副本启动之前不会完成,并且会出现问题。您可以通过让每个用户的顶级作业入队一个实际完成工作的辅助作业来解决这个问题。然后,只要你能在 30 秒内做到这一点,并且有足够的 resque 工人来满足你的工作量,事情就应该没问题。


处理它的第三种方法是让用户的作业将其自身的另一个副本排入队列,如下所示:

class BackgroundJob
  def self.perform(user_id)
    do_work(user_id)
    Resque.enqueue_in(30.seconds, BackgroundJob, user_id)
  end
end

当用户访问您的页面时,您将第一个作业排入队列,然后它会继续运行。这种方式很好,因为每个用户的作业都是独立运行的,您不需要为每个用户单独的计划或管理所有内容的顶级计划。一旦用户离开,您仍然需要一种方法来停止运行的作业,可能使用超时或 TTL 计数器。


最后,我会质疑 Resque 是否适合您的任务。如果您希望用户在页面上每 30 秒发生一次,大概您希望用户看到它发生,因此您应该考虑在浏览器中实现它。例如,您可以设置 30 秒的 JavaScript 间隔来访问 API 端点,该端点执行一些工作并返回值。这完全避免了对 Resque 的任何需求,并且会在用户离开时自动停止,即使他们断电并且您的代码没有机会清理。

于 2015-10-05T04:24:08.033 回答