前后端分離已成為互聯網項目開發的業界標准使用方式,通過Nginx+Tomcat的方式(也可以中間加一個NodeJS)有效地對前端和后端的開發進行解耦。並且前后端分離會為以后的大型分布式架構、彈性計算架構、微服務架構、多端化服務(各種客戶端,比如瀏覽器、車載終端、安卓、IOS等)打下堅實的基礎。前后端分離的核心思想就是前端HTML頁面通過AJAX調用后端的RESTFUL API接口,並通過JSON數據進行交互。
前端的開發與后端的開發分離
以前的JavaWeb項目,大多數都是Java程序員又搞前端,又搞后端。而隨着時代的發展,漸漸的許多大中小公司開始把前后端的界限分得越來越明確,即前端工程師只管前端的開發,后端工程師只管后端的開發。大中型公司需要專業的人才,小公司需要全才(省錢)。但是對於個人職業發展來說,還是分開比較好,因為當生涯發展到后期的時候,企業往往要的是在某一方面上的專家,而不是熟練工。如果一個人前端后端都會一點,那也可以說什么都不會。
前端追求的是頁面的表現、速度的流暢、兼容性和用戶的體驗等。因此對於前端開發工程師來說,需要把精力放在HTML5、CSS3、JavaScript、jQuery、AngularJS、BootStrap、ReactJS、VueJS、Webpack、Less/Sass、Gulp、NodeJS、Google V8引擎、JS多線程、JS模塊化、JS面向切面編程、設計模式、瀏覽器兼容性、性能優化等上。
后端追求的是三高(高並發、高可用、高性能)、安全、存儲、業務等。因此對於后端開發工程師來說,需要把精力放在Java基礎、設計模式、JVM原理、Spring全家桶的原理及源碼、Linux、關系型數據庫事務隔離和鎖機制、MongoDB、HTTP/TCP、多線程、分布式架構、彈性計算架構、微服務架構、Java性能優化、數據庫性能優化以及相關的項目管理等等。
往某個方向深深鑽研,這樣你的核心競爭力才會越來越高。正所謂你往生活中投入什么,生活就會反饋給你什么。千萬別勉強自己去追求所謂的全棧,因為不管是前端和后端,到后期都有很多高深的東西讓你花費所有精力,你想什么都會,最后可能什么都不會(太貪心的人最后什么都得不到,別信聖誕老人說的那句我全都要)。當然也不是說就不能去學,學也是可以的,但是要偏重點,分清主次,就像人生不要把時間浪費在不值得的人和物身上一樣。
前后端分離解決的耦合問題
曾幾何時,我們的JavaWeb項目都是使用了若干個后台框架,比如Spring MVC、Structs、Spring Boot、Spring JDBC、Hibernate、MyBatis等。大多數項目在Java后端都是分了三層,即控制層(Controller)、業務層(Service)和持久層(Dao)。控制層負責接收參數,調用相關業務層,封裝數據,以及路由和渲染到JSP頁面(或模板頁面,如Freemarker)。然后JSP頁面上使用各種標簽或者手寫Java表達式將后台的數據展現出來,玩的就是MVC那套思路。
我們先看這種情況:我們開發一個網站,需求定完了,代碼寫完了,測試跑完了,用Maven或IDE等工具把代碼打成一個war包完了,把war包發布到生產環境下的WEB容器完了,啟動WEB容器完了,域名、DNS配置完了,網站就可以訪問了。這時候,我們的前后端代碼全都在那個war包里了,包括JS、CSS、圖片等各種靜態資源文件。
接下來,我們在瀏覽器中輸入網站域名(www.xxx.com),瀏覽器在通過域名和DNS服務器找到我們的服務器外網IP,將HTTP請求發送到你的服務器,在TCP三次握手之后(HTTP下面是TCP/IP),通過TCP協議開始傳輸數據,我們的服務器得到請求后,開始提供服務,接收參數,之后返回我們的應答給瀏覽器,瀏覽器再通過Content-type來解析返回的內容,呈現給用戶。
這里我們假設我們的首頁中有100張圖片,那么當用戶訪問首頁的時候,就需要建立100個HTTP請求,我們的服務器接收這些請求,都需要耗費內存去創建Socket來玩TCP傳輸(消耗服務器上的計算資源)。重點來了,這樣的話,我們的服務器的壓力就會非常大,因為頁面中的所有請求都是只請求到我們的這台服務器上,如果一個人還好,如果是有10000個人並發訪問呢(這里先不說服務器集群,就說單實例服務器),那么我們的服務器恐怕扛不住(TCP連接、帶寬、內存、硬盤、IO、WEB服務器的內存等方面),恐怕會宕機。
這就是為什么大中型的WEB應用要解耦的原因。理論上我們是可以把數據庫、應用服務、消息隊列、緩存、用戶上傳的文件、日志等都丟在一台服務器上,也不用玩什么服務治理,也不用做什么性能監測,什么報警機制等,愛宕機就宕機好了(佛系青年)。可是現實不允許我們宕機啊,當某個子應用因為內存不穩定而導致整個服務器內存溢出,再而導致我們的網站掛掉的時候,我們就糟糕了。比如我們公司的業務如果這時候正好處於井噴式發展的高峰期的時候,服務器掛掉了,業務就被技術卡住了,那么就可能會導致大量用戶的流失,后果不堪設想。這里順便提一句,技術一定要走在業務前面,否則可能會錯過最佳的發展期。
另外的,當你的應用全都耦合在一起,就相當於一塊巨石,當服務器負載能力不足時,一般會使用負載均衡的方式,將服務器做成集群。這樣其實你是在水平擴展一塊塊巨石,性能加速度會越來越低。要知道,本身負載就低的功能或模塊是沒有必要水平擴展的,水平擴展要針對性能瓶頸去擴展才對。在這里的例子中,性能瓶頸跟前端沒有關系,沒有必要去水平擴展前端。同時,發版部署的時候,如果只是改了后端的代碼,卻要前端代碼也跟着重新發布,顯然也降低了效率。
正因為這樣,正常的互聯網架構都是把WEB服務器集群、應用服務器集群、文件服務器集群、數據庫服務器集群、消息隊列集群、緩存集群等集群拆分開的。
前后端耦合的缺點(以JSP為例)
以前的JavaWeb項目大多數使用JSP作為頁面層展示數據給用戶,因為流量不高,因此也沒有那么苛刻的性能要求,但現在是大數據時代,對於互聯網項目的性能要求是越來越高,因此原始的前后端耦合在一起的架構模式已經逐漸不能滿足我們,因此我們需要找到一種解耦的方式,以大幅提升我們的負載能力。前后端耦合主要有以下的缺點(以JSP為例):
1.動態資源和靜態資源全部耦合在一起,服務器壓力大,因為服務器會收到各種HTTP請求,例如CSS的HTTP請求,JS的,圖片的等等。一旦服務器出現狀況,前后台一起完犢子,用戶體驗極差。如果前后端分離,你后端的服務器掛了,前端服務器沒掛,用戶仍然可以看到界面,雖然請求不到數據,嘿嘿。
2.UI出好設計圖之后,前端開發工程師只負責將設計圖切成HTML,需要由Java開發工程師來將HTML套成JSP頁面,出錯率較高(因為頁面中經常會出現大量的JS代碼),修改問題的時候需要雙方協同開發,效率低下。
3.JSP頁面必須要在支持Java的WEB服務器上運行(如Tomcat、Jetty、Resin等),無法使用Nginx等(官方宣稱單實例HTTP並發高達5W),性能提升不上來。
4.第一次請求JSP,必須要在WEB服務器中編譯成Servlet,第一次運行會較慢。然后之后的每次請求JSP都是訪問Servlet再用輸出流輸出的HTML頁面,效率沒有直接使用HTML高(注意是每次噢)。
5.JSP內有較多內置的專屬標簽和表達式,前端開發工程師在修改頁面時往往會撓頭撓到禿頭。
6.如果JSP中的內容很多,頁面響應會很慢,因為是同步加載,一次輸出所有內容。
7.修改JSP頁面需要前端開發工程師使用Java的IDE以及配置各種后端的開發環境,對前端開發工程師極不友好。
前后端分離的優點
1.前后端分離可以真正地實現前后端解耦,前端服務器使用Nginx。前端/WEB服務器放的是CSS、JS和圖片等一系列靜態資源(甚至可以把這些靜態資源放到特定的文件服務器,如阿里雲的OSS,並使用CDN加速)。前端服務器負責控制頁面引用、跳轉和路由,前端頁面通過AJAX異步調用后端的接口,后端/應用服務器使用Tomcat(把風花雪月交給前端,只提供數據),加快整體響應數據。(這里需要使用一些前端工程化的框架,如NodeJS、React、Router、Reudx、Webpack等)
2.前后端分離的模式下,如果發現Bug,可以快速定位是誰的問題,不會出現互相踢皮球的現象。如果是頁面邏輯、跳轉錯誤、瀏覽器兼容性問題、腳本錯誤、頁面樣式等問題,通通是前端開發工程師的鍋。如果是接口數據出錯、數據提交失敗、應答超時等問題,則后端開發工程師應該挺身接鍋。雙方職責分明,就不會打架。
3.前后端分離在大並發的情況下,我們可以同時水平擴展前后端服務器。要知道淘寶的一個首頁就需要2000+台前端服務器做集群來抗住日均多少億的日均PV。(阿里的技術峰會說到他們的WEB容器都是自己寫的,就算單實例抗10萬HTTP並發,2000台是2億HTTP並發,並且他們還可以根據預知洪峰來無限擴展,好恐怖啊,就一個首頁。。)
4.前后端分離可以減少后端服務器的並發/負載壓力。除了接口以外的其他所有HTTP請求全部轉移到前端Nginx上,接口的請求則轉發調用Tomcat。且除了第一次頁面請求外,瀏覽器會大量調用本地緩存去緩存頁面。
5.前后端分離的模式下,即使后端服務器暫時超時或宕機了,前端頁面也會正常訪問,只不過數據刷不出來而已。
6.前后端分離的模式下,后台的接口可以復用。比如需要做微信相關的輕應用,接口就完全可以共用,再比如說APP相關的服務,也可以只通過一些代碼重構,就可以大量復用接口,提升開發效率。(多端應用)
7.頁面顯示再多的內容也不怕了,因為從同步加載改成了異步加載。
8.Nginx支持頁面的熱部署,不用重啟服務器,前端升級更無縫。
9.增加代碼的維護性和易讀性,提高開發效率(前后端代碼耦合在一起很難讀,比如JSP頁面)。
10.前后端可以並行開發,提高開發效率。
11.在Nginx中部署證書,外網使用HTTPS訪問,並且只開放443和80端口,其他端口一律關閉(防止黑客端口掃描),內網使用HTTP,性能和安全都有保障。
前后端分離的注意事項
1.在開需求會議的時候,前后端工程師必須全部參加,並且需要制定好接口文檔,后端工程師要寫好測試用例(2個維度),不要讓前端工程師充當你的專職測試。
2.上述的接口並不是Java里的Interface,說白了調用接口就是調用你Controller里面的方法。
3.我們需要一些前端的框架來解決類似於頁面嵌套,分頁,頁面跳轉控制等功能。
4.小項目的話,比如單純的內網項目,就沒有必要前后端分離了,復雜架構反而會拖累性能。但是如果是大型外網項目,前后端分離就非常必要了。
5.以前還有人用類似於Velocity/Freemarker等框架模板生成靜態頁面,個人覺得和使用JSP其實也相差不遠,也就是性能好一點,沒有實現真正的前后端分離。
6.前后端分離會加重前端團隊的工作量,減輕后端團隊的工作量,提高了性能和可擴展性。
7.如果頁面上有一些權限等等相關的校驗,這些相關的和數據也可以通過AJAX從接口里拿。
8.對於既可以前端做又可以后端做的邏輯,建議放在前端,因為邏輯計算時需要計算資源的,如果放到后端去運行邏輯,則會消耗帶寬、內存和CPU等等計算資源。你要記住一點就是服務端的計算資源是有限的,而如果放到前端,使用的是客戶端的計算資源,這樣你的服務端負載就會下降(高並發場景)。類似於數據校驗這種,前后端都需要做。
9.前端需要有機制應對后端請求超時以及后端服務宕機的情況,友好地展示給客戶。
前后端分離的擴展閱讀
1.對於JS、CSS、圖片這類靜態資源可以考慮放到類似於阿里雲的OSS這類文件服務器上(如果是普通的服務器和操作系統,存儲在到達PB級(1000TB)的文件后,或者單個文件夾內的文件數量達到3-5萬,IO會有很嚴重的性能問題),再在OSS上配CDN(全國子節點加速),這樣你頁面打開的速度像飛一樣,無論你在全國的哪個地方,並且你的Nginx的負載會進一步降低。
2.如果你要玩輕量級微服務架構,要使用NodeJS做網關,用NodeJS的好處還有利於SEO優化,因為Nginx只是向瀏覽器返回頁面靜態資源,而國內的搜索引擎爬蟲只會抓取靜態數據,不會解析頁面中的JS,這使得應用得不到良好的搜索引擎支持。同時因為Nginx不會進行頁面的組裝渲染,需要把靜態頁面返回到瀏覽器,然后完成渲染工作,加重了瀏覽器的渲染負擔。瀏覽器發起的請求經過Nginx進行分發,URL請求統一分發到NodeJS,在NodeJS中進行頁面組裝渲染;API請求則直接發送到后端服務器,完成響應。
3.如果遇到跨域問題,Spring4的CORS可以完美解決。但一般使用Nginx做反向代理的都不會有跨域問題,除非你把前端服務和后端服務分成兩個域名。JSONP的方式也被淘汰掉了。
4.如果想玩多端應用,主要要去掉Tomcat原生的session機制。要使用token機制,使用緩存(因為是分布式系統),做單點。關於token機制的安全性問題,可以了解下JWT(JSON Web Token)。
5.前端項目中可以加入mock測試(構造虛擬測試對象來模擬后端,可以獨立開發和測試),后端則需要有詳細的測試用例,保證服務的可用性和穩定性。
總結
前后端分離並非僅僅是一種開發模式,而是一種架構模式(前后端分離架構)。千萬不要以為只有在寫代碼的時候把前端和后端分開就是前后端分離了,這樣的理解太片面了。前后端分離是需要區分前后端項目的,即前端項目和后端項目是兩個項目,放在兩個不同的服務器,需要獨立部署,兩個不同的工程,兩個不同的代碼庫,兩組不同的開發人員。前后端開發工程師需要約定交互的接口,實現並行開發。而在開發結束之后,前端項目和后端項目都需要進行獨立部署,前端通過AJAX來調用HTTP請求,調用后端的RESTFUL API。前端只需要關注頁面的樣式與動態數據的解析和渲染,不用關心數據是怎么產生的;后端則專注於具體的業務邏輯,返回前端展現所需要的業務數據即可。
"無論去哪里,都不要失去重新開始的勇氣。"