代码之家  ›  专栏  ›  技术社区  ›  Railsana

延迟工作:每个租户一名工人?

  •  0
  • Railsana  · 技术社区  · 6 年前

    我有一个多租户rails应用程序,有多个延迟的工作人员。

    为了避免特定于租户的工作重叠,我希望将工作人员与其他工作人员分开,这样每个工作人员一次只能处理一个特定于租户的任务。

    我想用(命名的) 队列 列并添加“tenant_1”、“tenant_2”等。不幸的是,队列必须在配置期间命名,因此对于许多租户来说,这一原则不够灵活。

    有没有办法定制延迟的工作选择下一个任务的方式?有没有其他方法来定义范围?

    0 回复  |  直到 6 年前
        1
  •  2
  •   jimcavoli    6 年前

    您最好的选择可能是运行一个实现分布式锁的自定义解决方案——本质上,工作线程都正常运行并从通常的队列中拉出,但是在与另一个系统(redis、rdbms、api等)执行工作检查之前,要验证没有其他工作线程正在执行jo。B给那个房客。如果该租户没有工作,则为该租户设置锁并执行该作业。如果房客被锁了,就不要做这项工作。这取决于很多实现细节,比如是否继续尝试另一个作业,在队列后面重新将作业排队,是否将其视为失败并将其绑定到重试限制,或者完全执行其他操作。这是非常开放的,所以我将把细节留给您,但这里有一些提示:

    • 继承将是您的朋友;在基本作业上定义此行为,并在您希望工作人员运行的作业上从中继承。这也允许你定制行为,如果你有“特殊”的情况下,某些工作出现,而没有打破其他一切。
    • 假设您没有运行activejob(因为没有提到它),请阅读 delayed_job 钩子: https://github.com/collectiveidea/delayed_job/#hooks -它们可能是适当和/或有用的工具
    • 熟悉悲观和乐观锁定策略中的一些差异和权衡-这个答案是一个很好的起点: Optimistic vs. Pessimistic locking
    • 阅读关于分布式锁概念的一般实践,以便您可以自己选择最佳工具和策略(这不一定是一个疯狂复杂的解决方案,数据库中存储租户标识符的简单表就足够了,但是您需要考虑失败案例-例如,如何管理被放弃的锁)

    认真考虑不这样做;这真的是严格要求系统正常运行吗?如果是这样的话,这可能表明了数据模型中的一个潜在缺陷,或者说明了如何围绕该数据构建转换。在考虑对数据的操作时,请尽量在应用程序中使用酸度,这样可以避免很多这些问题。这是一个原因,它不是一个常见的“开箱即用”的功能,对后台求职者。如果有一个潜在的缺陷,它不仅会咬你在这个问题上,但在其他东西-保证!

        2
  •  0
  •   Oshan Wisumperuma    6 年前

    如果您试图避免两个不同的工人在同一个租户上工作,那么这是一个糟糕的设计选择。有东西闻起来了。先把它修好。但是,如果您希望相同类型的工作实例在下面的不同属性上工作,则是最简单的解决方案。这些关系是我的假设。

    ExpiredOrderCleaner = Struct.new(:tenant_id) do
      def perform
        Order.where(tenant_id: tenant_id).expired.delete_all
      end
    end
    
    Tenant.each do |tenant|
      Delayed::Job.enqueue ExpiredOrderCleaner.new(tenant.id)
    end
    

    这将为每个租户创建唯一的作业。单个工作实例将在特定租户上工作。但是,也可以有其他类型的工作在同一个租户。这应该是好的。如果需要更小的作用域,只需为工作进程传递更多参数并在查询中使用,然后使用数据库事务来避免冲突。

    这些 best practices 对任何背景工作者来说都是正确的。

    • 使你的工作具有等幂性和事务性 意味着你的工作可以安全地执行多次
    • 接受并发性 设计你的作业,以便你可以并行运行它们

    如果你使用 apartment 宝石和 active job 包装纸。请参阅文档中的示例。