erlang poolboy 源碼剖析


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 的進程

各進程之間的關系:

  1. poolboy 進程中存有 poolboy_sup 的pid
  2. poolboy_sup supervise 所有 poolboy_worker 進程,exit poolboy_sup 時會同時exit 所有 poolboy_worker 進程
  3. poolboy 進程 link 所有 poolboy_worker 進程,在 poolboy_worker 進程 exit 的時候做出相應的處理,同理, poolboy 進程掛掉的時候結束所有 poolboy_worker 進程, 這里用 link 的另一層考慮應該時和下名 mornitor 的進程有所區分
  4. 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, 自己管理

疑問

  1. poolboy_sup 不負責 poolboy_worker 的重啟, 只是負責 poolboy_worker 的exit, 那么最后用 link 來結束所有 poolboy_worker, 有什么不同,有什么區別?
    • 通過supversior結束poolboy_worker, 會執行 poolboy_worker:terminate
    • 通過 linked process 來結束 poolboy_worker, 需要 poolboy_worker 設置 trapexit
  2. checkout 時候沒有直接使用 self PID 作為任務的ref,而是重新 make_ref, 注釋說是為了解決某些情況下checkout超時不會自動checkin, 需要等 client_process exit 才會被 checkin, 沒想通是為什么?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM