3

初学 Elixir, 我常常对 Supervisor 的概念感到疑惑. 没有Supervisor, 程序也能正常运行, 为什么还要加上这个东西呢? 原来, 这和 Erlang 的基本信条 —— “就让它崩溃” 有关, 在 Erlang 中, 是没有 try…rescue 这样的错误救援机制的. 取而代之的是 Supervisor 机制, Supervisor 是一个专门用来监督进程的进程, 每当它的名下有进程出错崩溃, Supervisor就会按照既定的策略重启进程.

我们将 Supervisor 名下的进程叫做子进程, 将由 Supervisor 和子进程所组成的监督网络称为 监督树. 有了监督树的帮助, 我们可以方便地构建一个高容错的应用.

这里要注意模块和进程的关系. 模块是函数的集合, 而类似 start_link 的函数可以创建进程,

那么, 如何在 phoenix 项目里使用 Supervisor 呢? ELixir 内置了两个模块: Supervisor 和 Supervisor.Spec.

Supervisor.Spec

我们可以这样设置 supervisor 名下的子进程:

import Supervisor.Spec

children = [
  worker(MyWorker, [arg1, arg2, arg3]),
  supervisor(MySupervisor, [arg1])   # 这个 supervisor 也是子进程
]

Supervisor.start_link(children, strategy: :one_for_one)

Supervisor

这是以模块的方式配置 supervisor, 它包含了 import Supervisor.Spec:

defmodule MySupervisor do
  use Supervisor

  def start_link(arg) do
    Supervisor.start_link(__MODULE__, arg)  # 会调用 init 回调
  end

  def init(arg) do
    children = [
      worker(MyWorker, [arg], restart: :temporary)
    ]

    supervise(children, strategy: :simple_one_for_one)
  end
end

‘Supervisor.Spec`的主要函数

supervise(children, options) 会以tuple 的形式返回一个 supervisor配置.

supervisor(module, args, options \ []) 将给定的模块设置为一个 supervisor.

worker(module, args, options \ []) 将一个给定的模块定义为 worker.

Supervisor 的主要函数

count_children(supervisor)
返回一个清点子进程数的映射.
delete_child(supervisor, child_id)
通过子进程的id 删除其配置信息.
restart_child(supervisor, child_id)
通过 id 重启子进程.
start_child(supervisor, child_spec_or_args)
动态添加子进程配置信息, 并启动它.
start_link(children, options)
按给定的子进程启动一个 supervisor.
start_link(module, arg, options \ [])
按给定的模块启动一个 supervisor .
stop(supervisor, reason \ :normal, timeout \ :infinity)
停止 supervisor.
terminate_child(supervisor, pid_or_child_id)
终止子进程.
which_children(supervisor)
返回子进程列表.


Ljzn
399 声望102 粉丝

网络安全;函数式编程;数字货币;人工智能