【问题标题】:Erlang: gen_server crashes when started over supervisorErlang:通过supervisor启动时gen_server崩溃
【发布时间】:2011-08-17 23:25:52
【问题描述】:

所以,我花了很多时间,仍然没有找到答案。

我在 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。)它工作得很好。

那么,问题是如何在不停机的情况下进行这项工作?

【问题讨论】:

    标签: erlang erlang-otp


    【解决方案1】:

    一个奇怪的值也是主管的RestartStrategy,它被设置为{one_for_one,0,1}。这意味着主管将在 1 秒内多次重启后放弃,实际上它将在孩子第一次崩溃后放弃。这通常不是您希望主管做的事情。

    【讨论】:

    • (我可能是错的,但看起来他改编自“Erlang and OTP in Action”一书中的一个例子,其中有确切的RestartStrategy,并解释说这不是一个正常的配置.)
    【解决方案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 的调用值:

    start_link(Port) ->    
        io:format("Server name: ~w, port: ~w.~n", [?SERVER, Port]),
        gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []).
    

    【讨论】:

    • 但这对于像 c、java 这样的序数语言来说有点奇怪。这意味着,主管跟踪所有输出通道。
    • 我不确定您所说的“跟踪所有输出通道”是什么意思。但是函数的返回值总是最后一个表达式,包括当它是io:format时,它的返回值是ok
    • 输出无关紧要,重要的是返回值。 Erlang 是一门函数式语言,每个操作总是返回一个值。
    猜你喜欢
    • 2011-02-16
    • 2015-11-05
    • 2011-06-18
    • 2014-11-09
    • 1970-01-01
    • 1970-01-01
    • 2011-02-06
    • 2019-07-27
    • 1970-01-01
    相关资源
    最近更新 更多