主管——任由事情崩溃
在之前的文章中,我们探讨了 Elixir 中进程管理的各个方面。从创建异步任务进程到跨长时间运行进程持久化状态,甚至实现基于消息的架构以进行高效的进程通信,天哪,Elixir 的进程还能做多少事情啊!?嗯——在本文中,我们将深入探讨 Elixir 进程管理的另一个关键方面:处理进程的生命周期,特别是进程终止/死亡的情况。
Supervisors 是一个围绕进程构建的简洁架构/抽象层(一如既往),它位于其他进程之上,用于检查我们进程的状态,并根据自定义策略恢复任何进程。有了它,我们就可以大声说出 Elixir 的经典短语“让它崩溃吧”。
目录
什么是主管
监控程序本身也是一个进程,你能想象吗?万物皆进程!但这个监控程序有其特定用途,即控制/监控其他进程,以检查它们是否仍在运行或是否已经崩溃。
使用这些监督器会创建我们所说的监督树,它驱动着许多大型框架(例如Phoenix)为应用程序中的进程提供容错控制和可视化,这让我们在信任强大的 Erlang VM 的同时,获得了更大的控制力和性能。
本文标题为“让程序崩溃”,原因很简单:这在 Elixir 社区是一种非常普遍的思维方式,这主要归功于 Supervisor 机制。通过让函数不可变并作为独立进程运行,进程只需终止并由 Supervisor 重启,而无需构建复杂的结构来防止崩溃,从而大大降低了崩溃的发生概率。Elixir 语言的结构本身也围绕着这个理念展开。
指定主管
监督者抽象行为的工作原理是接受start_link在模块列表中实现该功能的其他进程(基本上是生成服务器),因此声明它非常简单,如下所示:
defmodule OurSupervisor do
use Supervisor
def start_link(opts) do
Supervisor.start_link(__MODULE__, :ok, opts)
end
@impl true
def init(:ok) do
children = [
SomeGenServer
]
Supervisor.init(children, strategy: :one_for_one)
end
end
既然监督者也只是另一个进程,我们应该先定义它自己的start_link功能。还记得前面提到的监督者可以接受任何实现了该start_link功能的进程吗?所以,正如你现在所想的,我们可以让监督者接受其他监督者的监督,因为这本质上就是进程之间的交互!是不是很棒?
该init函数用于实例化子进程并定义一个特定的策略,该策略定义了当进程终止时,监督进程将如何重新启动它,我们将在下面看到更详细的信息:
one_for_one这是所有策略中“标准”且最简单的策略,它只是重新启动终止的特定子进程,同时监控其他进程,使其能够原地运行。one_for_all这个有点复杂,它不仅是《我的英雄学院》的强大能力,也是保持多个进程同步的非常有效的策略!一旦一个进程终止,所有其他剩余的进程都会与原来的进程一起重新启动,这使得在需要时保持所有进程同步变得非常容易。rest_for_one最后,这个方法与之前的方法类似,但也有one_for_one一些特殊之处。它会在特定进程终止后重启该进程,但也会重启所有在该进程之后启动的进程(很可能是由该进程启动的进程)。如果您有一个集中式进程,需要分发操作,并且该进程需要与其分发的进程同时保持运行,那么这个方法就很有用。
一旦我们定义了这个监控模块,我们就可以获得一些 API 来管理树下的进程:
which_children/1:此函数列出主监督者下的所有子进程及其对应的进程 ID,以便我们可以进一步重用和操作。start_child/2:此函数以编程方式启动特定的子进程,或者返回一个元组,{:error, {:already_started, pid}}表示该进程是否已存在。stop/1:此函数停止管理者本身(及其树下的所有子节点)。
什么是监督树以及如何用图形方式可视化它
与其他语言相比,Elixir 最突出的特点就是它使用监督者创建的监督树。它非常灵活,因为一个监督者可以把另一个监督者作为子监督者,但这同时也导致同时运行的进程数量呈指数级增长。
针对这种情况,我们可以使用 Erlang VM 原生提供的强大工具来可视化进程和监督树:
:observer.start()该工具通过调用REPL运行,但如果您使用iex -S mix此工具,请注意它并非免费提供,我们需要按如下方式激活它:
$ iex -S mix
iex(1)> Mix.ensure_application!(:wx); Mix.ensure_application!(:runtime_tools);Mix.ensure_application!(:observer)
:ok
iex(2)> :observer.start()
:ok
将会打开一个图形用户界面,您可以在其中查看和调试有关正在运行的 Erlang 虚拟机的全部详细信息,查看监控树,检查内存使用情况等等。在开发大型应用程序时,依靠这个工具来确保您遵循最佳模式和设计决策是非常强大的!
创建一个与主管已实施的混合项目
由于这种架构在 Elixir 世界中相当常见,因此mix可以使用已知的工具生成一个已经设置好监督树的示例项目,这样您就可以直接插入新的进程模块。下面我们将了解更实际的部分:
您只需运行以下命令即可启动一个新项目:
$ mix new project_name --sup
这将创建以下文件结构:
.
├── README.md
├── lib
│ ├── project_name
│ │ └── application.ex
│ └── project_name.ex
├── mix.exs
└── test
├── project_name_test.exs
└── test_helper.exs
4 directories, 6 files
该application.ex文件定义了我们的根监控程序,我们可以在其中注册不同的进程。
该项目创建过程中定义的另一项内容是修改mix.exs自动启动监督树的功能:
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger],
mod: {Teste.Application, []}
]
end
结论
最终目标达成!这是关于使用 Elixir 进行流程管理系列文章的最后一篇,希望这里的内容对大家有所帮助,就像它对我一样。Elixir 的核心概念和社区都非常棒,能和大家分享这段经历真是太好了。如果有什么需要帮忙的,尽管开口,愿原力与你同在🍒
文章来源:https://dev.to/cherryramatis/supervisors-letting-things-crash-1lh2