如何保证Erlang集群中的进程唯一性?

时间:2016-02-18 20:38:15

标签: erlang elixir

我正在试图找出解决Elixir应用程序的以下负载分配/流程唯一性保证问题的最佳方法。

该应用程序

我的Elixir应用程序在n个不同的节点上启动(从大型池中随机选择,没有固定的IP或主机名称预先知道)形成一个集群(我不确定什么是进行节点发现的最佳方式但是现在让我们忽略它。

简而言之,应用程序的主要目的是使两个系统随时间保持同步,基本上是一个集成。每个用户都有一个集成,可以添加新的集成,也可以随时删除现有集成。

问题

我希望每次集成都有一个Erlang进程,因为它在概念上非常优雅并带来许多好处(例如每个集成都有一个自然的同步点)。看来这也是扩展系统的方法。

问题在于,这个过程显然需要在整个集群中是唯一的(如果两个进程试图同步相同的集成,很难预测数据会发生什么),并且我想自动将工作重新分配为节点失败或新的整合进来。

此外,在部署新版本的应用程序时,新群集将在旧群集关闭之前启动(我们不依赖于热代码重新加载)。这个过渡阶段需要以某种方式处理。

可能的解决方案

一种解决方案可能是依靠全球流程。启动时,节点会自行注册,连接到其他已注册的节点,然后尝试启动全局Scheduler进程的副本,该进程的唯一作用是跨节点启动集成过程。

虽然这提供了容错功能,但它不能保证每个集成一个进程,因为群集可以通过网络分区分成两个。它也不处理新旧集群在线并且旧集群仍在工作的短暂时期。

某种全局锁定机制(通过共享的Redis实例?)可用于处理网络分区和应用程序重启,但这看起来相当糟糕。

有什么建议吗?

谢谢!

1 个答案:

答案 0 :(得分:6)

出于解释的目的,我们假设:

  • 每个用户都由一个在整个系统中唯一的ID标识 - UID
  • 每个流程都由一个节点唯一的ID标识 - PID
  • 每个节点都由一个在节点间唯一的ID标识 - NID
  • 每个用户的集成商流程由元组{NID, PID}
  • 标识

问题是要确保1-to-1UID之间存在{NID, PID}映射。

基本上有两种解决方法:

<强> I 即可。引入共享状态,类似寄存器,跟踪UID{NID, PID}之间的映射。我们称之为The Register。如果这是具有共享模式的mnesia数据库,则Redis实例,单独的节点或其他任何内容都是实现细节。在任何情况下,每个新进程都需要在开始集成特定UID之前在The Register注册。在网络分区的情况下,节点发生故障或以标准方式处理的其他灾难,例如:根据您的要求,您可以根据相应的CAP theorem设计注册。

<强> II 即可。通过算法为给定的PID分配NIDUID。这与hashtable的工作方式类似。作为一个例子,让我们说UID是一个整数(如果它不是,那么任何数据结构都可以简化为hash function的整数)。您可以选择如下节点:

NID = UID % NX

其中NX是节点数量(%当然是modulo operation)。在每个节点上,您可以将过程注册为UID。完成后,您可以根据UID唯一地处理每个集成商流程 - 您使用%操作获取NIDUID本身获取PID {1}}在节点上。

第二种方法要求节点数量不会发生变化,例如:监视节点,如果一个节点发生故障,另一个节点将被启动以替换它。它也适用于每个节点都是主从配对,并且它们之间发生了一些复制。

这两种方法的区别在于,在第一种情况下,您只有一个故障点 - 如果寄存器变得不可用,则无法启动新的集成器。而在第二种情况下,UID与其集成器进程之间的分配是完全分布式和异步的 - 如果一个节点发生故障,则其他节点不间断地工作,这使得它更容易扩展。

但是如果节点数量发生变化,第一种方法仍然可以像以前一样工作,而在第二种方法中,这也会导致哈希函数发生变化。这需要重新平衡进程(在节点之间移动),以便仍然可以正确地解决它们。