查看源 主管 行为 (Elixir v1.16.2)

用于实现主管的行为模块。

主管是一个进程,它主管其他进程,我们称之为子进程。主管用于构建一个称为主管树的分层进程结构。主管树提供容错功能,并封装我们的应用程序如何启动和关闭。

可以通过 start_link/2 带着子进程规范列表来直接启动主管,也可以通过定义一个模块化主管来实现所需回调。下列章节在大多数示例中使用 start_link/2 来启动主管,但也针对模块化主管包含一个专门章节。

示例

为了启动一个主管,我们需要首先一个要被主管的子进程。作为一个示例,我们将定义一个 GenServer,一个通用服务器,来保存计数器。然后,其他进程可以向该进程发送信息来读取计数器并增加其值。

免责声明

在实践中,你不会将计数器定义为 GenServer。相反,如果你需要一个计数器,你会将它作为输入和输出传递给需要它的函数。我们在本示例中选取计数器的理由是因为它的简洁性,因为它让我们能够专注于主管的工作原理。

defmodule Counter do
  use GenServer

  def start_link(arg) when is_integer(arg) do
    GenServer.start_link(__MODULE__, arg, name: __MODULE__)
  end

  ## Callbacks

  @impl true
  def init(counter) do
    {:ok, counter}
  end

  @impl true
  def handle_call(:get, _from, counter) do
    {:reply, counter, counter}
  end

  def handle_call({:bump, value}, _from, counter) do
    {:reply, counter, counter + value}
  end
end

Counterstart_link 中接受一个参数。此参数传递给 init/1 回调,该回调成为计数器的初始值。我们的计数器处理两个操作(称为调用)::get,获取当前计数器值,和 :bump,将计数器按给定的 value 增加,并返回旧计数器。

我们现在可以启动一个主管,它将启动并主管我们的计数器进程。第一步是定义一个控制每个子进程行为的子进程规范列表。每个子进程规范是一个映射,如下所示

children = [
  # The Counter is a child started via Counter.start_link(0)
  %{
    id: Counter,
    start: {Counter, :start_link, [0]}
  }
]

# Now we start the supervisor with the children and a strategy
{:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one)

# After started, we can query the supervisor for information
Supervisor.count_children(pid)
#=> %{active: 1, specs: 1, supervisors: 0, workers: 1}

请注意,当启动 GenServer 时,我们通过 name: __MODULE__ 选项将它注册为名称 Counter。这让我们能够直接调用它并获取它的值

GenServer.call(Counter, :get)
#=> 0

GenServer.call(Counter, {:bump, 3})
#=> 0

GenServer.call(Counter, :get)
#=> 3

然而,我们的计数器服务器有一个错误。如果我们用一个非数字值来调用 :bump,它将会崩溃

GenServer.call(Counter, {:bump, "oops"})
** (exit) exited in: GenServer.call(Counter, {:bump, "oops"}, 5000)

幸运的是,由于该服务器被一个主管主管,主管会自动启动一个新服务器,重置回初始值 0

GenServer.call(Counter, :get)
#=> 0

监管器遵循不同的策略;在上面的示例中,我们选择了 :one_for_one。此外,每个监管器可以有许多作为子代的 worker 和/或监管器,每个子代都有自己的配置(如“子规格”部分中所述)。

本文其余部分将介绍如何指定子进程、如何启动和停止子进程、不同的监督策略等内容。

子规范

子规范说明了监管器如何启动、关闭和重新启动子进程。

