閱讀952 返回首頁    go 美團網


美團點評點餐前後端分離實踐

前言

欲了解分離的童鞋可以大概過一下,老司機可以考慮跳過。今日早讀由美團點評@蔣歡分享。

正文從這開始~

隨著前端技術的發展,前端開發的邊界正逐漸被推向後端,兩者的界限在重合與分離中不斷交替。回首過往,Node.js在2009年的橫空出世可以看作前端開發的裏程碑事件,從此JavaScript不在局限於瀏覽器的狹窄空間,開始在服務器的廣闊天空上展翅高飛。

一、什麼是前後端分離

在介紹什麼是前後端分離之前,首先需要拋出我們在業務開發中遇到的問題。

在點餐研發團隊過去的開發模式中,我們采用了以後端為主的 MVC 架構方式。具體來說,每次項目評審後,前後端會先一起約定好接口,之後分別進行開發。通常前端會在本地先寫好Demo並跑通前端邏輯,之後再將html文件交給後端開發套ftl模板。正式基於這樣的開發模式,導致了總工作量的增加,同時溝通和聯調成本的消耗也十分顯著。

為了解決以上問題,我們期望引入前後端分離的解決方案,即:後端隻專注於提供接口,前端全權負責頁麵的展示。

比較常見的前後端分離解決方案有兩種:

一種是將前端的html, js, css等文件直接部署在靜態資源服務器上。當用戶請求頁麵時,首先會加載靜態html文件,之後通過js異步請求後端數據接口進行二次渲染。

另一種是通過Node服務器來作為中間層代理,通過服務端渲染動態渲染頁麵。

相對來說,第一種方案實施起來更為容易,但不足之處也十分明顯,諸如:不利於SEO、首屏加載白屏等。所以我們更傾向於采取基於Node.js的開發模式,並在點餐監控平台上進行了初步嚐試。

二、基於Node的前後端分離

在點餐監控平台的搭建上,我們采用了基於KOA的前後端分離架構,係統結構

如下圖所示:

在這種模式下,前後端的權責區分更加清晰:

用戶的Http請求會首先發送給前端的Node服務器,由於頁麵與Ajax接口在同域下因此跨域和SSO問題可以得到解決。

Node服務器在獲得請求後,作為代理通過內網將請求分發(毫秒級)給後端Java服務器,Java服務器在讀取數據和處理業務邏輯後,響應Node服務器的請求。

Node服務器在獲得響應後,在服務端動態渲染頁麵並返回給客戶端,解決了SEO和前後端分離的問題。

客戶端獲得響應,展示頁麵或者JS腳本執行下一步操作。

三、從無到有搭建一個Node服務器

常見的Node.js服務框架包括Express, Koa, Feathers等等。目前美團點評使用的是基於Koa封裝的自有開發框架,集成了公司的監控,服務調用,賬號解析等中間件,方便各條業務線快速搭建服務。我們的監控平台也是基於此搭建的。

由於涉及公司內部代碼,無法開源。因此,我隻能基於項目思路寫一個小Demo,方便大家參考。需要一提的是,Koa本身是不帶任何中間件的輕量級框架,這裏隻是提供一種工程化的思路,讀者也可以按自己的喜好進行配置。

0、項目資源地址

GitHub地址:https://github.com/Dragon-Rider/node-server

1、項目的工程目錄結構

Koa本身自帶快速生成項目的方法,在全局安裝Koa後,可以通過 "koa + project name" 的方式快速生成,但是目錄結構比較簡單,不適合我們工程化的解決方案。因此我們重構了新的目錄結構,如下所示:

|——src #業務邏輯目錄

||

||——common #公共文件目錄

||——controllers #控製器目錄

||——models #數據模型目錄

||——views #頁麵模板目錄

||——routes #路由目錄

|||

|||——index.js #主路由

|||——apiRouter.js #接口路由

|||——pageRouter.js #頁麵路由

||

||——public#靜態資源目錄

||——img

||——css

||——js

|

|——logs #日誌文件輸出目錄

|——config #項目配置文件目錄

|——node_modules #node包依賴目錄

|——app.js #項目入口文件

|——package.json #工程管理文件,涉及依賴配置、環境配置、啟動命令等。

在點餐監控平台的前端項目裏,我們依舊采用了MVC風格的結構。

當客戶端發起請求的時候,Node服務器會先由主路由文件作為請求入口,將請求分發給各個子路由。子路由在監聽到對應的HTTP請求後,會向對應的控製器請求數據。控製器訪問不同的數據Model,並對各個Model返回的數據進行統一處理與封裝。之後將處理後的數據返回給路由,最終響應客戶端的請求。一個標準的請求與響應過程如下圖所示:

2、靜態文件與模板渲染

