[erlang-questions] Socket acceptor strategy: pooling or async
Andrew Tereskin
tereskin@REDACTED
Thu Apr 4 12:24:19 CEST 2013
Hi everyone,
I'm trying to what is the best strategy for tcp socket acceptors. Just
after brief research I found 2 approaches:
1. Pool of acceptors that can work in parallel. According to documentation
ListenSocket can be used not only in the process where this socket was
created.
Example code (from http://learnyousomeerlang.com/buckets-of-sockets):
%%% The supervisor in charge of all the socket acceptors.
-module(sockserv_sup).
-behaviour(supervisor).
init([]) ->
.....
{ok, ListenSocket} = gen_tcp:listen(Port, [{active,once},
{packet,line}]),
spawn_link(fun empty_listeners/0),
{ok, {{simple_one_for_one, 60, 3600},
[{socket,
{sockserv_serv, start_link, [ListenSocket]}, % pass the socket!
temporary, 1000, worker, [sockserv_serv]}
]}}.
start_socket() -> supervisor:start_child(?MODULE, []).
empty_listeners() -> [start_socket() || _ <- lists:seq(1,20)], ok.
%%%%%%%%%%%%%%%%%%
%% socket acceptor
-module(sockserv_serv).
-behaviour(gen_server).
init(Socket) ->
....
gen_server:cast(self(), accept),
{ok, #state{socket=Socket}}.
handle_call(_E, _From, State) -> {noreply, State}.
handle_cast(accept, S = #state{socket=ListenSocket}) ->
{ok, AcceptSocket} = gen_tcp:accept(ListenSocket),
sockserv_sup:start_socket(), % a new acceptor is born, praise the lord
{noreply, S#state{socket=AcceptSocket, next=name}};
---------------------------------
As I examined code of mochiweb (
https://github.com/mochi/mochiweb/blob/master/src/mochiweb_socket_server.erl,
https://github.com/mochi/mochiweb/blob/master/src/mochiweb_acceptor.erl)
and ranch (https://github.com/extend/ranch) they use the same approach.
2. Using undocumented function prim_inet:async_accept
Example code (from
http://www.trapexit.org/Building_a_Non-blocking_TCP_server_using_OTP_principles
)
init([Port, Module]) ->
process_flag(trap_exit, true),
Opts = [binary, {packet, 2}, {reuseaddr, true},
{keepalive, true}, {backlog, 30}, {active, false}],
case gen_tcp:listen(Port, Opts) of
{ok, Listen_socket} ->
%%Create first accepting process
{ok, Ref} = prim_inet:async_accept(Listen_socket, -1),
{ok, #state{listener = Listen_socket,
acceptor = Ref,
module = Module}};
{error, Reason} ->
{stop, Reason}
end.
handle_info({inet_async, ListSock, Ref, {ok, CliSocket}},
#state{listener=ListSock, acceptor=Ref, module=Module} = State) ->
try
case set_sockopt(ListSock, CliSocket) of
ok -> ok;
{error, Reason} -> exit({set_sockopt, Reason})
end,
%% New client connected - spawn a new process using the
simple_one_for_one
%% supervisor.
{ok, Pid} = tcp_server_app:start_client(),
gen_tcp:controlling_process(CliSocket, Pid),
%% Instruct the new FSM that it owns the socket.
Module:set_socket(Pid, CliSocket),
%% Signal the network driver that we are ready to accept
another connection
case prim_inet:async_accept(ListSock, -1) of
{ok, NewRef} -> ok;
{error, NewRef} -> exit({async_accept, inet:format_error(NewRef)})
end,
{noreply, State#state{acceptor=NewRef}}
catch exit:Why ->
error_logger:error_msg("Error in async accept: ~p.\n", [Why]),
{stop, Why, State}
end;
It is used by erlyvideo (
https://github.com/erlyvideo/erlyvideo/blob/master/apps/erlyvideo/src/core/gen_listener.erl
).
Can you give some pros and cons of both approaches?
With best wishes,
Andrey Tereskin
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20130404/821df153/attachment.htm>
More information about the erlang-questions
mailing list