使用 Nginx 部署前后端分離項(xiàng)目,解決跨域問(wèn)題
松哥原創(chuàng)的 Spring Boot 視頻教程已經(jīng)殺青,感興趣的小伙伴戳這里-->Spring Boot+Vue+微人事視頻教程
前后端分離這個(gè)問(wèn)題其實(shí)松哥和大家聊過(guò)很多了,上周松哥把自己的兩個(gè)開源項(xiàng)目部署在服務(wù)器上以幫助大家可以快速在線預(yù)覽(喜大普奔,兩個(gè)開源的 Spring Boot + Vue 前后端分離項(xiàng)目可以在線體驗(yàn)了),然后群里就有小伙伴想讓松哥來(lái)聊聊如何結(jié)合 Nginx 來(lái)部署前后端分離項(xiàng)目?今天我們就來(lái)聊一聊這個(gè)話題。
不得不說(shuō)的跨域
很多人對(duì)前后端分離部署感到困惑,其實(shí)主要是困惑跨域問(wèn)題怎么解決。因?yàn)榍昂蠖朔蛛x項(xiàng)目在開發(fā)的時(shí)候,前端通過(guò) nodejs 來(lái)運(yùn)行,需要一個(gè)單獨(dú)的端口,后端通過(guò) Tomcat 或者 Jetty 來(lái)運(yùn)行,也需要端口,兩個(gè)不同的端口,就造成了跨域。
但是松哥之前多次和大家聊過(guò)這個(gè)問(wèn)題,這種跨域并不是我們傳統(tǒng)開發(fā)中真正的跨域,這個(gè)所謂的跨域只在開發(fā)環(huán)境中存在,生產(chǎn)環(huán)境下就不存在這個(gè)跨域問(wèn)題了。所以我們不能按照以往的通過(guò) JSONP 或者 CORS 之類的手段來(lái)解決這個(gè)跨域問(wèn)題。
前后端分離開發(fā)中,前端為了能夠模擬出測(cè)試數(shù)據(jù),并且模擬出請(qǐng)求,一般需要借助于 nodejs 來(lái)運(yùn)行,這是開發(fā)時(shí)候的狀態(tài),開發(fā)時(shí)候的配置大家可以參考這篇文章:
等開發(fā)完成后,我們會(huì)對(duì)前端項(xiàng)目編譯打包,編譯打包完成之后,就只剩下一堆 js、css 以及 html 文件了,我們把這些編譯打包后的文件拷貝到后端項(xiàng)目中,這樣再去運(yùn)行就不存在跨域問(wèn)題了(例如將編譯打包后的靜態(tài)文件拷貝到 Spring Boot 項(xiàng)目的 src/main/resources/static 目錄下)。這種方式我就不再多說(shuō)了,相信大家都會(huì),今天咱們主要來(lái)看看如何結(jié)合 Nginx 來(lái)部署。
Nginx 大殺器
結(jié)合 Nginx 來(lái)部署前后端分離項(xiàng)目算是目前的主流方案。一來(lái)部署方便,二來(lái)通過(guò)動(dòng)靜分離也可以有效提高項(xiàng)目的運(yùn)行效率。
大家知道我們項(xiàng)目中的資源包含動(dòng)態(tài)資源和靜態(tài)資源兩種,其中:
- 動(dòng)態(tài)資源就是那些需要經(jīng)過(guò)容器處理的資源,例如 jsp、freemarker、各種接口等。
- 靜態(tài)資源則是那些不需要經(jīng)過(guò)容器處理,收到客戶端請(qǐng)求就可以直接返回的資源,像 js、css、html 以及各種格式的圖片,都屬于靜態(tài)資源。
將動(dòng)靜資源分開部署,可以有效提高靜態(tài)資源的加載速度以及整個(gè)系統(tǒng)的運(yùn)行效率。
在前后端分離項(xiàng)目部署中,我們用 Nginx 來(lái)做一個(gè)反向代理服務(wù)器,它既可以代理動(dòng)態(tài)請(qǐng)求,也可以直接提供靜態(tài)資源訪問(wèn)。我們來(lái)一起看下。建議大家先閱讀松哥以前關(guān)于 Nginx 的一篇舊文,可以有效幫助大家理解后面的配置:
后端部署
后端接口的部署,主要看項(xiàng)目的形式,如果就是普通的 SSM 項(xiàng)目,那就提前準(zhǔn)備好 Tomcat ,在 Tomcat 中部署項(xiàng)目,如果是 Spring Boot 項(xiàng)目,可以通過(guò)命令直接啟動(dòng) jar,如果是微服務(wù)項(xiàng)目,存在多個(gè) jar 的話,可以結(jié)合 Docker 來(lái)部署(參考一鍵部署 Spring Boot 到遠(yuǎn)程 Docker 容器),無(wú)論是那種形式,對(duì)于我們 Java 工程師來(lái)說(shuō),這都不是問(wèn)題,我相信這一步大家都能搞定。
后端項(xiàng)目可以在一個(gè)非 80 端口上部署,部署成功之后,因?yàn)檫@個(gè)后端項(xiàng)目只是提供接口,所以我們并不會(huì)直接去訪問(wèn)他。而是通過(guò) Nginx 請(qǐng)求轉(zhuǎn)發(fā)來(lái)訪問(wèn)這個(gè)后端接口。
松哥這里以我去年為一個(gè)律所的小程序?yàn)槔蠖耸且粋€(gè) Spring Boot 工程,那么我可以通過(guò) Docker 部署,也可以直接通過(guò)命令來(lái)啟動(dòng),這里簡(jiǎn)單點(diǎn),直接通過(guò)命令來(lái)啟動(dòng) jar ,如下:
nohup?java?-jar?jinlu.jar?>?vhr.log?&
后端啟動(dòng)成功之后,我并不急著直接去訪問(wèn)后端,而是安裝并且去配置一個(gè) Nginx,通過(guò) Nginx 來(lái)轉(zhuǎn)發(fā)請(qǐng)求,Nginx 的基本介紹與安裝,大家可以參考(Nginx 極簡(jiǎn)入門教程!),我這里就直接來(lái)說(shuō)相關(guān)的配置了。
這里我們?cè)?nginx.conf 中做出如下配置:
首先配置上游服務(wù)器:
upstream?zqq.com{
??server?127.0.0.1:9999?weight=2;
}
在這里主要是配置服務(wù)端的地址,如果服務(wù)端是集群化部署,那么這里就會(huì)有多個(gè)服務(wù)端地址,然后可以通過(guò)權(quán)重或者 ip hash 等方式進(jìn)行請(qǐng)求分發(fā)。
然后我們?cè)?server 中配置轉(zhuǎn)發(fā)規(guī)則:
location?/jinlu/?{
??proxy_pass?http://zqq.com;
??tcp_nodelay?????on;
??proxy_set_header?Host????????????$host;
??proxy_set_header?X-Real-IP???????$remote_addr;
??proxy_set_header?X-Forwarded-For?$proxy_add_x_forwarded_for;
}
這樣配置完成后,假設(shè)我目前的域名是 javaboy.org,那么用戶通過(guò) http://www.javaboy.org/jinlu/** 格式的地址就可以訪問(wèn)到我服務(wù)端的接口。
前端部署
以 Vue 為例,如果是 SPA 應(yīng)用,項(xiàng)目打包之后,就是一個(gè) index.html 還有幾個(gè) js、css、images 以及 fonts ,這些都是靜態(tài)文件,我們將靜態(tài)文件首先上傳到服務(wù)器,然后在 nginx.conf 中配置靜態(tài)資源訪問(wèn),具體配置如下:
location?~?.*\.(js|css|ico|png|jpg|eot|svg|ttf|woff|html|txt|pdf|)?{
???root?/usr/local/nginx/html/;#所有靜態(tài)文件直接讀取硬盤
???expires?30d;?#緩存30天
}?
當(dāng)然我這里是按照資源類型來(lái)攔截的,即后綴為 js、css、ico 等的文件,統(tǒng)統(tǒng)都不進(jìn)行請(qǐng)求分發(fā),直接從本地的 /usr/local/nginx/html/ 目錄下讀取并返回到前端(我們需要將靜態(tài)資源文件上傳到 /usr/local/nginx/html/ 目錄下)。
如果我們的服務(wù)器上部署了多個(gè)項(xiàng)目,這種寫法就不太合適,因?yàn)槎鄠€(gè)項(xiàng)目的前端靜態(tài)文件肯定要分門別類,各自放好的,這個(gè)時(shí)候我們一樣可以通過(guò)路徑來(lái)攔截,配置如下:
location?/jinlu-admin/?{
???root?/usr/local/nginx/html/jinlu-admin/;#所有靜態(tài)文件直接讀取硬盤
???expires?30d;?#緩存30天
}?
這樣,請(qǐng)求路徑是 /jinlu-admin/ 格式的請(qǐng)求,則不會(huì)進(jìn)行請(qǐng)求分發(fā),而是直接從本機(jī)的 /usr/local/nginx/html/jinlu-admin/ 目錄下返回相關(guān)資源。采用這方方式配置靜態(tài)資源,我們就可以部署多個(gè)項(xiàng)目了,多個(gè)項(xiàng)目的部署方式和上面的一樣。
這樣部署完成之后,假設(shè)我的域名是 javaboy.org ,那么用戶通過(guò) http://www.javaboy.org/jinlu-admin/**格式的請(qǐng)求就可以訪問(wèn)到前端資源了。
此時(shí)大家發(fā)現(xiàn),前端的靜態(tài)資源和后端的接口現(xiàn)在處于同一個(gè)域之中了,這樣就不存在跨域問(wèn)題,所以我一開始基說(shuō)不必用 JSONP 或者 CORS 去解決跨域。特殊情況可能需要在 nginx 中配置跨域,這個(gè)松哥以后再和大家細(xì)聊~
