蘇寧的Node.js實踐:不低于Java的渲染性能、安全穩(wěn)定迭代快

作者 | 禹立彬編輯 | 薛梁前端 Node.js 的使用場景大多集中在前端工具上,當(dāng)前的前端主要把它定位為輔助。蘇寧易購使用 Node.js 作為前后端分離的主要手段,經(jīng)歷了從技術(shù)引進(jìn)到全面開花,從邊緣功能到核心業(yè)務(wù),從紛亂到穩(wěn)定的過程。同時 Node.js 作為新引入的技術(shù),與公司原有架構(gòu)融合銜接面臨著怎樣的挑戰(zhàn)?以下是蘇寧技術(shù)總監(jiān) 禹立彬老師在 7 月深圳 ArchSummit 全球架構(gòu)師峰會上的演講整理。(點擊“閱讀原文”查看 12 月 7 日北京 ArchSummit 架構(gòu)師峰會日程)

在蘇寧引入 Node.js 之前,蘇寧已經(jīng)有了成熟的技術(shù)架構(gòu)。

蘇寧的技術(shù)架構(gòu),由蘇寧云、基礎(chǔ)支撐、后臺、中臺和前臺組成。蘇寧云主要為業(yè)務(wù)開發(fā)提供云服務(wù)。基礎(chǔ)支撐,包括數(shù)據(jù)連接協(xié)議、防火墻、日志、中間件、短信等。在蘇寧云和基礎(chǔ)支撐之上,業(yè)務(wù)開發(fā)分為前中后臺。而 Web 前端,主要集中在前臺上。包含 PC 端、移動 WAP 端等。

Node.js 的應(yīng)用非常廣泛,在不同的公司,可以用作微服務(wù),也可以用來提供 API,蘇寧引入 Node.js,最主要的,是用 Node.js 做中間層,當(dāng)做一個 Web 渲染器,渲染頁面,來實現(xiàn)前后端分離。

Web 前臺系統(tǒng)

在前臺系統(tǒng)里,以前的開發(fā)模式,完全是 Java 技術(shù)棧。Java 系統(tǒng),分為 Java Service 服務(wù)器,和 Java Web 服務(wù)器,Java WEB 服務(wù)器讀取 Java Service 服務(wù)器提供的接口,通過 FTL 模板來渲染頁面。

引入了 Node.js 以后,蘇寧研發(fā)團(tuán)隊的目標(biāo)是使用 Node.js 替代 Java Web 服務(wù)器的渲染位置,使用 Node 模板,去替換 Java 模板,去除了模板文件誰寫這樣的模糊地帶,讓后端的 Java 工程師,只寫 JSON 服務(wù),實現(xiàn)前后端分離。

在應(yīng)用前后端分離后,顯式的獲得了一些好處,Node.js 系統(tǒng)的迭代速度優(yōu)勢明顯強(qiáng)于 Java Web,包括由于 Node.js 的輕量,帶來的快速開發(fā)快速迭代,以及減少了前后端溝通上的“聯(lián)調(diào)”時間成本,加快了項目的開發(fā)速度。同時,減少老項目里 Java 后臺工程師寫頁面導(dǎo)致的一些 BUG,提高了代碼質(zhì)量。

由于以上的這些優(yōu)點,現(xiàn)在蘇寧易購的 Node.js 項目越來越多,逐漸深入到核心業(yè)務(wù)。最早期,蘇寧只是在用戶體驗收集這樣的邊緣頁面使用 Node.js,現(xiàn)在已經(jīng)在海外購,小程序,大聚惠,我的易購,香港站,購物車等業(yè)務(wù)中都廣泛的使用了 Node.js,這樣就不可避免的直面更多的技術(shù)挑戰(zhàn)。

Node.js 如何融入已有技術(shù)架構(gòu)

當(dāng)只是作為邊緣業(yè)務(wù)時,Node.js 項目尚可以通過一些臨時方案,或者引入試點,來逃避,但是深入核心業(yè)務(wù)后,Node.js 項目很快,就會被納入總的技術(shù)架構(gòu)里。

這是一張比較簡單的 Node.js 應(yīng)用部署圖。一個 Node.js 應(yīng)用被訪問時,會使用公共的負(fù)載均衡,使用應(yīng)用防火墻,當(dāng)達(dá)到 Node 服務(wù)器時,要使用物理機(jī)和虛擬機(jī),Node 服務(wù)器要訪問 Java Service 服務(wù)器也需要連接協(xié)議

