成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

蘇寧Nodejs性能優(yōu)化實戰(zhàn)

開發(fā) 前端
自 2016 年以來,蘇寧大規(guī)模的使用了基于 Nodejs 渲染的項目,架構(gòu)使用 Nginx+Nodejs+PM2 組合,其中 Nodejs 版本從最初的 6.0+ 升級到如今的 8.0+,Nodejs 框架從 Express 過度到 Koa2,而 Nodejs 的性能優(yōu)化作為其中的核心,蘇寧在其性能提升上,也從 0 到 1,開始摸索。

 [[228719]]

Nodejs 項目背景介紹

自 2016 年以來,蘇寧大規(guī)模的使用了基于 Nodejs 渲染的項目,架構(gòu)使用 Nginx+Nodejs+PM2 組合,其中 Nodejs 版本從最初的 6.0+ 升級到如今的 8.0+,Nodejs 框架從 Express 過度到 Koa2,而 Nodejs 的性能優(yōu)化作為其中的核心,蘇寧在其性能提升上,也從 0 到 1,開始摸索。

初步優(yōu)化—css、js 注冊與合并

ejs 模板相關(guān)優(yōu)化

在蘇寧的 nodejs 項目中,剛開始使用 express 框架,后來隨著 node.js 8.0 LTS 版本的發(fā)布,又開始使用 kos 框架。無論是 express 還是 koa 框架,蘇寧在項目開發(fā)中都使用 ejs 模板語言(關(guān)于 ejs 模板語言這里就不多做介紹,有興趣的同學(xué)可以自行搜索)。

 合并 css 和 js 帶來的性能損失

在使用 ejs 模板過程中,蘇寧把公共部分抽出來為 layout.ejs 文件,頁面模板通過 ejs include 方法在 layout.ejs 引入,例如: 

  1. //layout.ejs 
  2. <link type="text/css" rel="stylesheet" href="public.css" /> 
  3. <script src="public.js"></script> 
  4. ... 
  5. include(page1); 
  6. ... 
  7.  
  8. //page1.ejs 
  9. <link type="text/css" rel="stylesheet" href="page1.css" /> 
  10. <script src="page1.js"></script> 
  11. <h1>hello</h1>  

這樣做解決了公共部分與頁面業(yè)務(wù)邏輯的分離,但是也帶來另一個問題 --layout 模板和 page1 模板中靜態(tài)資源標(biāo)簽位置的問題,以下是渲染過后返回給客戶端的 html 頁面: 

  1. ... 
  2. <link type="text/css" rel="stylesheet" href="public.css" /> 
  3. <script src="public.js"></script> 
  4. </header> 
  5. <body> 
  6. <div class="header"></div> 
  7. <link type="text/css" rel="stylesheet" href="page1.css" /> 
  8. <script src="page1.js"></script> 
  9. <h1>hello</h1> 
  10. </body> 
  11. ...  

我們可以看到 page1 的靜態(tài)資源引用標(biāo)簽都在 body 內(nèi),復(fù)雜的頁面可能還會有 page2、page3、pageN... 這樣會有大量的靜態(tài)資源引用標(biāo)簽出現(xiàn)在 body 內(nèi),這顯然不符合我們的預(yù)期,我們需要控制靜態(tài)資源標(biāo)簽在頁面中的調(diào)用位置,為了解決上面的問題,蘇寧引入了 ejs 模板靜態(tài)資源 register 機制,其注冊步驟如下:

a. 使用 getResource() 方法輸出占位符。

b. 使用 register() 注冊方法注冊資源,例如:register('a.css', 'b.js')。

c. 將注冊的靜態(tài)資源處理合并后進行字符串 replace 操作。

使用 register 方法后 ejs 模板渲染過后的 html 頁面如下: 

  1. ... 
  2. {{{CSS_PLACEHOLDER}}} 
  3. </header> 
  4. <body> 
  5. <div class="header"></div> 
  6. <h1>hello</h1> 
  7. </body> 
  8. {{{JS_PLACEHOLDER}}} 
  9. ... 

