[erlang-questions] Learning OTP - Application, Supervisor, FSM, Server

Serge Aleynikov <>
Mon Sep 1 14:38:28 CEST 2008


In your supervision tree you added one more child specification:

% TCP Listener
% Client instance supervisor
% Chat Server instance           <----

With the addition of this chat server the supervisor will attempt to 
start chat_server instance immediately and prechat_fsm lazily (when a 
client socket connection is detected, since it's a simple_one_for_one 
supervisor).  I suspect there's a problem in your chat_server.erl's 
init/1 function, which errors out and prevents the supervisor from 
starting up.  After enabling SASL (as Ulf suggested) try to start that 
server instance on it's own just to make sure it's not crashing immediately:

   chat_server:start_link(...).

Can't say anything more specific without looking at code.

Serge

Jeff Crane wrote:
> This code is modified from:
> http://www.trapexit.org/Building_a_Non-blocking_TCP_server_using_OTP_principles
> 
> 
> -module(tcp_server_app).
> -author('').
> 
> -behaviour(application).
> 
> %% Internal API
> -export([start_client/0]).
> 
> %% Application and Supervisor callbacks
> -export([start/2, stop/1, init/1]).
> 
> -define(MAX_RESTART,	5).
> -define(MAX_TIME,	60).
> -define(DEF_PORT,	6123).
> 
> %% A startup function for spawning new client connection handling FSM.
> %% To be called by the TCP listener process.
> start_client() ->
> 	supervisor:start_child(tcp_client_sup, []).
> 
> %%----------------------------------------------------------------------
> %% Application behaviour callbacks
> %%----------------------------------------------------------------------
> start(_Type, _Args) ->
> 	ListenPort = get_app_env(listen_port, ?DEF_PORT),
> 	supervisor:start_link({local, ?MODULE}, ?MODULE, [ListenPort, prechat_fsm]).
> 	
> stop(_S) ->
> 	ok.
> 
> %%----------------------------------------------------------------------
> %% Supervisor behaviour callbacks
> %%----------------------------------------------------------------------
> init([Port, Module]) ->
> 	{ok,
> 		{
> 			_SupFlags = {one_for_one, ?MAX_RESTART, ?MAX_TIME},
> 			[
> 				% TCP Listener
> 				{
> 					tcp_server_sup,										% Id = internal id
> 					{tcp_listener,start_link,[Port,Module]},						% StartFun = {M, F, A}
> 					permanent,											% Restart = permanent | transient | temporary
> 					2000,												% Shutdown = brutal_kill | int() >= 0 | infinity
> 					worker,											% Type = worker | supervisor
> 					[tcp_listener]										% Modules  = [Module] | dynamic
> 				},
> 				% Client instance supervisor
> 				{
> 					tcp_client_sup,										% Id  = internal id
> 					{supervisor,start_link,[{local, tcp_client_sup}, ?MODULE, [Module]]},	% StartFun = {M, F, A}
> 					permanent,											% Restart = permanent | transient | temporary
> 					infinity,											% Shutdown = brutal_kill | int() >= 0 | infinity
> 					supervisor,											% Type = worker | supervisor
> 					[]												% Modules  = [Module] | dynamic
> 				},
> 				% Chat Server instance
> 				{
> 					chat_server_sup,										% Id  = internal id
> 					{chat_server,start_link, []},								% StartFun = {M, F, A}
> 					permanent,											% Restart = permanent | transient | temporary
> 					2000,												% Shutdown = brutal_kill | int() >= 0 | infinity
> 					worker,											% Type = worker | supervisor
> 					[chat_server]										% Modules  = [Module] | dynamic
> 				}
> 			]
> 		}
> 	};
> 
> init([Module]) ->
> 	{ok,
> 		{
> 			_SupFlags = {simple_one_for_one, ?MAX_RESTART, ?MAX_TIME},
> 			[
> 				% TCP Client
> 				{
> 					undefined,											% Id = internal id
> 					{Module,start_link,[]},									% StartFun = {M, F, A}
> 					temporary,											% Restart = permanent | transient | temporary
> 					2000,												% Shutdown = brutal_kill | int() >= 0 | infinity
> 					worker,											% Type = worker | supervisor
> 					[]												% Modules = [Module] | dynamic
> 				}
> 			]
> 		}
> 	}.
> 
> %%----------------------------------------------------------------------
> %% Internal functions
> %%----------------------------------------------------------------------
> get_app_env(Opt, Default) ->
> 	case application:get_env(application:get_application(), Opt) of
> 		{ok, Val} -> 
> 			Val;
> 		_ ->
> 			case init:get_argument(Opt) of
> 				[[Val | _]] -> 
> 					Val;
> 				error -> 
> 					Default
> 			end
> 	end.
> 
> 
> This doesn't run, but what's worse is that I don't understand why. Let me walk through what I think I know.
> 
> In shell, I type:
> application:start(tcp_server).
> 
> The OTP framework looks for and at the .app file for the argument specified.
> It sees tcp_server.app and finds the line:
> {mod, {tcp_server_app, []}},
> 
> This tells what args (happen to be [] in this case) to send the module tcp_server_app and the Type defaults to the atom "normal" somehow. start is always the default initial function according to the OTP framework.
> 
> Putting this altogether, we get the initial function call of:
> tcp_server_app:start(normal,[])
> 
> Next it pulls the ListenPort out of the define directives.
> 
> According to what I've observed, behaviours are equivalent to java interfaces. Unlike java interfaces, you don't need to declare that you implement the interface for another module to call them, another module will simply do a "blind" call, assuming it's there. tcp_server_app implements both application and supervisor behaviours, even though it only declares 1 of them (application). The supervisor behaviour allows the supervisor module to start up a supervisor node via an init/1 fun. The following line:
> supervisor:start_link({local, ?MODULE}, ?MODULE, [ListenPort, prechat_fsm]),
> 
> ...says, spawn module tcp_server_app, function init/1, register the process locally as tcp_server_app, passing the args [ListenPort, prechat_fsm] to the init/1. This results in a spawned process:
> tcp_server_app:init([ListenPort, prechat_fsm])
> 
> The first init([Port,Module]) matches so it is used.
> Next, I had no idea what was going on:
> {ok,{_SupFlags,[Arg1,Arg2]}};
> until I ran into:
> http://www.erlang.org/doc/design_principles/part_frame.html (look for child specifications)
> 
> % Child specification consists of...
> Arg = 
> {	
> % unique c.spec identifier
> tcp_server_sup,  
> 
> % spawn a process of tcp_listener:init([Port,Module]),
> {tcp_listener,start_link,[Port,Module]}, 
> 
> % Restart always
> permanent,
> 
> % How long to die after getting a "die" message from supervisor	
> 2000,	
> 
> % Is this child a sub-supervisor, otherwise: worker
> worker,						
> 
> % No idea what this bad boy is for.
> [tcp_listener]
> 
> }
> 
> My chat_server.erl is a gen_server, and I think that if someone can explain this last bit of the child specification, I might understand why I'm getting this at runtime (startup):
> =INFO REPORT==== 1-Sep-2008::00:39:12 ===
>     application: tcp_server
>     exited: {shutdown,{tcp_server_app,start,[normal,[]]}}
>     type: temporary
> {error,{shutdown,{tcp_server_app,start,[normal,[]]}}}
> 
> Apologies for the inability to read specs, but I just don't understand.
> 
> 
>       
> _______________________________________________
> erlang-questions mailing list
> 
> http://www.erlang.org/mailman/listinfo/erlang-questions
> 




More information about the erlang-questions mailing list