问题描述
我使用 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) 需要时间将数据分发到集群的所有节点上。该分派在后台异步完成。
-
对于在具有 12 个内核且注册表为空的一台物理服务器(预计无网络延迟)上运行的应用程序来说,平均 250 毫秒似乎有点长。这是正常延迟还是我们应该多挖掘一点看看是否有问题?
-
我原以为
Horde.DynamicSupervisor.start_child(...)
是同步的,也就是说它只在所有调度完成后返回,因为它应该具有与标准DynamicSupervisor
相同的行为 。这个假设是否正确,有没有办法进行同步调用? -
我最后实现了下面的函数
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 毫秒左右的延迟。