子规范是一个包含 6 个元素的地图。该列表中的前两个键是必需的,其余的是可选的

  • :id - 任何项,由监管器内部用于识别子规范;默认为给定模块。此键必需。对于监管器,在发生 :id 值冲突的情况下,监管器将拒绝初始化并要求显式 ID。但是,动态监管器 并非如此。

  • :start - 一个元组,其中包含用于启动子进程的模块-函数-参数。此键必需。

  • :restart - 一个原子,用于定义何时应重新启动终止的子进程(请参见下面的“重启值”部分)。此键是可选的,默认为 :permanent

  • :shutdown - 整数或原子,用于定义如何终止子进程(请参见下面的“关闭值”部分)。此键是可选的,如果类型是 :worker,则默认为 5_000;如果类型是 :supervisor,则默认为 infinity

  • :type - 指定子进程是 :worker 还是 :supervisor。此键是可选的,默认为 :worker

  • :modules - 热代码升级机制用于确定哪些进程正在使用哪些模块的模块列表。它通常设置为类似于 GenServerSupervisor 之类的行为的回调模块。它是根据 :start 值自动设置的,在实践中它很少改变。

  • :significant - 布尔值,指示是否应将子进程视为具有自动关闭意义。只有 :transient:temporary 子进程才能标记为显着。此键是可选的,默认为 false。有关详细信息,请参见下面的“自动关闭”部分。

让我们了解 :shutdown:restart 选项控制什么。

关机值 (:shutdown)

以下关机值在 :shutdown 选项中受支持

  • :brutal_kill - 使用 Process.exit(child, :kill) 无条件立即终止子进程。

  • 任何整数 >= 0 - 主管在发出 Process.exit(child, :shutdown) 信号后等待其子进程终止的以毫秒为单位的时间量。如果子进程不能捕获退出,则初始 :shutdown 信号将立即终止子进程。如果子进程捕获了退出,则它有指定的时间来终止。如果它没有在指定的时间内终止,则子进程将通过 Process.exit(child, :kill) 被主管无条件终止。

  • :infinity - 作为整数运行,但主管将无限期地等待子进程终止。如果子进程是主管,则建议使用 :infinity 值来给予主管及其子进程足够的时间关闭。此选项可与常规工作进程一起使用,但并不鼓励这样做,并需要格外小心。如果不仔细使用,子进程将永远不会终止,从而阻止您的应用程序终止。

重新启动值 (:restart)

:restart 选项控制主管应如何考虑成功终止或不成功终止。如果终止成功,主管将不会重新启动子进程。如果子进程崩溃,主管将启动一个新的子进程。

以下重新启动值在 :restart 选项中受支持

  • :permanent - 子进程总是重新启动。

  • :temporary - 子进程永远不会重新启动,无论监管策略如何:任何终止(甚至是非正常的)都被视为成功。

  • :transient - 仅当子进程异常终止时,才会重新启动子进程,即,退出原因不是 :normal:shutdown{:shutdown, term}

要更全面地了解退出原因及其影响,请参阅“退出原因和重新启动”部分。

child_spec/1 函数

启动主管时,我们可以传递一个子规范列表。这些规范是地图,它们告诉主管应如何启动、停止和重新启动其每个子进程

%{
  id: Counter,
  start: {Counter, :start_link, [0]}
}

上面的映射定义了一个子级,带有 :id,即 Counter,启动方法为调用 Counter.start_link(0)

然而,将每个子级的子级规范定义为映射极易出错,因为我们可能会更改 Counter 实现,而且忘记更新其规范。这就是 Elixir 允许你传递一个包含模块名称和 start_link 参数的元组(而不是该规范)的原因。

children = [
  {Counter, 0}
]

然后,supervisor 将调用 Counter.child_spec(0) 来检索子级规范。此时 Counter 模块负责构建自己的规范,例如,我们可以编写

def child_spec(arg) do
  %{
    id: Counter,
    start: {Counter, :start_link, [arg]}
  }
end

然后 supervisor 将调用 Counter.start_link(arg) 来启动子级进程。以下流程图总结了这一流程。调用方是一个进程,它衍生出 Supervisor 进程。然后,Supervisor 继续调用你的代码(模块)来衍生其子级进程。

