Erlang 构建多层监督机制

本文详细介绍如何使用Erlang构建多层监督树,包括督程与拥程的设计与实现,适合初学者参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天在写项目时需要用到 Erlang 的多层监督机制,写过程中遇到点问题,再此记录下来,也希望对正在学习 Erlang 的小伙伴有帮助。

Erlang 支持多重监督机制。otp design 上面有详细的介绍。

大致样子如下图:

这里写图片描述

途中方块表示督程(supervisor),圆圈表示拥程(worker)。本篇文章从头到尾会建立一个上图所示的监督树。

开始我们先用 rebar 简单的建立个项目

rebar create-app appid=one

名称为 one,这也就是说我们最顶层的督程为 one_sup
建立完成后会产生一下 3 个文件:

one_app.erl
one.app.src
one_sup.erl

先放这里:)


建立督程 one_sup

one_sup.erl 文件如下,使用 rebar 建立会有默认模板,为代码的简洁性我就去掉了:)

-module(one_sup).

-behaviour(supervisor).

-export([start_link/0]).

-export([init/1]).

start_link() ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
    {ok, { {one_for_one, 5, 10}, 
           [
            %% 子督程
            {son_sup, {son_sup, start_link, []},
             temporary,
             brutal_kill,
             supervisor,
             [son_sup]
            },
            %% 子拥程
            {son_work, {son_work, start_link, []},
             temporary,
             brutal_kill,
             worker,
             [son_work]
            }
            %% 子拥程2
            {son_work2, {son_work, start_link, []},
             temporary,
             brutal_kill,
             worker,
             [son_work]
            }
           ]
         } }.

start_link 会创建一个 supervisor 督程,创建后init 函数将被调用。init 函数指定了督程两种子进程的类型,一种为 supervisor,另一种为 worker
init 函数内部剩余参数如果不理解戳 Erlang otp Supervisor 行为

注意:

start_link() ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, []).

这句代码指定了督程的名字为 ?MODULE 模块名,我们只有在确定某个进程唯一时(也就是确保 start_link 只被调用一次时)才能注册名字。否则第二次添加进程会报错 进程已存在


建立子督程 son_sup

-module(son_sup).

-behaviour(supervisor).

-export([start_link/0]).

-export([init/1]).

start_link() ->
    supervisor:start_link(?MODULE, []).

init([]) ->
    {ok, { {simple_one_for_one, 5, 10},
            [
             {
              grandson_work, {grandson_work, start_link, []},
              temporary,
              brutal_kill,
              worker,
              [grandson_work]
             }
            ]
         } }.

start_link 函数建立子督程,并指定该督程的子进程是 worker 拥程。

simple_one_for_one 表明我们必须手动添加(通过 start_child)该进程的子进程。


建立子拥程 son_work

-module(son_work).
-behaviour(gen_server).
-define(SERVER, ?MODULE).

-export([start_link/0]).

-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

start_link() ->
    io:format("我是子拥程~p~n", [self()]),
    gen_server:start_link(?MODULE, [], []).

init(Args) ->
    {ok, Args}.

handle_call(_Request, _From, State) ->
    {reply, ok, State}.

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

如果你是通过 rebar 构建项目的话,这里的代码命令就可生成。
rebar create template=simplesrv srvid=son_work
子拥程 son_work 是一个 gen_server


建立子督程的拥程 grandson_work

都是 worker,代码结构与上面大致相同。

-module(grandson_work).
-behaviour(gen_server).
-define(SERVER, ?MODULE).

-export([start_link/0]).

-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

start_link() ->
    gen_server:start_link(?MODULE, [], []).

init(Args) ->
    {ok, Args}.

handle_call(_Request, _From, State) ->
    {reply, ok, State}.

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.


运行观察:

代码分布如下图:

我们来一步一步运行并通过 observer 来观察。

首先,要通过 observer 观察结构图必须构建一个应用 application,因为 observer 上只能观察应用的进程结构图。

此时监督树如下图所示:
<0.38.0><0.39.0> 是应用 application_master 创建。

进程状态图:

现在,我们来给 <0.41.0> 子督程添加拥程。

监督树如下图:

到这里基本就完成了,读者可以自行尝试下给 son_sup 再添加一个拥程。就完全如同开篇图片的样子。


总结:

监督机制是 Erlang 的一个重大特性,在细节方面还需要注意一些问题。

比如:

什么情况下用 simple_one_for_one

督程是否需要命名,拥程呢,什么情况下需要命名?

上图中监督树的第二层拥程可否动态扩展,督程呢,哪个是真正有必要扩展?

以上问题欢迎讨论:)

本文源代码戳 这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏天的技术博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值