騰訊新聞構建高效能的 react 同構直出方案
在騰訊新聞搶金達人活動 node 同構直出渲染方案的總結文章中我們整體瞭解了下同構直出渲染方案在我們專案中的使用。正如我在上篇文章結尾所說的:
應用型技術的難點不是在克服技術問題,而是在於能夠不斷的結合自身的產品體驗,發現其中存在的體驗問題,不斷使用更好的技術方案去優化使用者的體驗,為整個產品發展添磚加瓦。
我們在根據產品的體驗效果選擇了 react 同構直出渲染方案,必然也要保證當前方案的可用性和可靠性。例如我們的服務能同時支撐多少人訪問,當用戶量增大時是否可以依然保證使用者的正常訪問,如何保證 CPU、記憶體等正常運作,而不被一直佔用無法釋放等。因此,這裡我們應當下我們專案的幾項資料:
- 專案一天的訪問量是多少,高峰期的訪問量是多少,即併發的使用者量有多少;
- 我們的單機服務最大能支援多少 QPS;
- 高併發時的服務響應時間如何,頁面,介面的失敗率有多少;
- CPU 和記憶體的使用情況,是否存在 CPU 使用不充分或者記憶體洩露等問題;
這些資料,都是我們上線前要知道的。壓力測試的重要性就提現出來了,我們在上線前進行充分的測試,能夠讓我們掌握程式和伺服器的執行效能,大致申請多少臺機器等等。
1. 初次壓力測試
我們這裡使用autocannon來對專案進行壓測。注意,我們現在還沒有進行任何的優化措施,就是要先暴露出問題來,然後針對性的進行優化。
每秒鐘 60 的併發,並持續 100 秒:
autocannon -c 60 -d 100
壓測後的資料:
從圖片中可以看到,每秒 60 的併發請求量時,QPS 平均有 266 左右,不過還有 23 個請求超時了,響應時間還可以,99%的請求在 1817ms 毫秒內完成。就目前這幾項資料來看,資料處理能力並不理想,我們還有很大的提升空間。
2. 解決方案
針對上面壓測出來的資料不理想,我們這裡需要採取一些措施了。
2.1 記憶體管理
我們現在寫純前端時,幾乎已經很少關注記憶體的使用了,畢竟在前端發展的過程中,記憶體的垃圾回收機制相對來說比較完善,而且前端頁面的生存週期比較短。如果真是要特別注意的話,也是早期在 IE 瀏覽器中,js 與 dom 的互動過程中可能會產生記憶體的洩露。而且如果真會真要是洩露的話,也只會影響當前終端的使用者,其他的使用者暫時不會受到影響。
而服務端則不同,所有使用者都會訪問當前執行的程式碼,只要程式有一丁點的記憶體洩露,在成千上萬的訪問量下,都會造成記憶體的堆積,垃圾無法回收,最終造成嚴重的記憶體洩露,並導致程式崩潰。為了預防記憶體洩露,我們在記憶體管理方面,主要三方面的內容:
- V8 引擎的垃圾回收機制;
- 造成記憶體洩露的原因;
- 如何檢測記憶體洩露;
Node 將 JavaScript 的主要應用場景擴充套件到了伺服器端,相應要考慮的細節也與瀏覽器端不同, 需要更嚴謹地為每一份資源作出安排。總的來說,記憶體在 Node 中不能隨心所欲地使用,但也不是完全不擅長。
2.1.1 V8 引擎的垃圾回收機制
在 V8 中,主要將記憶體分為新生代和老生代兩代。新生代的物件為存活時間比較短的物件,老生代中的物件為存活時間較長的或常駐記憶體的物件。
預設情況下,新生代的記憶體最大值在 64 位系統和 32 位系統上分別為 32 MB 和 16 MB。V8 對記憶體的最大值在 64 位系統和 32 位系統上分別為 1464 MB 和 732 MB。
為什麼這樣分兩代呢?是為了最優的 GC 演算法。新生代的 GC 演算法 Scavenge 速度快,但是不合適大資料量;老生代針使用 Mark-Sweep(標記清除) & Mark-Compact(標記整理) 演算法,合適大資料量,但是速度較慢。分別對新舊兩代使用更適合他們的演算法來優化 GC 速度。
2.1.2 記憶體洩露的原因
記憶體洩露的情況有很多,例如記憶體當快取、佇列、重複的事件監聽等。
記憶體當快取這種情況中,通常有用一個變數來快取資料,然後沒有過期時間,一直填充資料,例如下面一個簡單的例子:
let cached = new Map();
server.get('*', (req, res) => {
if (cached.has(req.url)) {
return cached.get(req.url);
}
const html = app.render(req, res);
cached.set(req.url, html);
res.send(html);
});
除此之外,還有閉包
也是其中的一種情況。這種使用記憶體的不好的地方是,它沒有可用的過期策略,只會讓資料越來越多,最終造成記憶體洩露。更好的方式使用第三方的快取機制,例如 redis、memcached 等,這些都有良好的過期和淘汰策略。
同時,也有一些佇列方面的處理,例如有些日誌的寫入操作,當海量的資料需要寫入時,就會造成佇列的堆積。這時,我們設定佇列的超時策略和拒絕策略,讓一些操作儘快地釋放掉。
再一個就是事件的重複監聽。例如對同一個事件重複監聽,忘記移除(removeListener),將造成記憶體洩漏。這種情況很容易在複用物件上新增事件時出現,所以事件重複監聽可能收到如下警告:
Warning: Possible EventEmitter memory leak detected. 11 /question listeners added。Use emitter。setMaxListeners() to increase limit
2.1.3 排查的手段
我們從記憶體的監控圖中可以看到,在使用者量基本保持不變的情況下,記憶體是一直在緩慢上漲,說明我們產生了記憶體洩露,使用的記憶體並沒有被釋放掉。
這裡我們可以通過node-heapdump
等工具來進行判斷,或者稍微簡單點,使用--inspect
命令實現:
node --inspect server.js
然後開啟 chrome 連結chrome://inspect
來檢視記憶體的使用情況。
通過兩次的記憶體抓取對比發現,handleRequestTimeout()
方法一直在產生,且每個 handle 方法中有無數個回撥,資源無法被釋放。
通過定位檢視使用的 axios 程式碼是:
if (config.timeout) {
timer = setTimeout(function handleRequestTimeout() {
req.abort();
reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', req));
}
}
這裡程式碼看起來是沒任何問題的,這是在前端處理中一個很典型的超時處理解決方式。
由於 Nodejs 中,io 的連結會阻塞 timer 處理,因此這個 setTimeout 並不會按時觸發,也就有了 10s 以上才返回的情況。
貌似問題解決了,巨大的流量和阻塞的 connection 導致請求堆積,伺服器處理不過來,CPU 也就下不來了。
通過定位並檢視axios 的原始碼:
if (config.timeout) {
// Sometime, the response will be very slow, and does not respond, the connect event will be block by event loop system.
// And timer callback will be fired, and abort() will be invoked before connection, then get "socket hang up" and code ECONNRESET.
// At this time, if we have a large number of request, nodejs will hang up some socket on background. and the number will up and up.
// And then these socket which be hang up will devoring CPU little by little.
// ClientRequest.setTimeout will be fired on the specify milliseconds, and can make sure that abort() will be fired after connect.
req.setTimeout(config.timeout, function handleRequestTimeout() {
req.abort();
reject(
createError(
'timeout of ' + config.timeout + 'ms exceeded',
config,
'ECONNABORTED',
req
)
);
});
}
額,我之前使用的版本比較早,跟我本地使用的程式碼不一樣,說明是更新過了,再檢視這個檔案的9 月 16 日的改動歷史:
這裡我們就需要把 axios 更新到最新的版本了。而且經過本地大量測試,發現在高負載下 CPU 和記憶體都在正常範圍內了。
2.2 快取
快取真是效能優化的一把好手,服務不夠,快取來湊
。不過快取的型別有很多種,我們應當根據專案的實際情況,合理地選擇使用快取的策略。這裡我們使用了 3 層的快取策略。
在 nginx 中,可以使用 proxy_cache 設定要快取的路徑和快取的時間,同時可以啟用proxy_cache_lock
。
當 proxy_cache_lock 被啟用時,當多個客戶端請求一個快取中不存在的檔案(或稱之為一個 MISS),只有這些請求中的第一個被允許傳送至伺服器。其他請求在第一個請求得到滿意結果之後在快取中得到檔案。如果不啟用 proxy_cache_lock,則所有在快取中找不到檔案的請求都會直接與伺服器通訊。
不過這個欄位的啟用也要非常慎重,當訪問量過大時,會造成請求的堆積,必須等待第一個請求返回完成後,才能處理後面的請求。
proxy_cache_path /data/cached keys_zone=answer:16m levels=1:2 inactive=60m;
server {
location / {
proxy_cache answer;
proxy_cache_valid 1m;
}
}
在業務層面,我們可以啟用 redis 快取,來快取整個頁面、頁面的某個部分或者介面等等,當穿透 nginx 快取時,可以啟用 redis 快取。使用第三方快取的特點我們在之前的文章也說了:多個程序之間可以共享,同時減少專案本身對快取淘汰演算法的處理。
當前面的兩層快取失效時,進入到我們的 node 服務層。二層的快取機制,能實現不同的快取策略和快取粒度,業務需要根據自身場景, 選用適合自己業務的快取即可。
3. 效果
這時我們專案的效能怎樣了呢?
autocanon -c 1000 -d 100
從圖片裡可以看到,99%的請求在182ms內完成,每秒平均處理的請求有15707左右,相比我們最開始只能處理200多個請求,效能足足提升了60倍多。
相關閱讀:
騰訊新聞搶金達人活動node同構直出渲染方案的總結
我的部落格原文地址:https://www.xiabingbao.com/post/node/node-high-performance.html
來自騰訊的前端開發工程師,與你分享前端快樂:wenzichel
相關推薦
騰訊新聞構建高效能的 react 同構直出方案
在騰訊新聞搶金達人活動 node 同構直出渲染方案的總結文章中我們整體瞭解了下同構直出渲染方案在我們專案中的使用。正如我在上篇文章結尾所說的: 應用型技術的難點不是在克服技術問題,而是在於能夠不斷的結合自身的產品體驗,發現其中存在的體驗問題,不斷使用更好的技術方案去優化使用者的體驗,為整個產品發展添磚加瓦。
騰訊新聞搶金達人活動node同構直出渲染方案的總結
我們的業務在展開的過程中,前端渲染的模式主要經歷了三個階段:服務端渲染、前端渲染和目前的同構直出渲染方案。 服務端渲染的主要特點是前後端沒有分離,前端寫完頁面樣式和結構後,再將頁面交給後端套資料,最後再一起聯調。同時前端的釋出也依賴於後端的同學;但是優點也很明顯:頁面渲染速度快,同時 SEO 效果好。 為了解
【第1145期】打造高可靠與高效能的React同構解決方案
前言本文為第12屆D2前端技術論壇《打造高可靠與高效能的React同構解決方案》分享內容,已經過
讓這三個月來的更猛烈些吧,前端react同構項目
aso ssr seo ava git blog 發展 客戶端請求 功能 昨天一篇文章講述了我在這三個月中由.net到java的過程,其中踩坑填坑的細節真不是三言兩語可以道盡,而完成時的喜悅也遠非尋常可比(僅次於漲工資)。然而到這並不算完結,作為前後端分離的忠實粉絲,我認
揭祕React同構應用
隨著React和Redux為服務端渲染提供了優良特性,同構應用變得越來越普遍。作為開發者,即使採用的技術架構並不是基於服務端渲染的同構設計,也很有必要對同構設計進行了解並掌握其原理。 前後端架構設計和服務端渲染概念 服務端渲染或直出的概念越來越流行。在瞭解如何基於React實現服務端渲染
讓這三個月來的更猛烈些吧,前端react同構專案
前端的框架、工具什麼的實在太多了,近乎亂象,然而這並不影響什麼,選擇並沒有想象中的那麼艱難,因為真正需要選擇的東西只有一個:框架。對於目前比較火熱的三大前端框架angular、react和vue。angular因為個人喜好問題被首先排除,所以我僅僅需要react和vue中二選一即可,最終我們選擇了react
來自騰訊的高效能伺服器架構思路
在伺服器端程式開發領域,效能問題一直是備受關注的重點。業界有大量的框架、元件、類庫都是以效能為賣點而廣為人知。然而,伺服器端程式在效能問題上應該有何種基本思路,這個卻很少被這些專案的文件提及。本文正式希望介紹伺服器端解決效能問題的基本策略和經典實踐,並分為幾個部
xLua 2.1.13 釋出,騰訊開源的手遊熱更新解決方案
新增特性 新增AdaptByDelegate注入模式; 新增xlua.get_generic_method,用於呼叫泛型函式; 支援類似CS.System.Collections.Generic.List(CS.System.Int32)的泛型寫法; 注入新選項
騰訊全球合夥人大會召開,劃出重點,喊話B端客戶
2018騰訊全球合作伙伴大會在南京江蘇大劇院開幕,作為騰訊新一輪架構調整後的首次大會,騰訊高階執行副總裁湯道生闡述了騰訊全面發力產業網際網路,深耕消費網際網路的“兩張網”戰略。 騰訊至上到下達成對TO B業務的共識 在2018年以前微信與QQ兩款2C業務領域超級App
【騰訊開源】iOS爆記憶體問題解決方案-OOMDetector元件
元件介紹 OOMDetector是手Q自研的IOS記憶體監控元件,騰訊內部目前已有多個App接入了OOMDetector,它主要有以下兩個功能: 爆記憶體堆疊統計:負責記錄程序記憶體分配堆疊和記憶體塊大小,在爆記憶體時Dump堆疊資料到磁碟 記憶體洩漏檢測
騰訊面試題:根據上排給出的十個數,在其下排填出對應的十個數。
問題描述:根據上排給出的十個數,在其下排填出對應的十個數,要求下排每個數都是先前上排那十個數在下排出現的次數。 上排的十個數如下: 0,1,2,3,4,5,6,7,8,9 答案是: 6,2,1,0,0,0,1,0,0,0 我在這裡使用DFS,並且使用兩個函式互相遞迴。 程
直播|百安居前端架構師陳國興:如何使用React構建同構(isomorphic)應用
移動開發 java app開發 隨著前端的發展,為了用戶體驗,H5越來越多的使用SPA架構,導致JS代碼越來越多,體積也變的龐大,這時傳統的ajax方式在首屏訪問時就變得慢了,而且ajax在seo方面有天然的弱勢,這時服務端渲染又回來了。我們使用React搭配React Router等類庫來實現服
騰訊雲批量計算:用搭積木的方式構建高效能運算系統
歡迎大家前往騰訊雲社群,獲取更多騰訊海量技術實踐乾貨哦~ 高效能運算(High Performance Computing)簡稱 HPC,在氣象預測、地震預警、生命科學、軍事、航天等高科技領域有著廣泛的應用,其代表超級計算機是一個國家科技實力的象徵。 超算如何從『
React前後端如何同構,防止重復渲染
linux首先解釋React前後端同構、React首屏渲染的概念。然後通過這2個概念解決服務端渲染完成後瀏覽器端重復渲染的問題。什麽叫前後端同構?為了解決某些問題(比如SEO、提升渲染速度等)react 提供了2個方法在服務端生成一個HTML文本格式的字符串。在得到了這個HTML格式的字符串之後,通常會將其組
使用React的static方法實現同構以及同構的常見問題
fonts eap 細致 tput isp shee 模塊 system device 代碼地址請在github查看,假設有新內容。我會定時更新。也歡迎您star,i
騰訊正式開源高效能的圖片框架 LKImageKit
開源最前線 猿妹編譯 素材來自:騰訊開源、GitHub等 近日,騰訊又有新的開源專案啦,高效能的圖片框架 LKImageKit 正式開源 圖片框架 LKImageKit 授權協議:BSD 開發語言:Objective-C 作
React server rendering —— 網易美學主站同構實錄
此文已由作者張碩授權網易雲社群釋出。 歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。 網易美學主站在最初開發時,因為各種歷史原因,引入了例如JQuery,Bootstrop,Angular, React等框架,程式碼結構比較混亂,給後續的開發和維護帶來了很大的不便。所以對它進行了重
React Native 三端同構實戰
WeiboGoogle+用電子郵件傳送本頁面 0 React Native 三端(Web、iOS、Android)同構是指在不改動原 React Native 的程式碼下,讓其在瀏覽器中執行出和在 React Native 環境下一樣的頁面。對於使用 React Na
Blade - 騰訊開源的構建系統 c/c++編譯環境
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
騰訊高效能分散式機器學習平臺 Angel 2.0.0 釋出
Angel 2.0.0 版本已釋出,在 2.0.0-alpha 版本上修復了大量 bug 並對穩定性做了較多的優化;同時對部分演算法進行了重構。 Angel Core [ISSUE-418] 對 PS 端 Matrix 儲存方式進行了優化,當模型格式配置為稀疏