sequenceDiagram
    participant C as Caller (Process)
    participant S as Supervisor (Process)
    participant M as Module (Code)

    note right of C: child is a {module, arg} specification
    C->>+S: Supervisor.start_link([child])
    S-->>+M: module.child_spec(arg)
    M-->>-S: %{id: term, start: {module, :start_link, [arg]}}
    S-->>+M: module.start_link(arg)
    M->>M: Spawns child process (child_pid)
    M-->>-S: {:ok, child_pid} | :ignore | {:error, reason}
    S->>-C: {:ok, supervisor_pid} | {:error, reason}

对我们来说,很幸运,use GenServer 已定义了一个 Counter.child_spec/1,完全和上面一样,因此无需自己编写以上定义。如果你希望自定义自动生成的 child_spec/1 函数,你可以将选项直接作为参数传递给 use GenServer

use GenServer, restart: :transient

最后,请注意,还可以只将 Counter 模块作为子级进行传递。

children = [
  Counter
]

仅给出模块名称时,等效于 {Counter, []},在我们这个例子中这是无效的,这就是我们始终显式传递初始计数器的原因。

通过将子级规范替换为 {Counter, 0},我们将其封装在 Counter 模块中。现在,我们能够将 Counter 实现与其他开发者共享,而且他们可以将其直接添加至自己的监管树中,而无需担心该计数器的底层详细信息。

总体上,子级规范可以是以下内容之一

  • 代表子级规范本身的映射,如“子级规范”部分所述

  • 将模块作为第一个元素、将启动参数作为第二个元素的元组,例如 {Counter, 0}。在这种情况下,将调用 Counter.child_spec(0) 来检索子级规范

  • 模块,例如 Counter。在这种情况下,将调用 Counter.child_spec([]),这对计数器无效,但它在诸多其他情况下非常有用,尤其是在你想要将一个选项列表传递给子级进程时。

如果需要将 {module, arg} 元组或模块子项规范转换为 子项规范 或者修改子项规范本身,可以使用 Supervisor.child_spec/2 函数。例如,使用不同的 :id 和 10 秒(10_000 毫秒)的 :shutdown 值运行计数器

children = [
  Supervisor.child_spec({Counter, 0}, id: MyCounter, shutdown: 10_000)
]

管理程序策略和选项

到目前为止,我们已经启动了管理程序,将单个子项作为元组传递,并使用名为 :one_for_one 的策略

children = [
  {Counter, 0}
]

Supervisor.start_link(children, strategy: :one_for_one)

提供给 start_link/2 的第一个参数是子项规范列表,如上面的“child_spec/1”部分所定义。

第二个参数是一个选项关键字列表

  • :strategy - 管理策略选项。可以是 :one_for_one:rest_for_one:one_for_all。必需。请参阅“策略”部分。

  • :max_restarts - 在一个时间范围内允许的最大重启次数。默认为 3

  • :max_seconds - :max_restarts 适用的时间范围。默认为 5

  • :auto_shutdown - 自动关闭选项。可以是 :never:any_significant:all_significant。可选。请参阅“自动关闭”部分。

  • :name - 要用于注册管理程序进程的名称。受支持的值在 GenServer 的文档中的“名称注册”部分中有说明。可选。

策略

管理程序支持不同的管理策略(通过 :strategy 选项,如上文所示)

  • :one_for_one - 如果子进程终止,则仅重新启动该进程。

  • :one_for_all - 如果子进程终止,则终止所有其他子进程,然后重新启动所有子进程(包括已终止的进程)。

  • :rest_for_one - 如果子进程终止,则终止已终止的子进程以及在它之后启动的其他子进程,并重新启动。

在上述情况下,进程终止指的是失败终止,这是由 :restart 选项决定的。

要高效地管理动态启动的子进程,请参阅 DynamicSupervisor

自动关闭

当标记为 :significant 的子进程退出时,管理程序能够自动关闭自身。

