我目前正在以合理的方式学习Erlang,但对主管的gen_server有疑问。如果gen_server进程崩溃并由主管重新启动,它将收到一个新的pid。现在,如果我希望其他进程通过Pid引用该进程,该怎么办?在这些流程中“更新”Pid有哪些好的惯用方法?
作为一个实际应用的练习,我正在编写一个锁定服务器,客户端可以使用任意键请求锁定。理想情况下,我希望有一个单独的进程来处理特定锁的锁定和释放,这个想法是我可以使用gen_server中的timeout参数来终止进程,如果在N个时间之后没有人请求它,那么只有当前相关的锁将留在内存中。现在,我有一个目录进程,它将锁名称映射到锁进程。当锁定进程终止时,它会从目录中删除锁定。
我关心的是如何处理客户端在锁定进程正在终止时请求锁定的情况。它还没有关闭,所以嗅到pid活着是行不通的。锁定进程尚未到达从目录中删除它的子句。
有没有更好的方法来解决这个问题?
修改的
目前有两个gen_servers:'目录',它维护一个来自LockName的ETS表 - >锁定进程和使用start_child动态添加到监督树的“锁定服务器”。理想情况下,我希望每个锁服务器直接处理与客户端的通话,但我担心当进程处于崩溃中时,获取/释放获取/释放请求的场景(因此将不会响应)消息)。
从{local}或{global}开始将无效,因为可能有N个。
答案 0 :(得分:5)
诀窍是命名进程,不要通过它的pid引用它。您通常有3个可行的选项,
使用注册名称。这就是andreypopp所暗示的。您可以通过注册名称来引用服务器。 本地注册名称必须是原子,这可能会对您有所限制。 全球注册名称没有此限制,您可以注册任何条款。
主管知道Pid。问一下。您必须将主管Pid传递给流程。
或者,使用gproc应用程序(存在于http://github.com上)。它允许您创建一个通用的流程注册表 - 您可以通过ETS完成该操作,但是窃取好的代码而不是自己实现。
如果所有进程都是同一监督树的一部分,则pid可用。因此,其中一人死亡意味着其他人的死亡。因此,Pids回收无关紧要。
答案 1 :(得分:4)
请勿通过pid参考gen_server进程。
您应该通过gen_server:call/2
或gen_server:call/3
函数为gen_server提供API。他们接受ServerRef
作为第一个参数,可以是Name | {Name,Node} | {global,GlobalName} | pid()
。所以,你的API看起来像:
lock(Key) ->
gen_server:call(?MODULE, {lock, Key}).
release(Key) ->
gen_server:call(?MODULE, {release, Key}).
请注意,此API与gen_server在同一模块中定义,我假设您使用以下内容启动服务器:
gen_server:start_link({local, ?MODULE}, ?MODULE, [], [])
因此,您的API方法不能通过pid查找服务器,而是通过服务器名称查找服务器名称,该名称等于?MODULE
。
有关详细信息,请参阅gen_server
documentation。
答案 2 :(得分:1)
您可以使用“erlang:monitor / demonitor”API完全避免使用“lock_server”进程。
当客户端请求锁定时,您发出锁定..并在客户端上执行erlang:monitor ..这将返回一个Monitor Reference ..然后您可以将此Reference与您的锁存储在一起。这是因为当客户端死亡时会通知您的目录服务器..您可以在客户端实现TIMEOUT事项。
以下是我最近编写的代码片段。 https://github.com/xslogic/phoebus/blob/master/src/table_manager.erl
基本上,table_manager是一个向客户端发出特定表资源锁定的进程。如果客户端死掉,表将返回到池中。