目錄
poolboy
poolboy是一個輕量的,通用的,高性能的,高可用的 Erlang Pooling Library.
實現思路
- 使用 gen_server 來保證多線程可用
- 使用 link, supversior 來監控管理 worker
- 使用 mornitor 來監控管理任務
- Checkout 出錯自動 cancel_waiting
- 工作線程出錯, 自動 checkin
進程圖
各進程介紹:
- poolboy 是 poolboy 的管理進程,可用給每個 poolboy 設定一個名字,不同名字的 poolboy 完全獨立
- poolboy_sup 是一個 supervsior, 監控所有的 poolboy_worker
- poolboy_worker 是工作進程
- client_process 是使用 poolboy 的進程
各進程之間的關系:
- poolboy 進程中存有 poolboy_sup 的pid
- poolboy_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: 監聽ets
- supversior: pool_boy supversior pid
- size: worker 數量限制
- workers:工作進程
- max_overflow: worker overflow 限制
- overflow: 當前 overflow
- strategry: lifo 或者 fifo
關鍵函數
child_spec
- 啟動poolboy gen_server, 可以傳進程名字進來
init
- 創角等待隊列 waiting 隊列
- 創角 mornitors ets
- State 中存儲 waiting queue 和 mornitors ets tid
- 創建 poolboy_sup 和 workers
支持的options
PoolArgs:
- worker_module: worker 模塊名, 啟動 pool_boy supversior, 傳入worker_module 參數
- size: 初始化 #state.size, init最后會創建該數量的worker
- max_overflow: 初始化 #state.max_overflow
- strategray: 添加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 ets
- Overflow + 1
- Block = 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, 有什么不同,有什么區別?
- 通過supversior結束poolboy_worker, 會執行 poolboy_worker:terminate
- 通過 linked process 來結束 poolboy_worker, 需要 poolboy_worker 設置 trapexit
- checkout 時候沒有直接使用 self PID 作為任務的ref,而是重新 make_ref, 注釋說是為了解決某些情況下checkout超時不會自動checkin, 需要等 client_process exit 才會被 checkin, 沒想通是為什么?