檢視原始碼 註冊表 (Elixir v1.16.2)
一種本機、分散式且可擴充的鍵值處理程序儲存。
它允許開發人員使用給定的鍵查詢一個或多個處理程序。如果註冊表有 :unique
鍵,一個鍵會指向 0 或 1 個處理程序。如果註冊表允許 :duplicate
鍵,一個鍵可能會指向任意數量的處理程序。在這兩種情況下,不同的鍵可以識別同一個處理程序。
註冊表中的每個條目都與註冊該鍵的處理程序關聯。如果處理程序崩潰,與該處理程序關聯的鍵會自動移除。註冊表中的所有鍵比較都是使用比對運算進行 (===/2
).
註冊表可供不同的目的使用,例如名稱查詢 (使用 :via
選項)、儲存屬性、自訂分派規則或 pubsub 實作。我們在下面探討其中一些使用案例。
註冊表也可以透明地進行分割,這為在具有數千或數百萬個條目的高度並行環境中執行註冊表提供了更具可擴充性的行為。
在 :via
中使用
一旦註冊表使用 Registry.start_link/1
以給定名稱啟動,就可以使用 {:via, Registry, {registry, key}}
元組註冊和存取命名處理程序
{:ok, _} = Registry.start_link(keys: :unique, name: MyApp.Registry)
name = {:via, Registry, {MyApp.Registry, "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(MyApp.Registry, "agent")
#=> [{self(), nil}]
但是,在某些情況下,可能希望使用備用 {:via, Registry, {registry, key, value}}
元組將值與處理程序關聯
{:ok, _} = Registry.start_link(keys: :unique, name: MyApp.Registry)
name = {:via, Registry, {MyApp.Registry, "agent", :hello}}
{:ok, agent_pid} = Agent.start_link(fn -> 0 end, name: name)
Registry.lookup(MyApp.Registry, "agent")
#=> [{agent_pid, :hello}]
到目前為止,我們一直使用 start_link/1
啟動 Registry
。不過,註冊表通常作為監督樹的一部分啟動
{Registry, keys: :unique, name: MyApp.Registry}
只有具有唯一鍵的註冊表才能在 :via
中使用。如果名稱已被使用,特定於案例的 start_link
函數 (Agent.start_link/2
如上例所示) 會傳回 {:error, {:already_started, current_pid}}
。
作為 dispatcher 使用
Registry
有一個 dispatch 機制,讓開發人員可以實作自訂的 dispatch 邏輯,由呼叫者觸發。例如,假設我們有一個重複的註冊表,啟動如下
{: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
Dispatching 發生在呼叫 dispatch/3
的程序中,在多個區段的情況下,會以串行或並行方式進行(透過產生的任務)。註冊的程序不會參與 dispatching,除非明確地讓它們參與(例如,在回呼中傳送訊息給它們)。
此外,如果在 dispatching 時發生故障,由於註冊錯誤,dispatching 將會永遠失敗,且不會通知註冊的程序。因此,讓我們至少包裝並回報這些錯誤
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
系統。這是我們接下來要看到的範例。
作為 PubSub 使用
註冊表也可以用於實作一個本地的、非分散式的、可擴充的 PubSub,透過依賴 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
訊息,而 send/2
則對死亡的程序作用為 no-op。
ETS
請注意,註冊表使用一個 ETS 表格加上每個分區兩個 ETS 表格。
摘要
類型
用於表示配對規格輸出格式部分的樣式
在註冊表中配對物件時要評估的防護
在註冊表中配對物件時要評估的防護清單
註冊時允許的金鑰類型
註冊表的類型
當程序註冊或取消註冊時,註冊表傳送給偵聽器的訊息。
在註冊表中配對物件的樣式
註冊表元資料金鑰的類型
註冊表元資料值的類型
註冊表識別碼
在註冊表中選取物件時使用的完整比對規格
用於 child_spec/1
和 start_link/1
的選項
註冊時允許的數值類型
函式
傳回一個規格,用於在監督程式下啟動註冊表。
傳回註冊表中已註冊的鍵數。執行時間為常數。
傳回在 registry
中,給定 key
下符合 pattern
的 {pid, value}
配對數。
運作方式類似 select/2
,但只會傳回符合記錄的數目。
刪除 registry
中給定 key
的註冊表中繼資料。
針對給定 registry
的每個分區,呼叫給定 key
下的所有條目,並使用回呼函式。
傳回 registry
中給定 pid
的已知鍵,順序不特定。
在 registry
中,尋找給定 key
的 {pid, value}
配對,順序不特定。
傳回 registry
中,給定 key
下符合 pattern
的 {pid, value}
配對。
讀取 start_link/1
上提供的註冊表中繼資料。
儲存註冊表中繼資料。
在 registry
中,使用給定 key
註冊目前的處理程序。
使用完整比對規格,選取已註冊的鍵、pid 和值。
以監督程序身分啟動註冊表。
在 registry
中,解除註冊與目前處理程序關聯的所有給定 key
條目。
在 registry
中,解除註冊與目前處理程序關聯且符合樣式的鍵條目。
在唯一的 registry
中,更新目前處理程序的 key
值。
讀取 registry
中 pid
的給定 key
的值。
類型
@type body() :: [term()]
用於表示配對規格輸出格式部分的樣式
在註冊表中配對物件時要評估的防護
@type guards() :: [guard()]
在註冊表中配對物件時要評估的防護清單
@type key() :: term()
註冊時允許的金鑰類型
@type keys() :: :unique | :duplicate
註冊表的類型
@type listener_message() :: {:register, registry(), key(), registry_partition :: pid(), value()} | {:unregister, registry(), key(), registry_partition :: pid()}
當程序註冊或取消註冊時,註冊表傳送給偵聽器的訊息。
參閱 start_link/1
中的 :listeners
選項。
在註冊表中配對物件的樣式
註冊表元資料金鑰的類型
@type meta_value() :: term()
註冊表元資料值的類型
@type registry() :: atom()
註冊表識別碼
@type spec() :: [{match_pattern(), guards(), body()}]
在註冊表中選取物件時使用的完整比對規格
@type start_option() :: {:keys, keys()} | {:name, registry()} | {:partitions, pos_integer()} | {:listeners, [atom()]} | {:meta, [{meta_key(), meta_value()}]}
用於 child_spec/1
和 start_link/1
的選項
@type value() :: term()
註冊時允許的數值類型
函式
@spec child_spec([start_option()]) :: Supervisor.child_spec()
傳回一個規格,用於在監督程式下啟動註冊表。
參閱 Supervisor
。
@spec 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
@spec count_match(registry(), key(), match_pattern(), guards()) :: non_neg_integer()
傳回在 registry
中,給定 key
下符合 pattern
的 {pid, value}
配對數。
模式必須是原子或與儲存在註冊表中的值結構相符的元組。原子 :_
可用於忽略給定的值或元組元素,而原子 :"$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
@spec count_select(registry(), spec()) :: non_neg_integer()
運作方式類似 select/2
,但只會傳回符合記錄的數目。
範例
在以下範例中,我們在唯一的登錄檔中使用不同的金鑰註冊目前的程序,但使用相同的值
iex> Registry.start_link(keys: :unique, name: Registry.CountSelectTest)
iex> {:ok, _} = Registry.register(Registry.CountSelectTest, "hello", :value)
iex> {:ok, _} = Registry.register(Registry.CountSelectTest, "world", :value)
iex> Registry.count_select(Registry.CountSelectTest, [{{:_, :_, :value}, [], [true]}])
2
刪除 registry
中給定 key
的註冊表中繼資料。
範例
iex> Registry.start_link(keys: :unique, name: Registry.DeleteMetaTest)
iex> Registry.put_meta(Registry.DeleteMetaTest, :custom_key, "custom_value")
:ok
iex> Registry.meta(Registry.DeleteMetaTest, :custom_key)
{:ok, "custom_value"}
iex> Registry.delete_meta(Registry.DeleteMetaTest, :custom_key)
:ok
iex> Registry.meta(Registry.DeleteMetaTest, :custom_key)
:error
@spec dispatch(registry(), key(), dispatcher, keyword()) :: :ok when dispatcher: (entries :: [{pid(), value()}] -> term()) | {module(), atom(), [any()]}
針對給定 registry
的每個分區,呼叫給定 key
下的所有條目,並使用回呼函式。
entries
清單是非空的二元組清單,其中第一個元素是 PID,第二個元素是與 PID 相關的值。如果給定的金鑰沒有任何項目,則永遠不會呼叫回呼函式。
如果登錄檔已分割,則會針對每個分割呼叫回呼函式多次。如果登錄檔已分割,且給定 parallel: true
作為選項,則會並行進行派送。在這兩種情況下,只有在該分割有項目時才會呼叫回呼函式。
請參閱模組文件,以取得使用 dispatch/3
函式建立自訂派送或 pubsub 系統的範例。
傳回 registry
中給定 pid
的已知鍵,順序不特定。
如果登錄檔是唯一的,則金鑰是唯一的。否則,如果程序已多次在相同金鑰下註冊,則它們可能包含重複項。如果程序已死亡或在這個登錄檔中沒有金鑰,則清單將會是空的。
範例
在唯一的登錄檔中註冊不允許多個項目
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"]
在 registry
中,尋找給定 key
的 {pid, value}
配對,順序不特定。
如果沒有相符項目,則為空清單。
對於唯一註冊表,需要單一分區查詢。對於重複註冊表,必須查詢所有分區。
範例
在下列範例中,我們註冊目前的程序,並從自身和其它程序中查詢它
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}]
傳回 registry
中,給定 key
下符合 pattern
的 {pid, value}
配對。
模式必須是原子或與儲存在註冊表中的值結構相符的元組。原子 :_
可用於忽略給定的值或元組元素,而原子 :"$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}}]
@spec 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
@spec 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"}
在 registry
中,使用給定 key
註冊目前的處理程序。
也必須提供要與此註冊關聯的值。在進行分派或鍵值查詢時,將會擷取此值。
此函式傳回 {:ok, owner}
或 {:error, reason}
。 owner
是註冊區段中負責 PID 的 PID。owner 會自動連結到呼叫者。
如果註冊區段有唯一鍵值,則會傳回 {:ok, owner}
,除非鍵值已關聯到 PID,否則會傳回 {:error, {:already_registered, pid}}
。
如果註冊區段有重複鍵值,則允許在同一個鍵值下從目前的程序進行多重註冊。
如果註冊區段有透過 start_link/1
中的 :listeners
選項指定的監聽器,則這些監聽器將會收到註冊通知,並會收到類型為 listener_message/0
的訊息。
範例
在唯一的登錄檔中註冊不允許多個項目
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"]
使用完整比對規格,選取已註冊的鍵、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"}}]}]) |> Enum.sort()
[{"hello", self(), :value}, {"world", 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"]}]) |> Enum.sort()
["hello", "world"]
@spec 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
- 已註冊並收到註冊和取消註冊事件通知的名稱處理程序清單。如果監聽器想要在註冊處理程序崩潰時收到通知,則註冊處理程序必須由監聽器監控。傳送給監聽器的訊息類型為listener_message/0
。:meta
- 要附加到註冊表的元資料關鍵字清單。
在 registry
中,解除註冊與目前處理程序關聯的所有給定 key
條目。
總是傳回 :ok
,如果沒有更多金鑰與目前的處理程序關聯,則會自動解除目前的處理程序與擁有者的關聯。另請參閱 register/3
以進一步了解「擁有者」。
如果註冊表已透過 start_link/1
中的 :listeners
選項指定監聽器,則這些監聽器會收到取消註冊的通知,並會收到類型為 listener_message/0
的訊息。
範例
對於唯一的註冊表
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())
[]
@spec unregister_match(registry(), key(), match_pattern(), guards()) :: :ok
在 registry
中,解除註冊與目前處理程序關聯且符合樣式的鍵條目。
範例
對於唯一的註冊表,可以用它來有條件地取消註冊一個鍵,根據它是否與特定值相符。
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}]
@spec update_value(registry(), key(), (value() -> value())) :: {new_value :: term(), old_value :: term()} | :error
在唯一的 registry
中,更新目前處理程序的 key
值。
傳回一個 {new_value, old_value}
tuple 或 :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}]
讀取 registry
中 pid
的給定 key
的值。
對於唯一的註冊表,它不是一個空清單就是一個只有一個元素的清單。對於重複的註冊表,它是一個有零、一或多個元素的清單。
範例
在下列範例中,我們註冊目前的程序,並從自身和其它程序中查詢它
iex> Registry.start_link(keys: :unique, name: Registry.UniqueLookupTest)
iex> Registry.values(Registry.UniqueLookupTest, "hello", self())
[]
iex> {:ok, _} = Registry.register(Registry.UniqueLookupTest, "hello", :world)
iex> Registry.values(Registry.UniqueLookupTest, "hello", self())
[:world]
iex> Task.async(fn -> Registry.values(Registry.UniqueLookupTest, "hello", self()) end) |> Task.await()
[]
iex> parent = self()
iex> Task.async(fn -> Registry.values(Registry.UniqueLookupTest, "hello", parent) end) |> Task.await()
[:world]
重複註冊表也適用相同情況
iex> Registry.start_link(keys: :duplicate, name: Registry.DuplicateLookupTest)
iex> Registry.values(Registry.DuplicateLookupTest, "hello", self())
[]
iex> {:ok, _} = Registry.register(Registry.DuplicateLookupTest, "hello", :world)
iex> Registry.values(Registry.DuplicateLookupTest, "hello", self())
[:world]
iex> {:ok, _} = Registry.register(Registry.DuplicateLookupTest, "hello", :another)
iex> Enum.sort(Registry.values(Registry.DuplicateLookupTest, "hello", self()))
[:another, :world]