我正在使用这个gen_server:
-module(user_info_provider).
-export([start_link/0, stop/0]).
-export([init/1, terminate/2, handle_info/2, handle_call/3, handle_cast/2,
code_change/3]).
-export([request_user_info/2]).
-behaviour(gen_server).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
stop() ->
gen_server:cast(?MODULE, stop).
request_user_info(From,UserId) ->
gen_server:cast(?MODULE, {request_user_info, From, UserId}).
%% Callback Functions
init(_) ->
process_flag(trap_exit, true),
io:format("I am ~w ~n", [self()]),
{ok, null}.
%% @doc terminate
terminate(Reason, _LoopData) ->
io:format("Terminating by ~w~n",[Reason]),
{ok, null}.
handle_cast({request_user_info,From,UserId}, LoopData) ->
{noreply, LoopData};
handle_cast(stop, LoopData) ->
{stop, normal, LoopData}.
handle_info(_Info, State) ->
{ok, State}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
handle_call(_,_From,LoopData) ->
{ok,ok,LoopData}.
问题是,正如我接下来所示,如果我从像erl -pa ebin/ -s user_info_provider start_link
这样的cli执行它,它会立即死掉,但是我可以从控制台中生成它并且它可以正常工作。
erl -pa ebin -s user_info_provider start_link
Erlang R14B02 (erts-5.8.3) [source] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false]
I am <0.32.0>
Terminating by normal
Eshell V5.8.3 (abort with ^G)
1> user_info_provider:start_link().
I am <0.35.0>
{ok,<0.35.0>}
如果我没有设置process_flag(trap_exit, true)
或我没有直接从-s module function
的控制台启动它,就不会发生这种情况。
我这样启动它是因为真正的gen_server更复杂,我从Makefile调用中独立测试它
有什么想法吗?
答案 0 :(得分:5)
解决方案是W55tKQbuRu28Q4xv
或不使用start_link
建议的解决方案,仅start
。以下是发生的事情:
-s
参数由init
处理。粗略地说,init将spawn
一个新进程,然后用它来初始化并运行所有-s
个参数。之后,这个生成的进程将退出。
由于init-process退出,并且您捕获了exits,因此您的进程会收到一条消息{'EXIT', P, Reason}
,其中P
是由init-by-init进程生成的pid()。此消息由您的流程的gen_server
部分处理。通常这样的消息会被转发到你的handle_info/2
回调(你的代码btw中的返回值错误,应该是noreply)。但在这种情况下,它将不转发。原因是gen_server
包含其父进程的概念。它通过流程字典和'$ancestors'
放置的值proc_lib
来记录产生它的流程。现在,如果退出消息从 parent 到达,则立即调用终止回调并终止进程。此类邮件不会转发给您。这就是你所看到的。
解决方案,漂亮的解决方案,是创建一个小应用程序,一个主管,并将您的流程置于该主管之下。然后从-s中调用application:start(your_app)
。这有效的原因是应用程序控制器单独运行。更好的解决方案是构建一个自动启动应用程序的版本。发行版是您的应用程序+它的依赖项与ERTS运行时捆绑在一起。这样的版本完全由它自己生存,可以复制到目标主机并运行。因此,目标系统不需要Erlang,因为版本是自包含的。
答案 1 :(得分:1)
使用supervisor执行最小应用程序(rebar可以生成此类应用程序的框架)并使用-s从cli运行它。