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

Jan*_*rix 4 erlang elixir

我试图弄清楚什么是解决Elixir应用程序的以下负载分配/流程唯一性保证问题的最佳方法.

应用程序

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

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

问题

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

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

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

可能解决方案

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

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

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

有什么建议?

谢谢!

Gre*_*reg 6

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

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

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

基本上有两种解决方法:

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

.分配PIDNID给定的UID算法.这与哈希表类似.作为一个例子,假设它UID是一个整数(如果不是,那么任何数据结构都可以简化为带有散列函数的整数).您选择这样的节点:

NID = UID % NX
Run Code Online (Sandbox Code Playgroud)

NX节点数量在哪里(%当然是模运算).在每个节点上,您可以将进程注册为UID.完成后,您可以基于以下方式唯一地处理每个集成器进程UID- 您使用%操作来获取NIDUID自己进入PID节点.

第二种方法要求节点的数量不会改变,例如节点被监视,如果一个节点发生故障,另一个节点将被启动以替换它.它也可以用于每个节点作为主从对,并且在它们之间发生一些复制.

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

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