I have a server which is created like this :
gateway.erl (supervisor of the supervisor) -> gateway_sup.erl (supervisor of gen_servers) -> gateway_serv.erl (Where every client are handled).
This is pretty basic as I saw over the internet, most people are doing like this.
The Listen Socket is created on the gateway_sup.erl, and I would like to listen over multiple sockets in case some client port restrictions.
So here's my code so far.
gateway.erl
-export([start_link/0, init/1, startWithPort/1]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
spawn_link(?MODULE, startWithPort, [8080]),
spawn_link(?MODULE, startWithPort, [443]),
{ok, {{simple_one_for_one, 3600, 3600},
[{socket,
{gateway_sup, start_link, []},
permanent, 1000, supervisor, [gateway_sup]}
]}}.
startWithPort(Port) ->
io:fwrite("Starting...: ~p~n", [Port]),
supervisor:start_child(?MODULE, [Port]).
gateway_sup.erl
-export([start_socket/0, init/1, start_link/1]).
start_link(Port) ->
io:fwrite("gateway_sup start_link Port: ~p // ~p~n", [list_to_atom(atom_to_list(?MODULE) ++ atom_to_list(Port)), Port])
supervisor:start_link({local, list_to_atom(atom_to_list(?MODULE) ++ atom_to_list(Port))}, ?MODULE, [Port]).
init([Port]) ->
io:fwrite("gateway_sup Init with port: ~p~n", [Port]),
R = ssl:listen(Port, ?SSL_OPTIONS),
{ok, LSocket} = R,
spawn_link(fun empty_listeners/0),
{ok, {{simple_one_for_one, 3600, 3600},
[{socket,
{gateway_serv, start_link, [LSocket]},
temporary, 1000, worker, [gateway_serv]}
]}}.
empty_listeners() ->
[start_socket() || _ <- lists:seq(1,128)],
ok.
start_socket() ->
supervisor:start_child(?MODULE, []).
the start_link() function on gateway_sup.erl is never called.
If the gateway is one_for_one and I'm not trying to pass a parameter, everything works fine, but I only do listen over one hardcoded port.
I can't see why it won't call the gateway_sup:start_link/1 ?
Ok, found it ! took me over a night for such minor mistake !
The error is when I'm creating the role within the start_link
list_to_atom(atom_to_list(?MODULE) ++ atom_to_list(Port))
has been replaced with
list_to_atom(atom_to_list(?MODULE) ++ lists:flatten(io_lib:format("~B", [Port])))
Related
Start the server using:
erlc server.erl ; erl -eval 'server:start()'
In another terminal:
telnet localhost 3547
Which could establish the connection successfully, but in a few seconds, the connection is closed by the server due to reasons beyond me. Reading the doc for handle_call/3, {noreply, NewState} is allowed as well.
Could someone explain it? Feels super confusing to me.
SOURCE CODE
-module(server).
-mode(compile).
-behavior(gen_server).
-compile(export_all).
-export([ main/1
, start/0
, stop/0
, stop_and_wait/0
]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-define(DEFAULT_PORT, 3547).
-record(state, {port, lsock}).
start_link(Port) ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []).
start() ->
start_link(?DEFAULT_PORT).
stop() ->
gen_server:cast(?SERVER, stop).
stop_and_wait() ->
gen_server:call(?SERVER, stop, infinity).
init([Port]) ->
{ok, LSock} = gen_tcp:listen(Port, [{active, false}, {reuseaddr, true}]),
{ok, #state{port = Port, lsock = LSock}, 0}.
handle_call({do_stuff, Arg}, _From, State) ->
io:format("do_stuff is called with ~p~n", [Arg]),
% {reply, ok, State};
{noreply, State};
handle_call(stop, _From, State) ->
{stop, normal, ok, State}.
handle_cast(stop, State) ->
{stop, normal, State}.
handle_info(timeout, #state{lsock = LSock} = State) ->
Server = self(),
Listener = spawn(fun() -> listen(Server, LSock) end),
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_Oldvsn, State, _Extra) ->
{ok, State}.
listen(Server, LSock) ->
{ok, Socket} = gen_tcp:accept(LSock),
gen_server:call(?SERVER, {do_stuff, 1}),
listen(Server, LSock).
main(_) ->
io:format("~p~n", [ok]),
ok.
Returning {noreply, NewState} from a gen_server:handle_call/3 implementation is allowed, but it does not mean the gen_server does not have to reply to the call. Rather, in that case it's assumed that the gen_server will reply at some later point using the gen_server:reply/2 call.
The default timeout for gen_server:call/2,3 is 5 seconds. What's happening in your code is your listen/2 function is running in a process that accepts a socket, and is thus the owner of that socket, after which it calls gen_server:call(?SERVER, {do_stuff, 1}). Since your gen_server does not reply to that call, the gen_server:call times out after 5 seconds, killing the process and thus closing the socket.
I am running Erlang R16B03-1 (erts-5.10.4) at OS X 10.9.2. Erlang was installed by using brew.
And I am trying to run a gen_server module.
-module(logger).
-author("evangelosp").
-behaviour(gen_server).
%% API
-export([start/0, stop/0, log/2]).
%% gen_server callbacks
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).
-define(SERVER, ?MODULE).
%%%===================================================================
%%% API
%%%===================================================================
start() -> gen_server:start_link({global, ?SERVER}, ?MODULE, [], []).
stop() -> gen_server:call(?MODULE, stop).
log(_Level, _MSG) -> gen_server:call(?MODULE, {add, {_Level, _MSG}}).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
init([]) -> {ok, ets:new(?MODULE, [])}.
handle_call(_Request, _From, Table) -> {reply, {ok, ["Mplah!", _Request, _From, Table]}, Table}.
handle_cast(_Request, State) -> {noreply, State}.
handle_info(_Info, State) -> {noreply, State}.
terminate(_Reason, _State) -> ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
In the erlang shell I am running:
Eshell V5.10.4 (abort with ^G)
1> c(logger).
{ok,logger}
2> logger:start().
{ok,<0.40.0>}
3> logger:log(info, "Hello World").
** exception exit: {noproc,{gen_server,call,
[logger,{add,{info,"Hello World"}}]}}
in function gen_server:call/2 (gen_server.erl, line 180)
And I can't get rid of that exception. I haven't actually found any useful resource by looking up the exception message, but this which didn't help much.
Cheers.
In you code start() -> gen_server:start_link({global, ?SERVER}, ?MODULE, [], [])., you use {global, ?SERVER} which means that:
If ServerName={global,GlobalName} the gen_server is registered
globally as GlobalName using global:register_name/2.
So when you send message to the server, you should write log(_Level, _MSG) -> gen_server:call({global, ?MODULE}, {add, {_Level, _MSG}}).. Please see the erlang doc:
call(ServerRef, Request, Timeout) -> Reply
Types:
ServerRef = Name | {Name,Node} | {global,GlobalName} |
ServerRef can be:
Name, if the gen_server is locally registered,
{global,GlobalName}, if the gen_server is globally registered.
Your server is not registered, so it is accesible only by its pid. But the interface functions use the implicit macro ?MODULE (which is replaced by logger at compilation) to access it.
You need either to change your interface functions, or, to register the server in the start function:
start() ->
{ok,Pid} = gen_server:start_link({global, ?SERVER}, ?MODULE, [], []),
register(?MODULE,Pid).
[edit] Thanks Evalon, I made the correction in the answer :o)
I have a simple server (below). I can create the server with cell_tracer:start_link() and this works; I know this because when I repeat the same call, I get an already_started error. The problem is that after the server is created, using cell_tracker:get_cells() (or doing any gen_server:call(...)) on it causes the server to exit with an exception like this:
** exception exit: {noproc,{gen_server,call,[cell_tracker,get_cells]}}
in function gen_server:call/2 (gen_server.erl, line 180)
I can call the handle_call(get_cells...) directly, and I get the expected result.
I'm not sure what's happening here. It doesn't give me much information to work with, and I don't see a problem. How can I dig into this further?
-module(cell_tracker).
-behavior(gen_server).
-export([start_link/0, stop/0]).
-export([add_cell/1, del_cell/1, get_cells/0]).
-export([init/1,
handle_cast/2,
handle_call/3,
terminate/2,
handle_info/2,
code_change/3]).
%% operational api
start_link() ->
gen_server:start_link({global, ?MODULE}, ?MODULE, [], []).
stop() ->
gen_server:cast(?MODULE, stop).
%% traking api
add_cell(Cell) ->
gen_server:call(?MODULE, { add_cell, Cell }).
del_cell(Cell) ->
gen_server:call(?MODULE, { del_cell, Cell }).
get_cells() ->
gen_server:call(?MODULE, get_cells).
%% gen_server callbacks
init(Coords) ->
{ok, Coords}.
handle_cast(stop, Coords) ->
{stop, normal, Coords}.
handle_call({add_cell, Cell}, _From, Cells) ->
{reply, ok, [Cell|Cells]};
handle_call({del_cell, Cell}, _From, Cells) ->
{reply, ok, Cells -- Cell};
handle_call(get_cells, _From, Cells) ->
{reply, Cells, Cells}.
terminate(unnormal, _State) -> ok.
handle_info(_,_) -> ok.
code_change(_,State,_) -> {ok, State}.
register the server locally
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
or if you register {global, ?MODULE} cast, call to {global, ?MODULE}
see gen_server
I have got the same problem working with python ( 2.7 ) and couchdb ( 2.1 ).
The point was I did save document without id. Here is an example
import couchdb
server = couchdb.Server('http://admin:PASSWORD#127.0.0.1:5984/')
db = server.create('numbers')
idd, revv = db.save({"taras":"vaskiv"})
after this I got this exception
431 # Store cachable responses
ServerError: (500, (u'noproc', u'{gen_server,call,[couch_uuids,create]}'))
But when I did save document with id, everything seems to work just fine.
idd, revv = db.save({"taras":"vaskiv", "_id": "55"})
My init() function creates UDP Socket and Returns Socket value as a State.
start() ->
{ok, ServerPid} = gen_server:start_link(?MODULE, [], []).
%%% gen_server API
init([]) ->
{ok, Socket} = gen_udp:open(8888, [list, {active,false}]),
{ok, Socket}.
How can I get Socket in my function start()?
You need to fetch the socket by making a gen_server:call to the newly created gen_server process. e.g.:
start() ->
{ok, ServerPid} = gen_server:start_link(?MODULE, [], []),
Socket = gen_server:call(ServerPid, fetch_socket),
... Use Socket ...
And in the gen_server add something like:
handle_call(fetch_socket, _From, State) ->
{reply, State, State}. %% State == Socket
If you need the udp socket int your start function, you can also create it in the start function, and pass it to the start link call as a parameter. That way you don't have to call the server after you created it.
rvirding points out that this will cause the starting process to receive messages from the udp socket, not the newly spawned server. See the comments for more information. It's not clear from the question what exactly the socket is needed for in the start method, but make sure this is the behavior you want.
start() ->
{ok, Socket} = gen_udp:open(8888, [list, {active,false}]),
{ok, ServerPid} = gen_server:start_link(?MODULE, Socket, []).
%%% gen_server API
init(Socket) ->
{ok, Socket}.
I'm trying implement a simple supervisor and just have it restart child processes if they fail. But, I don't even know how to spawn more than one process under a supervisor! I looked at simple supervisor code on this site and found something
-module(echo_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
start_link() ->
{ok, Pid} = supervisor:start_link(echo_sup, []),
unlink(Pid).
init(_Args) ->
{ok, {{one_for_one, 5, 60},
[{echo_server, {echo_server, start_link, []},
permanent, brutal_kill, worker, [echo_server]},
{echo_server2, {echo_server2, start_link, []},
permanent, brutal_kill, worker, [echo_server2]}]}}.
I assumed that putting "echo_server2" part in the init() function would spawn another process under this supervisor, but I end up getting an exception exit:shutdown message.
Both the files "echo_server" and "echo_server2" are the same code but different names. So I'm just confused now.
-module(echo_server2).
-behaviour(gen_server).
-export([start_link/0]).
-export([echo/1, crash/0]).
-export([init/1, handle_call/3, handle_cast/2]).
start_link() ->
{ok,Pid} = gen_server:start_link({local, echo_server2}, echo_server2, [], []),
unlink(Pid).
%% public api
echo(Text) ->
gen_server:call(echo_server2, {echo, Text}).
crash() ->
gen_server:call(echo_server2, crash).
%% behaviours
init(_Args) ->
{ok, none}.
handle_call(crash, _From, State) ->
X=1,
{reply, X=2, State};
handle_call({echo, Text}, _From, State) ->
{reply, Text, State}.
handle_cast(_, State) ->
{noreply, State}.
First you need read some docs about OTP/gen_server and OTP/supervisors. You have few errors in your code.
1) In echo_sup module change your start_link function as follow:
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
Dont know why do you unlink/1 after process has been started.
2) In both echo_servers change start_link function to:
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
You should not to change return value of this function, because supervisor expect one of this values:
{ok,Pid} | ignore | {error,Error}
You don't need two different modules just to run two instances of the same server. The conflict problem is due to the tag in the child specification which has to be unique. It is the first element in the tuple. So you could have something like:
[{echo_server, {echo_server, start_link, []},
permanent, brutal_kill, worker, [echo_server]},
{echo_server2, {echo_server, start_link, []},
permanent, brutal_kill, worker, [echo_server]}]}}.
Why do you unlink the child processes? The supervisor uses these links to supervise its children. The error you are getting is that the supervisor expects the functions which start the children to return {ok,ChildPid}, this is how it gets the pid of the children, so when it gets another return value it fails the startup of the children and then gives up itself. All according to how it is supposed to work.
If you want to register both servers then you could modify the start_link function to take the name to use as an argument and pass so you can explicitly pass it in through the child spec. So:
start_link(Name) ->
gen_server:start_link({local, Name}, ?MODULE, [], []).
and
[{echo_server, {echo_server, start_link, [echo_server]},
permanent, brutal_kill, worker, [echo_server]},
{echo_server2, {echo_server, start_link, [echo_server2]},
permanent, brutal_kill, worker, [echo_server]}]}}.
Using the module name as the registered name for a server is just a convention which only works if you run one instance of the server.