一、前戲
前后端分離已成為互聯(lián)網(wǎng)項目開發(fā)的業(yè)界標(biāo)準(zhǔn)使用方式糠赦,通過nginx+tomcat的方式(也可以中間加一個nodejs)有效的進行解耦会傲,并且前后端分離會為以后的大型分布式架構(gòu)锅棕、彈性計算架構(gòu)、微服務(wù)架構(gòu)淌山、多端化服務(wù)(多種客戶端裸燎,例如:瀏覽器,車載終端艾岂,安卓顺少,IOS等等)打下堅實的基礎(chǔ)。這個步驟是系統(tǒng)架構(gòu)從猿進化成人的必經(jīng)之路王浴。
核心思想是前端html頁面通過ajax調(diào)用后端的restuful api接口并使用json數(shù)據(jù)進行交互脆炎。
在互聯(lián)網(wǎng)架構(gòu)中,名詞解釋:
Web服務(wù)器:一般指像nginx氓辣,apache這類的服務(wù)器秒裕,他們一般只能解析靜態(tài)資源。
應(yīng)用服務(wù)器:一般指像tomcat钞啸,jetty几蜻,resin這類的服務(wù)器可以解析動態(tài)資源也可以解析靜態(tài)資源,但解析靜態(tài)資源的能力沒有web服務(wù)器好体斩。
一般都是只有web服務(wù)器才能被外網(wǎng)訪問梭稚,應(yīng)用服務(wù)器只能內(nèi)網(wǎng)訪問。
二絮吵、術(shù)業(yè)有專攻(開發(fā)人員分離)
以前的JavaWeb項目大多數(shù)都是java程序員又當(dāng)?shù)之?dāng)媽弧烤,又搞前端,又搞后端蹬敲。
隨著時代的發(fā)展暇昂,漸漸的許多大中小公司開始把前后端的界限分的越來越明確,前端工程師只管前端的事情伴嗡,后端工程師只管后端的事情急波。正所謂術(shù)業(yè)有專攻,一個人如果什么都會瘪校,那么他畢竟什么都不精澄暮。
大中型公司需要專業(yè)人才,小公司需要全才渣淤,但是對于個人職業(yè)發(fā)展來說赏寇,我建議是分開。
1价认、對于后端java工程師:
把精力放在java基礎(chǔ)嗅定,設(shè)計模式,jvm原理用踩,spring+springmvc原理及源碼渠退,linux忙迁,mysql事務(wù)隔離與鎖機制,mongodb碎乃,http/tcp姊扔,多線程,分布式架構(gòu)梅誓,彈性計算架構(gòu)恰梢,微服務(wù)架構(gòu),java性能優(yōu)化梗掰,以及相關(guān)的項目管理等等嵌言。
后端追求的是:三高(高并發(fā)赤套,高可用证舟,高性能)渴频,安全臭墨,存儲,業(yè)務(wù)等等敲茄。
2焰檩、對于前端工程師:
把精力放在html5退客,css3焚虱,jquery购裙,angularjs,bootstrap鹃栽,reactjs缓窜,vuejs,webpack谍咆,less/sass,gulp私股,nodejs摹察,Google V8引擎,javascript多線程倡鲸,模塊化供嚎,面向切面編程,設(shè)計模式峭状,瀏覽器兼容性克滴,性能優(yōu)化等等。
前端追求的是:頁面表現(xiàn)优床,速度流暢劝赔,兼容性,用戶體驗等等胆敞。
術(shù)業(yè)有專攻着帽,這樣你的核心競爭力才會越來越高杂伟,正所謂你往生活中投入什么,生活就會反饋給你什么仍翰。并且兩端的發(fā)展都越來越高深赫粥,你想什么都會,那你畢竟什么都不精予借。
通過將team分成前后端team越平,讓兩邊的工程師更加專注各自的領(lǐng)域,獨立治理灵迫,然后構(gòu)建出一個全棧式的精益求精的team秦叛。
三龟再、原始人時代(各種耦合)
幾曾何時利凑,我們的JavaWeb項目都是使用了若干后臺框架哀澈,springmvc/struts + spring + spring jdbc/hibernate/mybatis 等等。
大多數(shù)項目在java后端都是分了三層膨报,控制層现柠,業(yè)務(wù)層够吩,持久層丈氓⊥蛩祝控制層負責(zé)接收參數(shù)闰歪,調(diào)用相關(guān)業(yè)務(wù)層,封裝數(shù)據(jù)课竣,以及路由&渲染到j(luò)sp頁面。然后jsp頁面上使用各種標(biāo)簽或者手寫java表達式將后臺的數(shù)據(jù)展現(xiàn)出來公条,玩的是MVC那套思路靶橱。
我們先看這種情況:需求定完了,代碼寫完了传黄,測試測完了队寇,然后呢佳遣?要發(fā)布了吧零渐?你需要用maven或者eclipse等工具把你的代碼打成一個war包诵盼,然后把這個war包發(fā)布到你的生產(chǎn)環(huán)境下的web容器里,對吧洁墙?
發(fā)布完了之后扫俺,你要啟動你的web容器,開始提供服務(wù)羹呵,這時候你通過配置域名冈欢,dns等等相關(guān)歉铝,你的網(wǎng)站就可以訪問了(假設(shè)你是個網(wǎng)站)。那我們來看凑耻,你的前后端代碼是不是全都在那個war包里柠贤?包括你的js,css臼勉,圖片,各種第三方的庫餐弱,對吧宴霸?
好膏蚓,下面在瀏覽器中輸入你的網(wǎng)站域名(www.xxx.com)瓢谢,之后發(fā)生了什么?(這個問題也是很多公司的面試題)我撿干的說了啊驮瞧,基礎(chǔ)不好的童鞋請自己去搜氓扛。
瀏覽器在通過域名通過dns服務(wù)器找到你的服務(wù)器外網(wǎng)ip,將http請求發(fā)送到你的服務(wù)器论笔,在tcp3次握手之后(http下面是tcp/ip)采郎,通過tcp協(xié)議開始傳輸數(shù)據(jù)尉剩,你的服務(wù)器得到請求后管嬉,開始提供服務(wù)皂林,接收參數(shù),之后返回你的應(yīng)答給瀏覽器胎挎,瀏覽器再通過content-type來解析你返回的內(nèi)容,呈現(xiàn)給用戶犹菇。
那么我們來看德迹,我們先假設(shè)你的首頁中有100張圖片,此時揭芍,用戶的看似一次http請求胳搞,其實并不是一次,用戶在第一次訪問的時候,瀏覽器中不會有緩存肌毅,你的100張圖片筷转,瀏覽器要連著請求100次http請求(有人會跟我說http長連短連的問題,不在這里討論)悬而,你的服務(wù)器接收這些請求呜舒,都需要耗費內(nèi)存去創(chuàng)建socket來玩tcp傳輸(消耗你服務(wù)器上的計算資源)。
重點來了摊滔,這樣的話阴绢,你的服務(wù)器的壓力會非常大,因為頁面中的所有請求都是只請求到你這臺服務(wù)器上艰躺,如果1個人還好呻袭,如果10000個人并發(fā)訪問呢(先不聊服務(wù)器集群,這里就說是單實例服務(wù)器)腺兴,那你的服務(wù)器能扛住多少個tcp連接左电?你的帶寬有多大?你的服務(wù)器的內(nèi)存有多大页响?你的硬盤是高性能的嗎篓足?你能抗住多少IO?你給web服務(wù)器分的內(nèi)存有多大闰蚕?會不會宕機栈拖?
這就是為什么,越是大中型的web應(yīng)用没陡,他們越是要解耦涩哟。理論上你可以把你的數(shù)據(jù)庫+應(yīng)用服務(wù)+消息隊列+緩存+用戶上傳的文件+日志+等等都扔在一臺服務(wù)器上,你也不用玩什么服務(wù)治理盼玄,也不用做什么性能監(jiān)控贴彼,什么報警機制等等,就亂成一鍋粥好了埃儿。但是這樣就好像是你把雞蛋都放在一個籃子里器仗,隱患非常大。如果因為一個子應(yīng)用的內(nèi)存不穩(wěn)定導(dǎo)致整個服務(wù)器內(nèi)存溢出而hung住童番,那你的整個網(wǎng)站就掛掉了精钮。
如果出意外掛掉,而恰好這時你們的業(yè)務(wù)又處于井噴式發(fā)展高峰期剃斧,那么恭喜你杂拨,業(yè)務(wù)成功被技術(shù)卡住,很可能會流失大量用戶悯衬,后果不堪設(shè)想。(注意:技術(shù)一定是要走在業(yè)務(wù)前面的,否則你將錯過最佳的發(fā)展期喲筋粗,親~)
此外策橘,你的應(yīng)用全部都耦合在一起,相當(dāng)于一個巨石娜亿,當(dāng)服務(wù)端負載能力不足時丽已,一般會使用負載均衡的方式,將服務(wù)器做成集群买决,這樣其實你是在水平擴展一塊塊巨石沛婴,性能加速度會越來越低,要知道督赤,本身負載就低的功能or模塊是沒有必要水平擴展的嘁灯,在本文中的例子就是你的性能瓶頸不在前端,那干嘛要水平擴展前端呢躲舌?丑婿??還有發(fā)版部署上線的時候没卸,我明明只改了后端的代碼羹奉,為什么要前端也跟著發(fā)布呢?约计?诀拭?
正常的互聯(lián)網(wǎng)架構(gòu),是都要拆開的煤蚌,你的web服務(wù)器集群耕挨,你的應(yīng)用服務(wù)器集群+文件服務(wù)器集群+數(shù)據(jù)庫服務(wù)器集群+消息隊列集群+緩存集群等等。
四铺然、JSP的痛點
以前的javaWeb項目大多數(shù)使用jsp作為頁面層展示數(shù)據(jù)給用戶俗孝,因為流量不高,因此也沒有那么苛刻的性能要求魄健,但現(xiàn)在是大數(shù)據(jù)時代,對于互聯(lián)網(wǎng)項目的性能要求是越來越高沽瘦,因此原始的前后端耦合在一起的架構(gòu)模式已經(jīng)逐漸不能滿足我們革骨,因此我們需要需找一種解耦的方式析恋,來大幅度提升我們的負載能力筑凫。
1滓技、動態(tài)資源和靜態(tài)資源全部耦合在一起令漂,服務(wù)器壓力大,因為服務(wù)器會收到各種http請求丸边,例如css的http請求叠必,js的,圖片的等等妹窖。一旦服務(wù)器出現(xiàn)狀況纬朝,前后臺一起玩完,用戶體驗極差嘱吗。
2玄组、UI出好設(shè)計圖后,前端工程師只負責(zé)將設(shè)計圖切成html谒麦,需要由java工程師來將html套成jsp頁面俄讹,出錯率較高(因為頁面中經(jīng)常會出現(xiàn)大量的js代碼),修改問題時需要雙方協(xié)同開發(fā)绕德,效率低下患膛。
3、jsp必須要在支持java的web服務(wù)器里運行(例如tomcat耻蛇,jetty踪蹬,resin等),無法使用nginx等(nginx據(jù)說單實例http并發(fā)高達5w臣咖,這個優(yōu)勢要用上)跃捣,性能提不上來。
4夺蛇、第一次請求jsp疚漆,必須要在web服務(wù)器中編譯成servlet,第一次運行會較慢刁赦。
5娶聘、每次請求jsp都是訪問servlet再用輸出流輸出的html頁面,效率沒有直接使用html高(是每次喲甚脉,親~)丸升。
6、jsp內(nèi)有較多標(biāo)簽和表達式牺氨,前端工程師在修改頁面時會捉襟見肘狡耻,遇到很多痛點墩剖。
7、如果jsp中的內(nèi)容很多夷狰,頁面響應(yīng)會很慢涛碑,因為是同步加載。
8孵淘、需要前端工程師使用java的ide(例如eclipse),以及需要配置各種后端的開發(fā)環(huán)境歹篓,你們有考慮過前端工程師的感受嗎瘫证。
基于上述的一些痛點,我們應(yīng)該把整個項目的開發(fā)權(quán)重往前移庄撮,實現(xiàn)前后端真正的解耦背捌!
五、開發(fā)模式
以前老的方式是:
1洞斯、產(chǎn)品經(jīng)歷/領(lǐng)導(dǎo)/客戶提出需求
2毡庆、UI做出設(shè)計圖
3、前端工程師做html頁面
4烙如、后端工程師將html頁面套成jsp頁面(前后端強依賴么抗,后端必須要等前端的html做好才能套jsp。如果html發(fā)生變更亚铁,就更痛了蝇刀,開發(fā)效率低)
5、集成出現(xiàn)問題
6徘溢、前端返工
7吞琐、后端返工
8、二次集成
9然爆、集成成功
10站粟、交付
新的方式是:
1、產(chǎn)品經(jīng)歷/領(lǐng)導(dǎo)/客戶提出需求
2曾雕、UI做出設(shè)計圖
3奴烙、前后端約定接口&數(shù)據(jù)&參數(shù)
4、前后端并行開發(fā)(無強依賴翻默,可前后端并行開發(fā)缸沃,如果需求變更,只要接口&參數(shù)不變修械,就不用兩邊都修改代碼趾牧,開發(fā)效率高)
5、前后端集成
6肯污、前端頁面調(diào)整
7翘单、集成成功
8吨枉、交付
六、請求方式
以前老的方式是:
1哄芜、客戶端請求
2貌亭、服務(wù)端的servlet或controller接收請求(后端控制路由與渲染頁面,整個項目開發(fā)的權(quán)重大部分在后端)
3认臊、調(diào)用service,dao代碼完成業(yè)務(wù)邏輯
4圃庭、返回jsp
5、jsp展現(xiàn)一些動態(tài)的代碼
新的方式是:
1失晴、瀏覽器發(fā)送請求
2剧腻、直接到達html頁面(前端控制路由與渲染頁面,整個項目開發(fā)的權(quán)重前移)
3涂屁、html頁面負責(zé)調(diào)用服務(wù)端接口產(chǎn)生數(shù)據(jù)(通過ajax等等书在,后臺返回json格式數(shù)據(jù),json數(shù)據(jù)格式因為簡潔高效而取代xml)
4拆又、填充html儒旬,展現(xiàn)動態(tài)效果,在頁面上進行解析并操作DOM帖族。
總結(jié)一下新的方式的請求步驟:
大量并發(fā)瀏覽器請求--->web服務(wù)器集群(nginx)--->應(yīng)用服務(wù)器集群(tomcat)--->文件/數(shù)據(jù)庫/緩存/消息隊列服務(wù)器集群
同時又可以玩分模塊栈源,還可以按業(yè)務(wù)拆成一個個的小集群,為后面的架構(gòu)升級做準(zhǔn)備盟萨。
七凉翻、前后分離的優(yōu)勢
1、可以實現(xiàn)真正的前后端解耦捻激,前端服務(wù)器使用nginx制轰。前端/WEB服務(wù)器放的是css,js胞谭,圖片等等一系列靜態(tài)資源(甚至你還可以css垃杖,js,圖片等資源放到特定的文件服務(wù)器丈屹,例如阿里云的oss调俘,并使用cdn加速),前端服務(wù)器負責(zé)控制頁面引用&跳轉(zhuǎn)&路由旺垒,前端頁面異步調(diào)用后端的接口彩库,后端/應(yīng)用服務(wù)器使用tomcat(把tomcat想象成一個數(shù)據(jù)提供者),加快整體響應(yīng)速度先蒋。(這里需要使用一些前端工程化的框架比如nodejs骇钦,react,router竞漾,react眯搭,redux窥翩,webpack)
2、發(fā)現(xiàn)bug鳞仙,可以快速定位是誰的問題寇蚊,不會出現(xiàn)互相踢皮球的現(xiàn)象。頁面邏輯棍好,跳轉(zhuǎn)錯誤仗岸,瀏覽器兼容性問題,腳本錯誤借笙,頁面樣式等問題爹梁,全部由前端工程師來負責(zé)。接口數(shù)據(jù)出錯提澎,數(shù)據(jù)沒有提交成功,應(yīng)答超時等問題念链,全部由后端工程師來解決盼忌。雙方互不干擾,前端與后端是相親相愛的一家人掂墓。
3谦纱、在大并發(fā)情況下,我可以同時水平擴展前后端服務(wù)器君编,比如淘寶的一個首頁就需要2000+臺前端服務(wù)器做集群來抗住日均多少億+的日均pv跨嘉。(去參加阿里的技術(shù)峰會,聽他們說他們的web容器都是自己寫的吃嘿,就算他單實例抗10萬http并發(fā)祠乃,2000臺是2億http并發(fā),并且他們還可以根據(jù)預(yù)知洪峰來無限拓展兑燥,很恐怖亮瓷,就一個首頁。降瞳。嘱支。)
4、減少后端服務(wù)器的并發(fā)/負載壓力挣饥。除了接口以外的其他所有http請求全部轉(zhuǎn)移到前端nginx上除师,接口的請求調(diào)用tomcat,參考nginx反向代理tomcat扔枫。且除了第一次頁面請求外汛聚,瀏覽器會大量調(diào)用本地緩存。
5茧吊、即使后端服務(wù)暫時超時或者宕機了贞岭,前端頁面也會正常訪問八毯,只不過數(shù)據(jù)刷不出來而已。
6瞄桨、也許你也需要有微信相關(guān)的輕應(yīng)用话速,那樣你的接口完全可以共用,如果也有app相關(guān)的服務(wù)芯侥,那么只要通過一些代碼重構(gòu)泊交,也可以大量復(fù)用接口,提升效率柱查。(多端應(yīng)用)
7廓俭、頁面顯示的東西再多也不怕,因為是異步加載唉工。
8研乒、nginx支持頁面熱部署,不用重啟服務(wù)器淋硝,前端升級更無縫雹熬。
9、增加代碼的維護性&易讀性(前后端耦在一起的代碼讀起來相當(dāng)費勁)谣膳。
10竿报、提升開發(fā)效率,因為可以前后端并行開發(fā)继谚,而不是像以前的強依賴烈菌。
11、在nginx中部署證書花履,外網(wǎng)使用https訪問芽世,并且只開放443和80端口,其他端口一律關(guān)閉(防止黑客端口掃描)诡壁,內(nèi)網(wǎng)使用http捂襟,性能和安全都有保障。
12欢峰、前端大量的組件代碼得以復(fù)用葬荷,組件化,提升開發(fā)效率纽帖,抽出來宠漩!
八、注意事項
1懊直、在開需求會議的時候扒吁,前后端工程師必須全部參加,并且需要制定好接口文檔室囊,后端工程師要寫好測試用例(2個維度)雕崩,不要讓前端工程師充當(dāng)你的專職測試魁索,推薦使用chrome的插件postman或soapui或jmeter,service層的測試用例拿junit寫盼铁。ps:前端也可以玩單元測試嗎粗蔚?
2、上述的接口并不是java里的interface饶火,說白了調(diào)用接口就是調(diào)用你controler里的方法鹏控。
3、加重了前端團隊的工作量肤寝,減輕了后端團隊的工作量当辐,提高了性能和可擴展性。
4鲤看、我們需要一些前端的框架來解決類似于頁面嵌套缘揪,分頁,頁面跳轉(zhuǎn)控制等功能义桂。(上面提到的那些前端框架)寺晌。
5、如果你的項目很小澡刹,或者是一個單純的內(nèi)網(wǎng)項目,那你大可放心耘婚,不用任何架構(gòu)而言罢浇,但是如果你的項目是外網(wǎng)項目,呵呵噠沐祷。
6嚷闭、 以前還有人在使用類似于velocity/freemarker等模板框架來生成靜態(tài)頁面,仁者見仁智者見智赖临。
7胞锰、這篇文章主要的目的是說jsp在大型外網(wǎng)java web項目中被淘汰掉,可沒說jsp可以完全不學(xué)兢榨,對于一些學(xué)生朋友來說嗅榕,jsp/servlet等相關(guān)的java web基礎(chǔ)還是要掌握牢的,不然你以為springmvc這種框架是基于什么來寫的吵聪?
8凌那、如果頁面上有一些權(quán)限等等相關(guān)的校驗,那么這些相關(guān)的數(shù)據(jù)也可以通過ajax從接口里拿吟逝。
9帽蝶、對于既可以前端做也可以后端做的邏輯,我建議是放到前端块攒,為什么励稳?因為你的邏輯需要計算資源進行計算佃乘,如果放到后端去run邏輯,則會消耗帶寬&內(nèi)存&cpu等等計算資源驹尼,你要記住一點就是服務(wù)端的計算資源是有限的趣避,而如果放到前端,使用的是客戶端的計算資源扶欣,這樣你的服務(wù)端負載就會下降(高并發(fā)場景)鹅巍。類似于數(shù)據(jù)校驗這種,前后端都需要做料祠!
10骆捧、前端需要有機制應(yīng)對后端請求超時以及后端服務(wù)宕機的情況,友好的展示給用戶髓绽。
九敛苇、擴展閱讀
1、其實對于js顺呕,css枫攀,圖片這類的靜態(tài)資源可以考慮放到類似于阿里云的oss這類文件服務(wù)器上(如果是普通的服務(wù)器&操作系統(tǒng),存儲在到達pb級的文件后株茶,或者單個文件夾內(nèi)的文件數(shù)量達到3-5萬来涨,io會有很嚴重的性能問題),再在oss上配cdn(全國子節(jié)點加速)启盛,這樣你頁面打開的速度像飛一樣蹦掐, 無論你在全國的哪個地方,并且你的nginx的負載會進一步降低僵闯。
2卧抗、如果你要玩輕量級微服務(wù)架構(gòu),要使用nodejs做網(wǎng)關(guān)鳖粟,用nodejs的好處還有利于seo優(yōu)化社裆,因為nginx只是向瀏覽器返回頁面靜態(tài)資源,而國內(nèi)的搜索引擎爬蟲只會抓取靜態(tài)數(shù)據(jù)向图,不會解析頁面中的js泳秀,這使得應(yīng)用得不到良好的搜索引擎支持。同時因為nginx不會進行頁面的組裝渲染榄攀,需要把靜態(tài)頁面返回到瀏覽器晶默,然后完成渲染工作,這加重了瀏覽器的渲染負擔(dān)航攒。瀏覽器發(fā)起的請求經(jīng)過nginx進行分發(fā)磺陡,URL請求統(tǒng)一分發(fā)到nodejs,在nodejs中進行頁面組裝渲染;API請求則直接發(fā)送到后端服務(wù)器币他,完成響應(yīng)坞靶。
3、如果遇到跨域問題蝴悉,spring4的CORS可以完美解決彰阴,但一般使用nginx反向代理都不會有跨域問題,除非你把前端服務(wù)和后端服務(wù)分成兩個域名拍冠。JSONP的方式也被淘汰掉了尿这。
4、如果想玩多端應(yīng)用庆杜,注意要去掉tomcat原生的session機制射众,要使用token機制,使用緩存(因為是分布式系統(tǒng))晃财,做單點叨橱,對于token機制的安全性問題,可以搜一下jwt断盛。
5罗洗、前端項目中可以加入mock測試(構(gòu)造虛擬測試對象來模擬后端,可以獨立開發(fā)和測試)钢猛,后端需要有詳細的測試用例伙菜,保證服務(wù)的可用性與穩(wěn)定性。
十命迈、總結(jié)
前后端分離并非僅僅只是一種開發(fā)模式贩绕,而是一種架構(gòu)模式(前后端分離架構(gòu))。千萬不要以為只有在擼代碼的時候把前端和后端分開就是前后端分離了躺翻,需要區(qū)分前后端項目。前端項目與后端項目是兩個項目卫玖,放在兩個不同的服務(wù)器公你,需要獨立部署,兩個不同的工程假瞬,兩個不同的代碼庫陕靠,不同的開發(fā)人員。前后端工程師需要約定交互接口脱茉,實現(xiàn)并行開發(fā)剪芥,開發(fā)結(jié)束后需要進行獨立部署,前端通過ajax來調(diào)用http請求調(diào)用后端的restful api琴许。前端只需要關(guān)注頁面的樣式與動態(tài)數(shù)據(jù)的解析&渲染税肪,而后端專注于具體業(yè)務(wù)邏輯。
作者:偏頭痛楊