Реестр
Локальное, децентрализованное и масштабируемое хранилище пар «ключ-значение» для процессов.
Он позволяет разработчикам искать один или несколько процессов по заданному ключу. Если реестр имеет :unique ключей, ключ указывает на 0 или 1 процесс. Если реестр допускает :duplicate ключей, один ключ может указывать на любое количество процессов. В обоих случаях разные ключи могут идентифицировать один и тот же процесс.
Каждая запись в реестре связана с процессом, который зарегистрировал ключ. Если процесс завершается аварийно, ключи, связанные с этим процессом, автоматически удаляются. Все сравнения ключей в реестре выполняются с помощью операции сопоставления (===/2).
Реестр может быть использован для различных целей, таких как поиск по имени (используя :via опцию), хранение свойств, пользовательские правила маршрутизации или реализация pub/sub. Ниже мы рассмотрим некоторые из этих вариантов использования.
Реестр также может быть прозрачно разбит на части, что обеспечивает более масштабируемое поведение для работы реестров в высококонкурентных средах с тысячами или миллионами записей.
Использование в :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
Реестры также могут быть использованы для реализации локального, нераспределённого и масштабируемого 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()
Полная спецификация соответствия, используемая при выборе объектов в реестре.
- start_option()
Параметры, используемые для
child_spec/1иstart_link/1- value()
Тип значений, разрешённых при регистрации.
Функции
- child_spec(options)
Возвращает спецификацию для запуска реестра под надзором.
- 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)
Выбирает ключ, pid и значения, зарегистрированные с помощью полных спецификаций соответствия.
- 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() | tuple()
Условие, подлежащее оценке при сопоставлении объектов в реестре.
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()}] Полная спецификация соответствия, используемая при выборе объектов в реестре.
start_option()
Specs
start_option() ::
{:keys, keys()}
| {:name, registry()}
| {:partitions, pos_integer()}
| {:listeners, [atom()]}
| {:meta, [{meta_key(), meta_value()}]} Параметры, используемые для child_spec/1 и start_link/1
value()
Specs
value() :: term()
Тип значений, разрешённых при регистрации.
Функции
child_spec(options)
Спецификации
child_spec([start_option()]) :: Supervisor.child_spec()
Возвращает спецификацию для начала регистрации под надзором.
См. 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([start_option()]) :: {:ok, pid()} | {:error, term()} Запускает реестр как процесс-надзиратель.
Ручной запуск возможен следующим образом:
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.10.4/Registry.html