極速Node.js:來自LinkedIn的10個性能提升秘籍

jopen 11年前發布 | 14K 次閱讀 Node.js

        【譯注】:LinkedIn 最近從 Rails 轉移到 Node.js 獲得了巨大的成功,它砍掉了之前 90% 的服務器,并使性能提升了 20 倍。 這個消息令很多人把 Node.js 看成了葵花寶典一樣的神功,可是練習神功也不是一朝一夕的事,光練招式沒有內功也是不成的,更何況還得…那啥…總之不容易啊!那么除了 Node.js,LinkedIn 的性能提升還有什么秘密?LinkedIn 的軟件工程師 Shravya Garlapati 寫的這篇文章可以給我們一些啟示。

        以下是譯文:

        在之前的文章里,我們討論了我們如何測試 LinkedIn 的移動服務集群,包括我們的 Node.js 移動端服務器。今天,我們想給你們說說我們是如何讓這個服務器快速運行的。這里就是我們在 Node.js 上的十大性能提升秘籍:

        1. 避免同步的代碼

        Node.js 從一開始的設計就是單線程的。要讓一個線程處理很多并發請求,你就永遠不要讓這個線程在阻塞、同步或運行時間很長的操作中等待。Node.js 的一個超凡脫俗的特性就是它從上到下都是設計和實現為異步模式。這使它對事件驅動的應用是絕配。

        不幸的是,在 Node.js 里進行同步/阻塞式的調用還是有可能的。例如,很多文件系統操作都提供了異步和同步版本,例如 writeFile 和 writeFileSync。即便你在自己的代碼里避免了同步方法,卻有可能漫不經心地引入了一個外部庫,而在該庫中使用了阻塞式調用。當你這么做了之 后,它對性能的影響是巨大的。

        我們最初的日志實現就偶然性地引入了一個寫入磁盤的同步調用。在我們進行性能測試之前,沒人注意到它的存在。當我們在一臺開發機上對比單個 Node.js 服務器性能的時候,這一個同步調用導致性能從每秒處理 1000 個請求下降到幾十個!

        2. 關閉 socket 池

        Node.js 的 http 模塊會自動使用 socket 池,缺省是每臺主機限制為 5 個 socket。雖然這種 socket 復用方式可能對控制資源增長的速度有利,但如果你需要處理很多同時需要從同一臺主機獲取數據的并發請求,這個 socket 池會成為嚴重的性能瓶頸。在這種情況下,好的辦法是增大 maxSockets 參數或者干脆把 socket 池 disable 掉。

        3. 不要用 Node.js 處理靜態資源

        對于靜態資源,例如 CSS 和圖像,使用標準的 web 服務器而不是 Node.js 來處理。例如,LInkedIn mobile 使用 nginx。我們還利用內容分發網絡(CDN),它能把靜態資源復制到分布在全世界的很多服務器上。這樣做有兩大益處:①減少了 Node.js 服務器的負載,②CDN 能讓靜態內容從離用戶比較近的服務器上傳輸過去,這樣減少了延遲。

        4. 在客戶端渲染頁面

        讓我們快速比較一下服務器端渲染和客戶端渲染頁面。如果我們讓 Node.js 在服務器端渲染,我們會對所有請求發回一個類似于本文的 HTML 頁面。

        請注意,在這個頁面上,除了用戶名之外,所有內容都是靜態的:也就是說,對于每個用戶和每次刷新都是完全相同的。所以更有效率的方法是讓 Node.js 只以 JSON 格式返回頁面所需的動態數據。

        頁面的其他部分,也就是所有的靜態 HTML 標記,可以放到一個 JavaScript 模板里(例如一個 underscore.js 模板)。

        性能的受益是這么來的:根據秘籍3,靜態 JavaScript 模板可以從你的 web 服務器(例如 nginx)或者 CDN 獲取。而且,JavaScript 模板可以緩存在客戶端或保存在本地存儲,這樣初始加載頁面之后,唯一需要傳給客戶端的就是動態 JSON 數據,這是最高效率的做法。該方法極大地減少了 Node.js 服務器上占用的 CPU, IO 和負載。

極速Node.js:來自LinkedIn的10個性能提升秘籍

        5. 使用 gzip 壓縮

        大部分服務器和客戶端都支持 gzip 壓縮請求和響應的數據。一定要在對客戶端發送響應和對遠程服務器發送請求時利用它。

        6. 并行

        盡量讓你所有的阻塞式操作(比如,對遠程服務的請求,數據庫調用,和文件系統訪問)并發進行。這樣會把總延遲減少為最慢的哪個阻塞式操作的時間,而不是每個操作順序進行的延遲總和。為了保持回調函數和錯誤處理整潔清楚,我們使用了 Step 來進行流程控制。

        7. 避免使用 session

        LinkedIn mobile 使用 Express 框架來管理請求/響應循環。

        缺省情況下,session 數據是保存在內存里的,這樣會給服務器增加相當大的開銷,特別是在用戶數增長的情況下。你可以改用外部 session 存儲,例如 MongoDB 或 Redis,可是這樣每個請求又增加了獲取 session 數據的遠程調用的開銷。在可能的情況下,最好的辦法是根本就不在服務器端保存狀態信息。通過去掉 Express 里類似于 “app.use (express.session ()); ” 的配置,實現無 session 后,你會看到更好的性能。(【譯者注】原文缺了具體配置,這條是譯者自己琢磨著加的)

        8. 使用已編譯的模塊

        盡可能使用已編譯的模塊而不是 JavaScript 模塊。例如,當我們把 SHA 模塊從一個用 JavaScript 寫的版本轉到一個包含在 Node.js 里的已編譯版本,我們看到了一個巨大的性能飛躍。

        9. 使用標準 V8 JavaScript 而不是客戶端的庫

        大部分 JavaScript 庫是做出來用在 web 瀏覽器上的。而在瀏覽器上 JavaScript 環境真是千差萬別:例如,某個瀏覽器可能支持類似于 forEach, map/reduce 這樣的功能,而其他瀏覽器卻不支持。結果,客戶端的庫通常包含了很多低效率的代碼來克服瀏覽器帶來的差別。另一方面,在 Node.js 里,你能明確知道有哪些 JavaScript 函數,因為支撐 Node.js 的 V8 JavaScript 引擎是按第 5 版 ECMA-262 標準描述的 ECMAScript 標準實現的。通過直接使用標準的 V8 函數而不是客戶端庫里的東西,你可以看到相當大的性能改進。

        10. 保持你的代碼小規模、輕量級

        在設備慢、延遲高的移動客戶端環境下工作,你會學著保持代碼的小規模和輕量級。把這個思路也帶到你服務器端的代碼中去。經常重新思考你的決定, 問自己一些問題,例如:“我們真的需要這個模塊嗎?”,“我們為啥要用這個框架?與其產生的開銷相比它是否值得用?”,“我們能不能用更簡單的方式來做這 件事?”。更小,更輕量的代碼總是會更有效率,也會更快。

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!