【问题标题】:Scheduling background jobs without duplicates安排没有重复的后台作业
【发布时间】:2019-05-04 20:53:27
【问题描述】:

我们有与另一个应用程序同步的 rails 应用程序。它发生在后台。基本上,每次这项工作只是同步所有数据,所以目前它真的很慢,我们正在寻找通过使用并行性来加速这个过程。

目前基本上是这样的:

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

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

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

准确地说,我们想为此使用后台队列。对于初学者,我们希望每个帐户使用一项工作(我们有许多需要同步的帐户)。这里的问题是我们如何防止我们的队列多次获得同一个作业?

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

你会怎么做?

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

另一个问题是我们使用什么系统:delayed_job(已经被邮件发送者使用)还是 sidekiq?

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

我将不胜感激任何有关该主题的 cmets 或故事。

【问题讨论】:

  • Sidekiq Enterprise 具有“独特的工作”功能。
  • @SergioTulentsev 谢谢,我会调查的

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


【解决方案1】:

如果我们每小时安排一次工作

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

关于僵尸作业——恕我直言,这应该不是什么大问题。您的服务器不会经常崩溃,是吗?如果出现任何问题,您可以随时在 Web GUI 或控制台中清除内容。

【讨论】:

  • 僵尸作业的问题是,如果某个进程在我不知情的情况下死亡,那么这个特定帐户的同步将不再发生,直到我发现这一点并删除僵尸。可悲的是,唯一性会导致这个问题:) 不确定我们是否要每天监控它们或收到来自客户的愤怒邮件
  • @Nondv 那么是进程死亡还是服务器?这些是不同的事情 Sidekiq 对失败的工作有重试策略。 github.com/mperham/sidekiq/wiki/Error-Handling#best-practices
  • 这是一个工人在工作处理过程中死亡。所以作业被锁定但从未解锁
  • @Nondv for Sidekiq Pro 有一个 super_fetch 策略可以处理您的情况 github.com/mperham/sidekiq/wiki/Reliability#using-super_fetch 它是付费的,但如果您运行的应用程序必须非常可靠,那么它应该是值得的跨度>
  • @Nondv BTW with Sidekiq 你描述的情况不会发生。会有一点不同的问题。如果工人意外死亡 - 工作将丢失。所以它不会是僵尸,我想在你的情况下应该不是一个大问题。这是关于basic_fetchsuper_fetchblog.bigbinary.com/2018/05/08/…的一个很好的解释
【解决方案2】:

首先,您正在使用异步,而不是并行,细微的差异来加快进程。 :)

其次,听起来您要解决三个主要问题:

  1. 为每个帐户排队一个作业。
  2. 确保最多只有一个独特的作业在排队。
  3. 尽量避免长期工作。

过去我曾将 Resque 用于此类事情 - 但我确信有很多替代方案。

你会做这样的事情:

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

为确保它们在未来某个时间运行,您可以考虑使用 cronresque 调度程序

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

为了避免僵尸作业,我建议您将作业逻辑包装在合理的超时块中,是的,使用某种清洁剂来修剪死作业退出队列。

【讨论】:

  • 我们将为此使用多个服务器。并行,不是吗?
  • @Nondv 它与以往略有不同,而且经常被混为一谈。我将向您推荐一个 quora 答案,该答案很好地解释了与图像的区别:quora.com/…
  • 这正是我理解它们之间的区别的方式。无论如何感谢您的链接:) 以及您的回答。
【解决方案3】:

让我们看看。

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

  • 每个帐户案例一个 DJ:我会这样做。

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

ii) 之后,编写一个用于同步的自定义作业。这应该不难,因为您已经有了业务逻辑代码。同步完成后,您可以将状态更改为“完成”或返回“就绪”。

iii) 这样,只有在该帐户的“sync_status”已完成/准备就绪时,您才能将作业排队。

示例:

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

在 custom_sync.rb 中,最后:

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

    延迟::Worker.raise_signal_exceptions = :term

它会引发 SignalException。 DJ 将通过清除locked_by 列来优雅地处理这个问题。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-25
    • 1970-01-01
    • 2017-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多