2021 年 03 月

蒼時

Ruby 世界裡面有很多 Application 伺服器,差異在哪裡?為什麼 Passenger 需要依靠 Nginx 呢?

Application Server

這個名詞最初是用來討論主從式架構系統時,用來區分資料庫伺服器、檔案伺服器等的不同。但現今常指能執行Web應用程式的Web伺服器

從這個角度來看,Ruby 常見的 Puma、Passenger、Unicorn 都屬於 Application Server 的範圍

Web Server

簡單來說可以提供 Web 服務的都屬於 Web Server 的範圍,也因此 Nginx、Puma、Passenger、Unicorn 也都屬於 Web Server 的範圍

(Reverse) Proxy Server

也稱網路代理,是一種特殊的網路服務,允許一個(一般為客戶端)通過這個服務與另一個網路終端(一般為伺服器)進行非直接的連接。一些閘道器、路由器等網路裝置具備網路代理功能。一般認為代理服務有利於保障網路終端的隱私或安全,防止攻擊。

我們在 Rails 正式環境的部署上,通常會用 Nginx 搭配 Puma、Passenger 等 Web Server 實際上是為了將「擅長」的事情分工處理。

在 Ruby(以及其他某些語言)處理靜態檔案的效率是很差的,舉例來說 Nginx 可能只需要在硬碟讀取檔案後回傳給使用者,但經過 Puma 處理時卻需要先呼叫 Ruby 來處理,並且轉接到 Rails 手上經過好幾個標準程序,才會去讀取檔案再進行回傳,而這中間就會造成很大的時間差。

也因此我們會將同時具有 Web Server 和 Proxy Server 機制的 Nginx 作為前面的處理方案,當連線請求出現後先由 Nginx 判斷是否為靜態檔案來決定要以 Web Server 的方式處理,還是 Proxy 給後面的 Application Server 做動態網站相關的處理。

Puma / Passenger / Unicorn 比較

  • Puma 是 Multi-threading 為基礎設計的
  • Unicorn 和 Passenger 預設都是 Process 為基礎設計的

基本上要考慮到時代背景,Unicorn 和 Passenger 都是在 Thread 還沒有到非常成熟被應用在 Web 上的時代所開發的(要考慮到當時硬體、演算法的發展)而 Puma 則是比較晚才推出,因此是以 Thread 為前提設計。

不過 Passenger 在企業版裡面有提供 Multi-threading 的支援,而 Puma 也可以利用增加 Worker 來產生多個 Process 去提供服務。

Ruby 受限於 GVL 的限制,因此無法順利的讓 Thread 在不同的 CPU 上切換來提高效率,因此增加 Process 數量在某些情境上確實可以更加善用 CPU。

Puma 和 Unicorn 都是獨立的 Ruby Gem 來運行,而 Passenger 除了提供 Standalone 模式可以用 Gem 安裝(實際上會包 Nginx 在裡面),更常見的是作為 Nginx 的 Module 來使用。

Passenger 在這個情境來看,他本身只提供了 Application Server 的部分但 Web Server 是靠 Nginx 提供的。

Rack

Puma / Passenger / Unicorn 之所以可以作為 Ruby 的 Application Server 提供服務,是因為 Ruby 有一個官方的標準介面叫做 Rack 定義了標準,因此只要能產生正確的 Rack 呼叫就能夠被任意的 Ruby 專案所使用。

這幾個 Application Server 底層的呼叫牽涉到一些 C 語言的實作,這邊就不特別描述。