大部分人認同開發(fā)微信小程序或SPA(WEB單頁應(yīng)用)是實現(xiàn)“前后端分離”的臀栈。所有業(yè)務(wù)數(shù)據(jù)都是“后端應(yīng)用”通過HTTP接口提供給“前端應(yīng)用”。一個“完整的應(yīng)用”被物理隔離為兩個獨立的應(yīng)用挠乳,并由擅長的開發(fā)者負責(zé)权薯,從而實現(xiàn)職責(zé)分離:
前端開發(fā)者:負責(zé) View 和 Controller 層。
后端開發(fā)者:只負責(zé) Model 層睡扬,業(yè)務(wù)處理/數(shù)據(jù)等盟蚣。
“前后端分離”是強調(diào)“職責(zé)分離”,上面應(yīng)用分離的例子是為了方便理解接受卖怜,可以不應(yīng)用分離屎开。通常應(yīng)用分離能大量減少不小心的越界行為。
分離前:前端開發(fā)者提供靜態(tài)頁面马靠,后端開發(fā)者完成 Controller 和 View 層代碼奄抽。但前端開發(fā)者經(jīng)常會插一腳修改 Controller 和 View 層代碼。
分離后:面向用戶的 Controller 和 View 層都由前端開發(fā)者做虑粥;后端開發(fā)者只需復(fù)雜 Model 層如孝,提供業(yè)務(wù)數(shù)據(jù)接口。
在自己服務(wù)器測試兩種場景娩贷。
服務(wù)器配置:
騰訊云服務(wù)器(Ubuntu 16.04.1 LTS)
CPU: 2核
內(nèi)存:4GB
帶寬:2M
Node.js環(huán)境:
Node.js版本:v8.12.0(Alinode v3.12.0)
框架:Egg.js
worker進程數(shù):2
PHP環(huán)境:
PHP版本:7.2.10
框架:Lumen
php-fpm最大子進程數(shù):5
兩種場景測試代碼:
場景一第晰,直接輸出字符串,不做任何IO和計算。
PHP效率比Node.js高
Node.js:
// egg-4asyncfunctiontest(ctx){? ctx.body ='<h1>goddess.daifee.com</h1><p>Egg程序</p>';}
egg-4.png
PHP:
// php-4$router->get('/test',function()use($router){return'<h1>goddess-php.daifee.com</h1><p>這是PHP服務(wù)</p>';});
php-4.png
場景二茁瘦,模擬200ms的網(wǎng)絡(luò)請求品抽。
Node.js效率比PHP高。相對“場景一”PHP效率下降明顯甜熔,Node.js不明顯圆恤。
Node.js:
// egg-4—test2asyncfunctiontest2(ctx){conststart =Date.now();awaitnewPromise(resolve=>{? ? setTimeout(()=>{? ? ? resolve(true);? ? },200);? });constoffset =Date.now() - start;? ctx.body =`
goddess.daifee.com
Egg程序${offset}
`;}
egg-4-test2.png
PHP:
// ./WeTest_[php-4—test2]_20181014172129.pdf$router->get('/test2',function()use($router){? ? $start = microtime(true);? ? usleep(200000);? ? $offset = microtime(true) - $start;return"<h1>goddess-php.daifee.com</h1><p>這是PHP服務(wù) {$offset}</p>";});
php-4-test2.png
3.2 基于Egg.js框架開發(fā)(編碼)
下面用Egg.js框架開發(fā)一個頁面為例(瀏覽器端開發(fā)模式不需要改變)。
開發(fā)一個新頁面(用戶主頁頁)腔稀,只需要下面4個步驟:
模板:創(chuàng)建模板文件盆昙。
<!-- app/views/user.ejs -->這里是 ejs 模板
數(shù)據(jù)接口:幾行代碼封裝數(shù)據(jù)接口。
// app/service/user.jsconst{ Service } =require('egg');module.exports =classUserServiceextendsService{asyncget(userId) {consturl =`https://api.gateway.com/xxx/${userId}`;// http請求/ajaxconstresponse =awaitthis.app.curl(url);returnresponse;? }}
控制器:過濾/驗證用戶輸入焊虏、驗證權(quán)限淡喜、調(diào)用數(shù)據(jù)接口、渲染模板诵闭。
// app/controller/user.jsconstBaseController =require('./base-controller');module.exports =classUserControllerextendsBaseController{asyncprofile() {const{ params, service } =this.ctx;// 只有自己才能訪問自己主頁炼团。不是自己就拋出 403 異常this.assertUser(params.userId);? ? user =awaitservice.user.get(params.userId);awaitthis.render('user', {user: user});? }}
路由:一行代碼聲明路由。
// app/router.jsmodule.exports =app=>{const{ router, controller, middleware } = app;const{ authorize } = middleware;const{ user } = controller;// 已登錄用戶才能訪問router.get('/users/:userId', authorize.user, user.profile);}
只負責(zé) Controller 和 View 層的Node.js服務(wù)端開發(fā)非常簡單疏尿。一個企業(yè)應(yīng)用除了開發(fā)瘟芝,還有其他環(huán)節(jié)需要做好。
3.3 我們還缺什么褥琐?
前端開發(fā)者可以在服務(wù)端寫 Controller 和 View 層代碼锌俱,但整個生產(chǎn)環(huán)節(jié)還缺什么?
3.3.1 前端開發(fā)者
既然前端開發(fā)者需要做更多踩衩,能做更多嚼鹉,所以工作量增加。所以缺前端開發(fā)者驱富。
同一個項目锚赤,編碼的工作量增加了。
前端同事需要關(guān)注褐鸥、分析线脚、協(xié)助維護Node.js服務(wù)器。
肯定會有更多的xxx小程序需求叫榕。
3.3.2 適合我們的框架和項目腳手架
確定了web網(wǎng)站應(yīng)用使用Egg框架浑侥,還需為不同業(yè)務(wù)場景的項目創(chuàng)建拿來即用的腳手架。
未確定React Isomorphic應(yīng)用的框架晰绎。我體驗過next.js寓落,是一個不錯的選擇。
3.3.3 構(gòu)建/部署服務(wù)
目前還沒有Node.js項目的構(gòu)建/部署服務(wù)荞下。
基本需求:
構(gòu)建:開發(fā)者只需關(guān)注源碼伶选,“構(gòu)建服務(wù)”自動構(gòu)建項目史飞。
發(fā)布:支持選擇git tag構(gòu)建,部署到“生產(chǎn)環(huán)境”仰税。
發(fā)布:支持選擇git branch構(gòu)建构资,部署到“測試環(huán)境”。
回滾:支持指定版本回滾陨簇。
重啟:支持重啟Node.js服務(wù)器吐绵。
發(fā)布:允許測試人員發(fā)布“測試版本”。
3.3.4 嚴謹?shù)膅it工作流
需要依據(jù)“構(gòu)建/部署服務(wù)”定制嚴謹?shù)膅it工作流河绽。
3.3.5 風(fēng)險監(jiān)控和日志采集
風(fēng)險監(jiān)控可以考慮Alinode己单。
完全免費
支持性能監(jiān)控、安全提醒葵姥、故障排查荷鼠、性能優(yōu)化等
支持手動下載性能數(shù)據(jù)
日志采集接入大數(shù)據(jù)團隊的服務(wù)?
關(guān)于React Isomrophic
當時用React全家桶做了一個SPA Demo項目榔幸,然后用next.js框架再做一個React Isomorphic版。發(fā)現(xiàn)可以復(fù)用(copy)98%的代碼矮嫉,然后花一點時間將 React Router 替換為 next.js 自帶的 Router 即可
基于 next.js 框架 React Isomorphic 應(yīng)用與 SPA 的開發(fā)體驗差不多削咆。
畢竟一套代碼運行于兩個不同環(huán)境,React Isomorphic 還是帶來了一些坑:
需要意識到哪些代碼在2個環(huán)境都執(zhí)行蠢笋,哪些代碼只在其中一個環(huán)境執(zhí)行拨齐。
立即執(zhí)行的代碼在2個環(huán)境都執(zhí)行。
React組件生命周期的代碼只在Browser環(huán)境執(zhí)行昨寞。
Component.getInitialProps()方法在2個環(huán)境都執(zhí)行瞻惋。
小心使用運行環(huán)境的“接口”
next.js自帶Router,與“其他Router”肯定存在差異援岩。
某些組件需要用唯一自增ID或隨機數(shù)歼狼,這種做法會使服務(wù)端于客戶端存在差異。
“服務(wù)端”與“客戶端”基本只能用cookie共享“狀態(tài)”享怀,要珍惜cookie資源羽峰。
作者:daifee
鏈接:http://www.reibang.com/p/1f3f3f70be33
來源:簡書
簡書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請聯(lián)系作者獲得授權(quán)并注明出處添瓷。
個人倒是覺得梅屉。如果有條件的話,架構(gòu)上可以考慮底層接口采用java來提供基層的共性接口鳞贷,可以保證高并發(fā)和穩(wěn)定性坯汤。然后在業(yè)務(wù)邏輯處理上,采用php進行處理搀愧,為前端提供接口服務(wù)惰聂,而在前端則采用vue來模塊化界面卖毁。
所以我們就需要有發(fā)布系統(tǒng)票堵,發(fā)布系統(tǒng)定義了統(tǒng)一的回退操作,所有服務(wù)必須遵循發(fā)布系統(tǒng)的定義回退操作。 在餓了么對接發(fā)布系統(tǒng)是對所有人的強制要求歹苦,所有的系統(tǒng)必須全部接入發(fā)布系統(tǒng)。發(fā)布系統(tǒng)的框架很重要立倍,這個東西其實對于公司是很重要的一件事情澜薄,需要放到第一優(yōu)先級的隊列里面去考慮。
四叉钥、服務(wù)框架
緊接著就是餓了么的服務(wù)框架罢缸,把一個大的Repo拆分成一個小的Repo,把一個大的服務(wù)拆成一個小的服務(wù)投队,讓我們的服務(wù)盡量獨立出去枫疆,這需要一套分布式服務(wù)框架來支撐。 分布式服務(wù)框架包含的服務(wù)注冊敷鸦、發(fā)現(xiàn)息楔、負載均衡、路由扒披、流控值依、熔斷、降級等功能碟案,這里就不一一展開了愿险。前面已經(jīng)提及,餓了么是多語言的生態(tài)价说,有 Python的辆亏,也有Java的,我們的服務(wù)化框架對應(yīng)也是多語言的鳖目。這對我們后來一些中間件的選型是有影響的扮叨,比如說DAL層。
五疑苔、DAL數(shù)據(jù)訪問層
當業(yè)務(wù)量越來越大的時候甫匹,數(shù)據(jù)庫會變成一個瓶頸。 前期可以通過提升硬件的方式來提升數(shù)據(jù)庫的性能惦费。比如:
升級到一個有更多CPU的機器兵迅;
把硬盤改成 SSD 的或者更高級一點的。
但硬件提升終歸是有一個容量限制的薪贫。而且很多做業(yè)務(wù)的小伙伴恍箭,寫代碼的時候都直接操作數(shù)據(jù)庫,發(fā)生過很多次服務(wù)一上線數(shù)據(jù)庫就被打爆的情形瞧省。數(shù)據(jù)庫被打爆掉了之后扯夭,除非等待數(shù)據(jù)庫恢復(fù)鳍贾,沒有任何其它機會可以恢復(fù)業(yè)務(wù)。 如果數(shù)據(jù)庫里面數(shù)據(jù)是正常的交洗,業(yè)務(wù)其實都可以補償出來骑科。所以我們做DAL服務(wù)層的時候,第一件事情是限流构拳,其它的東西可以放一放咆爽。然后做連接復(fù)用,我們Python框架用的多進程單線程加協(xié)程的模型置森。 多進程之間其實是不可以共享一個連接的斗埂。比如:一臺機器上部署了10個 Python進程,每個進程10個數(shù)據(jù)庫連接凫海。再擴展到10臺機器上呛凶,就有1000個數(shù)據(jù)庫連接。對數(shù)據(jù)庫來說行贪,連接是一個很昂貴的東西漾稀,我們DAL層要做一個連接復(fù)用。 這個連接復(fù)用講的不是服務(wù)本身的連接復(fù)用建瘫,而是說DAL層上的連接復(fù)用县好,就是服務(wù)有1000個連接到DAL層,經(jīng)過連接復(fù)用后對數(shù)據(jù)庫可能只是保持著十幾個連接暖混。一旦發(fā)現(xiàn)某個數(shù)據(jù)庫請求是一個事務(wù)的話,那么DAL就幫你保留這個連接的對應(yīng)關(guān)系翁授。當這個事務(wù)結(jié)束之后拣播,就把數(shù)據(jù)庫的連接,放回到共用池里面去收擦,供其他人使用贮配。 然后做冒煙和熔斷。數(shù)據(jù)庫也可以熔斷的塞赂。當數(shù)據(jù)庫發(fā)生冒煙時泪勒,我們會殺掉一些數(shù)據(jù)庫的請求,保證數(shù)據(jù)庫不至于崩潰宴猾。六圆存、服務(wù)治理服務(wù)框架之后,涉及服務(wù)治理的問題仇哆。服務(wù)治理其實是一個很大的概念沦辙。首先是埋點,你要埋很多的監(jiān)控點讹剔。 比如有一個請求油讯,請求成功了或者失敗了详民,請求的響應(yīng)時間是多少,把所有的監(jiān)控指標放到監(jiān)控系統(tǒng)上面去陌兑。我們有一個很大的監(jiān)控屏幕沈跨,上面有很多的監(jiān)控指標。有專門小組72小時去盯著這個屏幕兔综,如果有任何曲線波動了饿凛,就找人去解決。另外是報警系統(tǒng)邻奠,一個監(jiān)控屏幕展示的東西總是有限的笤喳,只能放那些很重要的關(guān)鍵指標。這個時候就需要有報警系統(tǒng)碌宴。 羅馬不是一天建成的杀狡,基礎(chǔ)架構(gòu)更是一個演進的過程。我們的資源和時間總是有限的贰镣,作為架構(gòu)師和 CTO 來說呜象,如何在這種有限的資源下,產(chǎn)出更重要的東西碑隆? 我們做了很多系統(tǒng)恭陡,覺得自己做得很不錯了,但實則不是上煤,我感覺我們又回到了石器時代休玩,因為問題越來越多,需求也越來越多劫狠,總感覺你的系統(tǒng)里還缺點什么東西拴疤,想做的功能也一大堆。 比如對于流控系統(tǒng)独泞,現(xiàn)在我們還是需要用戶去配一個并發(fā)數(shù)呐矾,那么這個并發(fā)數(shù),是不是根本不需要用戶去配懦砂?是不是可以基于我們服務(wù)本身的一個狀態(tài)自動去控制并發(fā)數(shù)蜒犯? 然后是升級方式,SDK升級是個很痛苦的事情荞膘。比如說我們服務(wù)框架2.0發(fā)布的時候是去年12月份罚随,到現(xiàn)在還有人用的是1.0。是不是可以做到SDK的無損感升級衫画,我們自己來控制升級的時間和節(jié)奏毫炉。 還有,我們現(xiàn)在的監(jiān)控只支持同一個服務(wù)上的匯聚削罩,是不分集群瞄勾、不分機器的费奸,那是不是以后的指標可以分集群、分機器进陡?舉一個最簡單的例子愿阐,比如一個服務(wù)上有10臺機器,那么可能只是某一個機器上出了問題趾疚,但它所有的指標都會平均分攤到其它的9臺機器上去缨历。你只是看到了整個服務(wù)延時增加了,但有可能只是某一臺機器拖慢了整個服務(wù)集群糙麦。但我們現(xiàn)在還做不到更多維度的監(jiān)控辛孵。 還有智能化的報警,這個報警赡磅,就是要快魄缚、全、準焚廊,我們現(xiàn)在做到更快了冶匹,做到更全了,怎么才能做到更準咆瘟?每天的報警量高峰時間一分鐘一千多個報警發(fā)出去嚼隘。所有的一千報警都是有用的嗎?報警多了之后袒餐,就相當于沒有報警飞蛹。大家都疲勞了,就不去看了灸眼。我怎么能夠把這個報警更準確地區(qū)分出來桩皿?還有更智能化的鏈路分析?以后是不是我們的監(jiān)控不要放監(jiān)控指標幢炸,而是放鏈路分析,這樣就能夠很清晰地知道拒贱,這個問題對應(yīng)的是哪一個結(jié)點上出了問題宛徊。 這些問題涉及我們做事的一個原則:東西夠用就好,但是要能夠未雨綢繆逻澳,做一定的超前規(guī)劃闸天。