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

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->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:

{  
  "data": {    
    "city": {      
      "ID": 1,      
      "name": "北京",      
      "Code": "BJS"
    }  
}
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的使用場景,使其發揮更大的價值。

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章