В чем преимущество регистрации имени с помощью {:via, module, term} в 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
процессы, поэтому мне нужно дать этим трем процессам уникальные имена, чтобы при повторном запуске процесса супервизором он всегда использовал одно и то же символическое имя.
Я могу просто использовать интерполяцию строк для уникального 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 ожидает модуль, который экспортирует имя_регистра /2, имя_регистра /1, имя_сайта / 1 и отправить /2. Одним из таких примеров является модуль:global, который использует эти функции для хранения списка имен процессов и связанных с ними pid, которые доступны глобально для сети узлов Erlang.
Однако для того, чтобы использовать :via
вариант вы должны реализовать модуль, который экспортирует register_name/2
, unregister_name/1
, whereis_name/1
а также send/2
Это выглядит довольно громоздко по сравнению с простым использованием метода интерполяции строк, как показано выше.
Итак, мой вопрос:
- В чем преимущество регистрации имени с помощью {:via, module, term} по сравнению с простой интерполяцией строк?
- Есть ли прагматичный пример использования
:via
возможность зарегистрировать имя?
3 ответа
Основной пример - когда вы хотите использовать нестандартную библиотеку регистрации имен. Взять, к примеру, библиотеку gproc. Это соответствует требованиям интерфейса для использования :via
, поэтому минимальное вторжение требуется в код вашего приложения. Кроме того, он предоставляет несколько преимуществ по сравнению со стандартной системой регистрации имен:
- Используйте любой термин как псевдоним процесса
- Зарегистрировать процесс под несколькими псевдонимами
- Неуникальные свойства могут быть зарегистрированы одновременно многими процессами QLC и соответствовать интерфейсу спецификации для эффективных запросов в словаре.
- Ждите регистрации, подождите, пока процесс не зарегистрируется
- Атомно отдавать зарегистрированные имена и свойства другому процессу
- Счетчики и агрегированные счетчики, которые автоматически поддерживают общее количество всех счетчиков с данным именем
- Глобальный реестр, со всеми вышеперечисленными функциями, применяемыми к сети узлов
Эликсир-х Registry
Модуль является еще одним примером того, который требует сквозной кортеж.
тл; др - :via
Это позволяет вам использовать нестандартные библиотеки регистрации процессов. Они должны соответствовать интерфейсу (во многом как реализация интерфейса в Java) и могут предоставлять дополнительные функциональные возможности.
Один из сценариев состоит в том, что, когда вы хотите назначить имена своим сотрудникам динамически (возможно, они контролируются simple_one_for_one
супервизор и динамически порождаются с течением времени).
Поскольку атомы никогда не собираются мусором, вы не можете использовать их в качестве имен этих рабочих, иначе вам придется столкнуться с утечкой памяти.
По какой-то причине регистрация имен на :global
модуль заставляет меня чувствовать себя некомфортно, потому что глобальные состояния часто считаются злом, особенно в условиях высокой конкуренции (именно поэтому вы выбираете Erlang/Elixir).
Так что в этом случае лучше зарегистрировать имена в "пространстве имен". {:via, module, term}
вариация сияет в таких случаях. module
служит пространством имен, и term
может быть чем угодно (обычно строкой, потому что это легко понять и собирать мусор).
Кстати, если вы не хотите внедрять реестр самостоятельно, модуль уже есть Registry
только для этого. Вам просто нужно дать процессу реестра имя и контролировать его.
С помощью :via
кортежи позволяют вам красиво инкапсулировать обработку псевдонимов и дают вам фиксированную точку, где вы можете обнаружить процессы. В дополнение :via
кортежи могут быть произвольно сложными, например кортеж, такой как {:my_worker, 1}
с которым обычно будет приятнее работать, чем возиться с манипуляциями со строками.
(Обратите внимание, что я изучаю эликсир, так что не поверьте мне на слово. Кроме того, могут быть более сильные / лучшие аргументы для :via
кортежи.)
У меня был такой же вопрос. Я могу придумать 2 причины, по которым вы захотите использовать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")
[]