Elixir 什么';在GenServer.start_link/3中使用{:via,module,term}注册名称的好处是什么?
在Elixir 什么';在GenServer.start_link/3中使用{:via,module,term}注册名称的好处是什么?,elixir,otp,erlang-supervisor,gen-server,Elixir,Otp,Erlang Supervisor,Gen Server,在GenServer.start\u link/3中,我可以使用atom在本地注册一个名称,用于如下过程: defmodule Worker do use GenServer def start_link do GenServer.start_link(__MODULE__, nil, name: :worker) end end defmodule Worker do use GenServer def start_link(id) do GenSer
GenServer.start\u link/3
中,我可以使用atom在本地注册一个名称,用于如下过程:
defmodule Worker do
use GenServer
def start_link do
GenServer.start_link(__MODULE__, nil, name: :worker)
end
end
defmodule Worker do
use GenServer
def start_link(id) do
GenServer.start_link(__MODULE__, nil, name: :"worker_#{id}")
end
end
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
然后我可以启动一个主管来监督这个过程:
defmodule Boss do
use Supervisor
def init(_) do
children = [worker(Worker, [])]
supervise(children, strategy: :one_for_one)
end
end
现在我想让主管管理3个工作进程
进程,因此我需要为这3个进程指定唯一的名称,以便主管重新启动进程时始终使用相同的符号名称
我可以简单地对唯一的Worker
进程名使用字符串插值,如下所示:
defmodule Worker do
use GenServer
def start_link do
GenServer.start_link(__MODULE__, nil, name: :worker)
end
end
defmodule Worker do
use GenServer
def start_link(id) do
GenServer.start_link(__MODULE__, nil, name: :"worker_#{id}")
end
end
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
然后监督以下3个过程:
defmodule Worker do
use GenServer
def start_link do
GenServer.start_link(__MODULE__, nil, name: :worker)
end
end
defmodule Worker do
use GenServer
def start_link(id) do
GenServer.start_link(__MODULE__, nil, name: :"worker_#{id}")
end
end
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
defmodule Boss do
使用主管
def init(u)do
children=for id使用:通过
元组可以很好地封装别名处理,并为您提供一个可以发现进程的固定点。此外,:via
元组可以是任意复杂的,例如像{:my_worker,1}
这样的元组通常比处理字符串操作更好
(请注意,我正在学习长生不老药,所以不要相信我的话。此外,对于:通过元组,可能会有更有力/更好的论据。)tl;dr-:via
允许您使用非标准流程注册库。它们必须符合接口(很像在Java中实现接口),并且可能提供额外的功能
主要示例是当您希望使用非标准名称注册库时。以美国为例。它遵循接口要求使用:via
,因此对应用程序代码的入侵要求最小。此外,与标准名称注册系统相比,它还提供了一些优势:
使用任何术语作为流程别名
在多个别名下注册进程
非唯一属性可以由多个进程同时注册;和匹配规范接口,以便在字典上进行高效查询
等待注册,让我们等待进程注册自己
以原子方式将注册的名称和属性传递给另一个进程
计数器和聚合计数器,它们自动维护具有给定名称的所有计数器的总数
全局注册表,所有上述功能均应用于节点网络
Elixir的注册表
模块是另一个需要via元组的示例。一个场景是,当您想动态地为员工分配姓名时(可能他们由动态监督人
或简单的一对一监督人
监督人监督,并随时间动态生成)
因为原子从来都不是垃圾收集的,所以你不能用它们作为那些工作者的名字,否则你将不得不处理内存泄漏问题
出于某种原因,在:global
模块上注册名称让我感到不舒服,因为全局状态通常被认为是邪恶的,特别是在高度并发的环境中(这就是为什么选择Erlang/Elixir)
因此,在本例中,最好在“名称空间”中注册名称。在这种情况下,{:via,module,term}
变体会发光。模块
用作名称空间,术语
可以是任何内容(通常是字符串,因为它易于理解,并且是垃圾收集的)
顺便说一句,如果您不想自己实现注册表,那么已经有了一个用于此目的的模块。您只需要给注册表进程一个名称并对其进行监督。我也有同样的问题。我可以想出两个原因来解释为什么您希望使用注册表
模块而不是生成动态名称,例如:“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")
[]
iex(6)>{:ok,{}=Registry.start\u链接(key::unique,name:Registry.ViaTest2)
{:好的,#PID}
iex(7)>name={:via,Registry,{Registry.ViaTest2,“agent”}
{:via,注册表,{Registry.ViaTest2,“代理”}
iex(8)>{:ok,agent}=agent.start(fn->0 end,name:name)
{:好的,#PID}
iex(9)>Registry.lookup(Registry.ViaTest2,“代理”)
[{PID,nil}]
iex(10)>进程。活动?(代理)
真的
iex(11)>进程退出(代理:终止)
真的
iex(12)>进程。活动?(代理)
假的
iex(13)>Registry.lookup(Registry.ViaTest2,“代理”)
[]