Erlang:通过主管启动时 gen_server 崩溃
所以,我花了很多时间,仍然没有找到答案。
我在 gen_server 中有一个简单的 tcp 服务器,它通过 telnet 接受消息并在控制台中打印它们:
-module(st_server).
-behaviour(gen_server).
%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------
%% --------------------------------------------------------------------
%% External exports
-export([start_link/0, start_link/1, stop/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-define(DEFAULT_PORT, 1055).
-record(state, {lsock, port}).
%% ====================================================================
%% External functions
%% ====================================================================
%%--------------------------------------------------------------------
%% @doc Starts the server.
%%
%% @spec start_link(Port::integer()) -> {ok, Pid}
%% where
%% Pid = pid()
%% @end
%%--------------------------------------------------------------------
start_link(Port) ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []),
io:format("Server name: ~w, port: ~w.~n", [?SERVER, Port]).
%% @spec start_link() -> {ok, Pid}
%% @doc Calls `start_link(Port)' using the default port.
start_link() -> start_link(?DEFAULT_PORT).
%%--------------------------------------------------------------------
%% @doc Stops the server.
%% @spec stop() -> ok
%% @end
%%--------------------------------------------------------------------
stop() ->
gen_server:cast(?SERVER, stop).
%% ====================================================================
%% Server functions
%% ====================================================================
%% --------------------------------------------------------------------
%% Function: init/1
%% Description: Initiates the server
%% Returns: {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% --------------------------------------------------------------------
init([Port]) ->
{ok, LSock} = gen_tcp:listen(Port, [{active, true}]),
{ok, #state{lsock = LSock, port = Port}, 0}.
%% --------------------------------------------------------------------
%% Function: handle_cast/2
%% Description: Handling cast messages
%% Returns: {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State} (terminate/2 is called)
%% --------------------------------------------------------------------
handle_cast(stop, State) ->
{stop, normal, State}.
handle_call(_Request, _From, State) ->
{reply, ok, State}.
%% --------------------------------------------------------------------
%% Function: handle_info/2
%% Description: Handling all non call/cast messages
%% Returns: {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State} (terminate/2 is called)
%% --------------------------------------------------------------------
handle_info({tcp, _Socket, Data}, State) ->
io:format("Incoming info: ~s", [Data]),
{noreply, State};
handle_info({tcp_closed, _Socket}, #state{lsock = LSock} = State) ->
{ok, _Sock} = gen_tcp:accept(LSock),
{noreply, State};
handle_info(timeout, #state{lsock = LSock} = State) ->
{ok, _Sock} = gen_tcp:accept(LSock),
{noreply, State}.
%% --------------------------------------------------------------------
%% Function: terminate/2
%% Description: Shutdown the server
%% Returns: any (ignored by gen_server)
%% --------------------------------------------------------------------
terminate(Reason, _State) ->
io:format("~nServer shutdown. Reason: ~s.~n", [Reason]),
ok.
%% --------------------------------------------------------------------
%% Func: code_change/3
%% Purpose: Convert process state when code is changed
%% Returns: {ok, NewState}
%% --------------------------------------------------------------------
code_change(_OldVsn, _State, _Extra) ->
{ok, _State}.
%% --------------------------------------------------------------------
%%% Internal functions
%% --------------------------------------------------------------------
主管:
-module(st_sup).
-behaviour(supervisor).
%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------
%% --------------------------------------------------------------------
%% External exports
%% --------------------------------------------------------------------
-export([]).
%% --------------------------------------------------------------------
%% Internal exports
%% --------------------------------------------------------------------
-export([
init/1,
start_link/0
]).
%% --------------------------------------------------------------------
%% Macros
%% --------------------------------------------------------------------
-define(SERVER, ?MODULE).
%% --------------------------------------------------------------------
%% Records
%% --------------------------------------------------------------------
%% ====================================================================
%% External functions
%% ====================================================================
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
%% ====================================================================
%% Server functions
%% ====================================================================
%% --------------------------------------------------------------------
%% Func: init/1
%% Returns: {ok, {SupFlags, [ChildSpec]}} |
%% ignore |
%% {error, Reason}
%% --------------------------------------------------------------------
init([]) ->
Server = {st_server, {st_server, start_link, []},
permanent, 2000, worker, [st_server]},
Children = [Server],
RestartStrategy = {one_for_one, 0, 1},
{ok, {RestartStrategy, Children}}.
%% ====================================================================
%% Internal functions
%% ====================================================================
我在 erlang 控制台中启动它:
st_sup:start_link().
得到这个:
Server name: st_server, port: 1055.
** exception exit: shutdown
这意味着主管因某种原因关闭了服务器。 如果我自己启动服务器(st_server:start_link。)它工作得很好。
那么,问题是如何在不停机的情况下使其工作?
So, i spent LOTS of time and still didn't find the answer.
I have simple tcp server in gen_server, which accepts messages via telnet and print them in console:
-module(st_server).
-behaviour(gen_server).
%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------
%% --------------------------------------------------------------------
%% External exports
-export([start_link/0, start_link/1, stop/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-define(DEFAULT_PORT, 1055).
-record(state, {lsock, port}).
%% ====================================================================
%% External functions
%% ====================================================================
%%--------------------------------------------------------------------
%% @doc Starts the server.
%%
%% @spec start_link(Port::integer()) -> {ok, Pid}
%% where
%% Pid = pid()
%% @end
%%--------------------------------------------------------------------
start_link(Port) ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []),
io:format("Server name: ~w, port: ~w.~n", [?SERVER, Port]).
%% @spec start_link() -> {ok, Pid}
%% @doc Calls `start_link(Port)' using the default port.
start_link() -> start_link(?DEFAULT_PORT).
%%--------------------------------------------------------------------
%% @doc Stops the server.
%% @spec stop() -> ok
%% @end
%%--------------------------------------------------------------------
stop() ->
gen_server:cast(?SERVER, stop).
%% ====================================================================
%% Server functions
%% ====================================================================
%% --------------------------------------------------------------------
%% Function: init/1
%% Description: Initiates the server
%% Returns: {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% --------------------------------------------------------------------
init([Port]) ->
{ok, LSock} = gen_tcp:listen(Port, [{active, true}]),
{ok, #state{lsock = LSock, port = Port}, 0}.
%% --------------------------------------------------------------------
%% Function: handle_cast/2
%% Description: Handling cast messages
%% Returns: {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State} (terminate/2 is called)
%% --------------------------------------------------------------------
handle_cast(stop, State) ->
{stop, normal, State}.
handle_call(_Request, _From, State) ->
{reply, ok, State}.
%% --------------------------------------------------------------------
%% Function: handle_info/2
%% Description: Handling all non call/cast messages
%% Returns: {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State} (terminate/2 is called)
%% --------------------------------------------------------------------
handle_info({tcp, _Socket, Data}, State) ->
io:format("Incoming info: ~s", [Data]),
{noreply, State};
handle_info({tcp_closed, _Socket}, #state{lsock = LSock} = State) ->
{ok, _Sock} = gen_tcp:accept(LSock),
{noreply, State};
handle_info(timeout, #state{lsock = LSock} = State) ->
{ok, _Sock} = gen_tcp:accept(LSock),
{noreply, State}.
%% --------------------------------------------------------------------
%% Function: terminate/2
%% Description: Shutdown the server
%% Returns: any (ignored by gen_server)
%% --------------------------------------------------------------------
terminate(Reason, _State) ->
io:format("~nServer shutdown. Reason: ~s.~n", [Reason]),
ok.
%% --------------------------------------------------------------------
%% Func: code_change/3
%% Purpose: Convert process state when code is changed
%% Returns: {ok, NewState}
%% --------------------------------------------------------------------
code_change(_OldVsn, _State, _Extra) ->
{ok, _State}.
%% --------------------------------------------------------------------
%%% Internal functions
%% --------------------------------------------------------------------
And supervisor:
-module(st_sup).
-behaviour(supervisor).
%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------
%% --------------------------------------------------------------------
%% External exports
%% --------------------------------------------------------------------
-export([]).
%% --------------------------------------------------------------------
%% Internal exports
%% --------------------------------------------------------------------
-export([
init/1,
start_link/0
]).
%% --------------------------------------------------------------------
%% Macros
%% --------------------------------------------------------------------
-define(SERVER, ?MODULE).
%% --------------------------------------------------------------------
%% Records
%% --------------------------------------------------------------------
%% ====================================================================
%% External functions
%% ====================================================================
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
%% ====================================================================
%% Server functions
%% ====================================================================
%% --------------------------------------------------------------------
%% Func: init/1
%% Returns: {ok, {SupFlags, [ChildSpec]}} |
%% ignore |
%% {error, Reason}
%% --------------------------------------------------------------------
init([]) ->
Server = {st_server, {st_server, start_link, []},
permanent, 2000, worker, [st_server]},
Children = [Server],
RestartStrategy = {one_for_one, 0, 1},
{ok, {RestartStrategy, Children}}.
%% ====================================================================
%% Internal functions
%% ====================================================================
I start this in erlang console:
st_sup:start_link().
And get this:
Server name: st_server, port: 1055.
** exception exit: shutdown
This means, that supervisor shuts down server for some reason.
If I start server by itself (st_server:start_link.) it works just fine.
So, the question is how to make this work without shutdowns?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
乍一看,可能是因为
st_server:start_link/1
的返回值是调用io:format/2
的返回值。当您直接在 REPL 中调用
st_server:start_link()
时,这很好,但主管可能期望返回值更像{ok, Pid}
。您只需交换
st_server:start_link/1
中的两行即可解决此问题,以便它返回对gen_server:start_link/4
的调用值:At a glance, it's probably because the return value of
st_server:start_link/1
is the return value of the call toio:format/2
.This is fine when you call
st_server:start_link()
directly in the REPL, but the supervisor is probably expecting a return value more like{ok, Pid}
.You can fix this just by swapping the two lines in
st_server:start_link/1
, so that it returns the value of the call togen_server:start_link/4
:主管的
RestartStrategy
也是一个奇怪的值,它设置为{one_for_one,0,1}
。这意味着主管将在 1 秒内重新启动超过 0 次后放弃,实际上它将在子进程第一次崩溃后放弃。这通常不是您希望主管做的事情。A strange value is also the supervisor's
RestartStrategy
which is set to{one_for_one,0,1}
. This means that the supervisor will give up up after more than zero restarts in 1 second, in effect it will give up after the first crash by a child. Which is not usually not what you want a supervisor to do.