最簡單的是 IaaS,在這個層級上,Node 可以完全可以復(fù)用蘇寧云成熟的網(wǎng)絡(luò),存儲,物理機(jī),虛擬機(jī)等資源。

image

到了虛擬機(jī)這個層級,在這幅 Node.js 服務(wù)器圖上,可以看到單臺 Node 服務(wù)器,有一個 Nginx,來做訪問的 accessLog 和做反向代理到本機(jī)的 Node.js 應(yīng)用端口,這臺機(jī)器上同時安裝了 PM2,來啟動多個 Node 應(yīng)用,對應(yīng)不同的端口,來提供對外服務(wù)。

image

在 PaaS 這個層級上,蘇寧也盡量沿用了公司已有的技術(shù)資源。比如在操作系統(tǒng)方面,使用了 RedHat Linux,盡量向已有技術(shù)架構(gòu)靠攏。

在服務(wù)器的 Node.js 版本上,在去年年初的版本是 Node 6.9,去年年底已經(jīng)將 Node 版本升級到 Node 8 了,研發(fā)團(tuán)隊堅持使用 LTS 版本的 Node.js。到 Node 有大版本更新時,同時更新 PaaS 平臺的 Node 版本。

在 Nginx 上,選擇的也是已有的通用 Nginx 版本,Node 服務(wù)器對 Nginx 版本要求不嚴(yán)格,Nginx 監(jiān)聽多域名的 80 端口后,反向代理到 Node 端口就好。

Redis 的情況要麻煩一些,Node.js 由于進(jìn)程的原因,遇到 Session 這樣需要多進(jìn)程或者多服務(wù)器直接共享數(shù)據(jù)時,就必須借助 Redis。很顯然,Java 體系內(nèi),并沒有對應(yīng)的 Node 版本的 Redis 客戶端,于是蘇寧自己編寫了一個基于 ioredis 的 Redis 客戶端,來滿足需求。

在 DB 上,蘇寧則遵循總的技術(shù)架構(gòu)要求,Node 服務(wù)器不直連 DB,要獲取數(shù)據(jù)時,永遠(yuǎn)是連接 Java 服務(wù)。

監(jiān)控報警

解決了服務(wù)器環(huán)境后,Node 也要接入日志服務(wù)和報警系統(tǒng)。通過配置 Nginx 日志格式和 PM2 的日志插件 pm2-logrotate 來將日志格式符合總技術(shù)架構(gòu)的日志平臺要求,并在日志平臺上配置 4XX 和 5XX 報警,并且針對 Node 本身的一些特色,編寫 PM2 插件,監(jiān)控 Node 進(jìn)程異常,并發(fā)送異常到蘇寧內(nèi)部的即時通信團(tuán)建上。

CI/CD 發(fā)布系統(tǒng)

服務(wù)器好了,代碼編寫好了,也需要發(fā)布。利用公司的統(tǒng)一發(fā)布平臺,在平臺上新建了 Node.js 標(biāo)準(zhǔn)發(fā)布,統(tǒng)一了 Node 代碼包打包方案,統(tǒng)一了代碼部署目錄,統(tǒng)一從內(nèi)部私庫安裝 NPM 包,統(tǒng)一了應(yīng)用重啟的方法。

除此之外,為了滿足公司的總技術(shù)架構(gòu)要求,蘇寧研發(fā)團(tuán)隊還編寫了基于 Node 的調(diào)用鏈監(jiān)控組件,可以適配 ESB/RSF 通信協(xié)議的客戶端組件,以及適合 Varnish 下的 KOA,EXPRESS 中間件。

技術(shù)挑戰(zhàn)

解決了技術(shù)架構(gòu)要求方面的問題,Node 可以正規(guī)軍上崗了,核心業(yè)務(wù)又會帶來更高的技術(shù)要求。

以大聚惠頁面為例,大聚惠是蘇寧的一項營銷業(yè)務(wù),很多優(yōu)惠活動,是通過大聚惠的名義放出的,因此業(yè)務(wù)非常重要。又因為運營需要,總是在凌晨 0 點,貨品上新,要求此時頁面不能有緩存,而且由于電商業(yè)務(wù)的特殊性,會遇到 618,818,雙十一這種半夜搶購的情況,因此全靠服務(wù)器硬扛流量洪峰,這就對應(yīng)用性能,提出了很高的要求。

