这是一个最常用的组件,且最容易看懂的源码
先说下总体的执行逻辑
1、启动一个进程,用来管理工作进程,暂定为控制进程吧
2、在这个进程内,启动sup进程,子进程模式是为simple_one_for_one
3、在监督进程下,启动多个工作进程,并把工作进程PID放入控制进程的state内
4、然后你需要使用工作进程的话,检出:checkout、检入:checkin
直接上代码吧
-spec child_spec(PoolId :: term(), PoolArgs :: proplists:proplist())
-> supervisor:child_spec().
child_spec(PoolId, PoolArgs) ->
child_spec(PoolId, PoolArgs, []).
-spec child_spec(PoolId :: term(),
PoolArgs :: proplists:proplist(),
WorkerArgs :: proplists:proplist())
-> supervisor:child_spec().
child_spec(PoolId, PoolArgs, WorkerArgs) ->
{PoolId, {poolboy, start_link, [PoolArgs, WorkerArgs]},
permanent, 5000, worker, [poolboy]}.
这个是子进程规格,如果需要在supervisor下启动,那么用这个函数
里面有两个参数,一个参数PoolArgs是进程池参数列表,另外一个参数WorkerArgs是工作进程参数列表
你也可以直接使用以下方式启动
-spec start(PoolArgs :: proplists:proplist())
-> start_ret().
start(PoolArgs) ->
start(PoolArgs, PoolArgs).
-spec start(PoolArgs :: proplists:proplist(),
WorkerArgs:: proplists:proplist())
-> start_ret().
start(PoolArgs, WorkerArgs) ->
start_pool(start, PoolArgs, WorkerArgs).
-spec start_link(PoolArgs :: proplists:proplist())
-> start_ret().
start_link(PoolArgs) ->
%% for backwards compatability, pass the pool args as the worker args as well
start_link(PoolArgs, PoolArgs).
-spec start_link(PoolArgs :: proplists:proplist(),
WorkerArgs:: proplists:proplist())
-> start_ret().
start_link(PoolArgs, WorkerArgs) ->
start_pool(start_link, PoolArgs, WorkerArgs).
然后我们来看下
start_pool(start_link, PoolArgs, WorkerArgs)
这个函数做了什么
start_pool(StartFun, PoolArgs, WorkerArgs) ->
case proplists:get_value(name, PoolArgs) of
undefined ->
gen_server:StartFun(?MODULE, {PoolArgs, WorkerArgs}, []);
Name ->
gen_server:StartFun(Name, ?MODULE, {PoolArgs, WorkerArgs}, [])
end.
这就是启动了控制进程,注意下name这个字段的数据,从进程池参数列表中取
{name, {local, 本地注册名}} (也可以是其他的)
然后再看下init函数
init({PoolArgs, WorkerArgs}) ->
process_flag(trap_exit, true),
Waiting = queue:new(),
Monitors = ets:new(monitors, [private]),
init(PoolArgs, WorkerArgs, #state{waiting = Waiting, monitors = Monitors}).
init([{worker_module, Mod} | Rest], WorkerArgs, State) when is_atom(Mod) ->
{ok, Sup} = poolboy_sup:start_link(Mod, WorkerArgs),
init(Rest, WorkerArgs, State#state{supervisor = Sup});
init([{size, Size} | Rest], WorkerArgs, State) when is_integer(Size) ->
init(Rest, WorkerArgs, State#state{size = Size});
init([{max_overflow, MaxOverflow} | Rest], WorkerArgs, State) when is_integer(MaxOverflow) ->
init(Rest, WorkerArgs, State#state{max_overflow = MaxOverflow});
init([{strategy, lifo} | Rest], WorkerArgs, State) ->
init(Rest, WorkerArgs, State#state{strategy = lifo});
init([{strategy, fifo} | Rest], WorkerArgs, State) ->
init(Rest, WorkerArgs, State#state{strategy = fifo});
init([_ | Rest], WorkerArgs, State) ->
init(Rest, WorkerArgs, State);
init([], _WorkerArgs, #state{size = Size, supervisor = Sup} = State) ->
Workers = prepopulate(Size, Sup),
{ok, State#state{workers = Workers}}.
这块做了三件事
1、把进程池参数列表,存入进程的State内
2、当字段是worker_module,启动监督进程,但并没启动子进程
且把子进程模块和子进程参数列表传过来了就是WorkerArgs
start_link(Mod, Args) ->
supervisor:start_link(?MODULE, {Mod, Args}).
init({Mod, Args}) ->
{ok, {{simple_one_for_one, 0, 1},
[{Mod, {Mod, start_link, [Args]},
temporary, 5000, worker, [Mod]}]}}.
3、当进程池参数列表为空,开始启动子进程prepopulate(Size, Sup)
prepopulate(N, _Sup) when N < 1 ->
[];
prepopulate(N, Sup) ->
prepopulate(N, Sup, []).
prepopulate(0, _Sup, Workers) ->
Workers;
prepopulate(N, Sup, Workers) ->
prepopulate(N-1, Sup, [new_worker(Sup) | Workers]).
这段就是调用 new_worker(Sup) 这个函数Size次
new_worker(Sup) ->
{ok, Pid} = supervisor:start_child(Sup, []),
true = link(Pid),
Pid.
这段就是在sup下启动子进程,并link,返回子进程Pid
这个子进程PID列表放入控制进程State的workers字段内
下面就是使用了
-spec transaction(Pool :: pool(), Fun :: fun((Worker :: pid()) -> any()))
-> any().
transaction(Pool, Fun) ->
transaction(Pool, Fun, ?TIMEOUT).
-spec transaction(Pool :: pool(), Fun :: fun((Worker :: pid()) -> any()),
Timeout :: timeout()) -> any().
transaction(Pool, Fun, Timeout) ->
Worker = poolboy:checkout(Pool, true, Timeout),
try
Fun(Worker)
after
ok = poolboy:checkin(Pool, Worker)
end.
这个函数里面,就是检出PID, 使用PID,检入PID
更详细的内容需要你自己再阅读下