Nginx - 戰鬥民族開發的高效能 Web 伺服器

Nginx 這個名詞,大概是這幾年來越來越火紅,開始很多新的專案最後都會採用 Nginx 當作 Web 伺服器,而不是採用老牌的 Apache。

根據數據統計:

就統計到我今天寫文的時間,可以發現,整體的比例 ApacheNginx 已經相去不遠了,更主要的指標是在 rank 前 top 1,000 使用 Nginx 大幅度的高於 Apache,這代表前幾大的網站雖然可能是老系統了,也已經慢慢在轉換成使用 Nginx 了。

而且就我的觀察,目前很多雲端平台,例如:GCPAWSAzure,當部署上去的時候,會發現在 load balancer 這方面都是採用 Nginx。這也是 Nginx 最令人感到強大的地方,它的功能不僅僅只是作為單純的 Web Server,而是常被用來使用它的反向代理負載平衡器HTTP 快取

因此這篇文主要介紹 Nginx 的原理~

Nginx 簡介

Nginx 是由俄國人,沒有錯,就是戰鬥民族所開發出來的 XD

Nginx 的發音是 (engine X),是非同步框架的 Web Server,在 2004 年被首度公開發布,在 2011 年成立同名公司可以提供支援,現在 Nginx 這間公司記得好像被收購了 QQ

不過別擔心,Nginx 是免費的開源軟體,你用 Nginx 絕對不會被收費 ^^,而現在公司被收購後也推出了 Nginx Plus 的商業版,據我所知就是比 Nginx 多了一些功能,當然我覺得最重要的就是有技術支持,其實我覺得就是透過價錢買技術服務,但一般的情況下用 Nginx 免費版即可。

為什麼可以火紅

  • 面向效能設計

    根據效能比較,Nginx 對於 Apache 而言,處理 IO 並發能力、跑靜態文件,效能是遠比 Apache 還要好的,這個主要差別是來自於 Nginx 底層的設計,等等會談到一些相關內容。這也是為什麼大公司由於需要面對高流量,所以才會漸漸轉換成 Nginx,說實在如果是小系統用 Apache 還是 Nginx 根本不會有甚麼感受差異。

  • 配置簡潔

    Nginx 整體是採用模組化設計,配置檔案也非常簡潔,而 Apache 設定是相對複雜的,但是 Apache 的發展歷史悠久,所以提供的套件及額外的功能也是多於 Nginx。

  • 耗費記憶體少

    同樣起 Web Server,Nginx 比 apache 占用更少的內存及資源,在高並發下 nginx 能保持低資源低消耗高性能。

  • 反向代理

  • 負載平衡

    以上兩點其實 Apache 也是可以做到,但是就是會差在效能的問題,而且因為 Apache 會需要花費較多的資源,也就是在有限資源下,使用 Nginx 可以處理更多連線。

所以可以知道 Nginx 面對高並發以及靜態資源的處理是相對比 Apache 還要好的,所以也可以衍生出一個架構是前端搭配 Nginx,後端搭配 Apache,因為在動態載入的情況下,Apache 處理動態內容是比較高效的!

先理解為什麼需要有 Web Server

其實在我之前剛學 Nginx、Apache,我會有個疑惑就是我用 Golang 或是 Node.JS 不是輕輕鬆鬆就可以利用程式碼起一個 Web Server 了嗎?

其實 Golang 這類程式語言起的 Web Server 比較會被稱為 Application Server,而 Nginx、Apache 才會被稱為 Web Server。

簡單來說,有以下的差異:

  • Web Server:如 Nginx、Apache 只能拿來處理靜態資源、負載平衡,所謂動態的資源,它是會把需求轉發到我們的 Application Server,由 Application Server 處理完後,在 response 回去,由 Web Server 進行 Response,最後才回到 User 端
  • Application Server:一般就是可以用程式語言建立出 Server,並且可以靜態跟動態解析。