這個項目上線的時候,恰好是升級了 Node 8,研發(fā)團(tuán)隊把框架從 express 轉(zhuǎn)換為 KOA,寫了一樣的代碼,壓測時,4C4G 單機(jī)才 40TPS,而同樣的 Java 系統(tǒng),單機(jī)約為 200TPS,性能差距明顯。

并發(fā)性能優(yōu)化之路

首先考慮是緩存問題,經(jīng)過排查,在 KOA 下,并不會默認(rèn)開啟,模板緩存,導(dǎo)致每次總是去硬盤讀模板,當(dāng)然快不了。果斷優(yōu)化,TPS 上升為 120TPS,依然差距明顯。Express 框架中會默認(rèn)開啟模板緩存,而默認(rèn)的 KOA2 并不會開啟,首先開啟模板緩存性能提升明顯。

另外,通過 CPU-PROFILER 排查,發(fā)現(xiàn)路由消耗的時間挺多,匹配字符串路由,和匹配正則路由,時間消耗差距明顯,于是將路由排序,優(yōu)先選擇字符串路由,將 TPS 推向 140TPS。

再查,為了減少并發(fā),除渲染模板外,Node 還在業(yè)務(wù)里,合并了靜態(tài)資源地址,并且中間件加載的策略也可以優(yōu)化,去除掉一些可以不用的中間件。比如只用 EJS 模板,那么就去除其他模板引擎的支持,通過這些優(yōu)化,講 TPS 提升到了 180TPS。

TPS 沒有提升后,再排查,發(fā)現(xiàn) include 函數(shù)執(zhí)行時間很長,發(fā)現(xiàn) ejs 源碼處理 include 時,總是去硬盤里查找是否有被 include 的模板文件,而這個頁面是多人開發(fā),include 使用非常頻繁,再進(jìn)一步優(yōu)化,終于達(dá)到了 220TPS。

不低于 Java 的渲染性能

最后,獲得了不低于 Java 的渲染性能。很多文章博客對待 Node 渲染頁面時會兩級分化,一極認(rèn)為 Node 不適合做這種 CPU 密集型操作,另一極就是不斷宣揚 Node 性能強(qiáng)勁,但是在蘇寧實際的應(yīng)用中,從來沒想過超過 Java 多少倍的性能,事實上也是。Node 的基礎(chǔ)性能略高于或者持平于老的 Java FTL 渲染器。

安全與穩(wěn)定

除了性能,安全與穩(wěn)定也是重點需要的環(huán)節(jié)。針對 Node.js 來說,客觀的說,安全文檔方面是不如 Java 等語言的。

一方面是最后后來者,追趕前輩需要時間,另一方面也是 Node.js 的固有問題。Node.js 是單線程的,代碼報錯會導(dǎo)致進(jìn)程退出,在 Node 生態(tài)早期,會直接導(dǎo)致 Node 訪問掛掉;

第二個,JS 是弱類型語言,很多人認(rèn)為弱類型的語言寫的沒有安全感,沒有代碼檢查;

第三個,NPM 很好,開源社區(qū)很強(qiáng)大,組件很多很方便,但是對于企業(yè)用戶來說,都抵不過一個 left-pad 事件,公網(wǎng)的 NPM 包也會良莠不齊,如果出現(xiàn)了安全漏洞,影響就會非常大。

問題如何解決

NPM 包策略。蘇寧使用公司的私有 NPM 倉庫來安裝 NPM 包,避免外網(wǎng)擾動,導(dǎo)致無法安裝問題。在核心業(yè)務(wù)中,限制使用不流行的 NPM 包,減少風(fēng)險。在 package.json 里的包版本,使用確定的版本,不用符號,減少包升級導(dǎo)致的 bug。對于自己開發(fā)的 NPM 包,嚴(yán)格進(jìn)行單元測試及安全測試,進(jìn)一步的減少風(fēng)險。

使用 PM2。針對 Node 進(jìn)程掛掉的問題,蘇寧使用了留下的 PM2,來保證 Node 進(jìn)程的存活。當(dāng) Node 進(jìn)程掛掉時,PM2 會重啟他們。感謝 PM2,通過它,也實現(xiàn)了發(fā)布的無縫重啟,保證了平滑升級。

Node.js 系統(tǒng)的相對安全

