大家好,這一篇是 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) ,具體說明如下:
- {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() }).
好了,這個模塊,就先講到這里,謝謝大家支持。