В чем преимущество регистрации имени с помощью {: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Это выглядит довольно громоздко по сравнению с простым использованием метода интерполяции строк, как показано выше.

Итак, мой вопрос:

  1. В чем преимущество регистрации имени с помощью {:via, module, term} по сравнению с простой интерполяцией строк?
  2. Есть ли прагматичный пример использования :via возможность зарегистрировать имя?

3 ответа

Основной пример - когда вы хотите использовать нестандартную библиотеку регистрации имен. Взять, к примеру, библиотеку gproc. Это соответствует требованиям интерфейса для использования :via, поэтому минимальное вторжение требуется в код вашего приложения. Кроме того, он предоставляет несколько преимуществ по сравнению со стандартной системой регистрации имен:

  1. Используйте любой термин как псевдоним процесса
  2. Зарегистрировать процесс под несколькими псевдонимами
  3. Неуникальные свойства могут быть зарегистрированы одновременно многими процессами QLC и соответствовать интерфейсу спецификации для эффективных запросов в словаре.
  4. Ждите регистрации, подождите, пока процесс не зарегистрируется
  5. Атомно отдавать зарегистрированные имена и свойства другому процессу
  6. Счетчики и агрегированные счетчики, которые автоматически поддерживают общее количество всех счетчиков с данным именем
  7. Глобальный реестр, со всеми вышеперечисленными функциями, применяемыми к сети узлов

Эликсир-х 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")
[]
Другие вопросы по тегам