督导支持不同的自动关机选项(通过上面讨论的 :auto_shutdown 选项)

  • :never - 这是默认设置,禁用自动关机。

  • :any_significant - 如果任何显著的子进程退出,督导将自动关机它的子进程,然后它自身关机。

  • :all_significant - 当所有显著的子进程退出时,督导将自动关机它的子进程,然后它自身关机。

只有 :transient:temporary 子进程才能被标记为显著的,并且此配置影响行为。显著的 :transient 子进程必须正常退出才能考虑自动关机,而 :temporary 子进程可以因任何原因退出。

名称注册

督导与 GenServer 绑定到相同的名称注册规则。在 GenServer 的文档中详细了解这些规则。

基于模块的督导

在目前的例子中,督导通过将督导结构传递给 start_link/2 来启动。但是,也可以通过明确定义督导模块来创建督导

defmodule MyApp.Supervisor do
  # Automatically defines child_spec/1
  use Supervisor

  def start_link(init_arg) do
    Supervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
  end

  @impl true
  def init(_init_arg) do
    children = [
      {Counter, 0}
    ]

    Supervisor.init(children, strategy: :one_for_one)
  end
end

两种方法的区别在于,基于模块的督导让你对如何初始化督导有更直接的控制。我们并不调用 Supervisor.start_link/2,它附带一个隐式为我们初始化的子规范列表,我们必须显式地通过在它的 init/1 回调中调用 Supervisor.init/2 来初始化子级。 Supervisor.init/2 接受相同的 :strategy, :max_restarts:max_seconds 选项作为 start_link/2

使用 Supervisor

当你 使用 Supervisor 时,Supervisor 模块将设置 @behaviour Supervisor 并且定义一个 child_spec/1 函数,因此你的模块可用作督导树中的子级。

使用 Supervisor 还将定义 child_spec/1 函数,它允许我们运行 MyApp.Supervisor 作为另一个督导的子级或在你的督导树顶部作为

children = [
  MyApp.Supervisor
]

Supervisor.start_link(children, strategy: :one_for_one)

一般原则是,仅在监督树顶部使用没有回调模块的监督者,通常在 Application.start/2 回调中使用。我们建议对应用程序中的任何其他监督者使用基于模块的监督者,以便它们可以在树中作为另一个监督者的子项运行。由 Supervisor 自动生成的 child_spec/1 可以使用以下选项自定义

  • :id - 子项规范标识符,默认为当前模块
  • :restart - 监督者应该何时重新启动,默认为 :permanent

紧接在 use Supervisor 之前的 @doc 注释将附加到生成的 child_spec/1 函数中。

启动和关闭

当监督者启动时,它遍历所有子项规范,然后按照定义的顺序启动每个子项。这是通过调用子项规范中 :start 键下定义的函数来完成的,并且通常默认为 start_link/1

然后为每个子进程调用 start_link/1(或自定义)。start_link/1 函数必须返回 {:ok, pid},其中 pid 是链接到监督者的新进程的进程标识符。子进程通常通过执行 init/1 回调来开始其工作。概括地说,init 回调是我们初始化和配置子进程的位置。

关闭过程按相反的顺序发生。

当监督者关闭时,它以相反的列表顺序终止所有子项。终止通过向子进程发送关闭退出信号(Process.exit(child_pid, :shutdown))然后等待一段时间让子进程终止来完成的。此时间间隔默认为 5000 毫秒。如果子进程在此间隔内未终止,则监督者会以原因 :kill 突然终止子进程。可以在下一部分中充分详细了解子项规范中配置的关闭时间。

如果子进程未捕获退出,则在收到第一个退出信号时它将立即关闭。如果子进程正在捕获退出,则会调用 terminate 回调,并且子进程必须在被监督者突然终止之前在合理的时段内终止。

换句话说,如果一个进程在您的应用程序或监督树关闭时对其自体进行清理很重要,那么这个进程就必须捕获退出,并且它的子规范应该指定一个合法的 :shutdown 值,以确保它在一个合理的时间间隔内终止。