在這個樣本項目中,我采用了ejs模板引擎進行視圖模板的搭建。通過koa-static組件設置靜態資源目錄,同時使用koa-mount組件來將資源路徑映射到static 路徑下。

constapp=require( koa )();

conststaticServe=require( koa-static );

constpath=require( path );

conststaticDirectory=staticServe(__dirname+ /src/public );

app.use(mount( /static ,staticDirectory));

視圖文件統一放置於views目錄下,使用koa-ejs進行模板渲染,該組件支持layout布局和 ejs 的include模板嵌套語法。同時由於在之前已將靜態資源配置完畢,所以在ejs模板裏直接引入即可。

click me!

在大公司開發的過程中,往往還會將Node項目的View層再做抽離。目前比較流行的做法是用 React 或者 Vue 搭建一個純前端的單頁應用,但同樣也會帶來SEO問題,需要引入SPA下的SSR(服務端渲染)方案。

3、控製器與數據處理

控製器作為MVC框架的核心,在整個Node項目中起到了承前啟後的作用。每一個具體的controller.js文件,都是用來處理一種特定的請求。控製器會根據請求的不同,拉取對應的數據Model文件,之後對多路數據進行邏輯處理,並封裝成Http響應返回。

constipInfoModel=require( ../models/ipInfoModel );

constcityInfoModel=require( ../models/cityInfoModel );

constgetUserinfo=function(ip){

letresult={};

result.ipInfo=ipInfoModel.getIpInfo(ip);

result.dbCityList=cityInfoModel.getCityInfo();

returnresult;

};

4、數據獲取與數據庫操作

Node服務器的數據獲取操作是通過數據Model文件具體執行的。在美團點評的對外業務中,常見的數據獲取方式有兩種:

一種是通過我們的Model層直接調取後端Java的服務,這種方式在業務邏輯中使用較多。

另一種是調用公司配置管理平台提供的配置接口,這種方式在進行灰度測試中使用較多。

樣例中以第一種方案為例進行了實現,通過調用"淘寶"提供的公共api來模擬我們實際的開發場景。

constrequest=require( request-promise );

constENUM=require( ../common/ENUM );

constgetIpInfo=function*(ip){

constoptions={

url:ENUM.IP_INFO_SERVER,

method: GET ,

qs:{

ip,

},

json:true,

};

constres=yieldrequest(options);

returnres.data;

};

同時,在開發一期監控平台的過程中,項目主要由前端來開發。這就需要Node服務器跳過後端服務,直接對數據庫進行操作。在本文中也提供一種簡單的方案操作數據庫。

constmysql=require( mysql );

constthunkify=require( thunkify );

constutil=require( ../common/utils );

constgetCityInfo=function*(){

constpool=util.creatMysqlPool();

constconn=yieldthunkify(pool.getConnection).call(pool);

constquerySQL= SELECT * FROM `test_model_success` WHERE 1 ;

constresult=(yieldthunkify(conn.query).call(conn,querySQL))[];

conn.release();

returnresult;

};

5、服務器日誌打印

作為生產環境的項目,出了問題需要能迅速排查原因,因此增加服務穩定性監控和出錯記錄是必不可少的環節。目前,美團點評服務係統配套了專門的監控組件進行錯誤記錄與上報,而讀者也可以使用 PM2 或者 log4js-node 進行錯誤日誌輸出記錄,本文不在贅述。

這裏提一下樣例裏使用的一個Koa日誌中間件Koa-logger,該中間件可以在開發的過程中實時監聽請求,並在控製台實時打印日誌。啟用也十分方便,隻需在項目入口文件app.js裏配置"app.use(logger());"命令即可。其打印日誌效果如下圖所示:

四、不適合前後端分離的業務

商業產品,業務為主,永遠由業務來決定技術。同時,在軟件工程領域也不存在銀彈,沒有最好的技術,隻有最適合當下業務的技術方案。因此最後我們也列出部分不適合前後端分離的情況以供讀者參考:

簡單的內部管理係統,這樣的係統不需要前後端分離,否則隻會白白增加工作量。

對於無需頻繁更改的業務,也可以考慮使用更加傳統的開發方式。

對小公司或者創業公司來說,初期可以不用分的這麼細,畢竟使用越複雜的方式,對團隊成員的技術水平也會要求更高。

五、總結

前後端分離作為近年來的一個趨勢,在美團點評各條業務線已被廣泛采用。我根據自己的理解結合點餐團隊的具體業務對我們的分離方式進行了總結。

關於本文

作者:@蔣歡

最後更新:2017-08-27 23:10:48

  上一篇:go 七夕餐廳如何提高營收?美團點評《浪漫經濟大數據報告》為你解答
  下一篇:go Trustdata最新數據顯示,美團外賣APP奠定領先地位