寫給小白的 Nginx 文章
原文地址:Nginx concepts I wish I knew years ago
原文作者:Aemie Jariwala(已授權(quán))
譯者 & 校正:HelloGitHub-小魚干 & 鹵蛋
Nginx 是一個采用主從架構(gòu)的 Web 服務(wù)器,可用于反向代理、負載均衡器、郵件代理和 HTTP 緩存。
Emmm,上面的 Nginx 介紹看過去有些復(fù)雜而且充滿了不明覺厲的術(shù)語。Relax,在這篇文章里,我(原作者)會先帶你理解 Nginx 的架構(gòu)和專有術(shù)語,最后實踐一把安裝和配置 Nginx。

簡單來說,你只要記住一點:Nginx 是個神奇的 Web 服務(wù)器。(注:神奇之處下文會娓娓道來)
那什么是 Web 服務(wù)器呢?簡而言之,Web 服務(wù)器就是一個中間人。舉個例子,你要訪問 hellogithub.com(注:原文例子為 dev.to),在地址欄輸入 https://hellogithub.com 時,你的瀏覽器會找到 https://hellogithub.com 的網(wǎng)絡(luò)服務(wù)器地址并將它指向后端服務(wù)器,后端服務(wù)器再返回響應(yīng)給客戶端。
代理 vs 反向代理
Nginx 的基本特性是代理,所以你一定要明白什么是代理和反向代理。
代理
看個小例子,現(xiàn)在我們有 N 個客戶端(N >= 1),一個中間 Web 服務(wù)器(在本例中,我們稱之為代理)和一個服務(wù)器。這個例子主要的場景是,服務(wù)器不知道哪個客戶端在請求(響應(yīng))。是不是有點難以理解?下面讓我用示意圖講解下:
如圖,client1 和 client2 通過代理服務(wù)器向服務(wù)器發(fā)送請求 request1 和 request2,此時后端服務(wù)器不知道 request1 是由 client1 發(fā)送的還是 client2 發(fā)送的,但會執(zhí)行(響應(yīng))操作。
反向代理
簡單來說,反向代理與代理的功能相反。現(xiàn)在我們有一個客戶端、一個中間 Web 服務(wù)器和 N 個后端服務(wù)器(N >= 1),同樣的來看下示意圖:
如圖,客戶端將通過 Web 服務(wù)器發(fā)送請求。而 Web 服務(wù)器會通過一個算法,當中最有意思的算法是輪詢,直接將請求指向許多后端服務(wù)器中的一個,并通過 Web 服務(wù)器將響應(yīng)返回給客戶端。因此,在上面的例子中,客戶端其實并不知道在與哪個后端服務(wù)器進行交互。
負載均衡
又是枯燥的一個名詞:負載均衡,不過它很好理解,因為負載均衡本身就是反向代理的一個實例。
來看看負載均衡和反向代理的本質(zhì)區(qū)別。在負載均衡中,你必須有 2 個或者更多的后端服務(wù)器,但在反向代理中,多臺服務(wù)器不是必需的,甚至一臺后端服務(wù)器也能運作。我們再深入點,如果我們有很多來自客戶端的請求,負載均衡器會檢查每個后端服務(wù)器的狀態(tài),均勻地分配請求,更快地向客戶端發(fā)送響應(yīng)。
有狀態(tài) vs 無狀態(tài)應(yīng)用
Okay,在我們開始實踐 Nginx 之前,先搞清所有的基本知識!
有狀態(tài)應(yīng)用
有狀態(tài)應(yīng)用存了一個額外變量,只用來保存服務(wù)器中單個實例使用所需的信息。
如圖所示,一個后端服務(wù)器 server1 存儲了一些信息,服務(wù)器 server2 并不存儲此信息,因此,客戶端 (上圖 Bob) 的交互可能會也可能不會得到想要的結(jié)果,因為它可能會與 server1 或 server2 交互。在本例中,server1 允許 Bob 查看數(shù)據(jù)文件,但 server2 不允許。因此,雖然有狀態(tài)應(yīng)用避免對數(shù)據(jù)庫的多次 API 調(diào)用,并且(響應(yīng))速度更快,但它可能會在不同的服務(wù)器上導致這個(無法得到想要結(jié)果)問題。
無狀態(tài)應(yīng)用
無狀態(tài)應(yīng)用有更多的數(shù)據(jù)庫 API 調(diào)用,但當客戶端與不同后端服務(wù)器的交互時,無狀態(tài)應(yīng)用卻存在更少的問題。
沒明白?簡單來說,如果我通過 Web 服務(wù)器從客戶端向后端服務(wù)器 server1 發(fā)送請求,它將向客戶端返回一個令牌,用于任何進一步的訪問請求。客戶端可以使用令牌并向 Web 服務(wù)器發(fā)送請求。此 Web 服務(wù)器將請求連同令牌一起發(fā)送到任意后端服務(wù)器,而每個后端服務(wù)器都能提供相同的所需結(jié)果。
Nginx 是什么?
Nginx 是網(wǎng)絡(luò)服務(wù)器,到目前為止,我的整個博客一直在用這個網(wǎng)絡(luò)服務(wù)器。老實說,Nginx 這就像個中間人。
這個圖不難理解,它是目前為止所有概念的一個組合。在這里,我們有 3 個后端服務(wù)器運行在 3001、3002 和 3003 端口,這些后端服務(wù)器都能訪問同一個運行在 5432 端口的數(shù)據(jù)庫。
當一個客戶端向 https://localhost (默認端口 443)發(fā)起一個 GET /employees 請求時,Nginx 將基于算法向任意后端服務(wù)器發(fā)送請求,從數(shù)據(jù)庫獲取數(shù)據(jù)并將 JSON 數(shù)據(jù)返回 Nginx Web 服務(wù)器再發(fā)送給客戶端。
如果我們使用一個諸如輪詢這樣的算法,它讓 client2 向 https://localhost 發(fā)送一個請求,然后 Nginx 服務(wù)器會先將請求傳到 3000 端口并將響應(yīng)返回給客戶端。對另一個請求,Nginx 會把請求傳給 3002 端口,以此類推。
知識儲備完成!到這里,你對 Nginx 是什么以及 Nginx 所涉及的術(shù)語有了一個清晰的理解。是時候,了解安裝和配置技術(shù)了。
開始安裝 Nginx
時機到了,如果你了解了上面的概念,可以動手開始 Nginx 實踐了。
嗯,Nginx 的安裝過程對任何系統(tǒng)來說都很簡單。我是一個 Mac OSX 用戶,所以例子的命令是基于 macOS 的, Ubuntu、Windows 和其他 Linux 發(fā)行版操作和例子類似。
$ brew install Nginx
只要執(zhí)行上面這步,你的系統(tǒng)就有 Nginx 了!是不是很神奇!
運行 Nginx 如此簡單 ??
要檢查 Nginx 是否運行也很簡單。
$ nginx
# OR
$ sudo nginx
執(zhí)行上面指令,再打開瀏覽器并輸入 http://localhost:8080/ 回車查看下,你會看到以下畫面!
Nginx 基本配置 & 示例
下面,我們通過實操來感受下 Nginx 的魔力。
首先,在本地創(chuàng)建如下的目錄結(jié)構(gòu):
.
├── nginx-demo
│ ├── content
│ │ ├── first.txt
│ │ ├── index.html
│ │ └── index.md
│ └── main
│ └── index.html
└── temp-nginx
└── outsider
└── index.html
當然,.html 和 .md 文件中要包含基本信息。
我們想要得到什么呢?
這里,我們有兩個單獨的文件夾 nginx-demo 和 temp-nginx,每個文件夾都包含靜態(tài) HTML 文件。我們將著力在一個公共端口上運行這兩個文件夾,并設(shè)置我們想要的規(guī)則。
回到之前說的,如果要修改 Nginx 默認配置,得修改 usr/local/etc/nginx 目錄下的 nginx.conf文件。我的系統(tǒng)中有 vim,所以在這里用 vim 來更改 Nginx 配置,你可以用自己的編輯器來修改配置。
$ cd /usr/local/etc/nginx
$ vim nginx.conf
上面的命令會打開一個 Nginx 默認配置文件,我真的不想直接使用默認配置。因此,我通常的做法是復(fù)制這個配置文件,然后對主文件進行更改。這里也不例外。
$ cp nginx.conf copy-nginx.conf
$ rm nginx.conf && vim nginx.conf
上面命令將打開一個空文件,我們將為它添加配置。
-
添加配置的基本設(shè)置。一定要添加
events {},因為在 Nginx 架構(gòu)中,它通常用來表示 worker 的數(shù)量。在這里我們用http告訴 Nginx 我們將在 OSI 模型 的第 7 層作業(yè)。這里,我們告訴 Nginx 監(jiān)聽 5000 端口,并指向 main 文件夾中的靜態(tài)文件。
http {
server {
listen 5000;
root /path/to/nginx-demo/main/;
}
}
events {} -
接下來我們將為
/content和/outsiderURL 添加其他的規(guī)則,其中 outsider 將指向第一步中提到的根目錄之外的目錄。這里的
location /content表示無論我在葉(leaf)目錄中定義了什么根(root),content 子 URL 都會被添加到定義的根 URL 的末尾。因此,當我指定 root 為root /path/to/nginx-demo/時,這僅僅意味著我告訴 Nginx 在http://localhost:5000/path/to/nginx-demo/content/文件夾中顯示靜態(tài)文件的內(nèi)容。http {
server {
listen 5000;
root /path/to/nginx-demo/main/;
location /content {
root /path/to/nginx-demo/;
}
location /outsider {
root /path/temp-nginx/;
}
}
}
events {}酷斃了!現(xiàn)在 Nginx 不僅能定義 URL 根路徑,還可以設(shè)置規(guī)則,這樣我們就能阻止客戶端訪問某個文件了。
-
接下來,我們在主服務(wù)器上編寫一個規(guī)則來防止任意 .md 文件被訪問。我們可以在 Nginx 中使用正則表達式,因此我們將這樣定義規(guī)則:
location ~ .md {
return 403;
} -
最后,讓我們學習下
proxy_pass命令來結(jié)束這個章節(jié)。我們已經(jīng)了解了什么是代理和反向代理,在這里我們從定義另一個運行在 8888 端口上的后端服務(wù)器開始。現(xiàn)在,我們在 5000 和 8888 端口上運行了 2 個后端服務(wù)器。我們要做的是,當客戶端通過 Nginx 訪問 8888 端口時,將這個請求傳到 5000 端口,并將響應(yīng)返回給客戶端!
server {
listen 8888;
location / {
proxy_pass http://localhost:5000/;
}
location /new {
proxy_pass http://localhost:5000/outsider/;
}
}
看下,這是所有的配置信息 ??
http {
server {
listen 5000;
root /path/to/nginx-demo/main/;
location /content {
root /path/to/nginx-demo/;
}
location /outsider {
root /path/temp-nginx/;
}
location ~ .md {
return 403;
}
}
server {
listen 8888;
location / {
proxy_pass http://localhost:5000/;
}
location /new {
proxy_pass http://localhost:5000/outsider/;
}
}
}
events {}
使用 sudo nginx 來運行此配置。
其他 Nginx 命令
-
首次啟動 Nginx Web 服務(wù)器。
$ nginx
#OR
$ sudo nginx -
重新加載正在運行的 Nginx Web 服務(wù)器。
$ nginx -s reload
#OR
$ sudo nginx -s reload -
停止正在運行中的 Nginx Web 服務(wù)器。
$ nginx -s stop
#OR
$ sudo nginx -s stop -
查看系統(tǒng)上運行的 Nginx 進程。
$ ps -ef | grep Nginx
第 4 條命令很重要,如果前 3 條命令產(chǎn)生了一些問題,通常你可以用第 4 條命令找到所有正在運行的 Nginx 進程并殺死進程,然后重新啟動它們。
要殺死一個進程,你需要 PID,再用以下命令殺死它:
$ kill -9
#OR
$ sudo
kill -9
結(jié)束本文之前,聲明下,文中我用了些來自 Google 的圖片和 Hussein Nasser 發(fā)布在油管的視頻教程。
下面盡情享受 Coding、探索 Nginx 的魔力吧!??
最后,歡迎優(yōu)秀的你加入 HelloGitHub 的「譯文亦舞」系列,讓你的才華舞動起來!把優(yōu)秀的文章分享給更多的人。要求:
-
平時瀏覽 GitHub、開源、編程、程序員等英文資訊和文章 -
想把自己閱讀到優(yōu)秀的英文文章分享給更多的人 -
翻譯準確但不是直翻或機翻 -
保證每月至少翻譯或校正 1 篇高質(zhì)量文章 -
了解 Markdown 和排版規(guī)則 -
聯(lián)系微信:xueweihan (備注:翻譯)


關(guān)注公眾號第一時間收到更新