退出原因和重新启动

主管会依据孩子的 :restart 配置来重启子进程。例如,当 :restart 设置为 :transient 时,如果子进程在 :normal:shutdown{:shutdown, term} 等原因下退出,主管就不会重启这个子进程。

这些退出也会影响日志记录。默认情况下,当退出原因是 :normal:shutdown{:shutdown, term} 时,GenServers 等行为不会发送错误日志。

因此,有人可能会问:我应当选择哪个退出原因?有三个选项

  • :normal - 在这些情况下,退出不会被记录,在瞬态模式下不会重启,并且已链接的进程不会退出

  • :shutdown{:shutdown, term} - 在这些情况下,退出不会被记录,在瞬态模式下不会重启,并且已链接的进程会以同样的原因退出,除非它们捕获退出

  • 任何其他项 - 在这些情况下,退出将被记录,在瞬态模式下会有重新启动,并且已链接的进程会以同样的原因退出,除非它们捕获退出

总体而言,如果您正在由于预期的原因退出,您需要使用 :shutdown{:shutdown, term}

请注意,达到最大重启强度的主管会以 :shutdown 原因退出。在这种情况下,只有当其子进程规范被定义为 :restart 选项设置为 :permanent(默认为)时,主管才会被重启。

概要

类型

支持的自动关闭选项。

子进程。

主管子规范。

提供给 start_link/2init/1 的选项。

基于模块的子规范。

主管名称。

返回 start_link/2start_link/3 的值。

返回 start_child/2 的值。

选项 start_link/2start_link/3 函数用的值。

支持的重启选项。

支持的关闭选项。

支持的策略。

初始化中返回的 supervisor 标记。

supervisor 引用。

受 supervisor 监管的子级的类型。

回调

用于启动 supervisor 和热代码更新期间调用的回调。

函数

构建并重写子级规范。

返回包含指定 supervisor 的计数值的地图。

删除由 child_id 标识的子级规范。

接收要初始化的子级规范的列表和一组 options

重新启动一个由 child_id 标识的子进程。

将一个子级规范添加到 supervisor 并启动该子级。

启动一个具有给定子级的 supervisor。

使用给定的 moduleinit_arg 启动一个基于模块的 supervisor 进程。

使用给定的 reason 同步停止给定的 supervisor。

终止由 child_id 标识的给定子级。

返回一个有关给定 supervisor 的所有子级的信息的列表。

类型

@type auto_shutdown() :: :never | :any_significant | :all_significant

支持的自动关闭选项。

@type child() :: pid() | :undefined

子进程。

当子进程启动时,它可以是一个 PID,或者当子进程由 动态 supervisor 创建时,它可以是 :undefined

@type child_spec() :: %{
  :id => atom() | term(),
  :start => {module(), function_name :: atom(), args :: [term()]},
  optional(:restart) => restart(),
  optional(:shutdown) => shutdown(),
  optional(:type) => type(),
  optional(:modules) => [module()] | :dynamic,
  optional(:significant) => boolean()
}

主管子规范。

它定义了 supervisor 应如何启动、停止和重新启动其每个子级。

@type init_option() ::
  {:strategy, strategy()}
  | {:max_restarts, non_neg_integer()}
  | {:max_seconds, pos_integer()}
  | {:auto_shutdown, auto_shutdown()}

提供给 start_link/2init/1 的选项。

链接到此类型

module_spec()

查看源 (自 1.16.0 起)
@type module_spec() :: {module(), args :: term()} | module()

基于模块的子规范。

这是一种子级规范的格式,除了规范化的 child_spec/0 之外,你还可以在传递给 child_spec/2start_child/2start_link/2 等函数中使用此格式。

基于模块的子级规范可以是

  • 一个模块 — supervisor 调用 module.child_spec([]) 来检索子级规范

  • 一个形状为 {module, arg}二元素元组 — supervisor 调用 module.child_spec(arg) 来检索子级规范

