從前後端分離到GraphQL,攜程如何用Node實現?\n

weixin_33763244發表於2019-01-23

Nodejs自從2009年被開發出來以後,至今已經走過了9個年頭,目前最新的穩定版已經到了10.13。從問世以後,Nodejs就深受前端工程師的喜歡。

在攜程內部,Nodejs也是應用廣泛,從開發工具到web應用,從客戶端到服務端,都能見到它的身影。我們也從最初用Node.js來完成前後端的架構分離到最近使用GraphQL來做微服務,機票部門在Node.js的應用探索上越走越寬。

一、前後端分離

在機票事業部前端開發的web1.0時代,整個前後端程式碼耦合在一起,採用的是典型的服務端 MVC架構。

\"\"

在這樣的開發模式下,會存在一些問題和痛點:

1)前後端程式碼耦合在一起,維護成本比較大,前端的同學不熟悉服務端開發語言,服務端同學也不熟悉前端的互動;

2)展示邏輯和業務邏輯混在在一起,前後端開發同學的職責不明確,有些需求前端說這個邏輯在view層,應該後端改,後端說,前端做相容處理;

3)專案的擴充套件性比較低,維護性差,迭代速度慢。

在傳統的MVC模式中,由於view層所承載的內容過多,導致view層這一塊和前端的耦合太多,整體開發效率低下。

能否將這個剝離出來,讓前後端集中力量關注自己的領域呢?答案是肯定的,我們將客戶端和服務端隔離開,服務端負責資料聚合,提供標準的restful介面,前端負責資料渲染。

在機票H5實踐前後端分離過程中,我們改進了技術架構,在前端的應用層,採用PM2+Node.js(8.9.4)+Express(4.0)框架,內部基於攜程基礎框架ctriputil,同時對一些常用功能的封裝,如Redis的呼叫,ABTest的獲取,Qconfig的整合。

\"\"

為什麼選擇Nodejs呢?

1)Nodejs採用的是V8引擎,執行的是javascript程式碼,對於前端同學來說,學習成本低;

2)Nodejs是事件驅動的,非阻塞性I/O,非常適合對於前端這種IO密集型的應用;

3)社群活躍度高,有大量的庫可以被使用;

4)NPM生態圈內容豐富;

5)客戶端程式碼和應用端可以共享模版和部分邏輯,適合瀏覽器及服務端程式碼共用。

在客戶端這一層,選用vue.js ,依託於公司的lizard.lite框架,採用wbebpack作為構建工具,並通過結合UT來提升開發質量。

在vue的使用上採用Vuex進行狀態管理,用Vue Router進行路由管理以及用Lizard.lite進行model層管理(如資料獲取、轉換、快取、日誌記錄、環境切換等)。對於基礎資料資訊採用Localstore進行本地持久化儲存,對於狀態資料採用Sessionstore進行管理,確保狀態在當次session中是有效的。

自動化程式碼整合方面我們採用ESlint\\TSlint做一些基本的語法檢查,同時使用mocha進行單元測試,確保開發質量,同時按controller\\model\\fue進行分層,確保每個模組之間相對獨立。

整個改造後的架構具備以下特性:

模組化:ES6 import + System.import + vue單檔案元件;

單頁路由控制: vue-router + async component;

伺服器通訊: 同構的businiess model(LizardLite.AbsModel);

狀態管理: vuex store;

程式碼質量: standardjs + eslint + mocha + chai;

構建釋出: webpack dev server + npm scripts + html-minifier/uglify js/clean css;

整個機票H5預訂流程採用單頁+SSR模式進行開發,獲得了APP-LIKE式的體驗。

針對直接Landing頁面,採用APPSHELL進行服務端載入骨架,提升首屏可視載入速度,對非Landing頁面採用SPA模式,提升後續頁面載入速度流暢度,對於搜尋引擎的爬蟲,會自動識別並進行服務端渲染,做到客戶端和服務端程式碼複用。

為降低每個頁面的資源載入耗時,會對頁面資原始檔進行拆分和後續頁面資源的預載入,同時利用大資料進行使用者行為的預測以及介面資料預處理,使得頁面速度的載入耗時得到比較大的提升。

二、Node.js與restfulAPI

在採用Node.js來完成前後端分離後,整個前臺的架構分為三大塊,一個是以瀏覽器渲染為主的客戶端,二是Node.js為主的應用端,三是前臺的資料聚合層,在前臺的資料聚合層採用JAVA作為主要開發語言,對接後臺底層的介面。

在2016年以來,機票前臺開發組開始推行敏捷開發,採用scrum的模式進行敏捷管理,並組建比較多的敏捷團隊,由於有些敏捷團隊比較小,人數相比較少,團隊中經常客戶端、服務端都只有1個或者2個同學,如果遇到一個專案是服務端邏輯比較多的時候,服務端資源會爆掉,在遇到改版類專案時,前端資源會爆掉,但由於前後的技術棧不統一,團隊內部開發資源相互協調起來比較困難。

