You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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协议要求的所有回调,不需要自己写注册逻辑。

  1. 首先在应用的监督树中启动Registry实例,在application.ex的children列表中添加配置:
{Registry, keys: :unique, name: PokerRegistry}
  1. 修改你的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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 02:18:01