12 - 应用详解
第 12 章:Application — OTP 应用生命周期
Application 是 OTP 中组织代码和管理生命周期的最高级别抽象。本章学习应用的启动、配置和发布。
12.1 Application 行为
12.1.1 回调模块
%% myapp_app.erl
-module(myapp_app).
-behaviour(application).
-export([start/2, stop/1, prep_stop/1, config_change/3]).
%% 应用启动
start(StartType, StartArgs) ->
io:format("Starting ~p with type=~p args=~p~n",
[?MODULE, StartType, StartArgs]),
case myapp_sup:start_link() of
{ok, Pid} ->
{ok, Pid};
Error ->
Error
end.
%% 应用停止前
prep_stop(State) ->
io:format("Preparing to stop...~n"),
State.
%% 应用停止
stop(_State) ->
io:format("Application stopped~n"),
ok.
%% 配置变更通知
config_change(Changed, New, Removed) ->
io:format("Config changed: ~p new: ~p removed: ~p~n",
[Changed, New, Removed]),
ok.
12.1.2 StartType
| 类型 | 说明 |
|---|---|
normal | 正常启动 |
{takeover, Node} | 从另一个节点接管 |
{failover, Node} | 主节点故障后在备用节点启动 |
12.1.3 应用依赖
%% src/myapp.app.src
{application, myapp, [
{vsn, "1.0.0"},
{mod, {myapp_app, []}},
{applications, [
kernel, %% 必须
stdlib, %% 必须
cowboy, %% HTTP 服务器
jsx, %% JSON
mnesia %% 数据库
]},
{included_applications, [
%% 随主应用一起启动,但不在主应用的监督树中
]},
{optional_applications, [
%% 可选依赖,不会阻止启动
]}
]}.
12.2 应用管理
12.2.1 启动和停止
%% 启动应用
application:start(myapp).
application:ensure_all_started(myapp). %% 启动所有依赖
%% 停止应用
application:stop(myapp).
%% 应用状态
application:which_applications(). %% 所有运行的应用
application:get_application(). %% 当前进程所属应用
application:get_all_env(myapp). %% 所有配置
12.2.2 配置管理
%% config/sys.config
[
{myapp, [
{http_port, 8080},
{db_host, "localhost"},
{db_port, 5432},
{pool_size, 10},
{log_level, info}
]}
].
%% 读取配置
{ok, Port} = application:get_env(myapp, http_port).
Host = application:get_env(myapp, db_host, "127.0.0.1").
%% 动态设置配置
application:set_env(myapp, http_port, 9090).
%% 在 VM 参数中覆盖
%% erl -myapp http_port 9090
12.3 项目结构
myapp/
├── rebar.config
├── config/
│ ├── sys.config ← 应用配置
│ └── vm.args ← VM 参数
├── src/
│ ├── myapp.app.src ← 应用描述
│ ├── myapp_app.erl ← Application 回调
│ ├── myapp_sup.erl ← 顶层 Supervisor
│ ├── myapp_server.erl ← 业务 GenServer
│ └── myapp_handler.erl ← 请求处理器
├── include/
│ └── myapp.hrl
├── test/
│ └── myapp_test.erl
├── priv/
│ └── static/
└── _build/
12.4 实战:完整 Web 应用
%% myweb_app.erl
-module(myweb_app).
-behaviour(application).
-export([start/2, stop/1]).
start(_StartType, _StartArgs) ->
{ok, Port} = application:get_env(myweb, port),
Dispatch = cowboy_router:compile([
{'_', [
{"/", myweb_handler, []},
{"/api/users", myweb_users_handler, []}
]}
]),
{ok, _} = cowboy:start_clear(myweb_http,
[{port, Port}],
#{env => #{dispatch => Dispatch}}
),
myweb_sup:start_link().
stop(_State) ->
cowboy:stop_listener(myweb_http),
ok.
12.5 注意事项
⚠️ 常见问题
- 循环依赖:应用 A 依赖 B,B 又依赖 A → 报错
- 启动顺序:依赖的应用必须先启动
- 配置不生效:检查 sys.config 路径和格式
- included vs regular:included_applications 不会自动启动
💡 最佳实践
- 使用
application:ensure_all_started/1启动应用及其依赖 - 所有配置放在 sys.config 中,避免硬编码
- 一个应用一个监督树
- 使用 rebar3 profiles 管理不同环境配置
12.6 扩展阅读
上一章:11 - 监督者详解 下一章:13 - ETS 表