“{{{CSS_PLACEHOLDER}}}”和“{{{JS_PLACEHOLDER}}} ”就是getResource()輸出的占位符,在服務(wù)器response之前進行字符串replace操作,將占位符替換成register()方法中注冊的路徑: 

  1. ... 
  2. <link type="text/css" rel="stylesheet" href="public.css" /> 
  3. <link type="text/css" rel="stylesheet" href="page1.css" /> 
  4. </header> 
  5. <body> 
  6. <div class="header"></div> 
  7. <h1>hello</h1> 
  8. </body> 
  9. <script src="public.js"></script> 
  10. <script src="page1.js"></script> 
  11. ...  

這樣就符合了正常的頁面靜態(tài)資源引入位置,同時蘇寧在 register() 方法做路徑合并的功能,合并后的地址路徑如下: 

  1. … 
  2. <link type="text/css" rel="stylesheet" href="public.css,page1.css " /></header> 
  3. <body> 
  4. <div class="header"></div> 
  5. <h1>hello</h1> 
  6. </body> 
  7. <script src="public.js, page1.js "></script> 
  8. ...  

這樣瀏覽器中發(fā)起的請求就會少很多,減少頁面請求也是性能優(yōu)化的一個點。

 緩存機制

使用 register 機制后我們又發(fā)現(xiàn)了一個問題,當(dāng)客戶端每一個 request 請求發(fā)起,nodejs 服務(wù)在響應(yīng)之前都會進行字符串查找替換, 如果頁面夠復(fù)雜,最終渲染生成的字符串足夠大,每一次進行字符串查找替換的過程中也造成了一定的性能損耗。正常在實際的使用中我們多次訪問一個路由地址,其頁面引用的靜態(tài)資源并不會發(fā)生變化。利用這個特性蘇寧引入了靜態(tài)資源緩存機制。

當(dāng)一個新的頁面請求進來之后,在執(zhí)行 register 方法之前,會根據(jù)頁面請求地址的 pathname 進行緩存查找,如果命中緩存,則 getResource() 直接返回緩存內(nèi)容,相應(yīng)的 regsiter 方法也不會去執(zhí)行。否則執(zhí)行 register() 流程。引入緩存機制后,非第一次訪問代碼邏輯中少了注冊、替換流程,相應(yīng)的頁面響應(yīng)時間也縮短了,經(jīng)過多次測試,頁面響應(yīng)時間大概縮短 4-8ms。

進階優(yōu)化—大量路由的優(yōu)化匹配

在開發(fā)蘇寧易購香港站過程當(dāng)中,由于整站頁面較多、參數(shù)開發(fā)人員眾多及基于項目安全性的考慮,項目開發(fā)中配置了多達(dá) 173 條靜態(tài)路由以及 11 條動態(tài)路由,所以路由匹配效率明顯下降。究其原因,得從 express 源碼入手,express 框架在處理路由配置的方法是,將每一條配置信息轉(zhuǎn)換成一條正則表達(dá)式,在請求進入的時候,逐條進行匹配,直到匹配成功為止。

對于動態(tài)路由——路由中含有模糊匹配,則必須使用正則表達(dá)式來進行匹配,無法優(yōu)化。而對于靜態(tài)路由,就是固定的字符串的路由表達(dá)式,則可以通過鍵值對映射進行匹配,復(fù)雜度從 O(n) 變成了 O(1) 大大縮短匹配時間,且不會隨著路由增加而耗時加長。在實際代碼中,由于架構(gòu)采用了集中路由配置,所以很方便的從配置文件里面就篩選出了靜態(tài)路由,然后存放在一個 Object 中(HashMap)。然后形成一個中間件形式,相當(dāng)于把多條路由中間件變成了一條路由中間件。

缺陷:和原來的邏輯相比,優(yōu)化后的方案缺少了路由匹配的順序,所以在開發(fā)的時候需要額外注意,不過總體來說影響甚微,因為靜態(tài)路由優(yōu)先匹配,也是應(yīng)該優(yōu)先響應(yīng)的。

