[Erlang 0036] "HOW TO"不创建崩溃报告主动销毁gen_server进程-阿里云开发者社区

开发者社区> 唐玄奘> 正文

[Erlang 0036] "HOW TO"不创建崩溃报告主动销毁gen_server进程

简介:
+关注继续查看
 昨天SC遇到一个问题,创建一个gen_server的时候会在init方法中检查依赖的外部服务是否可用;如果不可用的话他就直接返回{stop,Reason},gen_server进程创建失败;这个目标他很容易就达到了,但是进程启动失败之后却创建了Crash Report,这种异常情况是可以预料的并不需要创建崩溃报告Crash Report;为什么会产生崩溃报告Crash Report?如何消除呢?他的init代码大体上是这样的:
复制代码
init([]) ->
process_flag(trap_exit, true),
case is_service_available() of
{ok, Sock} ->
{ok, #tcp_connector_state{sock = Sock}};
{error, Reason} ->
?Error("service not available:~p~n", [Reason]),
{stop,Reason}
end.
复制代码
首先要确认的是gen_server对init回调函数的返回值规格说明, 官方文档链接:http://www.erlang.org/doc/man/gen_server.html
复制代码
Module:init(Args) -> Result
Types:
Args = term()
Result = {ok,State} | {ok,State,Timeout} | {ok,State,hibernate}
| {stop,Reason} | ignore
State = term()
Timeout = int()>=0 | infinity
Reason = term()
复制代码
并且下面的解释也说了如果启动中遇到错误应该返回{stop,Reason}:
If something goes wrong during the initialization the function should return {stop,Reason} where Reason is any term, or ignore.
同时查看gen_server对start_link/start方法的注解:
 If Module:init/1 fails with Reason, the function returns {error,Reason}. If Module:init/1 returns {stop,Reason} or ignore, the process is terminated and the function returns {error,Reason} or ignore,respectively.
貌似也没有什么问题,下面最直接的方法就是去看看gen_server代码对init的各种返回值的处理了:
复制代码
%%% ---------------------------------------------------
%%% Initiate the new process.
%%% Register the name using the Rfunc function
%%% Calls the Mod:init/Args function.
%%% Finally an acknowledge is sent to Parent and the main
%%% loop is entered.
%%% ---------------------------------------------------
init_it(Starter, self, Name, Mod, Args, Options) ->
init_it(Starter, self(), Name, Mod, Args, Options);
init_it(Starter, Parent, Name0, Mod, Args, Options) ->
Name = name(Name0),
Debug = debug_options(Name, Options),
case catch Mod:init(Args) of
{ok, State} ->
proc_lib:init_ack(Starter, {ok, self()}),
loop(Parent, Name, State, Mod, infinity, Debug);
{ok, State, Timeout} ->
proc_lib:init_ack(Starter, {ok, self()}),
loop(Parent, Name, State, Mod, Timeout, Debug);
{stop, Reason} ->
%% For consistency, we must make sure that the
%% registered name (if any) is unregistered before
%% the parent process is notified about the failure.
%% (Otherwise, the parent process could get
%% an 'already_started' error if it immediately
%% tried starting the process again.)
unregister_name(Name0),
proc_lib:init_ack(Starter, {error, Reason}),
exit(Reason);
ignore ->
unregister_name(Name0),
proc_lib:init_ack(Starter, ignore),
exit(normal);
{'EXIT', Reason} ->
unregister_name(Name0),
proc_lib:init_ack(Starter, {error, Reason}),
exit(Reason);
Else ->
Error = {bad_return_value, Else},
proc_lib:init_ack(Starter, {error, Error}),
exit(Error)
end.
复制代码
   看到这里一切都明朗了,当Mod:init(Args)返回值是 {stop, Reason} 的时候在完成清理工作之后最后一步做了exit(Reason);这样退出又会怎样呢?注意gen_server创建进程是使用proc_lib创建的,而使用proc_lib创建的进程退出的原因不是normal也不是shutdown的时候,就会创建一个进程崩溃报告,这个会写入默认的SASL的事件handler,错误报告会在只有在启动了SASL的时候才能看到.这个在之前的文章中[Erlang 0017]Erlang/OTP基础模块 proc_lib 中我们已经提到提到过. proc_lib的官方文档:http://www.erlang.org/doc/man/proc_lib.html 
  原因知道了,解决方法也就很显然了,从上面的代码中可以看到当Mod:init返回值是ignore的时候处理方式是exit(normal);而进程这样退出是不会创建Crash Report的,搞定:
复制代码
init([]) ->
process_flag(trap_exit, true),
case is_service_available() of
{ok, Sock} ->
{ok, #tcp_connector_state{sock = Sock}};
{error, Reason} ->
?Error("service not available:~p~n", [Reason]),
ignore
end.
复制代码

还没有结束

     这个问题比较简单,但是思考解决方案的过程还是做一下调整的,可能是更直接:
  1. 首先我们的目标是不想生成崩溃报告Crash Report,那崩溃报告是谁产生的呢?
  2. 这是一个gen_server进程,它创建使用的是proc_lib
  3. 而proc_lib创建的进程只有非正常退出的时候才会创建Crash Report,换句话说退出的原因不是normal或者shutdown
  4. 看一下gen_server对init回调函数的处理,哪一种退出是不exit(normal)
OK.

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
怎么设置阿里云服务器安全组?阿里云安全组规则详细解说
阿里云服务器安全组设置规则分享,阿里云服务器安全组如何放行端口设置教程
8353 0
redis-server进程CPU百分百问题
结论: 待确认是否为redis的BUG,原因是进程实际占用的内存远小于配置的最大内存,所以不会是内存不够需要淘汰。 CPU百分百redis-server进程集群状态: slave 解决办法: 使用gdb将d.ht[0].used的值改为0 问题原因: dictGetRandomKey()过程中, 无法走到分支“if (dictSize(d) == 0) return NULL;”, 导致函数dbRandomKey()进入死循环。
2468 0
【终极解决方案】为应用程序池“XXX”提供服务的进程在与 Windows Process Activation Service 通信时出现严重错误。该进程 ID 为“XXXX”。数据字段包含错误号。
原文:【终极解决方案】为应用程序池“XXX”提供服务的进程在与 Windows Process Activation Service 通信时出现严重错误。该进程 ID 为“XXXX”。数据字段包含错误号。
2228 0
MariaDB 在创建外键的时候提示 1005 错误
MariaDB 在创建外键的时候提示 1005 错误
124 0
+关注
641
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载