2

我有一个有孩子的simple_one_for_one主管。gen_fsm我希望每个gen_fsm孩子只在最后一次终止时发送一条消息。有没有办法知道最后一个周期是什么时候?

这是我的主管:

-module(data_sup).

-behaviour(supervisor).

%% API
-export([start_link/0,create_bot/3]).

%% Supervisor callbacks
-export([init/1]).

%%-compile(export_all).


%%%===================================================================
%%% API functions
%%%===================================================================

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

init([]) ->
 RestartStrategy = {simple_one_for_one, 0, 1},
 ChildSpec = {cs_fsm, {cs_fsm, start_link, []},
 permanent, 2000, worker, [cs_fsm]},
 Children = [ChildSpec],
 {ok, {RestartStrategy, Children}}.

create_bot(BotId, CNPJ,Pid) ->
  supervisor:start_child(?MODULE, [BotId, CNPJ, Pid]).

Pid是启动监督者并下达命令启动子进程的进程的 Pid。

-module(cs_fsm).

-behaviour(gen_fsm).
-compile(export_all).

-define(SERVER, ?MODULE).
-define(TIMEOUT, 5000).

-record(params, {botId, cnpj, executionId, pid}).

%%%===================================================================
%%% API
%%%===================================================================

start_link(BotId, CNPJ, Pid) ->
  io:format("start_link...~n"),
  Params = #params{botId = BotId, cnpj = CNPJ, pid = Pid},
  gen_fsm:start_link(?MODULE, Params, []).


%%%===================================================================
%%% gen_fsm callbacks
%%%===================================================================

init(Params) ->
  io:format("initializing~n"),
  process_flag(trap_exit, true),
  {ok, requesting_execution, Params, 0}.

requesting_execution(timeout,Params) ->
  io:format("erqusting execution"),
  {next_state, finished, Params,?TIMEOUT}.

finished(timeout, Params) ->
  io:format("finished :)~n"),
  {stop, normal, Params}.

terminate(shutdown, _StateName, Params) ->
  Params#params.pid ! {terminated, self(),Params},
  ok;

terminate(_Reason, _StateName, Params) ->
  ok.

我的观点是,如果进程在任何状态下失败,它应该仅在它最后一次由主管重新启动时发送消息(根据其重新启动策略)。

如果gen_fsm失败,它是否从具有相同状态数据的相同状态重新启动?如果不是,我怎么能导致它发生?

4

1 回答 1

3

您可以将发送消息添加到Module:terminate/3函数之一,当StateName函数之一返回{stop,Reason,NewStateData}以指示gen_fsm应该停止时调用该函数。

gen_fsm是一个有限状态机,所以你决定它如何在状态之间转换。触发最后一个循环的东西也可能在StateData传递给的 中设置一些东西,Module:StateName/3以便处理状态的函数知道它是最后一个循环。除非您提供一些我们可以分析和评论的代码,否则很难给出更具体的答案。

进一步澄清后编辑:

主管不会通知其孩子何时重新启动它们,也无法通知孩子这是最后一次重新启动。这只是因为它不知道它将是最后一次,直到主管进程实际上再次崩溃,主管无法预测。只有在孩子崩溃之后,主管才能计算孩子在一段时间内崩溃了多少次,以及是否允许再次重新启动孩子,或者这是最后一次重新启动,现在主管也该死了。

然而,没有什么可以阻止孩子注册,例如在 ETS 表中,它已经重新启动了多少次。但这当然无助于扣除最后一次重启。

编辑2:

init当主管重新启动子进程时,它会使用标准函数从头开始。孩子在崩溃之前的任何先前状态都将丢失。

请注意,崩溃是一种特殊情况,并不总是可以恢复状态,因为崩溃可能已经破坏了状态。与其尝试恢复状态或询问主管何时重新启动孩子,为什么不首先防止崩溃发生呢?你有两个选择:

I.使用try/catch捕捉任何异常情况并采取相应措施。有可能捕获任何会导致进程崩溃并导致主管重新启动它的错误。您可以添加try/catchgen_fsm进程内的任何入口函数,以便在服务器崩溃之前捕获任何错误情况。请参见示例函数 1示例函数 2

read() ->
    try
        try_home() orelse try_path(?MAIN_CFG) orelse
            begin io:format("Some Error", []) end
    catch
        throw:Term -> {error, Term}
    end.

try_read(Path) ->
    try
        file:consult(Path)
    catch
        error:Error -> {error, Error}
    end.

二、生成一个新进程来处理作业并EXIT在进程终止时捕获信号。这允许gen_fsm异步处理作业并以自定义方式处理任何错误(不一定像主管那样重新启动进程)。本节标题为错误处理解释了如何捕获exit来自子进程的信号。这是一个gen_server. 检查handle_info包含几个子句的函数以捕获EXIT来自子进程的不同类型的消息。

init([Cfg, Id, Mode]) ->
    process_flag(trap_exit, true),
    (...)


handle_info({'EXIT', _Pid, normal}, State) ->
    {noreply, State};
handle_info({'EXIT', _Pid, noproc}, State) ->
    {noreply, State};
handle_info({'EXIT', Pid, Reason}, State) ->
    log_exit(Pid, Reason),
    check_done(error, Pid, State);
handle_info(_, State) ->
    {noreply, State}.
于 2016-02-25T11:59:32.490 回答