2

我们使用Spring来运行可以在单节点上正常工作的计划任务。我们希望在N个节点的集群中运行这些计划任务,这样任务在某个时间点最多由一个节点执行。这是针对企业用例的,我们可能预计高达10到20个节点。集群中使用zookeeper的计划任务

我看着各种选项:

  1. 使用Quartz这似乎是在一个集群中运行计划任务的热门选择。缺点:我想避免的数据库依赖。
  2. 使用zookeeper并始终仅在领导/主节点上运行计划任务。缺点:任务执行负载未分布
  3. 使用zookeeper并在所有节点上调用计划的任务。但是在任务运行之前,一旦执行完成后获得分布式锁定和释放。 缺点:如果应用程序过载导致系统时钟漂移,所有节点上的系统时钟应该同步,这可能是一个问题。
  4. 使用zookeeper并让主节点按照时间表继续生成任务并将其分配给随机工作人员。如果先前的计划任务尚未完成,则不分配新任务。缺点:这似乎增加了太多的复杂性。

我倾向于使用#3,这似乎是一个安全的解决方案,假设zookeeper合奏节点在单独的群集上运行,系统时钟使用NTP同步。这也假定如果系统时钟同步,那么所有节点都有相同的机会获得执行任务的锁。
编辑:经过一些更多的想法后,我意识到这可能不是一个安全的解决方案,因为系统时钟应该在计划任务运行的节点之间同步,而不仅仅是动物园管理员群集节点。我说的不安全,因为运行任务的节点可能因GC暂停和其他原因而过载,并且可能会出现时钟不同步的情况。但是我再次认为这是分布式系统的标准问题。

你能否告诉我,如果我对每个选项的理解是准确的?或者可能比列出的选项有更好的方法来解决这个问题。

+0

仅供参考 - 我为Curator/ZooKeeper编写了一个分布式任务调度程序。 https://github.com/NirmataOSS/workflow – Randgalt

回答

0

那么,你可以像这样改进#3

动物园管理员提供观察者。也就是说,您可以在给定的ZNode上设置观察者(例如,在路径/some/path处)。群集中的所有节点正在观察相同的Znode。每当一个节点认为(按计划或任何方式)现在应该运行计划任务,

  1. 首先,它创建/some/pathPERSISTENT_SEQUENTIAL子节点(所有节点都看)。另外,您可以根据需要设置该节点的数据。它可能是一个json字符串,指定要运行的任务的详细信息。新的ZNode路径看起来像/some/path/prefix_<sequence-number>
  2. 然后,集群中的所有节点都会收到有关创建的子节点的通知。他们然后获取新创建的ZNode的数据并解码任务。
  3. 现在,每个节点都尝试获取分布式锁。谁先得到它可以执行它。一旦执行,该节点应报告(通过创建一个名为success/some/path/prefix_<sequence-number>下创建一个新的ZNode),该任务已执行。然后释放锁。
  4. 每当一个节点试图执行任务时,在尝试获取分布式锁之前,它应该检查ZNode是否已经有一个success子节点。

此设计通过检查名为success的子节点在创建的给定ZNode中以通知以启动任务来确保没有任务运行两次。

我已将上述设计用于企业解决方案。实际上,对于分布式命令框架;-)

+0

我认为这将起作用,但它看起来与我列出的#4类似,只是所有节点都接收任务并争夺锁执行。 #4也使用监视器,但使用[link](https://github.com/fpj/zookeeper-book-example/blob/master/src/main/java/org/apache/zookeeper /book/curator/CuratorMasterSelector.java),它似乎是更好的选择,因为只有一个随机工作者将获得任务,并且不需要分布式锁。 – vkorimilli

+0

但是,如果存在任何缺陷或缺点,我希望检查输入,除复杂性因素外,我可能会缺少输入。 – vkorimilli

+0

在这种情况下,您不必担心主从复杂性。让他们争夺锁定并完成任务。 –

0

Zookeeper或Etcd不是此用例的最佳工具。

如果您的环境允许您使用akka,则使用akka群集+最小的邮箱路由器或您喜欢的任何群集路由器会更容易。然后将计划作业推送到集群的ActorRef。更容易设置,您可以使用它在集群中设置数千个节点(它使用协议cassandra和游牧民族使用)。

Scalecube也很容易再次使用SWIM。