流程解讀
Puma 進程(可以有一個或多個)通過 Reactor類中的線程來接受來自套接字的連接妒峦。連接一旦完全緩沖并讀取寝贡,就會移到 todo 列表中扒披,可用的線程會從那里拾取它
在集群模式下,首先啟動一個 master 進程圃泡,它會準備應用程序碟案,然后使用 fork() 系統(tǒng)調(diào)用創(chuàng)建一個或多個 child 進程。這些 child 進程都監(jiān)聽同一個套接字颇蜡。master 進程不監(jiān)聽套接字或處理請求价说,它的主要目的是管理和監(jiān)聽 UNIX 信號,并可能kill或啟動 child 進程风秤。
我們有時將 child 進程(或單進程模式下的 Puma 進程)稱為 worker鳖目,有時將 Puma 的 ThreadPool:創(chuàng)建的線程稱為 worker 線程。
啟動時缤弦,Puma 會監(jiān)聽一個 TCP 或 UNIX 套接字疑苔。
該套接字的 backlog 默認配置為 1024,但實際的 backlog 值會受到 net.core.somaxconn sysctl 值的限制。
backlog 決定了未接受連接的隊列大小惦费。如果 backlog 已滿兵迅,操作系統(tǒng)將不會接受新的連接。
當至少有一個 worker 線程可用于工作時薪贫,reactor 線程會監(jiān)聽套接字并接受請求(如果有等待的請求)恍箭。
- reactor 線程會等待整個 HTTP 請求接收完成。
- Puma 會將等待接收 HTTP 請求體所花費的時間以毫秒為單位暴露給 Rack 應用瞧省,作為 env['puma.request_body_wait']扯夭。
- 一旦完全緩沖并接收,連接就會被推送到 "todo" 集合中鞍匾。
- worker 線程從 "todo" 集合中彈出工作進行處理交洗。
- worker 線程通過調(diào)用配置的 Rack 應用來處理請求。Rack 應用生成 HTTP 響應橡淑。
- worker 線程將響應寫入到連接构拳。雖然 Puma 通過單獨的線程緩沖請求,但它不會為響應使用單獨的線程梁棠。
- 完成之后置森,線程變?yōu)榭捎茫梢蕴幚?"todo" 集合中的另一個連接符糊。
源碼解讀
cli里初始化Launcher并調(diào)用#run
方法
Launcher#run里調(diào)用runner(cluster)的run
Cluster#run
調(diào)用 #spawn_workers
啟動進程
啟動配置workers數(shù)量 - 已啟動workers數(shù)量
(工作進程)
spawn_workers方法在cluster
的run
方法內(nèi)循環(huán)調(diào)用
cluster
的spawn_worker
繼續(xù)調(diào)用worker方法凫海,初始化worker(這里fork進程執(zhí)行)并執(zhí)行run
worker方法里start_server
啟動server,并給到worker
worker run方法里處理一些信號量男娄;主要是啟動了server.run
start_server獲取到binder(io行贪,如socket和tcp_listener等)
server.run 方法,啟動線程池模闲;
調(diào)用server.handle_servers建瘫,開始io多路復用(select)
這里我們先記住,同時啟動了 清理空閑線程
使用client 并放入線程池
thread_pool里 <<
把任務放入@todo
里(使用mutex等保證線程安全)围橡,且 線程未創(chuàng)建滿時(小于最大數(shù)量)暖混,再起新線程(span_thread)
spawn_thread后線程內(nèi)
while true
無限循環(huán),從todo里取出work翁授,并執(zhí)行block.call(work)
block是創(chuàng)建時傳入的process_client
process_client又調(diào)用
handle_request
拣播,handle_request里最終出現(xiàn)了我們熟悉的@app.call
process_client會接著調(diào)用prepare_response -> fast_write_response 最終往io里寫入了http response,整個流程完成
自動清理空閑線程
循環(huán)中遇到 todo為空收擦,且有空閑線程清理標識贮配,結(jié)束當前線程