ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Erlang/OTP Application 应用开发指南:回调模块、.app 资源文件与启动配置全解

Erlang/OTP Application 应用开发指南:回调模块、.app 资源文件与启动配置全解 编程语言语言运行时标准库编译器并发编程【免费下载链接】otpErlang/OTP项目地址https://gitcode.com/gh_mirrors/ot/otp点击查看免费下载本指南以 Erlang/OTP 设计原则中的 Applications 章节system/doc/design_principles/applications.md为骨架系统讲解如何将一段实现特定功能的代码封装为可独立启动、停止、复用的 OTP 应用Application从回调模块与.app资源文件的编写到目录结构规范、应用控制器与主控进程的底层运行机制再到加载、启动、停止与配置参数的完整实操。读完本文你将能够按照 OTP 规范亲手创建一个可纳入 release 打包的应用并理解application:start/1,2、application:get_env/2等核心 API 背后的源码执行路径。Application 概念在 OTP 中应用application是实现特定功能、可作为整体启动和停止、并可在其他系统中复用的组件。把代码组织成应用需要完成两步创建一个描述应用如何启动、如何停止的application callback module应用回调模块创建一份application specification应用规格放在application resource file应用资源文件即.app文件中该文件指定应用由哪些模块组成、回调模块的名字等。如果使用systoolsErlang/OTP 的打包工具参见 Releases每个应用的代码会被放入遵循预定义目录结构的独立目录中。需要说明的是Erlang/OTP 本身也是按照这一套原则打包的因此整个仓库如lib/kernel、lib/ssl的布局本身就是最好的范例。应用回调模块应用的启动与停止包括其监督树由两个回调函数描述start(StartType, StartArgs) - {ok, Pid} | {ok, Pid, State} stop(State)start/2在应用启动时被调用职责是启动顶层监督者、创建监督树。返回值是顶层监督者的 pid以及可选的State默认[]该State会原样传给stop/1。如果启动失败应返回{error, Reason}。StartType通常是原子normal。只有在分布式应用的 takeover 或 failover 场景下才会是其他值参见 Distributed Applications。在 lib/kernel/src/application.erl 中start_type()被精确定义为normal、{takeover,Node}或{failover,Node}。StartArgs由.app资源文件中的mod键定义。stop/1在应用停止之后被调用用于执行必要的清理。真正的停止动作关闭监督树由系统自动完成详见下文启动与停止应用。把 Supervisor Behaviour 中的监督树封装成一个应用回调模块示例如下-module(ch_app). -behaviour(application). -export([start/2, stop/1]). start(_Type, _Args) - ch_sup:start_link(). stop(_State) - ok.无法启动/停止的**库应用library application**不需要回调模块。在 lib/kernel/src/application.erl 中可以看到application行为还定义了可选回调prep_stop/1在进程被关闭前调用用于在停止前做同步/转移数据、config_change/3代码热替换后配置参数变化时调用和start_phase/3带 included applications 的分阶段启动三者通过-optional_callbacks声明为可选。应用资源文件应用规格放在.app文件中文件格式为{application, Application, [Opt1,...,OptN]}.Application是原子即应用名文件必须命名为Application.app每个Opt是{Key,Value}元组定义应用的一个属性所有键都是可选的缺省时使用默认值。一个库应用libapp的最小.app文件{application, libapp, []}.一个监督树应用ch_app的最小.app文件{application, ch_app, [{mod, {ch_app,[]}}]}.mod键定义了回调模块和启动参数这里分别是ch_app和[]。于是应用启动时会调用ch_app:start(normal, [])停止时会调用ch_app:stop([])使用systools打包时参见 Releases还需指定description、vsn、modules、registered、applications等键{application, ch_app, [{description, Channel allocator}, {vsn, 1}, {modules, [ch_app, ch_sup, ch3]}, {registered, [ch3]}, {applications, [kernel, stdlib, sasl]}, {mod, {ch_app,[]}} ]}.各键的语义如下键含义默认值description简短描述字符串vsn版本号字符串modules本应用引入的所有模块。systools用它生成 boot script 和 tar 文件一个模块只能属于一个应用[]registered应用中所有注册进程的名字。systools用它检测应用之间的名字冲突[]applications本应用启动前必须先启动的应用列表。systools用它生成正确的 boot script。注意所有应用都至少依赖 Kernel 和 STDLIB[]关于.app文件的完整语法与内容参见 Kernel 中的 app 文档。仓库里的真实样例可以印证上述格式。例如 bootstrap/lib/kernel/ebin/kernel.app 中kernel 应用列出了descriptionERTS CXC 138 10、vsn11.0.3、长长的modules列表application、application_controller、application_master等、registeredapplication_controller、code_server等、env如{logger_level, notice}、{net_ticktime, 60}以及mod{kernel, []}还额外展示了runtime_dependencies键[erts-17.0, stdlib-8.0, sasl-3.0, crypto-5.8]。目录结构使用systools打包时每个应用的代码放在独立目录lib/Application-VsnVsn为版本号。即使不用systools这个约定也值得了解——Erlang/OTP 自身即按此原则打包。代码服务器Kernel 中的 code 模块在存在多个版本时会自动使用版本号最高的目录。开发环境目录结构开发环境的目录结构可以比较自由但发布目录结构必须符合下文规范。官方鼓励开发环境与发布环境采用相同结构且应用目录名不写版本号版本号是发布阶段的产物。─ ${application} ├── doc │ ├── internal │ ├── examples │ └── src ├── include ├── priv ├── src │ └── ${application}.app.src └── test各目录的约定src必需存放 Erlang 源码、.app文件的源文件以及应用内部使用的 include 文件。src下可以有不超过一层的子目录作为命名空间来组织源文件priv可选存放应用专属的运行时文件include可选存放需要被其他应用访问的公共 include 文件doc推荐源文档放其子目录doc/internal推荐描述实现细节、不对外发布的文档doc/examples推荐使用示例的源码鼓励公共文档从这里引用示例doc/src推荐所有文档源文件Markdown、AsciiDoc、XML 等test推荐测试套件、测试规格等所有测试相关文件。其他注意事项非 Erlang 源码如 NIF 的 C 代码应放在单独目录约定以语言名作为前缀如c_srcC、java_srcJava、go_srcGo。带_src后缀的目录表示它是应用的一部分并参与编译最终构建产物应落入priv/lib或priv/binpriv目录存放应用运行期需要的资源可执行文件放priv/bin动态链接库放priv/lib其余资源建议结构化存放生成 Erlang 代码的源语言文件如 ASN.1、MIB应放在顶层或src下与源语言同名的目录asn1、mibs构建产物放进各自语言目录Erlang 代码放srcJava 代码放java_src开发环境中.app文件放ebin目录可以接受但推荐由构建步骤生成约定在src目录放.app.src文件它与.app几乎相同只是版本等字段在构建时被替换目录名不要大写鼓励省略空目录。仓库中ssl应用的 lib/ssl/src/ssl.app.src 是.app.src的典型示例vsn字段写作%VSN%占位符modules列出全部模块如tls_handshake、tls_record、ssl_config等构建时占位符被替换为真实版本号。发布环境目录结构已发布的应用必须遵循如下结构─ ${application}-${version} ├── bin ├── doc │ ├── html │ ├── man[1-9] │ ├── pdf │ ├── internal │ └── examples ├── ebin │ └── ${application}.app ├── include ├── priv │ ├── lib │ └── bin └── srcsrc可选Erlang 源码与内部 include 文件可用于调试ebin必需Erlang 目标码.beam文件.app文件也必须放在这里priv可选应用专属文件必须用code:priv_dir/1访问priv/lib推荐NIF、linked-in driver 等共享对象文件priv/bin推荐port program 等可执行文件include可选需要对外发布的公共 include 文件bin可选应用产出的可执行文件escript、shell 脚本等doc可选发布版文档放其子目录。include目录只在有公共 include 文件时才发布同样鼓励省略空目录。应用控制器与主控进程Erlang 运行时系统启动时作为 Kernel 应用的一部分会启动若干进程其中之一就是注册名为application_controller的应用控制器进程。所有对应用的操作都由它协调用户通过 Kernel 中的 application 模块来加载、卸载、启动、停止应用。从源码看lib/kernel/src/application_controller.erl 中应用控制器与应用主控application master之间通过{ac_load_application_req, Name}、{ac_start_application_req, Name}等消息协作lib/kernel/src/application.erl 中start/2的实现是先经ensure_loaded/1确保应用已加载再向应用控制器发送application_controller:start_application(Name, RestartType)。启动应用时应用控制器还会创建一个application master应用主控进程对应源码 lib/kernel/src/application_master.erl。应用主控进程会把自己设为应用中所有进程的 group leader组领导者并把 I/O 转发给之前的 group leader。这样做的目的lib/kernel/src/application.erl 有明确说明一是便于追踪哪些进程属于该应用从而支持application:get_application/0和application:get_env/1二是在停止应用时确保属于该应用的所有进程都被终止。加载与卸载应用应用在启动前必须先加载load。加载时应用控制器读取并保存.app文件中的信息1 application:load(ch_app). ok 2 application:loaded_applications(). [{kernel,ERTS CXC 138 10,2.8.1.3}, {stdlib,ERTS CXC 138 10,1.11.4.3}, {ch_app,Channel allocator,1}]已经停止、或从未启动过的应用可以卸载unload应用控制器内部数据库中的相关信息会被擦除3 application:unload(ch_app). ok 4 application:loaded_applications(). [{kernel,ERTS CXC 138 10,2.8.1.3}, {stdlib,ERTS CXC 138 10,1.11.4.3}]注意加载/卸载应用并不会加载/卸载应用所用的代码代码加载由代码服务器按常规方式处理。启动与停止应用通过如下调用启动应用5 application:start(ch_app). ok 6 application:which_applications(). [{kernel,ERTS CXC 138 10,2.8.1.3}, {stdlib,ERTS CXC 138 10,1.11.4.3}, {ch_app,Channel allocator,1}]如果应用尚未加载应用控制器会先调用application:load/1加载它然后检查applications键的值确保所有应先启动的应用都在运行。之后应用控制器为该应用创建应用主控进程。应用主控进程通过mod键定义的模块调用回调start/2来启动应用。从 lib/kernel/src/application_master.erl 的start_it_old/4可以看到这一调用路径的精确实现对M:start(Type, A)的返回值分情况处理——{ok, Pid}与{ok, Pid, AppState}都会 link 该 pid 并进入主循环loop_it{error, Reason}返回{error, {Reason, {M,start,[Type,A]}}}其他返回值则报告bad_return。停止应用不卸载的调用7 application:stop(ch_app). ok停止流程同样可在 lib/kernel/src/application_master.erl 的loop_it中看到应用主控进程收到terminate后先调用可选的prep_stop/1获得新的状态然后向顶层监督者发送exit(Child, shutdown)——顶层监督者会让所有子进程按启动顺序的逆序关闭整棵监督树随之终止随后应用主控进程调用mod键定义模块的stop/1回调完成清理。另外值得一提的实用函数是application:ensure_started/1,2lib/kernel/src/application.erl它与start/1,2等价但应用已经启动时返回ok而非{error, {already_started, ...}}在启动脚本中经常比裸用start更省心。配置应用应用可以用**配置参数configuration parameters**进行配置它们是.app文件中env键指定的一组{Par,Val}元组{application, ch_app, [{description, Channel allocator}, {vsn, 1}, {modules, [ch_app, ch_sup, ch3]}, {registered, [ch3]}, {applications, [kernel, stdlib, sasl]}, {mod, {ch_app,[]}}, {env, [{file, /usr/local/log}]} ]}.Par必须是原子Val可以是任意 term。应用通过application:get_env(App, Par)等函数读取参数值完整的函数族见 Kernel 中的 application 模块% erl Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0] Eshell V5.2.3.6 (abort with ^G) 1 application:start(ch_app). ok 2 application:get_env(ch_app, file). {ok,/usr/local/log}.app文件中的值可以被**系统配置文件system configuration file**覆盖其格式为[{Application1, [{Par11,Val11},...]}, ..., {ApplicationN, [{ParN1,ValN1},...]}].系统配置文件必须命名为Name.config启动 Erlang 时用命令行参数-config Name指定详见 Kernel 中的 config。例如创建test.config[{ch_app, [{file, testlog}]}].则file的值覆盖.app中的定义% erl -config test Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0] Eshell V5.2.3.6 (abort with ^G) 1 application:start(ch_app). ok 2 application:get_env(ch_app, file). {ok,testlog}如果使用 release handling参见 release_handling.md则必须只使用一个系统配置文件且命名为sys.config——仓库根目录的 sys.config 即为该约定的最小示例内容为[].表示无任何覆盖项。.app文件与系统配置文件中的值还可以直接从命令行覆盖% erl -ApplName Par1 Val1 ... ParN ValN% erl -ch_app file testlog Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0] Eshell V5.2.3.6 (abort with ^G) 1 application:start(ch_app). ok 2 application:get_env(ch_app, file). {ok,testlog}因此配置参数的优先级从低到高为.app文件的env键 → 系统配置文件-config→ 命令行-ApplName Par Val参数。应用启动类型启动应用时可以指定启动类型start typeapplication:start(Application, Type)application:start(Application)等价于application:start(Application, temporary)。类型也可以是permanent或transientlib/kernel/src/application.erl 中统一定义为restart_type()permanent永久应用终止时所有其他应用和整个运行时系统也会被终止transient临时应用以normal原因终止时仅报告、不终止其他应用若以非normal原因异常终止则所有其他应用和运行时系统也会被终止temporary短暂应用终止时仅报告不终止其他任何应用。无论何种类型应用都可以随时通过application:stop/1显式停止且不影响其他应用。需要特别注意的是transient 类型实际用途很小源码注释中也有同样说明因为监督树终止时原因会被设置为shutdown而非normal所以 transient 应用一旦因监督树关闭而终止几乎总会触发整机终止——这与临时失败不应波及全局的预期相悖。日常开发中最常用的是默认的temporary需要应用即整个节点的强绑定语义时才用permanent。小结把功能代码封装为 OTP 应用是 Erlang/OTP 开发的基本功编写回调模块定义启动/停止逻辑编写.app资源文件声明模块、依赖与配置参数按规范组织目录结构再由应用控制器与应用主控进程负责加载、启动、停止与状态跟踪。通过application模块的 API 可以完成加载、启动、停止、查询与参数读取配置优先级为.app文件env、系统配置文件、命令行参数依次覆盖。结合 lib/kernel/src/application.erl、lib/kernel/src/application_controller.erl 与 lib/kernel/src/application_master.erl 的源码可以进一步验证并深化对整套机制的理解。关于分布式应用的 takeover/failover 与 release 打包可继续阅读 distributed_applications.md 和 release_structure.md。赞分享编程语言语言运行时标准库编译器并发编程【免费下载链接】otpErlang/OTP项目地址https://gitcode.com/gh_mirrors/ot/otp点击查看免费下载相关推荐Erlang/OTP SNMP 应用配置与启动指南Agent/Manager 配置参数、配置工具与调试方法Erlang/OTP SNMP 应用配置与启动指南Agent/Manager 配置参数、配置工具与调试方法 SNMPSimple Network Manag编程语言语言运行时标准库编译器并发编程Erlang/OTP 包含应用Included Applications深入解析从 .app 配置到 Start Phases 启动同步Erlang/OTP 包含应用Included Applications深入解析从 .app 配置到 Start Phases 启动同步 导读 在 Erl编程语言语言运行时标准库编译器并发编程Erlang/OTP Crypto Application 完全指南OpenSSL 依赖、fips_mode 与 rand_cache_size 配置深度解析Erlang/OTP Crypto Application 完全指南OpenSSL 依赖、fips_mode 与 rand_cache_size 配置深度解析编程语言语言运行时标准库编译器并发编程上一篇cargo-generate 使用教程下一篇SQLCheck 开源项目安装与使用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表