HTTP Server 大小事


Outline
  1. 1. Web server / Application server 傻傻分不清楚 ?
  2. 2. Web server
  3. 3. 什麼是 Proxy Server 、 Reverse Proxy Server?
    1. 3.1. Proxy Server(代理伺服器)
    2. 3.2. Reverse Proxy Server(反向代理伺服器)
  4. 4. Application server
    1. 4.1. 什麼是 Rack?
    2. 4.2. 比較 Rails 各種 App server
    3. 4.3. 現實世界的 App server 怎麼運作
    4. 4.4. 為什麼需要 Apache/Nginx reverse proxy server?
    5. 4.5. 現實中的 Requests
  5. 5. OS 課程回顧:
    1. 5.0.1. Process 去取得 CPU 資源,很多 Process 去運用資源的方式有兩種
    2. 5.0.2. 取得 process 的方法就得使用 Process scheduler:
    3. 5.0.3. 至於 multi-thread 的使用 CPU 資源又是另一個 Thread Scheduling 的事情了
    4. 5.0.4. 總結:
  • 6. 實作自己 Host HTTP server
  • 7. Reference
  • Web server / Application server 傻傻分不清楚 ?

    都通稱是 server,但確有些許微妙的不同,小時候一直很不懂之間的差別,直到最近才搞得比較清楚一點。

    Web server

    Apache、Nginx 都是 web server ,主要可以拿來 serve static files、serve dynamic web apps (ex: PHP 寫的)。注意!但是他們都不能夠直接拿來 serve Ruby web apps,必須要透過額外的 add-on 才行。
    其中 Apache 應該是比較常聽到的架設選項,但是近年大家都偏好使用 Nginx 是因為他比較沒那麼肥大,而且速度比 Apache 快,所以變成了架設 server 的首選。
    另外他們除了做 proxy server 外,也可以拿來做 Reverse Proxy Server ,因為彼此的特性不同,也常常混合者使用,例如拿 Nginx 來做 Reverse Proxy Server ,拿 Apache 來當做後面的 Web Server。

    Nginx vs Apache 比較圖表

    什麼是 Proxy Server 、 Reverse Proxy Server?

    Proxy Server(代理伺服器)

    他的工作是去各個 Web Server 抓取資料回來放在伺服器上來供用戶讀取下載,如此一來可以大幅減少到各個 Web server 抓取資料的時間。

    proxy server

    Reverse Proxy Server(反向代理伺服器)

    他做的事情恰好相反,負責將用戶端的資料傳送(HTTP)給藏在 Reverse Proxy Server 後面的 Web Server,這些躲在後面的 Web Server 不會、也不能直接被用戶直接連結,只能經由 Reverse Proxy Server 代理傳送和接收資料,如此不僅可以保護後方 Web Server 被攻擊,同時還可提供負載平衡、快取以及資料加密的功能。

    When that server responds with an HTTP response, Apache/Nginx will forward the response back to the client. You will learn later why this is relevant.

    Reverse Proxy Server

    Application server

    可以是很多語言寫的 app server,這邊拿 ROR 的 app server 來比較,通常你在 rails new project 的時候所預設 server 是 WEBrick,這就是一個 app server,所以你在 run $ rails s 的時候就會看到 => Booting WEBrick,這是因為 rails 內建的 app server 是 WEBrick,但是我們通常在部署的時候就不會使用 WEBrick 這樣的 app server,因為 慢!

    rails default app server

    所以我們通常會把 Rails 的 App server 給換掉,但是你可能會懷疑會什麼可以這樣直接換 app server 勒?答案是強大的 Rack 設計。

    什麼是 Rack

    Rack: a Ruby Webserver Interface.
    provides a minimal interface between webservers supporting Ruby and Ruby frameworks.

    Rack 是各種 web framework 與各種 app server 的橋梁(middleware),其中必須制定好規則也就是 Rack SPEC,簡單用圖來表示就是像這樣:

    Rack: app servers / web frameworks

    比較 Rails 各種 App server

    很抱歉因為志志實在太熱愛寫 ROR 了,這邊就只能做 Rail 的 app server 比較了。

    1. Mongrel:
      • 過時的 雜種
      • written in part Ruby part C
      • no process monitoring,當調需要手動重啓。
    2. WEBrick:
      • not fit for production
      • written entirely in Ruby (慢)
      • 但是因為他是純 ruby 寫的,所以被設為預設(heroku 也是),不需額外安裝。
    3. Unicorn:
      • fork of Mongrel,加上 process monitoring
      • 因為 make all processes listen on a single shared socket, instead of a separate socket for each process. 所以簡化了 reverse proxy 的設定。
    4. Thin:
      • no process monitoring
      • evented I/O
    5. Puma:
      • forked from Mongrel
      • purely multi-threaded
    6. Phusion Passenger:
      • Dynamically adjusting the number of processes based on traffic
      • written in C++, making it very fast.
    7. Rainbows:

    現實世界的 App server 怎麼運作

    在這多的 app servers 中,像是 Phusion Passenger, Rainbows 就可以直接透過 port 開在 80 來達到 directly exposed to the Internet。但是有些 app server 卻不建議這麼做,例如 Mongrel, Unicorn, Thin, Puma。他們需要放在 reverse proxy server 後面比較恰當。

    為什麼需要 Apache/Nginx reverse proxy server?

    1. 因為這些 app server 一次只能 handle 1 request concurrently,如果有兩個以上的 request,就必須要 run multiple app server instances(app server cluster),而這正是 Apache/Nginx 可以透過 reverse proxy,幫助你適當 distributing requests between the instances in the cluster,而且可以 buffer requests and responses 避免發生 slow clients 的情況發生。

      Apache and Nginx are very good at doing many things at the same time because they’re either multithreaded or evented.

    2. Web server 比起 App server 更適合來 serve static files。
    3. 安全,避免 corrupted requests。

    那為什麼 Phusion Passenger 可以不需要 Apache/Nginx reverse proxy server? 因為它在設計上具有獨特的特性:integrates directly into Apache or Nginx (Web server)所以你在架設的時候也相當簡易,可以參考 Nginx + Passenger + Rails4 Setup on Ubuntu12.04

    現實中的 Requests

    如果你只是需要做 Prototype demo 用,一般的開個 App server 當然沒問題,但是真的搬到台面上運作呢?當然不行,面對龐大的使用者你的 Server 必須要能夠應付得了。來看看實際上的 Web Request 是怎麼處理的。

    面對大量 requests

    OS 課程回顧:

    • Program:程式碼( OOP Class)。
    • Process:Program 所產生的執行個體( OOP Object )。因此一個 Program 可以同時執行多次產生多個 Process。每個proecess 包含:
      • 一個 Memory Space( OOP variable ),因此不同 Process 的 Memory Space 也不同,彼此看不到對方的 Memory Space。
      • 一個以上的Thread。Thread:CPU執行的最小單位。

    Process 去取得 CPU 資源,很多 Process 去運用資源的方式有兩種

    1. multiprogramming 一次執行一個 Process,等到 I/O 再換下一個,剩餘的放在 memory。
    2. time sharing 來快速切換 process。

    釐清:multi-tasking 利用 CPU 多核來同使處理 tasks

    取得 process 的方法就得使用 Process scheduler:

    1. short-term scheduler(CPU scheduler ),來決定哪下一個被處理的 Process。
    2. long-term scheduler (Job scheduler )來決定哪些 process 來放到 ready queue

    至於 multi-thread 的使用 CPU 資源又是另一個 Thread Scheduling 的事情了

    Thread Scheduling

    multi-thread

    我們可以將處理大量的 Requests (I/O concurrency models)方式分為四種:

    1. Single-threaded multi-process
      • 最傳統的做法,因為早期 Ruby 的生態系對於 multithreading 支援度不高。
      • 因此需要 web server load balances between processes
      • 適合 for fast, short-running workloads
      • 不適合 for slow, long-running blocking I/O workload
      • 而且如果每個 Request 都沒有 I/O blocking,利用的 CPU 效率就是最好的。
      • 同時連線線就等於能用的Process數量(例如最基本512 mb的主機上,通常可以開 3 個 Rails process,但是因為每個連線都控制在 20ms 以下,所以每秒鐘能處理的 requests 數量還是十分驚人,足以應用絕大部分的應用場景。
      • MongrelUnicorn
    2. Purely multi-threaded
      • 因為現今 Ruby 生態系的發展,multithreading 支援度高,所以有些 App server 採取這個方式,
      • Multithreading allows high I/O concurrency
      • 第一點提到的兩種情況都適合。
      • 因為採用 Global Interpreter Lock (GIL) 所以不能夠 leverage multiple CPU cores
      • no builtin cluster support. 所以 You need to take special care to ensure that you can utilize multiple cores
      • Puma
    3. Hybrid multi-threaded multi-process
      • implemented by Phusion Passenger Enterprise 4
      • 因為 hybrid 的設計,就算 Global Interpreter Lock (GIL) 也可以 fully utilize all CPU cores。
      • 任意切換組合: single-threaded multi-process, purely multithreaded, or perhaps even multiple processes each with multiple threads
      • Puma, the hybrid mode is called “clustered”
    4. Evented-driven
      • 完全不同的設計概念,無論有多少連線,只有在有事件發生時,才會讓CPU做事。
      • allows very high I/O concurrency
      • excellent for long-running blocking I/O workloads
      • 但是 Framework 的設計概念必須一致,例如 Rails、Sinatra 這樣的 Framework 就不行。ex: 雖然 Thin 在設計上是這樣,但是搭配 rails 就仍舊只能 single-threaded multi-process
      • thin

    總結:

    我們可以把網路真實的情況分為三個層級來做適當的處理:

    • Situation 1:天真的想法,但是其實堪用。

    面對大量的 Client Request,multi-proecess (這邊講的是 single-thread)是最原始的做法,讓每一個 Request 都產生一個完整的 Rails app Process,而每個 Process 去取得 CPU core 得到運算資源來作處理,當然 Process 取得 CPU 資源應該在 OS 課程上學到,也許會透過一些排程來作 CPU 資源分配,但是切換 Process 需要 Context switch 是一種負擔。multi-proecess 這樣的做法好處是,除了撰寫容易外,面對只需要運算而不需要做 I/O blocking 的程式就能達到最好的 CPU 資源利用。

    • Situation 2:中等的想法,但也是堪用。

    但是一旦碰到很多 I/O blocking ,能夠同時處理的 Request 就會受到限制,不過其實 multi-proecess 對於一般的服務,負載量是沒問題的。
    但是當面對非常大量的或連續的 HTTP request,例如聊天室,每個使用者連線持續佔用 Process,或是 Process 需要的 Context switch 總成本量太高,就會出現等待問題了。這時候想到的解決方案是 Multi-threaded,因為利用 Thread 來取代 process 得以降低 context switching 負擔,因為 Thread 共用 Memory Space 的特性。這樣的解法是沒問題的,雖然程式設計撰寫上比較沒這麼容易,但是因為 Ruby 對於 Multi-threaded 的支援度越來越高,所以也算是一個很好地解決方案。

    • Situation 3:天人的想法,好還要更好。

    現階段正在發展,最好的做法還是應該採用 Evented-driven 的 Actor model。就算是 Thread 很廉價,但是數量一多還是會成為 Server 負擔。Actor model 是個無窮的迴圈,無論有多少連線,只有在有事件發生時,才會讓CPU做事。
    所以要如何達到 Evented-driven 呢?在 Rails 架構下,除了把 App server 改成有 evented 特性的 thin server 外,要把 rails 本身的 I/O 操作都改成 evented 版本,不然只要有一個部分被 blocking ,等於做了白功。事實上, Rails 這個 web framework 本來就並非 Evented-driven 的設計概念,所以最的方法是會選令其他 web frameworks ,例如 ruby 的 CrampGoliath(base on EventMachine ruby library),或是其他語言的 Evented 框架如 node.js

    實作自己 Host HTTP server

    參考 Nginx + Passenger + Rails4 Setup on Ubuntu12.04,有簡易的步驟教學。

    Reference