天天看點

好未來資料中台 Node.js BFF實踐(一):基礎篇

好未來資料中台 Node.js BFF實踐系列文章清單:
  • 基礎篇
  • 實戰篇(TODO)
  • 進階篇(TODO)

好未來資料中台的Node.js中間層從7月份開始讨論可行性,截止到9月已經支援了4個平台,其中3個平台生産環境穩定,另1個在測試階段近期上線。

我4月份剛加入資料中台,原本的想法是半年内不做大刀闊斧的改變,優先完善團隊現有的基建設施,比如元件庫、charts庫、工具、規範等。Node.js中間層的立項完全是一個意外。

某次中台周例會上讨論到前後端協作效率問題,我一時嘴賤提到Node.js中間層的想法,将應用平台的一些與DB無關的邏輯放到Node層讓前端夥伴們負責,讓後端夥伴集中精力做底層服務建設。

本來就随口一提,結果老闆當真了,當場拍闆:搞!我當時的表情大概是...

好未來資料中台 Node.js BFF實踐(一):基礎篇

沒辦法,自己畫的餅,哭着也要吃完。

咦?怎麼有點往日重制的感覺?曾經在騰訊雲,剛給客戶吹完牛逼就想抽自己大嘴巴~

Node.js 的定位

資料中台 Node.js 中間層的定位類似一層 API Gateway,承載接口代理、聚合以及與DB 無關的部分業務邏輯。

在現階段資料中台的服務體系中有兩類服務:正常 Java 後端和 T-Service 。後者是資料中台将 OneService 方法論落地的統一資料服務,即服務于各個前台事業部,也為資料中台内部的各個應用平台提供資料服務。

使用 T-Service 的協作流程簡單描述就是數倉夥伴建表後将資料源接入 T-Service,然後 Java 後端夥伴配置取數 SQL,最後前端從統一的 query 接口查數展示。

T-Service 不直接對接前端,舊架構體系下需要在前端與 T-Service 之間搭建一層 Java 服務,說白了就是一堆 Controller,從 T-Service 取數後做一些很簡單的二次加工給到前端。Node.js 中間層的目标之一就是将這些複用性很差的 Controller 拿過來,好處有兩點:

  1. 舊架構體系下完成一個資料查詢功能需要牽涉數倉、後端和前端三方,新架構下隻需要前端和數倉;
    前提是前端夥伴需要掌握 SQL,前期由後端同僚輔助,過渡後需要前端夥伴自己寫 SQL。
  2. 基于第一條, Java 後端夥伴的生産力被解放,集中精力做底層建設或通用性更強的接口。

除了以上兩條以外還有另一個隐藏優勢,前端的能力邊界擴寬後技術視野也會更寬闊,能夠從縱向角度上全面思考業務。

好未來資料中台 Node.js BFF實踐(一):基礎篇

具體到職責配置設定上,Node.js 作為直接與用戶端互動的服務層,登入認證是最基本的功能之一,Java 後端服務隻需要關注 Node.js 傳遞的使用者 ID 即可。

資料中台有一個統一的使用者管理中心提供登入/登出服務,用戶端登入後會接收管理中心下發的 JWT,後續業務接口的請求會驗證 JWT 的有效性。接入 Node.js 中間層以後,JWT 的驗證邏輯就上浮到了 Node 層。

由于 Node 層隻需要驗證(解密) JWT,不需要 JWT 加密算子,是以非對稱加密是相對較優的方案,比如 RS256。使用者中心提供一個擷取公鑰的 API,Node.js 拿到公鑰後進行解密即可。

好未來資料中台 Node.js BFF實踐(一):基礎篇

技術選型

技術選型方面并沒有糾結太久,其實用什麼架構都行,雖然每個架構都有各自的優缺點,但由于 Node.js 中間層的邏輯不會很重,複雜度不高,架構層面的問題幾乎不會成為瓶頸。是以一句話總結技術選型的核心出發點就是:用着舒服就行了。

