檢視原始碼 DynamicSupervisor 行為 (Elixir v1.16.2)
最佳化僅動態啟動子項目的監督程式。
Supervisor
模組設計用於處理大多數靜態子項目,這些子項目會在監督程式啟動時以指定順序啟動。DynamicSupervisor
啟動時沒有子項目。相反地,子項目會透過 start_child/2
依需求啟動,且子項目之間沒有順序。這讓 DynamicSupervisor
能夠使用有效率的資料結構容納數百萬個子項目,並同時執行某些作業,例如關閉。
範例
動態監督程式啟動時沒有子項目,而且通常會有名稱
children = [
{DynamicSupervisor, name: MyApp.DynamicSupervisor, strategy: :one_for_one}
]
Supervisor.start_link(children, strategy: :one_for_one)
子項目規格中提供的選項記載於 start_link/1
中。
動態監督程式執行後,我們可以使用它依需求啟動子項目。假設有這個範例 GenServer
defmodule Counter do
use GenServer
def start_link(initial) do
GenServer.start_link(__MODULE__, initial)
end
def inc(pid) do
GenServer.call(pid, :inc)
end
def init(initial) do
{:ok, initial}
end
def handle_call(:inc, _, count) do
{:reply, count, count + 1}
end
end
我們可以使用 start_child/2
搭配子項目規格來啟動 Counter
伺服器
{:ok, counter1} = DynamicSupervisor.start_child(MyApp.DynamicSupervisor, {Counter, 0})
Counter.inc(counter1)
#=> 0
{:ok, counter2} = DynamicSupervisor.start_child(MyApp.DynamicSupervisor, {Counter, 10})
Counter.inc(counter2)
#=> 10
DynamicSupervisor.count_children(MyApp.DynamicSupervisor)
#=> %{active: 2, specs: 2, supervisors: 0, workers: 2}
可擴充性和分割
DynamicSupervisor
是負責啟動其他程序的單一程序。在某些應用程式中, DynamicSupervisor
可能會成為瓶頸。為了解決這個問題,您可以啟動 DynamicSupervisor
的多個執行個體,然後選擇一個「隨機」執行個體來啟動子執行個體。
取代
children = [
{DynamicSupervisor, name: MyApp.DynamicSupervisor}
]
和
DynamicSupervisor.start_child(MyApp.DynamicSupervisor, {Counter, 0})
您可以執行下列動作
children = [
{PartitionSupervisor,
child_spec: DynamicSupervisor,
name: MyApp.DynamicSupervisors}
]
然後
DynamicSupervisor.start_child(
{:via, PartitionSupervisor, {MyApp.DynamicSupervisors, self()}},
{Counter, 0}
)
在上述程式碼中,我們啟動一個分割區監督程式,它會預設為您的機器中的每個核心啟動一個動態監督程式。然後,您不是透過名稱呼叫 DynamicSupervisor
,而是透過分割區監督程式呼叫它,並使用 self()
作為路由金鑰。這表示每個程序會指派一個現有的動態監督程式。請閱讀 PartitionSupervisor
文件以取得更多資訊。
基於模組的監督程式
與 Supervisor
類似,動態監督程式也支援基於模組的監督程式。
defmodule MyApp.DynamicSupervisor do
# Automatically defines child_spec/1
use DynamicSupervisor
def start_link(init_arg) do
DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
end
@impl true
def init(_init_arg) do
DynamicSupervisor.init(strategy: :one_for_one)
end
end
請參閱 Supervisor
文件,以了解何時可能想要使用基於模組的監督程式。緊接在 use DynamicSupervisor
之前的 @doc
註解會附加到產生的 child_spec/1
函式。
use DynamicSupervisor
當您
use DynamicSupervisor
時,DynamicSupervisor
模組會設定@behaviour DynamicSupervisor
並定義child_spec/1
函式,因此您的模組可以用作監督樹中的子執行個體。
名稱註冊
監督程式與 GenServer
繫結到相同的名稱註冊規則。請在 GenServer
的文件中進一步了解這些規則。
從 Supervisor 的 :simple_one_for_one 進行移轉
如果您使用 Supervisor
模組中已棄用的 :simple_one_for_one
策略,您可以透過幾個步驟移轉到 DynamicSupervisor
。
想像給定的「舊」程式碼
defmodule MySupervisor do
use Supervisor
def start_link(init_arg) do
Supervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
end
def start_child(foo, bar, baz) do
# This will start child by calling MyWorker.start_link(init_arg, foo, bar, baz)
Supervisor.start_child(__MODULE__, [foo, bar, baz])
end
@impl true
def init(init_arg) do
children = [
# Or the deprecated: worker(MyWorker, [init_arg])
%{id: MyWorker, start: {MyWorker, :start_link, [init_arg]}}
]
Supervisor.init(children, strategy: :simple_one_for_one)
end
end
它可以像這樣升級到 DynamicSupervisor
defmodule MySupervisor do
use DynamicSupervisor
def start_link(init_arg) do
DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
end
def start_child(foo, bar, baz) do
# If MyWorker is not using the new child specs, we need to pass a map:
# spec = %{id: MyWorker, start: {MyWorker, :start_link, [foo, bar, baz]}}
spec = {MyWorker, foo: foo, bar: bar, baz: baz}
DynamicSupervisor.start_child(__MODULE__, spec)
end
@impl true
def init(init_arg) do
DynamicSupervisor.init(
strategy: :one_for_one,
extra_arguments: [init_arg]
)
end
end
DynamicSupervisor
的不同之處在於,它會在呼叫 start_child/2
的時候預期子規格,而不是在 init 回呼中。如果在初始化時給予任何初始引數,例如 [initial_arg]
,它可以在 DynamicSupervisor.init/1
上的 :extra_arguments
旗標中給予。
摘要
回呼
呼叫回呼以啟動監督程式,以及在熱程式碼升級期間。
函式
回傳一個規格,以在監督程式下啟動動態監督程式。
回傳包含監督程式計數值的對應。
接收一組 options
,用於初始化動態監督程式。
動態將子規格新增到 supervisor
並啟動該子項。
使用給定的選項啟動監督程式。
使用給定的 module
和 init_arg
啟動基於模組的監督程式程序。
使用給定的 reason
同步停止給定的監督程式。
終止由 pid
識別的給定子項。
傳回包含所有子項目的資訊清單。
類型
@type init_option() :: {:strategy, strategy()} | {:max_restarts, non_neg_integer()} | {:max_seconds, pos_integer()} | {:max_children, non_neg_integer() | :infinity} | {:extra_arguments, [term()]}
提供給 start_link
和 init/1
函式的選項
@type on_start_child() :: {:ok, pid()} | {:ok, pid(), info :: term()} | :ignore | {:error, {:already_started, pid()} | :max_children | term()}
start_child
函式的回傳值
@type option() :: GenServer.option()
提供給 start_link
函式的選項
@type strategy() :: :one_for_one
支援的策略
@type sup_flags() :: %{ strategy: strategy(), intensity: non_neg_integer(), period: pos_integer(), max_children: non_neg_integer() | :infinity, extra_arguments: [term()] }
在 init 時回傳的監督旗標
回呼
呼叫回呼以啟動監督程式,以及在熱程式碼升級期間。
開發人員通常會在 init 回呼的結尾呼叫 DynamicSupervisor.init/1
以傳回適當的監控旗標。
函式
回傳一個規格,以在監督程式下啟動動態監督程式。
它接受與 start_link/1
相同的選項。
有關子項目的詳細資訊,請參閱 Supervisor
。
@spec count_children(Supervisor.supervisor()) :: %{ specs: non_neg_integer(), active: non_neg_integer(), supervisors: non_neg_integer(), workers: non_neg_integer() }
回傳包含監督程式計數值的對應。
此映射包含下列金鑰
:specs
- 子程序的數量:active
- 由此監控器管理的所有正在執行中的子程序計數:supervisors
- 所有監控器的計數,無論子程序是否仍存在:workers
- 所有工作程序的計數,無論子程序是否仍存在
@spec init([init_option()]) :: {:ok, sup_flags()}
接收一組 options
,用於初始化動態監督程式。
這通常會在模組化監控器的 init/1
回呼的結尾呼叫。有關詳細資訊,請參閱模組文件中的「模組化監控器」區段。
它接受與 start_link/1
相同的 options
(:name
除外),並傳回包含監控器選項的組元。
範例
def init(_arg) do
DynamicSupervisor.init(max_children: 1000)
end
@spec start_child( Supervisor.supervisor(), Supervisor.child_spec() | {module(), term()} | module() | (old_erlang_child_spec :: :supervisor.child_spec()) ) :: on_start_child()
動態將子規格新增到 supervisor
並啟動該子項。
child_spec
應為有效的子項規格,詳見 Supervisor
文件中的「子項規格」區段。子項程序將按照子項規格中定義的內容啟動。請注意,儘管規格中仍然需要 :id
欄位,但值會被忽略,因此不需要是唯一的。
如果子項程序啟動函數傳回 {:ok, child}
或 {:ok, child, info}
,則子項規格和 PID 會新增到監控器,而此函數也會傳回相同的值。
如果子項程序啟動函數傳回 :ignore
,則不會將任何子項新增到監控樹,而此函數也會傳回 :ignore
。
如果子項程序啟動函數傳回錯誤組或錯誤值,或如果它失敗,則會捨棄子項規格,而此函數也會傳回 {:error, error}
,其中 error
是子項程序啟動函數傳回的錯誤或錯誤值,或如果它失敗,則為失敗原因。
如果監控器已經有 N 個子項,而 N 超過監控器初始化時設定的 :max_children
數量(請參閱 init/1
),則此函數會傳回 {:error, :max_children}
。
@spec start_link([option() | init_option()]) :: Supervisor.on_start()
使用給定的選項啟動監督程式。
此函數通常不會直接呼叫,而是當使用 DynamicSupervisor
作為另一個監控器的子項時呼叫
children = [
{DynamicSupervisor, name: MySupervisor}
]
如果監控器成功產生,則此函數會傳回 {:ok, pid}
,其中 pid
是監控器的 PID。如果監控器已命名,且已存在具有指定名稱的程序,則函數會傳回 {:error, {:already_started, pid}}
,其中 pid
是該程序的 PID。
請注意,使用此函數啟動的監控器會連結到父程序,且不僅會在發生崩潰時結束,也會在父程序以 :normal
原因結束時結束。
選項
:name
- 在給定的名稱下註冊監督者。支援的值在GenServer
模組文件中的「名稱註冊」區段中說明。:strategy
- 重新啟動策略選項。唯一支援的值是:one_for_one
,這表示如果子程序終止,不會終止其他子程序。您可以在Supervisor
模組文件中進一步了解策略。:max_restarts
- 在時間範圍內允許的最大重新啟動次數。預設為3
。:max_seconds
-:max_restarts
套用的時間範圍。預設為5
。:max_children
- 在此監督者下同時執行的最大子程序數量。當超過:max_children
時,start_child/2
會傳回{:error, :max_children}
。預設為:infinity
。:extra_arguments
- 傳遞給start_child/2
的子程序規格中指定參數之前附加的參數。預設為空清單。
@spec start_link(module(), term(), [option()]) :: Supervisor.on_start()
使用給定的 module
和 init_arg
啟動基於模組的監督程式程序。
要啟動監督者,init/1
回呼會在給定的 module
中呼叫,並以 init_arg
作為其參數。init/1
回呼必須傳回監督者規格,可以使用 init/1
函數建立。
如果 init/1
回呼傳回 :ignore
,此函數也會傳回 :ignore
,而監督者會以原因 :normal
終止。如果它失敗或傳回不正確的值,此函數會傳回 {:error, term}
,其中 term
是包含錯誤資訊的術語,而監督者會以原因 term
終止。
:name
選項也可以在註冊監督者名稱時提供,支援的值在 GenServer
模組文件中的「名稱註冊」區段中說明。
如果監控器成功產生,則此函數會傳回 {:ok, pid}
,其中 pid
是監控器的 PID。如果監控器已命名,且已存在具有指定名稱的程序,則函數會傳回 {:error, {:already_started, pid}}
,其中 pid
是該程序的 PID。
請注意,使用此函數啟動的監控器會連結到父程序,且不僅會在發生崩潰時結束,也會在父程序以 :normal
原因結束時結束。
@spec stop(Supervisor.supervisor(), reason :: term(), timeout()) :: :ok
使用給定的 reason
同步停止給定的監督程式。
如果監督程式終止於給定的原因,則回傳 :ok
。如果終止於其他原因,則呼叫會結束。
此函數會保留 OTP 語意,關於錯誤回報。如果原因是任何除了 :normal
、:shutdown
或 {:shutdown, _}
以外的原因,則會記錄一個錯誤回報。
@spec terminate_child(Supervisor.supervisor(), pid()) :: :ok | {:error, :not_found}
終止由 pid
識別的給定子項。
如果成功,此函數會回傳 :ok
。如果沒有具有給定 PID 的程序,則此函數會回傳 {:error, :not_found}
。
@spec which_children(Supervisor.supervisor()) :: [ {:undefined, pid() | :restarting, :worker | :supervisor, [module()] | :dynamic} ]
傳回包含所有子項目的資訊清單。
請注意,在低記憶體條件下監督大量子程序時呼叫此函數可能會導致記憶體不足例外狀況。
此函數會回傳包含下列元組的清單
id
- 對於動態監督程式,它總是:undefined
child
- 對應子程序的 PID 或原子:restarting
,如果程序即將重新啟動type
-:worker
或:supervisor
,如子規格中定義modules
- 如子規格中定義