圖解 Unicorn 工作原理

我很早之前,就是通過這篇文章搞懂了 Python Web 應用服務器是個什么鬼,雖然本文講的是 Ruby 的 Web 應用服務器,但原理是通的所以翻出來推薦給大家,下面是正文。
什么是 Unicorn
Unicorn 是 Ruby Web 應用中的一款應用服務器,提供兩個功能:
為 Rack 應用(使用 Ruby 編程語言開發(fā)的 Web 服務器和 Web 應用程序之間的模塊化接口)提供 HTTP 服務能力 為 Web 應用實現(xiàn)高并發(fā)能力
注:Python 也有類似功能的應用服務器:Gunicorn 就是從 Unicorn 移植的 pre-fork worker 模型,推薦 Python 程序員閱讀本文。
Unicorn 工作在 Web 的應用層,直接調用后端 Handler 處理請求。
如何工作提供 HTTP 服務功能:
監(jiān)聽端口,接收 HTTP 請求 解析 HTTP 請求,調用應用處理請求 將處理結果返回
通過多進程提供高并發(fā)能力。由于 Ruby 全局解釋鎖阻礙的同一進程中的線程并行執(zhí)行,Ruby 應用中的高并發(fā)必須通過多進程實現(xiàn)。
線程中如果有 IO 操作時,GIL 會自動釋放,所以在線程中有 IO 操作的情況下,同一進程中的多個線程可以實現(xiàn)近似的并行執(zhí)行。

通過 Master-Workers 進程結構提供服務:
一個 Master,管理 Worker 進程,處理外部信號,不處理請求 多個 Worker 進程,處理實際的請求,彼此獨立
此結構和 Nginx 多進程模式一致。Master 作為勞心者,不處理實際的事務,只做頂層調度。Workers 作為勞力者,只處理實際的請求,受制于 Master。
請求處理過程

通過上圖總結如下:
Master 監(jiān)聽端口,F(xiàn)ork 子進程 (也可以通過 sparn) 子進程通過 select && accept 調用獲取連接 子進程讀 socket 數(shù)據(jù),調用 Rack App,再將結果回寫 socket
另外:
通過 kgio 實現(xiàn)非阻塞系統(tǒng)調用 通過 C 擴展實現(xiàn)高速 HTTP 協(xié)議解析
IO 模型
由上圖可以看出 unicorn 的 IO 模型有如下特點:
IO 低效,阻塞(宏觀視角,本身使用非阻塞調用) 一個進程同時只能處理一個請求,吞吐量低下 在 socket 讀寫或數(shù)據(jù)庫查詢等等 IO 操作時,進程空閑,資源浪費 無法處理慢 IO,需要前置 Nginx
如何管理進程
Master
殺掉超時的子進程 維持子進程數(shù)量
Worker
檢測 Master,同生共死,代碼 接收 Master 的指令
通信方式:
pipe raindrops (共享數(shù)據(jù))
如何平滑重啟

平滑關鍵點在于:
通過環(huán)境變量傳遞監(jiān)聽的 socket 設置監(jiān)聽 socket close_on_exec,讓操作系統(tǒng)保留監(jiān)聽 socket
unicorn-killer 的代碼
def process_client(client)
super(client) # Unicorn::HttpServer#process_client
return if @_worker_memory_limit_min == 0 && @_worker_memory_limit_max == 0
@_worker_process_start ||= Time.now
@_worker_memory_limit ||= @_worker_memory_limit_min + randomize(@_worker_memory_limit_max - @_worker_memory_limit_min + 1)
@_worker_check_count += 1
if @_worker_check_count % @_worker_check_cycle == 0
rss = GetProcessMem.new.bytes
logger.info "#{self}: worker (pid: #{Process.pid}) using #{rss} bytes." if @_verbose
if rss > @_worker_memory_limit
logger.warn "#{self}: worker (pid: #{Process.pid}) exceeds memory limit (#{rss} bytes > #{@_worker_memory_limit} bytes)"
Unicorn::WorkerKiller.kill_self(logger, @_worker_process_start) # 關鍵點
end
@_worker_check_count = 0
end
end
通過 hack process_client 方法,在請求處理完后,檢測進程內存消耗等參數(shù),操作閾值,則將自己干掉,Master 會自動起新的 Worker 進程。
?? 點擊關注寶藏公眾號 HelloGitHub ??
評論
圖片
表情
