erlang poolboy 源码剖析
2019-11-30

目录

poolboy实现思路进程图State 结构关键函数child_specinit支持的optionsnew_wokercheckoutcheckintranscationhandle_info疑问

poolboy

poolboy是一个轻量的,通用的,高性能的,高可用的 Erlang Pooling Library.

实现思路

使用 gen_server 来保证多线程可用使用 link, supversior 来监控管理 worker使用 mornitor 来监控管理任务Checkout 出错自动 cancel_waiting工作线程出错, 自动 checkin

进程图

各进程介绍:

poolboy 是 poolboy 的管理进程,可用给每个 poolboy 设定一个名字,不同名字的 poolboy 完全独立poolboy_sup 是一个 supervsior, 监控所有的 poolboy_workerpoolboy_worker 是工作进程client_process 是使用 poolboy 的进程

各进程之间的关系:

    poolboy 进程中存有 poolboy_sup 的pidpoolboy_sup supervise 所有 poolboy_worker 进程,exit poolboy_sup 时会同时exit 所有 poolboy_worker 进程poolboy 进程 link 所有 poolboy_worker 进程,在 poolboy_worker 进程 exit 的时候做出相应的处理,同理, poolboy 进程挂掉的时候结束所有 poolboy_worker 进程, 这里用 link 的另一层考虑应该时和下名 mornitor 的进程有所区分poolboy 进程 mornitor 所有 client_process 进程,在 client_process 进程结束的时候,自动 check_in 对应的 poolboy_worker 进程

State 结构

waiting: 等待队列monitors: 监听etssupversior: pool_boy supversior pidsize: worker 数量限制workers:工作进程max_overflow: worker overflow 限制overflow: 当前 overflowstrategry: lifo 或者 fifo

关键函数

child_spec

启动poolboy gen_server, 可以传进程名字进来

init

创角等待队列 waiting 队列创角 mornitors etsState 中存储 waiting queue 和 mornitors ets tid创建 poolboy_sup 和 workers

支持的options

PoolArgs:

worker_module: worker 模块名, 启动 pool_boy supversior, 传入worker_module 参数size: 初始化 #state.size, init最后会创建该数量的workermax_overflow: 初始化 #state.max_overflowstrategray: 添加worker的策略

new_woker

new_worker(Sup):在Sup下创建worker, ==将 worker 与 poolboy 链接起来==, 监控worker状态new_worker(Sup, FromPid): 不链接 worker, 而是监控 FromPid

checkout

checkout(Pool, Block, Timeout)handle_call({checkout, CRef, Block}, {FromPid, _} = From, State):

存在worker时:监控FromPid, 将监控关系{WorkerID, CRef, MRef}插入到mornitor ets.移除使用的Workers已经overflow,未超过上限new_worker(Sup, FromPid), 监控FromPid将监控关系保存到 mornitor etsOverflow + 1Block = false{reply, full, State};[]监控FromPid将{FromPid, CRef, MRef} 存储到 waiting队列

有异常时 cacel_waiting: ==必要性==, 防止fun(Worker)崩掉,自动checkin:

在 mornitor ets 或者 waiting 队列中删除 CRef对应的项

checkin

gen_server:cast(Pool, {checkin, Worker}).

查找对应的监控关系,解除监控关系handle_checkin存在等待队列, 转让给等待队列中的任务使用不存在等待队列且 overflow, 删除worker, overflow -1否则,将 worker 添加到 State#state.workers中

transcation

transaction(Pool, Fun, Timeout) -> Worker = poolboy:checkout(Pool, true, Timeout), try Fun(Worker) after ok = poolboy:checkin(Pool, Worker) end.

handle_info

handle_info({"DOWN", MRef, _, _, _}, State)

用户进程崩掉的时候:

删除监控关系, handle_checkin, 自动checkin或者删除等待队列

handle_info({"EXIT", Pid, _Reason}, State) 工作中worker: exit的时候出列流程和 handle_check_in类似,但是要注意新建 worker空闲worker: 重建新的, ==不会自动restart吗?==, supvisor策略为 never restart, 自己管理

疑问

    poolboy_sup 不负责 poolboy_worker 的重启, 只是负责 poolboy_worker 的exit, 那么最后用 link 来结束所有 poolboy_worker, 有什么不同,有什么区别?checkout 时候没有直接使用 self PID 作为任务的ref,而是重新 make_ref, 注释说是为了解决某些情况下checkout超时不会自动checkin, 需要等 client_process exit 才会被 checkin, 没相通是为什么?