Просмотр исходного кода

Add a max_connections transport option.

Limits the number of parallel requests processed at once.
Waiting requests are kept in the accept queue.
The limit is not explicitly observed, but it should be
around the given value at any time. Defaults to 1024.

Thanks to ostinelli's benchmark to point out the issue hopefully
solved by this and the previous commit.
Loïc Hoguin 14 лет назад
Родитель
Сommit
5d698250b2
2 измененных файлов с 23 добавлено и 10 удалено
  1. 21 9
      src/cowboy_acceptor.erl
  2. 2 1
      src/cowboy_acceptors_sup.erl

+ 21 - 9
src/cowboy_acceptor.erl

@@ -13,28 +13,31 @@
 %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 -module(cowboy_acceptor).
--export([start_link/5]). %% API.
--export([acceptor/5]). %% Internal.
+-export([start_link/6]). %% API.
+-export([acceptor/6]). %% Internal.
 
 %% API.
 
 -spec start_link(LSocket::inet:socket(), Transport::module(),
-	Protocol::module(), Opts::term(), ReqsSup::pid()) -> {ok, Pid::pid()}.
-start_link(LSocket, Transport, Protocol, Opts, ReqsSup) ->
+	Protocol::module(), Opts::term(),
+	MaxConns::non_neg_integer(), ReqsSup::pid()) -> {ok, Pid::pid()}.
+start_link(LSocket, Transport, Protocol, Opts, MaxConns, ReqsSup) ->
 	Pid = spawn_link(?MODULE, acceptor,
-		[LSocket, Transport, Protocol, Opts, ReqsSup]),
+		[LSocket, Transport, Protocol, Opts, MaxConns, ReqsSup]),
 	{ok, Pid}.
 
 %% Internal.
 
 -spec acceptor(LSocket::inet:socket(), Transport::module(),
-	Protocol::module(), Opts::term(), ReqsSup::pid()) -> no_return().
-acceptor(LSocket, Transport, Protocol, Opts, ReqsSup) ->
+	Protocol::module(), Opts::term(),
+	MaxConns::non_neg_integer(), ReqsSup::pid()) -> no_return().
+acceptor(LSocket, Transport, Protocol, Opts, MaxConns, ReqsSup) ->
 	case Transport:accept(LSocket, 2000) of
 		{ok, CSocket} ->
 			{ok, Pid} = supervisor:start_child(ReqsSup,
 				[CSocket, Transport, Protocol, Opts]),
-			Transport:controlling_process(CSocket, Pid);
+			Transport:controlling_process(CSocket, Pid),
+			limit_reqs(MaxConns, ReqsSup);
 		{error, timeout} ->
 			ignore;
 		{error, _Reason} ->
@@ -42,4 +45,13 @@ acceptor(LSocket, Transport, Protocol, Opts, ReqsSup) ->
 			%%       we may want to try and listen again on the port?
 			ignore
 	end,
-	?MODULE:acceptor(LSocket, Transport, Protocol, Opts, ReqsSup).
+	?MODULE:acceptor(LSocket, Transport, Protocol, Opts, MaxConns, ReqsSup).
+
+-spec limit_reqs(MaxConns::non_neg_integer(), ReqsSup::pid()) -> ok.
+limit_reqs(MaxConns, ReqsSup) ->
+	Counts = supervisor:count_children(ReqsSup),
+	Active = lists:keyfind(active, 1, Counts),
+	case Active < MaxConns of
+		true -> ok;
+		false -> timer:sleep(1)
+	end.

+ 2 - 1
src/cowboy_acceptors_sup.erl

@@ -32,8 +32,9 @@ start_link(NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts, ReqsPid) ->
 -spec init(list(term())) -> term(). %% @todo These specs should be improved.
 init([NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts, ReqsPid]) ->
 	{ok, LSocket} = Transport:listen(TransOpts),
+	MaxConns = proplists:get_value(max_connections, TransOpts, 1024),
 	Procs = [{{acceptor, self(), N}, {cowboy_acceptor, start_link, [
-				LSocket, Transport, Protocol, ProtoOpts, ReqsPid
+				LSocket, Transport, Protocol, ProtoOpts, MaxConns, ReqsPid
 			]}, permanent, brutal_kill, worker, dynamic}
 		|| N <- lists:seq(1, NbAcceptors)],
 	{ok, {{one_for_one, 10, 10}, Procs}}.