如何讓團隊的效能發揮到最大是我們一直在思考的問題,於是我們在scrum團隊嘗試技術棧統一,將前臺的資料聚合層改為用Node.js來實現,使得整個團隊內部以前端開發工程師為主。

\"\"

整個Node層的架構和H5應用層類似,也是採用PM2+Node.js(8.9.4)+Express(4.0)+CtripUtil,為了提供標準的restfulAPI,我們在服務入口做了自動化的註冊方式,方便客戶端接入;

\"\"

在Node層內部針對後臺介面的呼叫做了深度封裝,在使用上更加方便快捷,同時接入公司cat/clog等通用日誌系統。

\"\"

經過對服務端的改造以及技術棧的統一,整個團隊的效能也得到了提升,用Node.js實現的介面在上線後效能穩定,整體耗時控制在50ms以內。

三、RestfulAPI-\u0026gt;GraphQL

經過了前面用Node.js進行標準的restfulAPI開發嘗試,有越來越多Node.js實現的介面上線,整個前臺的架構如下:

\"\"

在經歷過幾個版本迭代之後,我們發現了一些新的問題:

1)不同版本的客戶端需求不同,相同的介面需要針對不同的版本做不同的處理;

2)不同的客戶端對於契約的需求也不一樣,比如PC由於螢幕尺寸的關係,在介面設計上給使用者的資訊要比APP多的多,PC與APP在顯示的資訊上是有差異的,相同的契約資料下發對於某一端來說會存在浪費,從而加大網路開銷,

3)在APP上也會存在著版本之間的差異,比如7.15的版本和7.16的版本,7.16上了一些新的功能,加了一些新的fetch,如果統一下發給前端,對於老的版本也是也是資源上的浪費,

4)客戶端在某些時候需要呼叫多個介面彙總資料一起顯示,某些情況下又要分開呼叫,對於服務來說,動態可擴充套件的架構尤為重要,

5)前端在model層使用的結構和服務端結構可能會存在差異性,如何磨平這些差異,也非常考驗開發同學的技術能力;

在這個時候,GraphQL進入到了我們的視野。GraphQL是一種新的API標準,它提供一種更高效、更加靈活的資料查詢方式,在2015年被Facebook正式開源。

其在本質上是一種基於API的查詢語言,是對restfulAPI的一種封裝,目的在於構建一種更加易用的服務,通過GraphQL,客戶端可以很方便的獲取所需要的資料。

比如下面的這個例子,獲取ID為1的城市資訊,只要返回的schema保護ID,name,Code即可,其中name為重定義schema。

Request:{    city: getCityInfo(id: 1) {        ID    name: Name    Code  }}Response:{    \u0026quot;data\u0026quot;: {        \u0026quot;city\u0026quot;: {            \u0026quot;ID\u0026quot;: 1,            \u0026quot;name\u0026quot;: \u0026quot;北京\u0026quot;,            \u0026quot;Code\u0026quot;: \u0026quot;BJS\u0026quot;    }  }Schema:module.exports = new GraphQLObjectType({    'name': 'CityInfo',    'description': '城市實體',    'fields': {        'Code': {            'description': '城市三字碼',            'type': GraphQLString    },        'ID': {            'description': '城市ID',            'type': GraphQLInt    },        'Name': {            'description': '城市名稱',            'type': GraphQLString    }  }})

GraphQL和傳統的restAPI相比:

1)資料獲取:GraphQL可以按需獲取,通過呼叫方指定schema返回不同報文,RestfulAPI則是下發相同的結構;

2)型別系統,強校驗:GraphQL使用Type System來定義API,公開的型別都是通過SDL模式進行編寫,統一前後端契約結構,便於使用;

3)URL入口:Rest不同的請求入口不同,在請求的URL上需要做區分,GraphQL則是一個入口(/graphql?query=),通過呼叫的request來區分;

4)呼叫方式:Rest獲取多個不同介面資料時,需要併發呼叫多次,而GraphQL可以合併查詢,降低網路開銷;

於是我們開始在團隊內部試點GraphQL,在技術架構上採用PM2+Node.js+Express+Express-GraphQL,選用Express-GraphQL作為核心中介軟體,統一客戶端的請求入口為/graphql?query=*,由呼叫端來決定自己需要哪些資料。

\"\"

四、總結

Node.js在機票團隊從早期的前後端分離到GraphQL的實踐,目前已經深度應用到前端組的各個模組,現在機票前端應用層已全部採用Node.js來實現。

有近20+介面採用Node.js來開發,其中一大半是通過GraphQL來實現,日均的流量在200W左右,整體Node服務端效能穩定,後續我們還將繼續拓寬Node.js的使用場景,使其發揮更大的價值。

更多內容,請關注前端之巔。

\"\"

相關文章