GitLab系列3 Unicorn

GitLab 爲何須要 Unicorn

上一回咱們主要講解了 GitLab-workhorse 組件的智能代理功能,從這一回起將開始介紹最核心也最複雜的組件:Unicorn(GitLab Rails),上次也說了這個組件主要處理動態網頁和 api 接口html


此篇文章先介紹 Unicorn 的做用,光是這個就能講不少內容了java


Unicorn 是使用多進程模型的 Ruby web 服務器,遵循 Rack 協議。若是要類比 java web 開發技術棧的話,Rails 應用至關於 Spring MVC 框架應用,Unicorn web 服務器至關於 tomcatnginx

GitLab 的 Rails 應用程序(即 gitlab-ce)是在 Unicorn 服務器內運行的,使用 Unicorn 的緣由是: Unicorn 能爲 Rails 應用提供併發處理客戶端請求的能力,而且提供了更強的容錯處理git

Unicorn 的多進程模型能很好地利用服務器多核 CPU 的資源,以提供更好的併發能力。當 Unicorn 啓動時,Unicorn 的主進程,即 master 進程,會以 fork 的方式建立一系列 worker 進程。Unicorn 經過一個 master 進程來管理多個 worker 進程,其中 master 進程不負責處理客戶端的 HTTP 請求,多個 worker 進程監聽同一組套接字以處理客戶端請求github


實際上,worker 進程可能會掛掉或者超時(超時是指,若是 master 進程發現某個 worker 進程耗費太長時間在處理一個請求,master 進程將以發送信號(SIGKILL, kill -9)終止 worker 進程)web

# unicorn_stderr.log 日誌,如下表示id 爲 10 的 worker 的進程超時,master 進程殺掉後又重啓了新進程,重啓先後 pid 是不一致的
[2015-06-05T10:58:08.660325 #56227] ERROR -- : worker=10 PID:53009 timeout (61s > 60s), killing
[2015-06-05T10:58:08.699360 #56227] ERROR -- : reaped #<Process::Status: pid 53009 SIGKILL (signal 9)> worker=10
[2015-06-05T10:58:08.708141 #62538] INFO -- : worker=10 spawned pid=62538
[2015-06-05T10:58:08.708824 #62538] INFO -- : worker=10 ready

複製代碼

無論 worker 進程會以哪一種方式終止,master 進程總會建立全新的 worker 進程替代原 worker 進程,整個過程不會丟棄用戶的請求編程

gitlab-ce 自己是內存泄漏的應用。因爲 Unicorn 在運行過程會 fork 大量的 worker 進程,內存泄漏會顯如今長時間運行的進程中,如 worker 進程(master 進程由於不頻繁處理用戶請求而幾乎沒有內存泄漏的現象)。而 Unicorn 自己並無提供自動重啓 worker 進程的功能,爲解決這個問題就出現了 unicorn-worker-killer ,具體查看 github.com/kzk/unicorn…api

GitLab 使用了 unicorn-worker-killer 以讓這些進程的內存泄漏得以管控:Unicorn worker 進程在每處理 16 個請求後將進行內存自檢,若是 worker 進程佔用的內存大小超過了預設定的值,Unicorn master 進程將會自動將此 worker 進程替換掉,整個過程不會影響任何請求的處理tomcat

# unicorn_stderr.log 日誌,如下表示 worker 進程的當前佔有內存超過約 250M,因而對此 worker 進程執行重啓替換的操做
[2015-06-05T12:07:41.828374 #125918] WARN -- : #<Unicorn::HttpServer:0x00000002734770>: worker (pid: 125918) exceeds memory limit (256413696 bytes > 254802235 bytes)
[2015-06-05T12:07:41.828472 #125918] WARN -- : Unicorn::WorkerKiller send SIGQUIT (pid: 125918) alive: 23 sec (trial 1)
[2015-06-05T12:07:42.025916 #117565] INFO -- : reaped #<Process::Status: pid 125918 exit 0> worker=4
[2015-06-05T12:07:42.034527 #127549] INFO -- : worker=4 spawned pid=127549
[2015-06-05T12:07:42.035217 #127549] INFO -- : worker=4 ready

複製代碼

綜上所述,GitLab 使用 Unicorn 的目的:bash

  1. 充分利用服務器多核 CPU 以併發處理客戶端請求
  2. 可用性:一個進程的異常不會導致整個 GitLab 應用癱瘓;
  3. 管理 Rails 應用的內存泄漏

固然它也帶來了一些問題:

  1. 使用多進程就不得不面臨吃內存的難題,同時使得 unicorn 的 worker 進程數量較受限制
  2. 多進程阻塞式 IO 難以接受慢客戶端形成的性能損失(想象全部的 worker 進程都正在處理慢客戶端,若是客戶端還在慢慢地讀取 worker 進程準備好的響應信息,那 worker 進程就沒辦法處理下一個請求),所以通常狀況下需經過反向代理服務器(如 nginx 服務器,或 gitlab-workhorse 等)才能解決慢客戶端的問題(worker 進程把處理好的響應消息交給反向代理服務器緩衝區,由反向代理服務器繼續和客戶端慢慢糾纏,本身繼續處理下一個請求,這樣 Unicorn 的吞吐量天然就不會受影響了)


  3. Unicorn 的設計與 GitLab 的 git-over-http/https 業務不兼容,即經過 HTTP/HTTPS 對 Git 存儲倉庫進行訪問(git clone/push 等)。git-over-http/https 自己是相對比較耗時的操做,而 unicorn 服務器若是爲了知足此業務而調大請求的超時參數顯然是不合適的

附錄

參考連接

相關文章
相關標籤/搜索