安全上,蘇寧強(qiáng)制了所有的 Node 系統(tǒng)加入應(yīng)用防火墻 WAF,使用基于 KOA 的安全中間件 XSS,盡量使用精確匹配的路由,減少注入。并在上線前,做完全的安全測試,實現(xiàn) Node 系統(tǒng)的相對安全。

前端團(tuán)隊的挑戰(zhàn)

另一方面,由于引入了 Node,前端工程師對 Node 相關(guān)的知識了解較少,也會犯一些低級錯誤,技術(shù)挑戰(zhàn)也是非常大的,知識要求被增加了很多。

為了解決這個問題,蘇寧成立了專門的前端架構(gòu)組,為各業(yè)務(wù)團(tuán)隊保駕護(hù)航。在發(fā)布,配置,安全監(jiān)測等各個方面幫助業(yè)務(wù)開發(fā)團(tuán)隊。

全棧技能提升計劃

并在工作中,加強(qiáng) Node 技能培訓(xùn)。梳理出容易犯的低級錯誤,比如 promise 不寫 catch,某個條件分支里,不寫請求返回,通過宣講的方式,提高代碼質(zhì)量,并組織代碼評審等活動,進(jìn)一步的提升技術(shù)能力。

Node.js 的影響

可以說,進(jìn)入了核心業(yè)務(wù),前端團(tuán)隊遇到的挑戰(zhàn)是越來越大的,同時,Node 的推進(jìn)也帶來了一些正面負(fù)面的影響,時間有限,不做太多的講解,僅舉幾個方面。

第一個方面,項目更敏捷了,Node.js 發(fā)布不涉及后臺服務(wù),即使發(fā)布出了小問題,也可以快速再次發(fā)布和回滾,因為 Node.js 系統(tǒng)其實是可以 24 小時發(fā)布,對業(yè)務(wù)支撐顯然更迅速敏捷。運營商務(wù)。都顯然的歡迎 Node。

同時,Node 的引入,也對前端團(tuán)隊帶來了影響。Node 的引入帶來了前端工作量的增加,需要更多的前端工程師投入。

另一方面,也顯著的提高了團(tuán)隊的技術(shù)活力,在團(tuán)隊內(nèi)部刮起了全棧風(fēng),技術(shù)更活躍,解決問題的方案也更多了。

最后還有一些小型的負(fù)面影響,定位 bug 時,時間有所增加。需要查 Node 的問題,還是 Java 的問題;另一方面,訪問性能因為 HTTP 的問題,略微增加了幾毫秒。當(dāng)然這對于前面的好處來講都是可以接受的。

嘉賓介紹

禹立彬,蘇寧技術(shù)總監(jiān),十年 Web 前端開發(fā)經(jīng)歷,中國最早一批前端開發(fā)者,歷任西祠胡同前端負(fù)責(zé)人,途牛旅游網(wǎng)前端架構(gòu)師等職務(wù)。現(xiàn)任蘇寧消費者平臺研發(fā)中心前端技術(shù)總監(jiān),負(fù)責(zé)蘇寧易購網(wǎng)站前端領(lǐng)域的技術(shù)管理工作。在基于 Node.js 的前后端分離,ReactNative/Weex 開發(fā)上有豐富的技術(shù)實踐經(jīng)歷。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Node全棧技術(shù)開發(fā)介紹 node和js介紹 node服務(wù)端開發(fā) node前端vuejs node前端reactj...
    燕京博士閱讀 3,676評論 1 19
  • Node.js是目前非常火熱的技術(shù),但是它的誕生經(jīng)歷卻很奇特。 眾所周知,在Netscape設(shè)計出JavaScri...
    Myselfyan閱讀 4,089評論 2 58
  • 作為一個在Sun微系統(tǒng)公司Java SE團(tuán)隊工作了十多年的人,難道不應(yīng)該是體內(nèi)流淌著Java字節(jié)碼的血、只要一息尚...
    Java架構(gòu)學(xué)習(xí)者閱讀 2,571評論 4 17
  • 周末參加了高中同學(xué)的大聚會,這種十年一聚的聚會,總的來說很開心。不過過程有點像電視劇情節(jié),有人根本不想來,有人覺得...
    仲林_dde2閱讀 454評論 0 0
  • 小時候家風(fēng)對于我來說是一種痛! 雖長于鄉(xiāng)野,奈何祖上還是有一定榮光的,只是后來家道中落。(曾祖是名成功的...
    唱著歌走路閱讀 1,120評論 3 3