@type name() :: atom() | {:global, term()} | {:via, module(), term()}

主管名称。

@type on_start() ::
  {:ok, pid()}
  | :ignore
  | {:error, {:already_started, pid()} | {:shutdown, term()} | term()}

返回 start_link/2start_link/3 的值。

@type on_start_child() ::
  {:ok, child()}
  | {:ok, child(), info :: term()}
  | {:error, {:already_started, child()} | :already_present | term()}

返回 start_child/2 的值。

@type option() :: {:name, name()}

选项 start_link/2start_link/3 函数用的值。

@type restart() :: :permanent | :transient | :temporary

支持的重启选项。

@type shutdown() :: timeout() | :brutal_kill

支持的关闭选项。

@type strategy() :: :one_for_one | :one_for_all | :rest_for_one

支持的策略。

@type sup_flags() :: %{
  strategy: strategy(),
  intensity: non_neg_integer(),
  period: pos_integer(),
  auto_shutdown: auto_shutdown()
}

初始化中返回的 supervisor 标记。

@type supervisor() :: pid() | name() | {atom(), node()}

supervisor 引用。

@type type() :: :worker | :supervisor

受 supervisor 监管的子级的类型。

受 supervisor 监管的子级是 worker 还是 supervisor。

回调

@callback init(init_arg :: term()) ::
  {:ok,
   {sup_flags(),
    [child_spec() | (old_erlang_child_spec :: :supervisor.child_spec())]}}
  | :ignore

用于启动 supervisor 和热代码更新期间调用的回调。

开发人员通常会在其 init 回调的末尾调用 Supervisor.init/2,以返回适当的监督标志。

函数

指向该函数的链接

child_spec(module_or_map, overrides)

查看源
@spec child_spec(
  child_spec() | module_spec(),
  keyword()
) :: child_spec()

构建并重写子级规范。

start_link/2init/2 类似,它需要一个模块、{模块,参数}子项规范

如果给定形状为 {模块,参数} 的元素二元组,则通过调用 module.child_spec(参数) 来检索子项规范。

如果给定一个模块,则通过调用 module.child_spec([]) 来检索子项规范。

检索子项规范后,overrides 中的字段将直接应用于子项规范。如果 overrides 具有未映射到任何子项规范字段的键,则会引发错误。

请参阅模块文档中的“子项规范”部分,了解可覆盖的所有键。

示例

该函数通常用于在监督树中多次启动同一模块时设置 :id 选项

Supervisor.child_spec({Agent, fn -> :ok end}, id: {Agent, 1})
#=> %{id: {Agent, 1},
#=>   start: {Agent, :start_link, [fn -> :ok end]}}
指向该函数的链接

count_children(supervisor)

查看源
@spec count_children(supervisor()) :: %{
  specs: non_neg_integer(),
  active: non_neg_integer(),
  supervisors: non_neg_integer(),
  workers: non_neg_integer()
}

返回包含指定 supervisor 的计数值的地图。

映射包含以下键

  • :specs - 所有子项的总数,无论是否存活

  • :active - 该监督程序管理的所有主动运行的子进程的数量

  • :supervisors - 所有监督程序的数量,无论这些子监督程序是否仍然存活

  • :workers - 所有工作进程的数量,无论这些子进程是否仍然存活

指向该函数的链接

delete_child(supervisor, child_id)

查看源
@spec delete_child(supervisor(), term()) :: :ok | {:error, error}
when error: :not_found | :running | :restarting

删除由 child_id 标识的子级规范。

相应的子进程一定不能在运行;如果正在运行,请使用 terminate_child/2 终止该进程。

如果成功,该函数将返回 :ok。如果找不到 child_id,或者当前进程正在运行或正在重新启动,该函数可能会返回带有适当错误元组的错误。

指向该函数的链接

init(children, options)

