OTP 应用程序的结构
OTP是一个提供标准来帮助构建 Erlang 应用程序的框架,它使用应用程序将代码打包成单元或组件。此约定有助于将代码组织成逻辑模块组,并提供一种启动和停止应用程序监控器的方法。您的 Phoenix 项目是一个应用程序,但 Phoenix 框架和 Ecto 也是应用程序。
应用程序是一个可以自行编译的组件。它可以依赖于其他应用程序,并且可以采用自己的配置。与其他语言不同,此约定提供了一种标准化的方式来指定虚拟机应如何与其交互。
除了包含虚拟机代码的编译.beam
文件之外,已编译的应用程序还包含一个应用程序规范 .app
文件(用于告诉虚拟机如何处理应用程序)和一个可选的应用程序回调模块(用于启动、运行和停止应用程序)。
应用程序回调模块
对于带有监管器的应用程序,应用程序回调模块定义了启动和停止应用程序的函数。不带有监管器的应用程序(通常是一些库,其中包含一些无需内部状态即可调用的函数)则省略了回调模块,因为应用程序无需启动。
在 Elixir 中,应用程序回调模块使用行为。该Application
行为实现了start/2
和stop/1
回调。前者启动应用程序的主监督器,后者是一个可选回调,用于在监督器停止后进行清理。
mix new
当使用该标志生成新项目时,Elixir 的任务会自动创建应用程序回调模块--sup
。
$ mix new --sup elixir_app
生成的项目包含一个应用程序回调模块lib/elixir_app/application.ex
。
defmodule ElixirApp.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
def start(_type, _args) do
# List all child processes to be supervised
children = [
# Starts a worker by calling: ElixirApp.Worker.start_link(arg)
# {ElixirApp.Worker, arg},
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: ElixirApp.Supervisor]
Supervisor.start_link(children, opts)
end
end
使用时,该Application
模块会添加函数的存根stop/1
,该存根返回一个 ok 元组。生成的start/2
函数会启动应用程序的主监控器。
应用程序规范
运行mix compile.app
将应用程序的规范放在.app
目录中的文件中ebin
,这是使用 编译应用程序时采取的步骤之一mix compile
。
该规范包含定义应用程序的 Erlang 术语。它列出了应用程序中定义的模块、版本号以及应用程序所依赖的其他应用程序列表。它还指定了用作启动应用程序的回调的模块。
规范文件基于应用程序mix.exs
文件中的设置。为了能够生成规范,每个应用程序必须在其项目函数中定义名称和版本号。
defmodule ElixirApp.MixProject do
use Mix.Project
def project do
[
app: :elixir_app,
version: "0.1.0"
]
end
end
下面是一个最小示例mix.exs
文件,其中仅列出项目名称和版本号,并生成一个列出 Elixir 默认应用程序并指定应用程序中的模块的规范。
{application,elixir_app,
[{applications,[kernel,stdlib,elixir]},
{description,"elixir_app"},
{modules,['Elixir.ElixirApp','Elixir.ElixirApp.Application']},
{registered,[]},
{vsn,"0.1.0"}]}.
指定和配置应用程序回调模块
除了应用程序的名称和版本号之外,mix.exs
使用该--sup
选项生成的文件还具有带键application
的功能mod
。
defmodule ElixirApp.MixProject do
# ...
def application do
[
mod: {ElixirApp.Application, []}
]
end
end
生成规范时,它包含回调模块。此键指向应用程序的回调模块,因此虚拟机知道使用哪个模块来启动应用程序。
{application,elixir_app,
[{applications,[kernel,stdlib,elixir,logger]},
{description,"elixir_app"},
{modules,['Elixir.ElixirApp','Elixir.ElixirApp.Application']},
{registered,[]},
{vsn,"0.1.0"},
{mod,{'Elixir.ElixirApp.Application',[]}}]}.
提示:-tuple 中的列表:mod
用于将配置选项传递给应用程序。传入的任何内容最终都会作为回调模块start/2
函数的参数。
:applications
和:extra_applications
applications
规范中的键列出了你的应用所依赖的所有应用程序。默认情况下,mix compile.app
包括kernel
stdlib
和elixir
。
defmodule ElixirApp.MixProject do
use Mix.Project
def project do
[
app: :elixir_app,
version: "0.1.0",
deps: [{:appsignal, "~> 1.0.0"}]
]
end
end
依赖项会自动添加到applications
列表中,并且如果它们具有应用程序模块,则会在应用程序启动之前自动启动。
{application,elixir_app,
[{applications,[kernel,stdlib,elixir,appsignal]},
{description,"elixir_app"},
{modules,['Elixir.ElixirApp','Elixir.ElixirApp.Application']},
{registered,[]},
{vsn,"0.1.0"}]}.
要添加更多类似 Elixir 的应用程序logger
,您可以将它们添加到:extra_applications
密钥中,然后它们将被添加到现有列表中。
defmodule ElixirApp.MixProject do
# ...
def application do
[
extra_applications: [:logger],
mod: {ElixirApp.Application, []}
]
end
end
该:applications
键用于明确指定要包含在规范中的应用程序。使用时,只会添加列出的应用程序和默认应用程序。任何其他依赖项都不会自动包含。
应用程序一路下降
每个独立的库以及你的应用程序本身都是一个应用程序。有些应用程序有一个监督树,需要它们有一个回调模块来负责启动和停止整个应用程序。
你的应用程序可以依赖于其他应用程序,每个应用程序都可以拥有自己的监督树和回调模块。无论其中包含什么,它始终会在应用程序规范文件中指定。你可以把它想象成 Elixir 的配方。😉
以上就是我们对 Elixir 中 OTP 应用的概述。我们尝试展现了 OTP 约定之一的魅力。这也是我们喜爱 Elixir 的众多原因之一。如果您也喜欢,请在评论区告诉我们您希望我们写什么,或者订阅Elixir Alchemy 的新闻简报。
文章来源:https://dev.to/appsignal/how-otp-applications-are-structed-59e4