Nginx(發(fā)音同“engine X”)是異步框架的網(wǎng)頁(yè)服務(wù)器店乐,也可以用作反向代理器一、負(fù)載平衡器和HTTP緩存。
本文會(huì)講述如何使用 Nginx 的代理功能幫助進(jìn)行前端頁(yè)面的開(kāi)發(fā)强窖。
Web 開(kāi)發(fā)通常使用的是前后端分離的開(kāi)發(fā)模式释树,即前端和后端分別進(jìn)行開(kāi)發(fā),前端通過(guò) Ajax 請(qǐng)求后端的接口乡小,將獲取數(shù)據(jù)將數(shù)據(jù)渲染到頁(yè)面上阔加。前端開(kāi)發(fā)會(huì)使用腳手架搭建前端開(kāi)發(fā)環(huán)境,其底層通常會(huì)啟動(dòng)一個(gè)本地服務(wù)器满钟,通常使用的是 nodejs 的 Express 框架胜榔。而后端則是提供接口,一般是放在線上的一個(gè)開(kāi)發(fā)用的域名下湃番。
這在開(kāi)發(fā)過(guò)程中會(huì)導(dǎo)致 跨域 問(wèn)題夭织,即在一個(gè)域名下的網(wǎng)頁(yè),是無(wú)法通過(guò) Ajax 請(qǐng)求另一個(gè)(不同源)域名下的接口 API 的吠撮。這是瀏覽器的同源策略尊惰,是瀏覽器的一個(gè)非常重要的安全策略。
解決這個(gè)問(wèn)題的其中一個(gè)方案是使用 代理。具體來(lái)說(shuō)弄屡,就是在本地啟動(dòng)一個(gè)服務(wù)器(如 localhost:4000
)题禀,發(fā)送給該服務(wù)器的請(qǐng)求會(huì)根據(jù)請(qǐng)求路由(比如判斷 url 是否有前綴 /api
)進(jìn)行轉(zhuǎn)發(fā),分別轉(zhuǎn)發(fā)到前端開(kāi)發(fā)的服務(wù)器(如 localhost:3000
)琢岩,以及后端服務(wù)器(比如 dev.yoursite.com
)投剥。這樣通過(guò)一個(gè)代理服務(wù)器,因?yàn)檎?qǐng)求的 api 都是同一個(gè)域名下的担孔,自然就不會(huì)造成跨域問(wèn)題江锨,從而導(dǎo)致請(qǐng)求失敗。
下面我們就來(lái)講解如何使用 Nginx 來(lái)實(shí)現(xiàn)反向代理糕篇。
簡(jiǎn)單認(rèn)識(shí) Nginx 配置文件
安裝好 Nginx 后啄育,我們需要確定下 Nginx 的默認(rèn)配置文件的位置。執(zhí)行命令 nginx -t
拌消,該命令會(huì)檢測(cè) nginx 的默認(rèn)配置文件語(yǔ)法是否正確挑豌,并進(jìn)行測(cè)試,最后輸出結(jié)果墩崩。我們可以從輸出中得到默認(rèn)配置文件所在的位置氓英。
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
還有另外一種方法可以得到默認(rèn)配置文件的位置,那就是執(zhí)行 nginx -h
鹦筹。該命令會(huì)輸出 nginx 的簡(jiǎn)易幫助文檔铝阐,其中的 -c filename
的配置項(xiàng)說(shuō)明也指出了默認(rèn)配置項(xiàng)的路徑。
-c filename : set configuration file (default: /etc/nginx/nginx.conf)
通過(guò)這個(gè)文檔铐拐,我們也可以知道徘键,使用 -c
配置項(xiàng)可以自定義配置文件。如果不指定文件遍蟋,使用默認(rèn)配置文件吹害。
下面我們來(lái)修改一下 Nginx 到這個(gè)默認(rèn)配置文件 nginx.config
來(lái)實(shí)現(xiàn)代理功能。
nginx.config
文件的 http
后面的代碼塊中虚青,應(yīng)該會(huì)有類(lèi)似下面這行的代碼:
include /etc/nginx/conf.d/*.conf;
這行代碼的作用是將 /etc/nginx/conf.d
目錄下的后綴為 .conf
的文件內(nèi)容嵌入到引入位置中它呀,作為配置的一部分執(zhí)行。
如果你是在 macOS 安裝的 Nginx挟憔,可能會(huì)有點(diǎn)不同钟些。我使用 brew 安裝的 Nginx 為為 include servers/*;
,對(duì)應(yīng)嵌入的是 servers
目錄下的所有文件绊谭。
為什么要用到這種嵌入的語(yǔ)法呢政恍?因?yàn)檫@樣可以將不同項(xiàng)目需要用到的配置放到不同的配置文件里,好處是可以快速地找到對(duì)應(yīng)項(xiàng)目要修改的配置文件达传,不用擔(dān)心不小心修改了其他項(xiàng)目的配置篙耗。另外如果全部都直接在 nginx.conf
上修改迫筑,會(huì)讓這個(gè)文件變得臃腫,當(dāng)配置多的時(shí)候變得難以配置宗弯。這符合設(shè)計(jì)模式的 單一職責(zé)原則脯燃。
此外,你可能會(huì)奇怪 conf.d
目錄的命名為什么要加上 .d
蒙保?如果你使用 Linux 過(guò)一段時(shí)間辕棚,你會(huì)發(fā)現(xiàn)某些目錄或文件的末尾會(huì)加上一個(gè) d,比如 httpd
邓厕、crond
逝嚎、vsftpd
等。其實(shí)這是為了說(shuō)明這些文件都屬于是 daemon(服務(wù))详恼。這里的服務(wù)指的是系統(tǒng)的服務(wù)补君,主要分為系統(tǒng)本身需要的服務(wù),以及負(fù)責(zé)網(wǎng)絡(luò)的服務(wù)昧互。我們的 conf.d 屬于后者挽铁。
編寫(xiě) Nginx 配置文件
我們?cè)?conf.d 目錄下創(chuàng)建名為 demo.conf
的文件,寫(xiě)入以下內(nèi)容敞掘,然后啟動(dòng) Nginx叽掘。
server {
listen 5000;
server_name localhost;
location / {
proxy_pass http://localhost:3000;
}
location /api/ {
proxy_pass http://localhost:4000;
}
}
該配置啟用了 localhost:5000
的服務(wù)器,將 localhost:5000
下開(kāi)頭為 /api/
url 請(qǐng)求代理到了 localhost:4000
(后端接口服務(wù)器)玖雁。其他請(qǐng)求則是代理到 localhost:3000
(前端)够掠。下面我們具體解析一下配置文件里面內(nèi)容的作用。
listen
設(shè)置了服務(wù)器的端口號(hào)茄菊,server_name
則設(shè)置了主機(jī)名。
location
location
表示進(jìn)行路由的匹配赊堪,如果匹配則執(zhí)行對(duì)應(yīng)代碼塊里的操作面殖。location
可以使用 前綴匹配 以及 正則匹配(需要以 ~*
或 ~
開(kāi)頭)。我們這里的配置使用的是前綴匹配哭廉。
這里有個(gè)點(diǎn)需要注意一下脊僚,Nginx 的路由匹配和一般的按順序匹配第一個(gè)的路由匹配方案(比如后端的 gin、前端的 vue-router 的路由匹配方案)不同遵绰,nginx 匹配路由的方式為:
- 首先進(jìn)行前綴匹配辽幌,遍歷所有的前綴匹配,從中選擇前綴匹配最長(zhǎng)的椿访;
- 然后會(huì)進(jìn)行正則匹配乌企,在所有正則匹配中,從前往后選擇第一個(gè)符合的成玫;
- 如果能找到匹配的正則匹配加酵,使用其對(duì)應(yīng)的配置拳喻;如果沒(méi)有,則使用之前找到的那個(gè)最長(zhǎng)的前綴匹配對(duì)應(yīng)的配置猪腕。
所以冗澈,當(dāng)請(qǐng)求為 localhost:5000/api/xx
時(shí), /
和 /api/
都能夠前綴匹配陋葡。根據(jù)規(guī)則亚亲,雖然位置更靠前的 /
也符合前綴匹配,但 /api
更長(zhǎng)腐缤,所以最終匹配的是 /api
捌归。
proxy_pass
確定好匹配的 location
后,我們?cè)倏纯?proxy_pass
又做了什么操作柴梆。proxy_pass
用于將請(qǐng)求路由映射到指定的協(xié)議和地址陨溅。本質(zhì)是將發(fā)送給 Nginx 的請(qǐng)求處理并發(fā)送到另一個(gè)服務(wù)器,然后將返回的數(shù)據(jù)作為 Nginx的返回?cái)?shù)據(jù)返回绍在。
-
proxy_pass
后如果使用的是 URI(端口后面至少有一個(gè)/
)门扇,那么 Nginx 就會(huì) 替換 掉 location 匹配的那部分字符。
listen 5000;
server_name localhost;
location /name/ {
proxy_pass http://127.0.0.1/remote/;
}
# localhost:5000/name/fstar
# 會(huì)被映射請(qǐng)求為
# 127.0.0.1/remote/fstar
可以看到偿渡,/name/ 的部分在映射時(shí)被移除(或者說(shuō)是替換)了臼寄。
-
proxy_pass
后如果使用的是不是 URI(端口后沒(méi)有任何東西),Nginx 會(huì)將源請(qǐng)求完全映射到代理服務(wù)上:
listen 5000;
server_name localhost;
location /some/path/ {
proxy_pass http://127.0.0.1;
}
# localhost:5000/some/path/x/y
# 會(huì)被映射請(qǐng)求為
# 127.0.0.1/some/path/x/y
這里的 /some/path
并沒(méi)有被移除溜宽。
我們的 demo.conf
文件的 proxy_pass
使用的不是 URI吉拳,所以是將路由完全映射到另一個(gè)服務(wù)。
思考題
請(qǐng)問(wèn)适揉,下面有兩段配置(區(qū)別是 proxy_pass
結(jié)尾是否有 /
)留攒,如果請(qǐng)求 /kite/api/xx
,分別會(huì)映射為什么嫉嘀?
location /kite/api/ {
proxy_pass http://localhost:5000;
}
location /kite/api/ {
proxy_pass http://localhost:5000/;
}
前面我們講 proxy_pass
的時(shí)候說(shuō)過(guò)炼邀,proxy_pass
后面如果不是 URI,會(huì)正常轉(zhuǎn)發(fā)剪侮;如果是 URI拭宁,就移除 location
匹配的前綴再進(jìn)行轉(zhuǎn)發(fā),體現(xiàn)的是替換路由的效果瓣俯。上面這兩個(gè)配置的區(qū)別就在于末尾的這個(gè) /
杰标,有 /
是 URI,沒(méi)有的不是 URI彩匕,從而導(dǎo)致完全不一樣的結(jié)果腔剂,依次分別為:
http://localhost:5000/kite/api/xx
http://localhost:5000/xx
所以,在寫(xiě) Nginx 配置的時(shí)候驼仪,一定要注意端口后面的 /
是否有必要保留桶蝎。因?yàn)樗挠袩o(wú)會(huì)導(dǎo)致兩種截然不同的效果驻仅。
文章首發(fā)于我的 個(gè)人博客網(wǎng)站