[英]What's the benefit of registering name using {:via, module, term} in GenServer.start_link/3?
In GenServer.start_link/3
I can register a name locally using an atom for a process like this:在
GenServer.start_link/3
我可以使用原子在本地注册一个名称,用于这样的过程:
defmodule Worker do
use GenServer
def start_link do
GenServer.start_link(__MODULE__, nil, name: :worker)
end
end
Then I can start a supervisor to supervise this process:然后我可以启动一个主管来监督这个过程:
defmodule Boss do
use Supervisor
def init(_) do
children = [worker(Worker, [])]
supervise(children, strategy: :one_for_one)
end
end
Now I want to make the supervisor to supervise 3 Worker
processes, so I need to give those 3 processes unique names, so that when supervisor restarts the process it will always use the same symbolic name.现在我想让监督者监督 3 个
Worker
进程,所以我需要给这 3 个进程赋予唯一的名称,以便监督者重新启动进程时,它将始终使用相同的符号名称。
I can simply use string interpolation for the unique Worker
process name like this:我可以简单地对唯一的
Worker
进程名称使用字符串插值,如下所示:
defmodule Worker do
use GenServer
def start_link(id) do
GenServer.start_link(__MODULE__, nil, name: :"worker_#{id}")
end
end
Then supervise 3 processes like this:然后像这样监督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
It works like expected.它像预期的那样工作。
In the doc for GenServer under " Name registration " section , it says you can use {:via, module, term}
to register a name as well.在“名称注册”部分下的 GenServer 文档中,它说您也可以使用
{:via, module, term}
来注册名称。
{:via, module, term}
- the GenServer is registered with the given mechanism and name.{:via, module, term}
- GenServer 使用给定的机制和名称注册。 The:via
option expects a module that exportsregister_name/2
,unregister_name/1
,whereis_name/1
, andsend/2
.:via
选项需要一个导出register_name/2
、unregister_name/1
、whereis_name/1
和send/2
。 One such example is the:global
module which uses these functions for keeping the list of names of processes and their associated PIDs that are available globally for a network of Elixir nodes.一个这样的例子是
:global
模块,它使用这些函数来保存进程的名称列表及其相关联的 PID,这些列表在 Elixir 节点网络全局可用。 Elixir also ships with a local, decentralized and scalable registry called Registry for locally storing names that are generated dynamically.Elixir 还附带一个名为 Registry 的本地、分散且可扩展的注册表,用于在本地存储动态生成的名称。
However, in order to use :via
option you have to implement a module that exports register_name/2
, unregister_name/1
, whereis_name/1
and send/2
, which seems pretty cumbersome comparing to simply use string interpolation technique as shown above.但是,为了使用
:via
选项,您必须实现一个导出register_name/2
、 unregister_name/1
、 whereis_name/1
和send/2
,与简单地使用如上所示的字符串插值技术相比,这似乎非常麻烦。
So my question is:所以我的问题是:
:via
option to register name?:via
选项注册名称的实用示例?tl;dr - :via
is there to allow you to use non-standard process registration libraries. tl;dr -
:via
是否允许您使用非标准进程注册库。 They must conform to an interface (much like implementing an interface in Java), and may provide extra functionality.它们必须符合接口(很像在 Java 中实现接口),并且可以提供额外的功能。
The main example is when you want to use a non-standard name registration library.主要示例是当您要使用非标准名称注册库时。 Take for example the gproc library .
以gproc 库为例。 It follows the interface requirements to use
:via
, so minimal intrusion is required into your application code.它遵循使用
:via
的接口要求,因此对您的应用程序代码的侵入最少。 In addition, it provides several advantages over the standard name registration system:此外,与标准名称注册系统相比,它具有以下几个优点:
Elixir's Registry
module is another example of one which requires a via tuple. Elixir 的
Registry
模块是另一个需要 via 元组的示例。
One scenario is that when you want to assign names to your workers dynamically (maybe they are supervised by a DynamicSupervisor
or a simple_one_for_one
supervisor and are dynamically spawned over time).一种情况是,当您想动态地为您的工作人员分配名称时(也许他们由
DynamicSupervisor
或simple_one_for_one
主管监督,并且随着时间的推移动态产生)。
Because atoms are never garbage collected , you can't use them as the names of those workers, otherwise you'll have to deal with memory leak.因为原子永远不会被垃圾收集,所以你不能将它们用作那些工人的名字,否则你将不得不处理内存泄漏。
For some reason, registering names on :global
module makes me feel uncomfortable because global states are often considered evil, especially in a highly concurrent environment (which is why you choose Erlang/Elixir).出于某种原因,在
:global
模块上注册名称让我感到不舒服,因为全局状态通常被认为是邪恶的,尤其是在高度并发的环境中(这就是您选择 Erlang/Elixir 的原因)。
So in this case, it's better to register the names in a "namespace".所以在这种情况下,最好在“命名空间”中注册名称。 The
{:via, module, term}
variation shines in such cases. {:via, module, term}
变体在这种情况下大放异彩。 The module
serves as a namespace, and term
can be anything (usually a string because it's easy to understand, and is garbage collected). module
用作命名空间, term
可以是任何东西(通常是字符串,因为它易于理解,并且会被垃圾收集)。
BTW, if you don't want to implement the registry yourself, there is already a module Registry
just for this purpose.顺便说一句,如果您不想自己实现注册表,那么已经有一个模块
Registry
专门用于此目的。 You just need to give the Registry process a name and supervise it.您只需要为 Registry 进程命名并对其进行监督。
Using :via
tuples allows you to nicely encapsulate the the alias handling, and gives you a fixed point where you can discover processes.使用
:via
元组可以很好地封装别名处理,并为您提供一个可以发现进程的固定点。 In addition, the :via
tuples can be arbitrarily complex, eg a tuple such as {:my_worker, 1}
which will usually be nicer to work with than messing around with string manipulation.此外,
:via
元组可以是任意复杂的,例如像{:my_worker, 1}
这样的元组通常比处理字符串操作更好用。
(Note that I'm learning Elixir, so don't take my word for it. In addition, there could be stronger/better arguments for :via
tuples.) (请注意,我正在学习 Elixir,所以不要相信我的话。此外,
:via
tuples 可能有更强/更好的论据。)
I had this same question.我有同样的问题。 I can think of 2 reasons why you'd want to use the
Registry
module instead of generating a dynamic name such as :"worker:#{id}"
我可以想到两个原因,为什么您要使用
Registry
模块而不是生成动态名称,例如:"worker:#{id}"
So it seems that the registry links to the processes that are in it, and will remove the entries if those processes fail:因此,注册表似乎链接到其中的进程,如果这些进程失败,它将删除条目:
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.