查看源代码 (自 1.5.0 起)
@spec init(
  [
    child_spec()
    | module_spec()
    | (old_erlang_child_spec :: :supervisor.child_spec())
  ],
  [
    init_option()
  ]
) ::
  {:ok,
   {sup_flags(),
    [child_spec() | (old_erlang_child_spec :: :supervisor.child_spec())]}}

接收要初始化的子级规范的列表和一组 options

这通常在基于模块的监督程序的 init/1 回调的末尾调用。有关更多信息,请参阅模块文档中的“监督程序策略和选项”和“基于模块的监督程序”部分。

此函数返回包含监控标记和子项规范的元组。

示例

def init(_init_arg) do
  children = [
    {Counter, 0}
  ]

  Supervisor.init(children, strategy: :one_for_one)
end

选项

  • :strategy - 监控策略选项。它可以是 :one_for_one:rest_for_one:one_for_all

  • :max_restarts - 在一个时间范围内允许的最大重启次数。默认为 3

  • :max_seconds - :max_restarts 适用的秒时间范围。默认为 5

  • :auto_shutdown - 自动关闭选项。它可以是 :never:any_significant:all_significant

:strategy 选项是必需的,并且默认情况下在 5 秒内最多允许重启 3 次。查看 Supervisor 模块,了解可用策略的详细描述。

指向该函数的链接

restart_child(supervisor, child_id)

查看源
@spec restart_child(supervisor(), term()) ::
  {:ok, child()} | {:ok, child(), term()} | {:error, error}
when error: :not_found | :running | :restarting | term()

重新启动一个由 child_id 标识的子进程。

子项规范必须存在,并且相应的子进程不得正在运行。

请注意,对于临时子项,子项终止时子项规范将自动删除,因此无法重新启动此类子项。

如果子进程启动函数返回 {:ok, child}{:ok, child, info},那么 PID 会被添加到监控器,并且此函数返回相同的值。

如果子进程启动函数返回 :ignore,那么 PID 仍设置为 :undefined,并且此函数返回 {:ok, :undefined}

如果未找到 child_id,或是当前进程正在运行或正在重启,此函数可能会返回带有相应错误元组的错误。

如果子进程启动函数返回错误元组或错误值,或者它失败,那么此函数会返回 {:error, error}

指向该函数的链接

start_child(supervisor, child_spec)

查看源
@spec start_child(
  supervisor(),
  child_spec()
  | module_spec()
  | (old_erlang_child_spec :: :supervisor.child_spec())
) :: on_start_child()

将一个子级规范添加到 supervisor 并启动该子级。

child_spec 应为有效的子项规范。子进程将按照子项规范中定义的那样启动。

如果已存在具有指定 ID 的子项规范,则 child_spec 会被丢弃,并且此函数会返回一个带有 :already_started:already_present 的错误(如果相应的子进程正在运行或未运行)。

如果子进程启动函数返回 {:ok, child}{:ok, child, info},那么子项规范和 PID 会被添加到监控器,并且此函数会返回相同的值。

如果子进程启动函数返回 :ignore,那么子项规范会添加到监控器,PID 设置为 :undefined,并且此函数会返回 {:ok, :undefined}

如果子进程启动函数返回一个错误元组或一个错误值,或如果它失败,则子级规范将被丢弃,并且该函数返回 {:error, error} 其中 error 是一个包含有关错误和子级规范信息的术语。

指向该函数的链接

start_link(children, options)

查看源
@spec start_link(
  [
    child_spec()
    | module_spec()
    | (old_erlang_child_spec :: :supervisor.child_spec())
  ],
  [
    option() | init_option()
  ]
) ::
  {:ok, pid()}
  | {:error, {:already_started, pid()} | {:shutdown, term()} | term()}
@spec start_link(module(), term()) :: on_start()

启动一个具有给定子级的 supervisor。