但是,這樣會覺得為什麼不用 Application Server 就好了?

其實,如果真的是一個小網站,沒什麼人在用,或者只是 demo 用的,是根本就不需要用到 Nginx、Apache 的。會用到 Nginx 這些是因為需要應付以下幾個情境:

  • 如果我在一台主機上想要跑兩個網站怎麼辦?

    你會說,那我開啟兩個 Application Server,然後各自使用不同的 Port 不就好了?

    所以假設你的主機的 Public IP 是 234.1.1.1,然後你有兩台 Application Server 分別使用 8080 跟 9090 port,所以當開放外連的時候,使用者需要在網址輸入:234.1.1.1:8080234.1.1.1:9090 才能分別連進去,可是,你有看過網址上面出現 port 的嗎?這樣也太難記了吧!因此,所以網頁都是直接用 80 port (HTTP 的標準),省去使用者要自己輸入 port 的麻煩。此外這也會有一些資安的問題,這樣大家就會知道你這台主機開放了那些 port 出來,如此以來會增加被攻擊的風險。

    這時候你就會需要反向代理的功能了!透過 User 輸入不同的域名,Web Server 透過反向代理的功能根據域名的不同轉發到不同的 Application Server 的 port 上!

  • 負載平衡

    簡單來說就是要解決單一 Application Server 的負荷量的問題,今天如果要面對高流量的需求,一台 Application Server 肯定是撐不住的,所以背後會有多台 Application Server 來分擔流量的問題,這時候前面就需要 Web Server 負載平衡的功能來進行分發囉!

此外,Application Server 處理靜態資源的效能也是遠遠不及 Nginx 的,加上現今前後端開發的趨勢,前端會有大量的靜態檔案,這時候就需要 Nginx 來進行處理,而動態資源則轉發到背後的 Application Server 處理。

正向代理 vs 反向代理

既然有反向代理那就會有正向代理,到底這個是甚麼涵義呢?

一句話了解差別之處:正向代理隐藏真實客戶端,反向代理隱藏真實服務端

網路上生動的解釋都是用借錢來解釋,所以我這邊也用這個例子 XD

  • 正向代理

    我缺錢,想跟銀行進行貸款,無奈我的信用已經破產,但是我有一個好朋友,願意用他的好信用來向銀行借錢,而他願意將借來的錢無償給我使用。所以,可以知道兩件事情:

    1. 銀行只知道他借給的是我的好朋友,而不是借給我
    2. 我的好朋友就是扮演中間人的角色,這個角色就是我們的代理 (proxy)

    所以以技術的角度來解釋的話就是:

    Client 端發送一個 request 先連到 proxy server,proxy server 在幫我轉發到我想要連到的網站,這時那個網站收到的 request,他只知道請求得過來身分是 proxy server,而不是真實的 client 端的身分。

  • 反向代理

    恰恰就是正向代理的相反!

    有個很生動的例子是使用客服:

    今天我使用某產品用到不爽了,我要打給客服去客訴!但是大家都知道客服人員不可能只有一個人負責,當我打電話過去的時候,我並不知道這次接洽的客服人員是誰,但是我並不會在乎,我在乎的是我的問題有沒有明確的表達給客服人員知道。

    所以以技術的角度來解釋的話就是:

    Client 端發送一個 request 到我想要連的網站,這個網站背後可能就會有好幾台的 Server 來為我們服務,就像前面說的負載平衡那樣,為了負荷流量,會分配到不同的 Server,而 Client 端根本不會知道具體到底是哪一台 Server,而 Client 端也不需要知道,Client 端只需要知道反向代理 Server 是誰就好了,因為就是反向代理 Server 作為中間的角色將請求轉發到背後好幾台的 Server 上。

因此精闢的解釋:正向代理隐藏真實客戶端,反向代理隱藏真實服務端

以下帶來網路的正向代理與反向代理的圖,會更了解~

