![](/img/trans.png)
[英]Trouble registering a GenServer with a via tuple. ** (UndefinedFunctionError) function PokerServer.whereis_name/1 is undefined or private)
[英]What's the benefit of registering name using {:via, module, term} in GenServer.start_link/3?
在GenServer.start_link/3
我可以使用原子在本地注冊一個名稱,用於這樣的過程:
defmodule Worker do
use GenServer
def start_link do
GenServer.start_link(__MODULE__, nil, name: :worker)
end
end
然后我可以啟動一個主管來監督這個過程:
defmodule Boss do
use Supervisor
def init(_) do
children = [worker(Worker, [])]
supervise(children, strategy: :one_for_one)
end
end
現在我想讓監督者監督 3 個Worker
進程,所以我需要給這 3 個進程賦予唯一的名稱,以便監督者重新啟動進程時,它將始終使用相同的符號名稱。
我可以簡單地對唯一的Worker
進程名稱使用字符串插值,如下所示:
defmodule Worker do
use GenServer
def start_link(id) do
GenServer.start_link(__MODULE__, nil, name: :"worker_#{id}")
end
end
然后像這樣監督3個進程:
defmodule Boss do
use Supervisor
def init(_) do
children = for id <- 1..3 do
worker(Worker, [id], id: id)
end
supervise(children, strategy: :one_for_one)
end
end
它像預期的那樣工作。
在“名稱注冊”部分下的 GenServer 文檔中,它說您也可以使用{:via, module, term}
來注冊名稱。
{:via, module, term}
- GenServer 使用給定的機制和名稱注冊。:via
選項需要一個導出register_name/2
、unregister_name/1
、whereis_name/1
和send/2
。 一個這樣的例子是:global
模塊,它使用這些函數來保存進程的名稱列表及其相關聯的 PID,這些列表在 Elixir 節點網絡全局可用。 Elixir 還附帶一個名為 Registry 的本地、分散且可擴展的注冊表,用於在本地存儲動態生成的名稱。
但是,為了使用:via
選項,您必須實現一個導出register_name/2
、 unregister_name/1
、 whereis_name/1
和send/2
,與簡單地使用如上所示的字符串插值技術相比,這似乎非常麻煩。
所以我的問題是:
:via
選項注冊名稱的實用示例? tl;dr - :via
是否允許您使用非標准進程注冊庫。 它們必須符合接口(很像在 Java 中實現接口),並且可以提供額外的功能。
主要示例是當您要使用非標准名稱注冊庫時。 以gproc 庫為例。 它遵循使用:via
的接口要求,因此對您的應用程序代碼的侵入最少。 此外,與標准名稱注冊系統相比,它具有以下幾個優點:
Elixir 的Registry
模塊是另一個需要 via 元組的示例。
一種情況是,當您想動態地為您的工作人員分配名稱時(也許他們由DynamicSupervisor
或simple_one_for_one
主管監督,並且隨着時間的推移動態產生)。
因為原子永遠不會被垃圾收集,所以你不能將它們用作那些工人的名字,否則你將不得不處理內存泄漏。
出於某種原因,在:global
模塊上注冊名稱讓我感到不舒服,因為全局狀態通常被認為是邪惡的,尤其是在高度並發的環境中(這就是您選擇 Erlang/Elixir 的原因)。
所以在這種情況下,最好在“命名空間”中注冊名稱。 {:via, module, term}
變體在這種情況下大放異彩。 module
用作命名空間, term
可以是任何東西(通常是字符串,因為它易於理解,並且會被垃圾收集)。
順便說一句,如果您不想自己實現注冊表,那么已經有一個模塊Registry
專門用於此目的。 您只需要為 Registry 進程命名並對其進行監督。
使用:via
元組可以很好地封裝別名處理,並為您提供一個可以發現進程的固定點。 此外, :via
元組可以是任意復雜的,例如像{:my_worker, 1}
這樣的元組通常比處理字符串操作更好用。
(請注意,我正在學習 Elixir,所以不要相信我的話。此外, :via
tuples 可能有更強/更好的論據。)
我有同樣的問題。 我可以想到兩個原因,為什么您要使用Registry
模塊而不是生成動態名稱,例如:"worker:#{id}"
因此,注冊表似乎鏈接到其中的進程,如果這些進程失敗,它將刪除條目:
iex(6)> {:ok, _} = Registry.start_link(keys: :unique, name: Registry.ViaTest2)
{:ok, #PID<0.445.0>}
iex(7)> name = {:via, Registry, {Registry.ViaTest2, "agent"}}
{:via, Registry, {Registry.ViaTest2, "agent"}}
iex(8)> {:ok, agent} = Agent.start(fn -> 0 end, name: name)
{:ok, #PID<0.449.0>}
iex(9)> Registry.lookup(Registry.ViaTest2, "agent")
[{#PID<0.449.0>, nil}]
iex(10)> Process.alive?(agent)
true
iex(11)> Process.exit(agent, :kill)
true
iex(12)> Process.alive?(agent)
false
iex(13)> Registry.lookup(Registry.ViaTest2, "agent")
[]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.