Регистр
Локальное, децентрализованное и масштабируемое хранилище процессов по принципу ключ-значение.
Позволяет разработчикам искать один или несколько процессов по заданному ключу. Если регистр имеет :unique ключи, ключ указывает на 0 или 1 процесс. Если регистр допускает :duplicate ключи, один ключ может указывать на любое количество процессов. В обоих случаях разные ключи могут идентифицировать один и тот же процесс.
Каждая запись в реестре связана с процессом, который зарегистрировал ключ. Если процесс завершается, ключи, связанные с этим процессом, автоматически удаляются. Все сравнения ключей в реестре выполняются с помощью операции соответствия (===/2).
Регистр может использоваться для различных целей, таких как поиск по имени (используя опцию :via), хранение свойств, настраиваемые правила распределения или реализация системы публикации-подписки. Ниже мы рассмотрим некоторые из этих вариантов использования.
Регистр также может быть прозрачно разбит на разделы, что обеспечивает более масштабируемое поведение для работы с реестрами в высококонкурентных средах с тысячами или миллионами записей.
Использование в :via
После запуска реестра с заданным именем с помощью Registry.start_link/1, его можно использовать для регистрации и доступа к именованным процессам, используя кортеж {:via, Registry, {registry, key}}:
{:ok, _} = Registry.start_link(keys: :unique, name: Registry.ViaTest)
name = {:via, Registry, {Registry.ViaTest, "agent"}}
{:ok, _} = Agent.start_link(fn -> 0 end, name: name)
Agent.get(name, & &1)
#=> 0
Agent.update(name, &(&1 + 1))
Agent.get(name, & &1)
#=> 1 В предыдущем примере нас не интересовало связывание значения с процессом:
Registry.lookup(Registry.ViaTest, "agent")
#=> [{self(), nil}] Однако в некоторых случаях может потребоваться связать значение с процессом, используя альтернативный кортеж {:via, Registry, {registry, key, value}}:
{:ok, _} = Registry.start_link(keys: :unique, name: Registry.ViaTest)
name = {:via, Registry, {Registry.ViaTest, "agent", :hello}}
{:ok, _} = Agent.start_link(fn -> 0 end, name: name)
Registry.lookup(Registry.ViaTest, "agent")
#=> [{self(), :hello}] До этого момента мы запускали Registry с помощью start_link/1. Обычно же реестр запускается как часть дерева управления:
{Registry, keys: :unique, name: Registry.ViaTest} Только реестры с уникальными ключами могут быть использованы в :via. Если имя уже занято, функция с учетом конкретного случая start_link (Agent.start_link/2 в примере выше) вернет {:error, {:already_started, current_pid}}.
Использование в качестве диспетчера
Registry имеет механизм распределения, позволяющий разработчикам реализовывать пользовательскую логику распределения, вызываемую вызывающей стороной. Например, предположим, что у нас есть дублирующий регистр, запущенный следующим образом:
{:ok, _} = Registry.start_link(keys: :duplicate, name: Registry.DispatcherTest) Вызвав register/3, разные процессы могут регистрироваться под заданным ключом и связывать любое значение под этим ключом. В данном случае давайте зарегистрируем текущий процесс под ключом "hello" и прикрепим к нему кортеж {IO, :inspect}:
{:ok, _} = Registry.register(Registry.DispatcherTest, "hello", {IO, :inspect}) Теперь сущность, заинтересованная в отправке событий для заданного ключа, может вызвать dispatch/3, передав ключ и обратный вызов. Этот обратный вызов будет вызван со списком всех значений, зарегистрированных под запрошенным ключом, вместе с PID процесса, который зарегистрировал каждое значение, в виде кортежей {pid, value}. В нашем примере value будет кортежем {module, function} в приведенном выше коде:
Registry.dispatch(Registry.DispatcherTest, "hello", fn entries ->
for {pid, {module, function}} <- entries, do: apply(module, function, [pid])
end)
# Prints #PID<...> where the PID is for the process that called register/3 above
#=> :ok Распределение происходит в процессе, вызывающем dispatch/3, либо последовательно, либо параллельно в случае нескольких разделов (через запущенные задачи). Зарегистрированные процессы не участвуют в распределении, если их участие не выполняется явно (например, путем отправки им сообщения в обратном вызове).
Кроме того, если при распределении произойдет сбой из-за неправильной регистрации, распределение всегда завершится ошибкой, и зарегистрированный процесс не будет уведомлен. Поэтому давайте убедимся, что мы по крайней мере обернем и сообщим об этих ошибках:
require Logger
Registry.dispatch(Registry.DispatcherTest, "hello", fn entries ->
for {pid, {module, function}} <- entries do
try do
apply(module, function, [pid])
catch
kind, reason ->
formatted = Exception.format(kind, reason, __STACKTRACE__)
Logger.error("Registry.dispatch/3 failed with #{formatted}")
end
end
end)
# Prints #PID<...>
#=> :ok Вы также можете заменить всю систему apply явным отправлением сообщений. Это пример, который мы рассмотрим далее.
Использование в качестве Pub/Sub
Регистры также могут быть использованы для реализации локальной, не распределенной, масштабируемой системы публикации-подписки, используя функцию dispatch/3, аналогично предыдущему разделу: однако в данном случае мы будем отправлять сообщения каждому связанному процессу, а не вызывать заданную модульную функцию.
В этом примере мы также установим количество разделов в количество запущенных планировщиков, что сделает регистр более производительным в высококонкурентных средах:
{:ok, _} =
Registry.start_link(
keys: :duplicate,
name: Registry.PubSubTest,
partitions: System.schedulers_online()
)
{:ok, _} = Registry.register(Registry.PubSubTest, "hello", [])
Registry.dispatch(Registry.PubSubTest, "hello", fn entries ->
for {pid, _} <- entries, do: send(pid, {:broadcast, "world"})
end)
#=> :ok В приведенном выше примере сообщение {:broadcast, "world"} было разослано всем процессам, зарегистрированным под «темой» (или «ключом», как мы его называли до сих пор) "hello".
Третий аргумент, переданный в register/3, — это значение, связанное с текущим процессом. Хотя в предыдущем разделе мы использовали его при распределении, в этом конкретном примере мы в нем не заинтересованы, поэтому мы установили его в пустой список. При необходимости вы можете сохранить более значимое значение.
Регистрации
Поиск, распределение и регистрация эффективны и мгновенны за счет задержки отписки. Например, если процесс завершается, его ключи автоматически удаляются из реестра, но изменение может не распространиться сразу. Это означает, что некоторые операции могут возвращать процессы, которые уже завершены. Когда такое может произойти, это будет явно указано в документации к функции.
Однако имейте в виду, что такие случаи обычно не являются проблемой. В конце концов, процесс, на который ссылается PID, может завершиться в любое время, в том числе между получением значения из реестра и отправкой ему сообщения. Многие части стандартной библиотеки разработаны для работы с этим, например, Process.monitor/1, который будет передавать сообщение :DOWN немедленно, если отслеживаемый процесс уже завершен, и Kernel.send/2, которая действует как нет-операция для завершенных процессов.
ETS
Обратите внимание, что регистр использует одну таблицу ETS плюс две таблицы ETS на раздел.
Краткое описание
Типы
- body()
Шаблон, используемый для представления части формата вывода в спецификации соответствия.
- guard()
Условие, которое должно быть выполнено при сопоставлении с объектами в реестре.
- guards()
Список условий, которые должны быть выполнены при сопоставлении с объектами в реестре.
- key()
Тип ключей, разрешенных при регистрации.
- keys()
Тип реестра.
- match_pattern()
Шаблон для сопоставления с объектами в реестре.
- meta_key()
Тип ключей метаданных реестра.
- meta_value()
Тип значений метаданных реестра.
- registry()
Идентификатор реестра.
- spec()
Полная спецификация соответствия, используемая при выборе объектов в реестре.
- value()
Тип значений, разрешенных при регистрации.
Функции
- child_spec(opts)
Возвращает спецификацию для начала реестра под надзором.
- count(registry)
Возвращает количество зарегистрированных ключей в реестре. Выполняется за константное время.
- count_match(registry, key, pattern, guards \\ [])
Возвращает количество пар
{pid, value}под заданнымkeyвregistryкоторые соответствуютpattern.- dispatch(registry, key, mfa_or_fun, opts \\ [])
Вызывает обратный вызов со всеми записями под
keyв каждом разделе для заданногоregistry.- keys(registry, pid)
Возвращает известные ключи для заданного
pidвregistryв произвольном порядке.- lookup(registry, key)
Находит пару
{pid, value}для заданногоkeyвregistryв произвольном порядке.- match(registry, key, pattern, guards \\ [])
Возвращает пары
{pid, value}под заданнымkeyвregistryкоторые соответствуютpattern.- meta(registry, key)
Считывает метаданные реестра, заданные в
start_link/1.- put_meta(registry, key, value)
Сохраняет метаданные реестра.
- register(registry, key, value)
Регистрирует текущий процесс под заданным
keyвregistry.- select(registry, spec)
Выбирает ключ, идентификатор процесса и значения, зарегистрированные с помощью полных спецификаций соответствия.
- start_link(options)
Запускает реестр в качестве процесса-надзорщика.
- unregister(registry, key)
Удаляет все записи для данного
keyсвязанного с текущим процессом вregistry.- unregister_match(registry, key, pattern, guards \\ [])
Удаляет записи для данного ключа, соответствующие шаблону.
- update_value(registry, key, callback)
Обновляет значение для
keyдля текущего процесса в уникальномregistry.
Типы
body()
Specs
body() :: [atom() | tuple()]
Шаблон, используемый для представления части формата вывода в спецификации соответствия.
guard()
Specs
guard() :: {atom() | term()} Условие, которое должно быть выполнено при сопоставлении с объектами в реестре.
guards()
Specs
guards() :: [guard()] | []
Список условий, которые должны быть выполнены при сопоставлении с объектами в реестре.
key()
Specs
key() :: term()
Тип ключей, разрешенных при регистрации.
keys()
Specs
keys() :: :unique | :duplicate
Тип реестра.
match_pattern()
Specs
match_pattern() :: atom() | term()
Шаблон для сопоставления с объектами в реестре.
meta_key()
Specs
meta_key() :: atom() | tuple()
Тип ключей метаданных реестра.
meta_value()
Specs
meta_value() :: term()
Тип значений метаданных реестра.
registry()
Specs
registry() :: atom()
Идентификатор реестра.
spec()
Specs
spec() :: [{match_pattern(), guards(), body()}] Полная спецификация соответствия, используемая при выборе объектов в реестре.
value()
Specs
value() :: term()
Тип значений, разрешенных при регистрации.
Функции
child_spec(opts)
Возвращает спецификацию для запуска реестра под надзором.
См. Supervisor.
count(registry)
Спецификации
count(registry()) :: non_neg_integer()
Возвращает количество зарегистрированных ключей в реестре. Выполняется за постоянное время.
Примеры
В примере ниже мы регистрируем текущий процесс и запрашиваем количество ключей в реестре:
iex> Registry.start_link(keys: :unique, name: Registry.UniqueCountTest)
iex> Registry.count(Registry.UniqueCountTest)
0
iex> {:ok, _} = Registry.register(Registry.UniqueCountTest, "hello", :world)
iex> {:ok, _} = Registry.register(Registry.UniqueCountTest, "world", :world)
iex> Registry.count(Registry.UniqueCountTest)
2 То же самое относится к дублирующим реестрам:
iex> Registry.start_link(keys: :duplicate, name: Registry.DuplicateCountTest)
iex> Registry.count(Registry.DuplicateCountTest)
0
iex> {:ok, _} = Registry.register(Registry.DuplicateCountTest, "hello", :world)
iex> {:ok, _} = Registry.register(Registry.DuplicateCountTest, "hello", :world)
iex> Registry.count(Registry.DuplicateCountTest)
2 count_match(registry, key, pattern, guards \\ [])
Спецификации
count_match(registry(), key(), match_pattern(), guards()) :: non_neg_integer()
Возвращает количество пар {pid, value} под заданным key в registry, которые соответствуют pattern.
Шаблон должен быть атомом или кортежем, который будет соответствовать структуре значения, хранящегося в реестре. Атом :_ можно использовать для игнорирования заданного значения или элемента кортежа, а атом :"$1" можно использовать для временного присвоения части шаблона переменной для последующего сравнения.
По желанию можно передать список условий защиты для более точного соответствия. Каждое условие защиты представляет собой кортеж, описывающий проверки, которые должна пройти присвоенная часть шаблона. Например, условие защиты $1 > 1 будет выражено как кортеж {:>, :"$1", 1}. Обратите внимание, что условия защиты будут работать только для присвоенных переменных, таких как :"$1", :"$2" и т. д. Избегайте использования специальных переменных сопоставления :"$_" и :"$$", так как это может привести к неожиданному результату.
Ноль будет возвращен, если совпадений нет.
Для уникальных реестров необходимо выполнить поиск в одном разделе. Для дублирующих реестров необходимо выполнить поиск во всех разделах.
Примеры
В примере ниже мы регистрируем текущий процесс под одним и тем же ключом в дублирующем реестре, но с различными значениями:
iex> Registry.start_link(keys: :duplicate, name: Registry.CountMatchTest)
iex> {:ok, _} = Registry.register(Registry.CountMatchTest, "hello", {1, :atom, 1})
iex> {:ok, _} = Registry.register(Registry.CountMatchTest, "hello", {2, :atom, 2})
iex> Registry.count_match(Registry.CountMatchTest, "hello", {1, :_, :_})
1
iex> Registry.count_match(Registry.CountMatchTest, "hello", {2, :_, :_})
1
iex> Registry.count_match(Registry.CountMatchTest, "hello", {:_, :atom, :_})
2
iex> Registry.count_match(Registry.CountMatchTest, "hello", {:"$1", :_, :"$1"})
2
iex> Registry.count_match(Registry.CountMatchTest, "hello", {:_, :_, :"$1"}, [{:>, :"$1", 1}])
1
iex> Registry.count_match(Registry.CountMatchTest, "hello", {:_, :"$1", :_}, [{:is_atom, :"$1"}])
2 dispatch(registry, key, mfa_or_fun, opts \\ [])
Спецификации
dispatch(registry(), key(), dispatcher, keyword()) :: :ok
when dispatcher:
(entries :: [{pid(), value()}] -> term()) | {module(), atom(), [any()]} Вызывает обратный вызов со всеми записями под key в каждом разделе для заданного registry.
Список entries представляет собой непустой список кортежей из двух элементов, где первый элемент — PID, а второй — значение, связанное с PID. Если для данного ключа нет записей, обратный вызов никогда не вызывается.
Если реестр разделен, обратный вызов вызывается несколько раз на раздел. Если реестр разделен и parallel: true задано в качестве опции, диспетчеризация происходит параллельно. В обоих случаях обратный вызов вызывается только если для этого раздела есть записи.
См. документацию модуля для примеров использования функции dispatch/3 для создания пользовательской диспетчеризации или системы публикаций/подписок.
keys(registry, pid)
Спецификации
keys(registry(), pid()) :: [key()]
Возвращает известные ключи для данного pid в registry в произвольном порядке.
Если реестр уникальный, ключи уникальны. В противном случае они могут содержать дубликаты, если процесс был зарегистрирован под одним и тем же ключом несколько раз. Список будет пустым, если процесс мертв или у него нет ключей в этом реестре.
Примеры
Регистрация в уникальном реестре не позволяет нескольких записей:
iex> Registry.start_link(keys: :unique, name: Registry.UniqueKeysTest)
iex> Registry.keys(Registry.UniqueKeysTest, self())
[]
iex> {:ok, _} = Registry.register(Registry.UniqueKeysTest, "hello", :world)
iex> Registry.register(Registry.UniqueKeysTest, "hello", :later) # registry is :unique
{:error, {:already_registered, self()}}
iex> Registry.keys(Registry.UniqueKeysTest, self())
["hello"] Однако это возможно для дублирующих реестров:
iex> Registry.start_link(keys: :duplicate, name: Registry.DuplicateKeysTest)
iex> Registry.keys(Registry.DuplicateKeysTest, self())
[]
iex> {:ok, _} = Registry.register(Registry.DuplicateKeysTest, "hello", :world)
iex> {:ok, _} = Registry.register(Registry.DuplicateKeysTest, "hello", :world)
iex> Registry.keys(Registry.DuplicateKeysTest, self())
["hello", "hello"] lookup(registry, key)
Спецификации
lookup(registry(), key()) :: [{pid(), value()}] Находит пару {pid, value} для заданного key в registry в произвольном порядке.
Пустой список, если совпадений нет.
Для уникальных реестров необходим поиск в одном разделе. Для дублирующих реестров необходимо выполнить поиск во всех разделах.
Примеры
В примере ниже мы регистрируем текущий процесс и ищем его как из самого себя, так и из других процессов:
iex> Registry.start_link(keys: :unique, name: Registry.UniqueLookupTest)
iex> Registry.lookup(Registry.UniqueLookupTest, "hello")
[]
iex> {:ok, _} = Registry.register(Registry.UniqueLookupTest, "hello", :world)
iex> Registry.lookup(Registry.UniqueLookupTest, "hello")
[{self(), :world}]
iex> Task.async(fn -> Registry.lookup(Registry.UniqueLookupTest, "hello") end) |> Task.await()
[{self(), :world}] То же самое относится к дублирующим реестрам:
iex> Registry.start_link(keys: :duplicate, name: Registry.DuplicateLookupTest)
iex> Registry.lookup(Registry.DuplicateLookupTest, "hello")
[]
iex> {:ok, _} = Registry.register(Registry.DuplicateLookupTest, "hello", :world)
iex> Registry.lookup(Registry.DuplicateLookupTest, "hello")
[{self(), :world}]
iex> {:ok, _} = Registry.register(Registry.DuplicateLookupTest, "hello", :another)
iex> Enum.sort(Registry.lookup(Registry.DuplicateLookupTest, "hello"))
[{self(), :another}, {self(), :world}] match(registry, key, pattern, guards \\ [])
Спецификации
match(registry(), key(), match_pattern(), guards()) :: [{pid(), term()}] Возвращает пары {pid, value} под заданным key в registry, которые соответствуют pattern.
Шаблон должен быть атомом или кортежем, который будет соответствовать структуре значения, хранящегося в реестре. Атом :_ можно использовать для игнорирования заданного значения или элемента кортежа, а атом :"$1" можно использовать для временного присвоения части шаблона переменной для последующего сравнения.
По желанию можно передать список условий защиты для более точного соответствия. Каждое условие защиты представляет собой кортеж, описывающий проверки, которые должна пройти присвоенная часть шаблона. Например, условие защиты $1 > 1 будет выражено как кортеж {:>, :"$1", 1}. Обратите внимание, что условия защиты будут работать только для присвоенных переменных, таких как :"$1", :"$2" и т. д. Избегайте использования специальных переменных сопоставления :"$_" и :"$$", так как это может привести к неожиданному результату.
Если совпадений нет, возвращается пустой список.
Для уникальных реестров необходим поиск в одном разделе. Для дублирующих реестров необходимо выполнить поиск во всех разделах.
Примеры
В примере ниже мы регистрируем текущий процесс под одним и тем же ключом в дублирующем реестре, но с различными значениями:
iex> Registry.start_link(keys: :duplicate, name: Registry.MatchTest)
iex> {:ok, _} = Registry.register(Registry.MatchTest, "hello", {1, :atom, 1})
iex> {:ok, _} = Registry.register(Registry.MatchTest, "hello", {2, :atom, 2})
iex> Registry.match(Registry.MatchTest, "hello", {1, :_, :_})
[{self(), {1, :atom, 1}}]
iex> Registry.match(Registry.MatchTest, "hello", {2, :_, :_})
[{self(), {2, :atom, 2}}]
iex> Registry.match(Registry.MatchTest, "hello", {:_, :atom, :_}) |> Enum.sort()
[{self(), {1, :atom, 1}}, {self(), {2, :atom, 2}}]
iex> Registry.match(Registry.MatchTest, "hello", {:"$1", :_, :"$1"}) |> Enum.sort()
[{self(), {1, :atom, 1}}, {self(), {2, :atom, 2}}]
iex> guards = [{:>, :"$1", 1}]
iex> Registry.match(Registry.MatchTest, "hello", {:_, :_, :"$1"}, guards)
[{self(), {2, :atom, 2}}]
iex> guards = [{:is_atom, :"$1"}]
iex> Registry.match(Registry.MatchTest, "hello", {:_, :"$1", :_}, guards) |> Enum.sort()
[{self(), {1, :atom, 1}}, {self(), {2, :atom, 2}}] meta(registry, key)
Спецификации
meta(registry(), meta_key()) :: {:ok, meta_value()} | :error Считывает метаданные реестра, указанные в start_link/1.
В качестве ключей разрешены атомы и кортежи.
Примеры
iex> Registry.start_link(keys: :unique, name: Registry.MetaTest, meta: [custom_key: "custom_value"])
iex> Registry.meta(Registry.MetaTest, :custom_key)
{:ok, "custom_value"}
iex> Registry.meta(Registry.MetaTest, :unknown_key)
:error put_meta(registry, key, value)
Спецификации
put_meta(registry(), meta_key(), meta_value()) :: :ok
Сохраняет метаданные реестра.
В качестве ключей разрешены атомы и кортежи.
Примеры
iex> Registry.start_link(keys: :unique, name: Registry.PutMetaTest)
iex> Registry.put_meta(Registry.PutMetaTest, :custom_key, "custom_value")
:ok
iex> Registry.meta(Registry.PutMetaTest, :custom_key)
{:ok, "custom_value"}
iex> Registry.put_meta(Registry.PutMetaTest, {:tuple, :key}, "tuple_value")
:ok
iex> Registry.meta(Registry.PutMetaTest, {:tuple, :key})
{:ok, "tuple_value"} register(registry, key, value)
Спецификации
register(registry(), key(), value()) ::
{:ok, pid()} | {:error, {:already_registered, pid()}} Регистрирует текущий процесс под заданным key в registry.
Также необходимо указать значение, которое будет ассоциировано с этой регистрацией. Это значение будет извлекаться при диспетчеризации или поиске ключа.
Эта функция возвращает {:ok, owner} или {:error, reason}. owner — PID в разделе реестра, отвечающем за PID. Владелец автоматически связан с вызывающим.
Если у реестра уникальные ключи, он вернет {:ok, owner}, если ключ еще не ассоциирован с PID, в противном случае возвращает {:error, {:already_registered, pid}}.
Если у реестра дублирующиеся ключи, несколько регистраций текущего процесса под одним и тем же ключом разрешены.
Примеры
Регистрация в уникальном реестре не допускает нескольких записей:
iex> Registry.start_link(keys: :unique, name: Registry.UniqueRegisterTest)
iex> {:ok, _} = Registry.register(Registry.UniqueRegisterTest, "hello", :world)
iex> Registry.register(Registry.UniqueRegisterTest, "hello", :later)
{:error, {:already_registered, self()}}
iex> Registry.keys(Registry.UniqueRegisterTest, self())
["hello"] Однако это возможно для дублирующих реестров:
iex> Registry.start_link(keys: :duplicate, name: Registry.DuplicateRegisterTest)
iex> {:ok, _} = Registry.register(Registry.DuplicateRegisterTest, "hello", :world)
iex> {:ok, _} = Registry.register(Registry.DuplicateRegisterTest, "hello", :world)
iex> Registry.keys(Registry.DuplicateRegisterTest, self())
["hello", "hello"] select(registry, spec)
Спецификации
select(registry(), spec()) :: [term()]
Выбор ключа, pid и значений, зарегистрированных с помощью полных спецификаций сопоставления.
spec состоит из списка кортежей из трёх частей, в форме [{match_pattern, guards, body}].
Первая часть, шаблон сопоставления, должна быть кортежем, который будет соответствовать структуре данных, хранящихся в реестре, которая является {key, pid, value}. Атом :_ можно использовать для игнорирования заданного значения или элемента кортежа, а атом :"$1" для временного присвоения части шаблона переменной для последующего сравнения. Это может быть комбинировано, например, как {:"$1", :_, :_}.
Вторая часть, условия защиты, представляет собой список условий, позволяющих фильтровать результаты. Каждое условие защиты — кортеж, описывающий проверки, которые должна пройти присвоенная часть шаблона. Например, условие защиты $1 > 1 будет выражено как кортеж {:>, :"$1", 1}. Обратите внимание, что условия защиты работают только для присвоенных переменных, таких как :"$1", :"$2" и т. д.
Третья часть, тело, представляет собой список форм возвращаемых записей. Как и в случае с условиями защиты, вы можете получить доступ к присвоенным переменным, таким как :"$1", которые можно комбинировать со жёстко заданными значениями, чтобы свободно формировать записи. Обратите внимание, что кортежи должны быть заключены в дополнительный кортеж. Чтобы получить формат результата, как %{key: key, pid: pid, value: value}, предполагая, что эти переменные привязаны в нужном порядке в части сопоставления, вы должны предоставить тело, как [%{key: :"$1", pid: :"$2", value: :"$3"}]. Как и в случае с условиями защиты, вы можете использовать некоторые операции, такие как :element, чтобы изменить формат вывода.
Не используйте специальные переменные сопоставления :"$_" и :"$$", потому что они могут работать не так, как ожидается.
Обратите внимание, что для больших реестров с множеством разделов это будет дорогостоящим, так как результат формируется путем конкатенации всех разделов.
Примеры
Этот пример показывает, как получить всё из реестра.
iex> Registry.start_link(keys: :unique, name: Registry.SelectAllTest)
iex> {:ok, _} = Registry.register(Registry.SelectAllTest, "hello", :value)
iex> {:ok, _} = Registry.register(Registry.SelectAllTest, "world", :value)
iex> Registry.select(Registry.SelectAllTest, [{{:"$1", :"$2", :"$3"}, [], [{{:"$1", :"$2", :"$3"}}]}])
[{"world", self(), :value}, {"hello", self(), :value}] Получить все ключи в реестре.
iex> Registry.start_link(keys: :unique, name: Registry.SelectAllTest)
iex> {:ok, _} = Registry.register(Registry.SelectAllTest, "hello", :value)
iex> {:ok, _} = Registry.register(Registry.SelectAllTest, "world", :value)
iex> Registry.select(Registry.SelectAllTest, [{{:"$1", :_, :_}, [], [:"$1"]}])
["world", "hello"] start_link(options)
Характеристики
start_link(
keys: keys(),
name: registry(),
partitions: pos_integer(),
listeners: [atom()],
meta: meta
) :: {:ok, pid()} | {:error, term()}
when meta: [{meta_key(), meta_value()}] Запускает реестр в качестве процесса-супервизора.
Вручную его можно запустить следующим образом:
Registry.start_link(keys: :unique, name: MyApp.Registry)
В вашей дереве супервизоров вы напишите:
Supervisor.start_link([
{Registry, keys: :unique, name: MyApp.Registry}
], strategy: :one_for_one) Для интенсивных нагрузок реестр также можно разделить (указав опцию :partitions). Если разделение необходимо, то хорошим значением по умолчанию является установка количества разделов равным количеству доступных планировщиков:
Registry.start_link( keys: :unique, name: MyApp.Registry, partitions: System.schedulers_online() )
или:
Supervisor.start_link([
{Registry, keys: :unique, name: MyApp.Registry, partitions: System.schedulers_online()}
], strategy: :one_for_one) Параметры
Реестр требует следующих ключей:
-
:keys- выбирает, являются ли ключи:uniqueили:duplicate -
:name- имя реестра и его таблиц
Следующие ключи являются необязательными:
-
:partitions- количество разделов в реестре. По умолчанию1. -
:listeners- список именованных процессов, которые уведомляются о событиях:registerи:unregister. Зарегистрированный процесс должен контролироваться слушателем, если слушатель хочет быть уведомлен о сбое зарегистрированного процесса. -
:meta- список ключевых слов метаданных, которые необходимо добавить к реестру.
unregister(registry, key)
Характеристики
unregister(registry(), key()) :: :ok
Удаляет все записи для данного key, связанного с текущим процессом в registry.
Всегда возвращает :ok и автоматически разрывает связь текущего процесса с владельцем, если больше нет ключей, связанных с текущим процессом. См. также register/3, чтобы узнать больше о "владельце".
Примеры
Для уникальных реестров:
iex> Registry.start_link(keys: :unique, name: Registry.UniqueUnregisterTest) iex> Registry.register(Registry.UniqueUnregisterTest, "hello", :world) iex> Registry.keys(Registry.UniqueUnregisterTest, self()) ["hello"] iex> Registry.unregister(Registry.UniqueUnregisterTest, "hello") :ok iex> Registry.keys(Registry.UniqueUnregisterTest, self()) []
Для дублируемых реестров:
iex> Registry.start_link(keys: :duplicate, name: Registry.DuplicateUnregisterTest) iex> Registry.register(Registry.DuplicateUnregisterTest, "hello", :world) iex> Registry.register(Registry.DuplicateUnregisterTest, "hello", :world) iex> Registry.keys(Registry.DuplicateUnregisterTest, self()) ["hello", "hello"] iex> Registry.unregister(Registry.DuplicateUnregisterTest, "hello") :ok iex> Registry.keys(Registry.DuplicateUnregisterTest, self()) []
unregister_match(registry, key, pattern, guards \\ [])
Характеристики
unregister_match(registry(), key(), match_pattern(), guards()) :: :ok
Удаляет записи для данного ключа, соответствующего шаблону.
Примеры
Для уникальных реестров это можно использовать для условного удаления ключа на основе того, соответствует ли он определенному значению.
iex> Registry.start_link(keys: :unique, name: Registry.UniqueUnregisterMatchTest) iex> Registry.register(Registry.UniqueUnregisterMatchTest, "hello", :world) iex> Registry.keys(Registry.UniqueUnregisterMatchTest, self()) ["hello"] iex> Registry.unregister_match(Registry.UniqueUnregisterMatchTest, "hello", :foo) :ok iex> Registry.keys(Registry.UniqueUnregisterMatchTest, self()) ["hello"] iex> Registry.unregister_match(Registry.UniqueUnregisterMatchTest, "hello", :world) :ok iex> Registry.keys(Registry.UniqueUnregisterMatchTest, self()) []
Для дублируемых реестров:
iex> Registry.start_link(keys: :duplicate, name: Registry.DuplicateUnregisterMatchTest)
iex> Registry.register(Registry.DuplicateUnregisterMatchTest, "hello", :world_a)
iex> Registry.register(Registry.DuplicateUnregisterMatchTest, "hello", :world_b)
iex> Registry.register(Registry.DuplicateUnregisterMatchTest, "hello", :world_c)
iex> Registry.keys(Registry.DuplicateUnregisterMatchTest, self())
["hello", "hello", "hello"]
iex> Registry.unregister_match(Registry.DuplicateUnregisterMatchTest, "hello", :world_a)
:ok
iex> Registry.keys(Registry.DuplicateUnregisterMatchTest, self())
["hello", "hello"]
iex> Registry.lookup(Registry.DuplicateUnregisterMatchTest, "hello")
[{self(), :world_b}, {self(), :world_c}] update_value(registry, key, callback)
Характеристики
update_value(registry(), key(), (value() -> value())) ::
{new_value :: term(), old_value :: term()} | :error Обновляет значение для key для текущего процесса в уникальном registry.
Возвращает кортеж {new_value, old_value} или :error если такой ключ не назначен текущему процессу.
Если указан не уникальный реестр, генерируется ошибка.
Примеры
iex> Registry.start_link(keys: :unique, name: Registry.UpdateTest)
iex> {:ok, _} = Registry.register(Registry.UpdateTest, "hello", 1)
iex> Registry.lookup(Registry.UpdateTest, "hello")
[{self(), 1}]
iex> Registry.update_value(Registry.UpdateTest, "hello", &(&1 + 1))
{2, 1}
iex> Registry.lookup(Registry.UpdateTest, "hello")
[{self(), 2}]
© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.9.4/Registry.html