最終用的 NestJS v7,當然也并不是完全沒有量化的因素。首先跟 express 和 koa 相比,NestJS 的子產品抽象層次更高,将中間件進一步抽象為 guards 、 filter 、 interceptor 等等,能夠滿足大多數場景,幾乎不需要感覺中間件這個概念。雖然有一定的了解門檻,但熟悉之後寫代碼能夠将各子產品的功能劃分更加清晰容易維護。其次,NestJS 與 Express 完全相容,生态足夠豐富。最後,完美支援 TypeScript,搭配 DI 、IoC 等機制,代碼的結構和子產品體系非常清晰。

之是以選了 v7 而沒有用最新的 v8 版本,原因之一是 NestJS 的 v8 版本依賴 RxJS v7。RxJS v7 廢棄了很多 v6 版本的操作符,用慣了 v6 一時之間切換過來很不習慣。

不過 NestJS 也并不是沒有缺點。舉個例子,Node.js 通常使用 async hooks 進行異步資源跟蹤,比如日志。NestJS 的依賴注入機制提供了一種 Request 作用域的 Provider,表面上看完全可以解決請求上下文的資源共享,但實際上并不好用,因為 NestJS 對 Request 作用域的 Provider 有一條額外的限制:依賴 Request 作用域 Provider 的 Provider 也必須是 Request 作用域的(很拗口吧)。由于日志子產品是通用子產品,被很多子產品依賴,是以在這條限制下,從 app scope 到 module scope,幾乎每個 Provider 都會被牽涉。是以最後還是用了正常方案:cls-hooked。

也有可能是我學藝不精,還沒掌握 NestJS 的精髓,歡迎指正。

服務治理

Node.js 服務部署在公司的未來雲 k8s,上層沒有接網關,是以 https 的支援是在由 ingress 這一層提供,目前的方案比較原始,沒有自動化工具,需要手動修改 ingress 配置。除此之外,在服務治理方面需要重點關注兩個方面:守護程序和日志管理。

守護程序

在 k8s 普及之前,Node.js 的程序守護需要借助一些第三方工具,比較知名的比如 forever 和 pm2。使用這些工具會占用一些額外的機器資源(cpu、記憶體),借助 k8s 探針完全可以取代它們。

未來雲提供了兩種存活檢查的探針:Http 和 TCP。

Http 探針本質上是向某個接口發起 Get 請求,響應成功狀态碼代表服務健康,否則判定為壞死重新開機 pod。對于 Node.js 來說就相當于一次請求,是以需要 Node.js 提供一個專用的接口比如

/health

,需要額外工作,并且這個接口不應該記錄日志。

TCP 探針本質上是嘗試與容器建立 socket 連接配接,成功代表服務存活,否則判定為壞死重新開機 pod,對 Node.js 服務本身沒有依賴和影響。

Http 探針由于發起的是一個真實請求,是以通用性更強,但是需要額外的工作。

如果 Node.js 上層有額外的一層反向代理比如Nginx,那麼一定不要使用 TCP 探針。因為 Nginx 本身就能夠建立 TCP 連接配接,是以如果用 TCP 探針的話檢測結果永遠是健康的。

資料中台的 Node.js 服務每個 pod 都是單核,沒有起多程序,也就沒有使用反向代理的必要性,是以最終使用 TCP 探針做存活檢測。

好未來資料中台 Node.js BFF實踐(一):基礎篇

日志管理

與 Java 服務的日志一樣,Node.js 服務的日志也是 ELK 一把梭。需要注意兩點:

  • 與 Java 後端的日志串聯。

    Node.js 與 Java 後端約定一個日志串聯的規範,Node.js 向 Java 發起的請求頭中攜帶一個額外字段

    x-trace-id

    ,值為 Node.js 生成的 requestId。
  • 過期日志檔案及時清理。

    Node.js 的日志檔案以天為機關分割檔案,每天都會建立幾個單獨的檔案(errors/warnsing/infos/expcetions),如果不及時清理的話會把磁盤打爆進而造成服務重新開機,是以需要添加一個定時任務清理過期檔案。

好未來資料中台 Node.js BFF實踐(一):基礎篇

總結

現階段的 Node.js 中間層剛起步,還比較輕量,以後還會遇到更多挑戰。本文簡單記錄了一些搭建過程中的經驗,有正面的可能也有反面的,歡迎指導。

下一篇會寫一下目前接入的幾個項目中 Node.js 中間層扮演的角色和具體做的事情,敬請期待。