安排无重复的后台作业

时间:2018-12-03 14:30:00

标签: ruby-on-rails sidekiq delayed-job

我们有与其他应用程序同步的Rails应用程序。它在后台发生。基本上,每次此作业仅同步所有数据时,此刻它确实很慢,我们正在寻求通过使用并行性来加快处理速度。

目前基本上是这样的:

accounts.each { |a| sync_account(a) }

我们希望它看起来像这样:

accounts.each { |a| SyncAccountJob.perform_later(a) }

确切地说,我们要为此使用后台队列。首先,我们希望每个帐户使用一份工作(我们有许多需要同步的帐户)。这里的问题是如何防止队列多次获得相同的作业?

例如,如果我们每小时安排一次工作,有时某些帐户尚未同步,则将安排新工作(对不起,我的英语)。

你会做什么?

我们认为我们应该在帐户表中保留一个已创建作业的ID,并在再次计划该作业之前检查该作业是否不存在。

其他问题是我们使用什么系统:delay_job(已由邮件使用)或sidekiq?

另一个问题:“僵尸”工作。例如,假设我安排了一些作业(delayed_job),然后工作人员开始处理它。现在已锁定。然后服务器崩溃,因此作业仍被锁定,但没有任何处理。 delay_job / sidekiq是否可以自行解决此问题,还是应该写一些更干净的文件?

对于该主题的任何评论或故事,我将不胜感激。

3 个答案:

答案 0 :(得分:2)

  

如果我们每小时安排工作

在这种情况下,您可以使用sidekiq-cron。它将确保不会同时运行相同的作业。 当然,存储ID的方法也可以使用。

关于僵尸工作-恕我直言,这应该不是一个大问题。您的服务器不会定期崩溃,不是吗?如有任何问题,您始终可以在Web GUI或控制台中清除内容。

答案 1 :(得分:2)

首先,您要使用异步(而不是并行性)细微的差异来加快流程。 :)

第二,听起来好像要解决三个主要问题:

  1. 为每个帐户排队一个工作。
  2. 确保最多只能排队一个唯一的作业。
  3. 尝试避免长期工作。

从历史上看,我已经将Resque用于此类事情-但我敢肯定还有很多选择。

您将执行以下操作:

accounts.each { |a| Resque.enqueue(SyncAccount, a) }

为确保它们在将来的某个时刻运行,您可以使用 cron resque Scheduler

就确保作业的唯一性而言,您可以使用某种缓存层,例如 Redis ,在其上存储哈希函数的输出,其中与您用于创建作业的帐户相关联的参数,您可以在排队作业之前查询该参数,并在完成作业后写入redis。

为了避免Zombie Jobs,大多数情况下,我建议将您的工作逻辑包装在合理的超时块中,是的,请使用某种清洁剂修剪死掉的工作不在队列中。

答案 2 :(得分:0)

让我们看看。

  • 延迟的作业或Sidekiq:这取决于应用程序的性质。由于您已经具有用于作业排队的后端系统,因此可以很好地使用它。每个系统都有差异(正负),因此最终取决于您的选择。举一个例子,如果您的应用程序是数据库密集型应用程序,那么最好避免delay_jobs。

  • 每个案件一名DJ:我愿意。

i)在您的帐户表中添加一列。说'sync_status'。在将同步作业排队之前,将状态设置为“进行中”。

ii)之后,编写一个自定义作业进行同步。既然您已经准备好了业务逻辑代码,这应该不难。同步完成后,您可以将状态更改为“完成”或恢复为“就绪”。

iii)这样,仅当该帐户的“ sync_status”完成/就绪时,您才可以将作业排队。

示例:

Delayed::Job.enqueue(CustomSyncJob.new()) if account.ready_to_sync? 

在custom_sync.rb中,最后:

account.status = 'ready'
account.save
  • 处理信号:您的应用程序绝不应该崩溃,并且您的代码应确保崩溃。但是要优雅地杀死DJ,您可以添加以下设置:

    Delayed :: Worker.raise_signal_exceptions =:term

它将引发SignalException。 DJ会通过清除locked_by列来妥善处理此问题。

希望这会有所帮助。干杯。