我怎么知道这是由 erlang 中的主管重新启动我的进程的最后一个周期
Posted
技术标签:
【中文标题】我怎么知道这是由 erlang 中的主管重新启动我的进程的最后一个周期【英文标题】:How can I know when it's the last cycle of my process restarted by the supervisor in erlang 【发布时间】:2016-06-08 04:01:47 【问题描述】:我有一个 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 = #paramsbotId = 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
失败,它是否从具有相同状态数据的相同状态重新启动?如果不是,我怎么能导致它发生?
【问题讨论】:
好的,现在经过您的进一步解释,我明白这不是关于gen_fsm
由于某些预期的触发器而终止,而是因为进程死亡而异常终止?您只想计算主管何时重新启动 cs_fsm
并在主管不再重新启动它时发送消息,以防它再次死亡?
是的。有没有办法做到这一点?
为了让我的问题更笼统:主管可以在上次重新启动它的孩子时为其孩子做出不同的终止吗?
主管如何知道它最后一次重启了孩子?在孩子真正崩溃之前,主管无法计算出这是最后一次崩溃,因为它甚至不知道孩子是否或何时会崩溃。只有在崩溃监督员之后才能扣除是否允许重新启动孩子。在编辑中查看我更完整的答案。顺便说一句,您为什么想知道孩子最后一次重新启动?如果主管无法重新启动孩子,因为它崩溃了太多次,这是一个不可恢复的情况,主管应该自己死!
我希望孩子通知崩溃到某个进程的原因,但仅在最后一次崩溃时通知。也许我应该在状态记录中有一个计数(所以我防止使用 ets 表),我会在终止时增加它,然后我可以知道它运行了多少次。顺便说一句,当我的 gen_fsm 孩子崩溃时,它会在之前的状态下重新启动吗?
【参考方案1】:
您可以添加将消息发送到Module:terminate/3
函数,当StateName
函数之一返回stop,Reason,NewStateData
以指示应该停止gen_fsm
时调用该函数。
gen_fsm
是一个有限状态机,因此您可以决定它如何在状态之间转换。触发最后一个循环的某些东西也可能在传递给Module:StateName/3
的StateData
中设置一些东西,以便处理状态的函数知道它是最后一个循环。除非您提供一些我们可以分析和评论的代码,否则很难给出更具体的答案。
进一步澄清后编辑:
Supervisor 不会通知它的孩子它是什么时候重新启动它们的,它也不能通知孩子它是最后一次重新启动。这只是因为它不知道它将是最后一次,直到主管进程实际上再次崩溃,主管无法预测。只有在孩子崩溃后,主管才能计算出孩子在一段时间内崩溃了多少次,是否允许再次重新启动孩子,或者这是最后一次重启,现在主管也该死了。
但是,没有什么可以阻止孩子注册,例如在 ETS 表中,它被重新启动了多少次。但它当然无助于扣除最后一次重启。
编辑 2:
当主管重新启动子进程时,它会使用标准 init
函数从头开始。孩子在崩溃之前的任何先前状态都将丢失。
请注意,崩溃是一种特殊情况,并不总是可以恢复状态,因为崩溃可能会破坏状态。与其尝试恢复状态或询问主管何时重新启动孩子,为什么不首先防止崩溃发生呢?你有两个选择:
I. 使用try/catch 捕捉任何异常情况并采取相应措施。有可能捕获任何会导致进程崩溃并导致主管重新启动它的错误。您可以将try/catch
添加到gen_fsm
进程内的任何入口函数,以便在服务器崩溃之前捕获任何错误情况。见example function 1或example function 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.
II. 生成一个新进程来处理作业并在进程终止时捕获EXIT
信号。这允许gen_fsm
异步处理作业并以自定义方式处理任何错误(不一定像主管那样重新启动进程)。这部分标题为Error Handling 解释了如何捕获来自子进程的exit
信号。这是gen_server
中的example of trapping signals。检查包含几个子句的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.
【讨论】:
谢谢!!我实际上在子进程中使用 ets 表。关于处理错误我更喜欢“让它崩溃”的方法。 “让它崩溃的哲学”并不意味着你不应该尽可能地捕捉错误。这仅意味着您不应该进行防御性编码。使用 try-catch 比尝试添加更多逻辑来处理故障要好。请参阅这张幻灯片:qconlondon.com/london-2011/qconlondon.com/dl/qcon-london-2011/… 这是一般性评论,因为我不知道您的架构的确切设计:)以上是关于我怎么知道这是由 erlang 中的主管重新启动我的进程的最后一个周期的主要内容,如果未能解决你的问题,请参考以下文章