大家好,这一篇是 cowboy 源码分析的第五篇文章了,可能我的写作能力不好,很多朋友看的比较迷糊,我也是尽力去说的更明白,希望越写越好吧。
上一篇,我们讲到了 cowboy:child_spec/6 这个方法,这个方法返回 动态启动 cowboy_listener_sup 模块的子进程规格,然后通过添加到监督进程,并启动supervisor:start_child(cowboy_sup, child_spec(Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts)).
接下来我们看下cowboy_listener_sup 模块:
-module(cowboy_listener_sup). -behaviour(supervisor). -export([start_link/5]). %% API. -export([init/1]). %% supervisor. %% API. -spec start_link(non_neg_integer(), module(), any(), module(), any()) -> {ok, pid()}. start_link(NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts) -> MaxConns = proplists:get_value(max_connections, TransOpts, 1024), {ok, SupPid} = supervisor:start_link(?MODULE, []), {ok, ListenerPid} = supervisor:start_child(SupPid, {cowboy_listener, {cowboy_listener, start_link, [MaxConns, ProtoOpts]}, permanent, 5000, worker, [cowboy_listener]}), {ok, ReqsPid} = supervisor:start_child(SupPid, {cowboy_requests_sup, {cowboy_requests_sup, start_link, []}, permanent, 5000, supervisor, [cowboy_requests_sup]}), {ok, _PoolPid} = supervisor:start_child(SupPid, {cowboy_acceptors_sup, {cowboy_acceptors_sup, start_link, [ NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts, ListenerPid, ReqsPid ]}, permanent, 5000, supervisor, [cowboy_acceptors_sup]}), {ok, SupPid}. %% supervisor. -spec init([]) -> {ok, {{one_for_all, 10, 10}, []}}. init([]) -> {ok, {{one_for_all, 10, 10}, []}}.
这个模块有2个函数,init/1 定义了 重启策略和最大重启频率,重点看下 start_link/5 这个方法:
a. 首先:
MaxConns = proplists:get_value(max_connections, TransOpts, 1024),
这个方法我第一次遇到,查了下 erlang doc,地址:http://www.erlang.org/doc/man/proplists.html
proplists:get_value(Key, List, Default) -> term().
Key = term()
List = [term()]
Default = term()
Returns the value of a simple key/value property in List. If lookup(Key, List) would yield {Key, Value}, this function returns the corresponding Value, otherwise Default is returned.
{ok, SupPid} = supervisor:start_link(?MODULE, []),
{ok, ListenerPid} = supervisor:start_child(SupPid,
{cowboy_listener, {cowboy_listener, start_link, [MaxConns, ProtoOpts]},
permanent, 5000, worker, [cowboy_listener]}),
{ok, ReqsPid} = supervisor:start_child(SupPid,
{cowboy_requests_sup, {cowboy_requests_sup, start_link, []},
permanent, 5000, supervisor, [cowboy_requests_sup]}),
{ok, _PoolPid} = supervisor:start_child(SupPid,
{cowboy_acceptors_sup, {cowboy_acceptors_sup, start_link, [
NbAcceptors, Transport, TransOpts,
Protocol, ProtoOpts, ListenerPid, ReqsPid
]}, permanent, 5000, supervisor, [cowboy_acceptors_sup]}),
%% @private %% %% We set the process priority to high because cowboy_listener is the central %% gen_server in Cowboy and is used to manage all the incoming connections. %% Setting the process priority to high ensures the connection-related code %% will always be executed when a connection needs it, allowing Cowboy to %% scale far beyond what it would with a normal priority. -spec start_link(non_neg_integer(), any()) -> {ok, pid()}. start_link(MaxConns, ProtoOpts) -> gen_server:start_link(?MODULE, [MaxConns, ProtoOpts], [{spawn_opt, [{priority, high}]}]).
我们看下 start_link/2 函数。这里 MaxConns = proplists:get_value(max_connections, TransOpts, 1024), ProtoOpts = [{dispatch, Dispatch}]。我们看下gen_server:start_link方法这2个参数的作用。erlang doc 地址:http://www.erlang.org/doc/man/gen_server.html
第二个参数 Args = [MaxConns, ProtoOpts],官方文档对这个参数的解释:Args is an arbitrary term which is passed as the argument to Module:init/1. 大意是 Args 是一个任意的 term() 作为参数传递给 Module:init/1。
第三个参数 Options = [{spawn_opt, SOpts}] = [{spawn_opt, [{priority, high}]}],官方文档对这个参数的解释:If the option {spawn_opt,SOpts} is present, SOpts will be passed as option list to the spawn_opt BIF which is used to spawn the gen_server. 大意如下:如果该选项 {spawn_opt,SOpts} 存在,SOpts 将通过该选项列表将作为 spawn_otp的参数。
我看了下 OTP 源码,最后这个参数是出现在 proc_lib.erl 模块中,如下图:
那么我们看下 elang:spawn_opt/4 这个方法究竟是啥意思。还是查 erlang doc,地址:http://www.erlang.org/doc/man/erlang.html#spawn_opt-4 传递这个参数,设置新创建的进程的优先级,相当于执行在进程的开始函数执行 process_flag(priority, Level) ,具体说明如下:
Sets the priority of the new process. Equivalent to executing process_flag(priority, Level) in the start function of the new process, except that the priority will be set before the process is selected for execution for the first time. For more information on priorities see process_flag(priority, Level).
好了,跑了有点远了,继续回来说这个函数,其实,上面那个参数,通俗的理解就是设置进程优先级。
%% @private -spec init(list()) -> {ok, #state{}}. init([MaxConns, ProtoOpts]) -> ReqsTable = ets:new(requests_table, [set, private]), Queue = queue:new(), {ok, #state{reqs_table=ReqsTable, max_conns=MaxConns, proto_opts=ProtoOpts, queue=Queue}}.
这个方法接受了一个参数,就是 start_link/3 的第二个参数,然后创建 ets表 requests_table,类型是[set, private];定义了一个先进先出的队列;最后返回 {ok, State}。State的记录类型是:
-record(state, { req_pools = [] :: pools(), reqs_table :: ets:tid(), queue = undefined :: queue(), max_conns = undefined :: non_neg_integer(), proto_opts :: any(), proto_opts_vsn = 1 :: non_neg_integer() }).
好了,这个模块,就先讲到这里,谢谢大家支持。