Elixir:在分布式部落中创建和注册 GenServer 与调用其 API 之间的延迟

问题描述

我使用 Horde 0.8.3 来管理 Elixir 应用程序的分布式注册表和主管。应用程序创建UserAgent-s(简称UA),实现为GenServer-s。每个 UA 代表一个连接到应用程序的用户。 UA 是用 Horde.DynamicSupervisor.start_child(MyApi.DSup,{MyApi.UserAgent,user}) 创建的。在创建 UA 的同一函数中,应用程序调用 UA 客户端 API,例如 UserAgent.ping(ua_id)(此 API 的性质在这里并不重要)。 ping(...) 使用 via_tuple注册表中查找 UA 的 pid 并向其发送 :ping 消息。

应用程序在两个节点上启动,比如 N1 和 N2。假设 start_child(...)ping()... 在 N1 上执行,而 UA 已在 N2 上创建。在这些情况下调用 ping(...) 时,大多数情况下它在 Horde 注册表中找不到 UA。我做了一些实验,发现 UA 在大约 150 毫秒到 350 毫秒(平均 250 毫秒)后可用(在部落中)。

显然,Horde 机器 (deltacrdt) 需要时间将数据分发到集群的所有节点上。该分派在后台异步完成。

  1. 对于在具有 12 个内核且注册表为空的一台物理服务器(预计无网络延迟)上运行的应用程序来说,平均 250 毫秒似乎有点长。这是正常延迟还是我们应该多挖掘一点看看是否有问题?

  2. 我原以为 Horde.DynamicSupervisor.start_child(...) 是同步的,也就是说它只在所有调度完成后返回,因为它应该具有与标准 DynamicSupervisor 相同的行为 。这个假设是否正确,有没有办法进行同步调用

  3. 我最后实现了下面的函数 get_pid(id,...),我调用函数获取 UA 的 pid。这真的是一个好的解决方案吗?

    def get_pid(_id,_wait,waited,max_wait) when waited > max_wait,do: :error
    
    def get_pid(id,wait,max_wait) do
      Logger.debug("get_pid(#{id},wait=#{wait},waited=#{waited},max_wait=#{max_wait})")
      case Horde.Registry.lookup(via_tuple(id)) do  [
        {pid,_}] ->
          Logger.debug("\t-> got pid=#{inspect pid}")
          pid
        [] ->
          Logger.debug("\t-> got []. Going for a nap for #{wait}ms")
          Process.sleep(wait)
          get_pid(id,wait*2,waited+wait,max_wait)
      end
    end
    

解决方法

我实际上在 github 开了一个案例,并得到了作者的确认,这是正常行为。通过传递 sync_interval 选项 (...),可以使用 (...) :delta_crdt_options 调整 250 毫秒左右的延迟。