children 是以下形式的列表

  • 一个子级规范(见 child_spec/0)

  • 一个模块,其中监督程序调用 module.child_spec([]) 来检索子级规范(见 module_spec/0)

  • 一个 {module, arg} 元组,其中监督程序调用 module.child_spec(arg) 来检索子级规范(见 module_spec/0)

  • 一个(旧的)Erlang 风格的子级规范(见 :supervisor.child_spec())

需要通过 :strategy 选项提供一个策略。有关示例和其他选项,请参见“监督程序策略和选项”。

这些选项还可以用于注册监督程序名称。受支持的值在 GenServer 模块文档中的“名称注册”一节中进行了说明。

如果成功生成监督程序和所有子进程(如果每个子进程的启动函数返回 {:ok, child}, {:ok, child, info}:ignore),则此函数返回 {:ok, pid},其中 pid 是监督程序的 PID。如果给监督程序一个名称,并且已经存在一个具有指定名称的进程,则该函数返回 {:error, {:already_started, pid}},其中 pid 是该进程的 PID。

如果任何子进程的启动函数失败或返回一个错误元组或一个错误值,则监督程序首先以 :shutdown 原因终止所有已经启动的子进程,然后终止其自身并返回 {:error, {:shutdown, reason}}

请注意,使用此函数启动的监督程序链接到父进程,并不仅在崩溃时退出,而且当父进程以 :normal 原因退出时退出。

指向该函数的链接

start_link(module, init_arg, options \\ [])

查看源
@spec start_link(module(), term(), [option()]) :: on_start()

使用给定的 moduleinit_arg 启动一个基于模块的 supervisor 进程。

要启动监督程序, init/1 回调将在给定 module 中调用,其中 init_arg 为其参数。 init/1 回调必须返回一个监督程序规范,该规范可以在 init/2 函数的帮助下创建。

如果 init/1 回调返回 :ignore,此函数也将返回 :ignore,并且监督器终止,其原因是 :normal。如果回调失败或返回不正确的的值,则此函数将返回 {:error, term},其中 term 是包含有关错误的信息的项,并且监督器终止,其原因是 term

:name 选项还可以用于注册监督器名称,支持的值在 GenServer 模块文档中的“名称注册”部分进行了描述。

指向该函数的链接

stop(supervisor, reason \\ :normal, timeout \\ :infinity)

查看源
@spec stop(supervisor(), reason :: term(), timeout()) :: :ok

使用给定的 reason 同步停止给定的 supervisor。

如果监督器以给定的原因终止,则此函数将返回 :ok。如果监督器以其他原因终止,则该调用将退出。

此函数保留了 OTP 在错误报告方面的语义。如果原因不是 :normal:shutdown{:shutdown, _},则会记录一条错误报告。

指向该函数的链接

terminate_child(supervisor, child_id)

查看源
@spec terminate_child(supervisor(), term()) :: :ok | {:error, :not_found}

终止由 child_id 标识的给定子级。

如果存在进程,则终止该进程。如果该子进程是临时的,则保留子进程规范。

一个非临时子进程稍后可以由监督器重新启动。也可以通过调用 restart_child/2 显式重新启动子进程。使用 delete_child/2 删除子进程规范。

如果成功,此函数将返回 :ok。如果给定子进程 ID 没有子进程规范,则此函数将返回 {:error, :not_found}

指向该函数的链接

which_children(supervisor)

查看源
@spec which_children(supervisor()) :: [
  {term() | :undefined, child() | :restarting, :worker | :supervisor,
   [module()] | :dynamic}
]

返回一个有关给定 supervisor 的所有子级的信息的列表。

请注意,当在内存不足的情况下监督大量子进程时,调用此函数可能会导致内存不足异常。

此函数返回 {id, child, type, modules} 元组列表,其中

  • id - 如子进程规范中所定义

  • child - 相应子进程的 PID,如果该进程即将重新启动,则为 :restarting,如果该进程不存在,则为 :undefined

  • type - :worker:supervisor,由子进程规范指定

  • modules - 如子进程规范中所指定