高階優(yōu)化—TPS 的提升

在蘇寧易購大聚惠系統(tǒng)的前后端分離中,初次提交壓力測試結(jié)果非常差。懷疑有什么配置沒有配好,當(dāng)時的數(shù)據(jù)是這樣的(16 臺 4C4G):

TPS 低的不能忍,而且當(dāng)時已經(jīng)配備了 Node.js 8.9.1 這個版本,理論上絕不可能那么差,在觀察代碼,也沒有發(fā)現(xiàn)特別消耗性能的地方。最后我們找到了原因,在 ejs 模版配置的時候沒有開啟模版緩存導(dǎo)致。如果不開啟模版緩存那么每次請求渲染的時候,都會從磁盤中讀取本地模版文件進行操作,這個磁盤讀取的動作消耗了很多 CPU。平時使用不會察覺,只有當(dāng)壓力測試的時候才會體現(xiàn)出來。設(shè)置好了參數(shù)后,我們得到了 10 倍的性能提升。

但我們的優(yōu)化并沒有止步于此,我們定的目標(biāo)是 3000TPS,也就是還需要再提高 50% 的渲染性能。這時候我們就必須找到影響 nodejs 性能的點。Nodejs 的特點是單線程異步編程,意味著異步操作對性能的影響不大,而同步操作則會嚴(yán)重影響性能。

所以第一步,是先檢查代碼中同步操作的邏輯,是否有消耗 CPU 的代碼。經(jīng)過檢查,排除了代碼部分的嫌疑。只好借助 chrome 提供的 devtools 來進行分析,啟動 node 參數(shù)—inspect,打開 chrome 的 devtools 插件就可以通過 CPU profile 進行分析了。排除掉不可避免的 CPU 消耗,問題浮出水面,原來還有一部分的 CPU 消耗來自于 ejs 模版引擎的內(nèi)部。

從圖中可以看出來有兩部分消耗,一部分是來自 ejs 模版引擎內(nèi)部的淺拷貝,一部分是來自查找文件是否存在的系統(tǒng)命令。由于大聚會系統(tǒng)的 ejs 里面大量使用 include,導(dǎo)致了這部分消耗凸顯了出來。打開 ejs 引擎源碼查看,發(fā)現(xiàn)雖然緩存了模版,但每次 include 函數(shù)依然會去執(zhí)行 fs.exsitSync 函數(shù)。找到罪魁禍?zhǔn)滓院螅薷钠饋砥鋵嵑芎唵危趫?zhí)行改函數(shù)的判斷條件里面加上先判斷緩存中是否存在。修改后這部分消耗減少了不少。

淺拷貝的問題,通過 js 的原型鏈解決,將傳入的數(shù)據(jù)對象作為原型對象,通過 Object.create 函數(shù)構(gòu)造一個派生對象,實現(xiàn)原來淺拷貝達(dá)到的目的(模版內(nèi)部修改對象屬性不會影響原始對象,防止污染原始對象傳入到其他模版中去)。派生對象修改屬性,并不會修改原型中對象的屬性,只會在派生對象中新建一個同名的屬性,所以不會污染原始對象。新增屬性也只會在派生對象中。這一步優(yōu)化減少了很多賦值操作。

經(jīng)過以上的優(yōu)化,再進行 CPU profile 分析,發(fā)現(xiàn)在 ejs 引擎內(nèi)部依然有一個函數(shù)在消耗 CPU,那就是 getIncludePath。這個函數(shù)的目的是在執(zhí)行 include 的時候講傳入的相對路徑轉(zhuǎn)成絕對路徑,目的是防止嵌套的 include 中傳入相同的相對路徑字符串,卻是代表不同的模版文件。但是在轉(zhuǎn)換成絕對路徑這一步里面會調(diào)用文件系統(tǒng)函數(shù)造成 CPU 消耗。

