使用via元组注册GenServer时触发whereis_name/1未定义错误
问题原因
{:via, 模块, 标识} 是Elixir的通用进程注册协议,要求你传入的第二个位置的模块必须实现4个约定的回调函数:register_name/2、unregister_name/1、whereis_name/1、send/2,用来完成进程的注册、注销、查询、发消息逻辑。你当前的代码直接把业务模块PokerServer作为注册模块传入了via元组,而这个模块没有实现上述回调,所以触发了找不到whereis_name/1函数的报错。
解决方案
通常有两种可选方案,优先推荐第一种:
方案1:使用官方自带的Registry实现进程注册
这是最常用的生产级方案,Registry已经内置实现了via协议要求的所有回调,不需要自己写注册逻辑。
- 首先在应用的监督树中启动Registry实例,在
application.ex的children列表中添加配置:
{Registry, keys: :unique, name: PokerRegistry}
- 修改你的
start/1函数中的via元组配置即可:
def start(id) do GenServer.start(__MODULE__, id, name: {:via, Registry, {PokerRegistry, id}}) end
方案2:自定义实现via协议回调
如果你有特殊的注册逻辑需求,也可以自己在PokerServer中补全四个回调函数,示例如下(基于ETS存储进程映射):
defmodule PokerServer do use GenServer # 首次使用前调用一次初始化ETS存储表,可放在应用启动逻辑中 def init_registry do :ets.new(__MODULE__, [:named_table, :public, :set]) end # 实现via协议要求的四个回调 def register_name(id, pid) do case :ets.insert_new(__MODULE__, {id, pid}) do true -> :yes false -> :no end end def unregister_name(id) do :ets.delete(__MODULE__, id) end def whereis_name(id) do case :ets.lookup(__MODULE__, id) do [{^id, pid}] -> pid [] -> :undefined end end def send(id, msg) do case whereis_name(id) do :undefined -> exit({:badarg, {id, msg}}) pid -> Kernel.send(pid, msg) end end # 原有业务代码保持不变 def start(id) do GenServer.start(__MODULE__, id, name: {:via, __MODULE__, id}) end @impl GenServer def init(id) do {:ok, id} end end
注意自定义实现需要额外处理进程异常退出后的映射清理逻辑,没有特殊需求不推荐自己实现。
内容的提问来源于stack exchange,提问作者David B.
相关产品推荐
相关产品推荐