Nginx 的 Master-Worker 模式

直接上圖:

當 Nginx 啟動服務後,因為 Nginx 通常做為 Web Server 和反向代理服務器就會在 80 Port 進行監聽服務

  1. Master 負責管理 Worker 進程,並且會讀取並驗證配置檔案,如果當主配置檔案發生改變,那麼並不會立刻影響到 Worker 進程,而是 Master 會等到 Worker 進程的連接請求處理完畢後,再 Kill 掉這個 Worker 進程,然後重新生成一個 Worker 進程,而新的 Worker 進程就會以新的配置啟動,所以重新加載配置文件不會中斷正在處理的請求。

  2. 每個 Worker 進程裡面只會包含一個現程,並可以並行處理數千個的並發連接和請求,或處理反向代理的功能,通常 Worker 進程的個數對應於 CPU 個數。

  3. 一個請求只能由一個 worker 進程處理並只能由這個 worker 進程完全處理,因為一個 worker 裡面只有一個線程,也避免了線程的上下文切換。

  4. 通常許多網站會慢,很多時候都是卡在 IO 請求,當等太久就會被中斷,因此如果要等待 IO 請求結束,才能繼續工作,其他請求就會處於等待的狀態,因此 Nginx 採用的是非阻塞的方式。如果發生 IO 中斷,那麼你去做你的事情,但是過一段時間來看看 IO 調用是否結束,這就是非阻塞。

    這邊 Nginx 使用了 Linux 的 Epoll 模型:

    Epoll 模型是提供一種事件驅動機制,它可以監控多個事件是否準備好了,如果準備好了,那麼就放入 Epoll 隊列中。這種機制是異步的。因此 worker 進程只需要循環處理 Epoll 隊列中的請求,這種循環處理已經準備好的請求,可以讓 Nginx 高效的處理高並發的問題。

如果想知道更多原理可以參考:

  1. https://zhuanlan.zhihu.com/p/34943332
  2. https://zhuanlan.zhihu.com/p/31196264

來看看 Nginx 的配置檔案

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80;
server_name localhost;

#charset koi8-r;

#access_log logs/host.access.log main;

location / {
root html;
index index.html index.htm;
}

這個是主配置檔其中一個內容,其實可以看成是某台 Server 的設定檔,每個 Server 會對應一個設定檔的內容,

這邊預設就 listen 80 port 代表就是當作 Web Server 的意思。

  • location 後面接的是路徑的匹配,可以配合正則規則式
  • 這樣的設定代表是網址的根目錄請求進來後,會進行怎樣的操作,這邊可以選擇看到靜態資源,或是進行反向代理的轉發
  • 以這邊的設定,root 代表放網站的靜態資料夾,也就是位於 html 目錄下
  • index 指令後面可以有多個文件名稱,查找順序由左而右,直到找到相對應的檔案,所以這邊預設就是 index.html
  • 所以如果想要用成反向代理給 Application Server,就要用 proxy_pass,需要轉發的意思。

如果要用到負載平衡的設定可以像這樣:

1
2
3
4
5
upstream myloadbalancer {
server 192.168.80.121:80 weight=3;
server 192.168.80.122:80 weight=2;
server 192.168.80.123:80 weight=3;
}

使用 upstream 來設定要做分流的 Server,而 weight 則是設定權重,權重越高代表被分配的機率越大。

總結

以上是 Nginx 的基礎介紹,其實最複雜的地方還是再設定檔的設置,因為要先了解各個指令才能設定好,所以之後會發設定檔的一些教學,及一些簡單架設的教學。

最後最後!請聽我一言!

如果你還沒有註冊 Like Coin,你可以在文章最下方看到 Like 的按鈕,點下去後即可申請帳號,透過申請帳號後可以幫我的文章按下 Like,而 Like 最多可以點五次,而你不用付出任何一塊錢,就能給我寫這篇文章的最大的回饋!