解決的思路很快就出來了,就是需要講相對路徑映射成絕對路徑,然后緩存起來,這樣就不必每次去計算絕對路徑了。當(dāng)然這個緩存不能是全局的,必須每一個 include 創(chuàng)建一個緩存,這樣才能避免相同的相對路徑有歧義的問題。

原始邏輯:

優(yōu)化的邏輯:

說明:路徑映射 Map 是一個定義在模版函數(shù)所在作用域上的,只有該模版函數(shù)內(nèi)部能訪問到,每次執(zhí)行模版函數(shù)的時候都會擁有一個獨立的 Map。

經(jīng)過上述優(yōu)化后,本地進行壓測有 50% 的性能提升,故提交測試組對大聚會進行線上壓測。

壓測結(jié)果非常好,從 2000tps 到了 3500 多,提升了 75% 之多。單臺機器大約 220tps 左右,而原 java 系統(tǒng)單臺大概 150tps 左右。

 總  結(jié)  

Nodejs 系統(tǒng)的性能優(yōu)勢主要體現(xiàn)在異步 IO 上面,所以性能瓶頸基本都是出在同步操作上面,那么優(yōu)化也是主要盡量減少同步操作,適當(dāng)使用一些 js 的技巧,另外 npm 包的開源特點也給優(yōu)化工作帶來了便利。

 

責(zé)任編輯:龐桂玉 來源: 前端之巔
相關(guān)推薦

2018-11-13 08:00:14

蘇寧iOS開發(fā)

2013-02-20 10:07:29

蘇寧電器蘇寧云商云服務(wù)

2016-11-10 19:31:00

蘇寧雙11

2009-04-20 08:51:50

MySQL查詢優(yōu)化數(shù)據(jù)庫

2017-03-29 14:44:20

網(wǎng)絡(luò)性能優(yōu)化

2022-05-17 09:02:30

前端性能優(yōu)化

2013-10-31 09:49:07

2018-11-12 12:45:19

2018-11-14 10:48:52

蘇寧管理系統(tǒng)服務(wù)性能

2019-12-13 10:25:08

Android性能優(yōu)化啟動優(yōu)化

2017-09-01 13:19:35

蘇寧無人店Biu

2013-10-29 10:41:06

SAP

2020-02-27 08:00:41

混沌工程系統(tǒng)失控條件

2020-06-19 07:00:00

蘇寧數(shù)字孿生平臺

2015-05-30 10:04:24

線下公開課51CTO沙龍MDSA

2014-02-19 10:40:07

虛擬運營商

2019-12-24 09:30:59

蘇寧高可用高并發(fā)

2017-08-21 09:03:43

2017-03-14 18:48:06

Android性能優(yōu)化內(nèi)存優(yōu)化

2009-10-26 08:36:49

Windows 7中國銷售售價
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 久久婷婷av | 免费人成激情视频在线观看冫 | 国产精品美女久久久av超清 | 91.xxx.高清在线 | 亚洲天堂中文字幕 | www久久av | 一区二区视频 | 欧美在线综合 | 一区二区三区在线 | 国产精品999 | 国产一区999| 91精品国产色综合久久 | 国产大片一区 | 日韩一区二区在线观看视频 | 美女视频黄色片 | 久久久久一区二区三区 | 蜜桃色网| 中文字幕日韩av | 老司机午夜性大片 | 国产高清免费在线 | 国产成人精品一区二区三区网站观看 | 91www在线观看 | 男人天堂99 | 国产国拍亚洲精品av | 国产精品毛片在线 | 日韩欧美网 | 亚洲成人福利 | 欧美精品一区二区三区蜜桃视频 | 国产精品亚洲综合 | 亚洲二区视频 | a级大毛片 | 欧美日韩在线视频一区 | 日本精品一区二区 | 亚洲一区视频在线 | 国产资源视频 | 亚洲伊人久久综合 | 欧美国产91| 午夜影院网站 | 超碰97免费| 国内精品久久久久久 | 中文字幕日韩欧美 |