提升 Node.js 應用效能的 5 個技巧

2015-12-12    分類:WEB開發、程式設計開發、首頁精華1人評論發表於2015-12-12

本文由碼農網 – 小峰原創翻譯,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃

“如果nginx沒有在你的節點伺服器之前,那麼你可能就錯了。”Bryan Hughes在Twitter上說

Node.js是全球領先的用JavaScript——世界上最流行的程式語言建立伺服器應用程式的工具。提供web伺服器和應用伺服器的功能,Node.js被認為是各種以微服務為基礎的開發和交付的關鍵工具。 (下載關於Node.js和NGINX的免費Forrester報告。)

Node.js可以替代或增強Java和.NET用於後端應用程式的開發。

Node.js是單執行緒的,並且使用非阻塞I / O,允許它擴充套件並支援數以萬計的並行操作。它和NGINX共享這些架構特性,並解決C10K問題——支援超過10000個併發連線——NGINX也可以解決並行操作問題。 Node.js以它的高效能和高開發效率享譽全球。

那麼,哪裡會出問題呢?

Node.js有一些薄弱環節和漏洞,這些薄弱環節和漏洞會使得基於Node的系統容易出現效能不佳,甚至崩潰的現象。尤其是當基於Node.js的web應用程式碰到訪問量高速增長的時候,問題就會出現得更加頻繁。

此外,Node.js是用於建立和執行產生核心可變網頁內容邏輯的強大工具。但它在服務靜態內容——例如影像和JavaScript檔案——以及平衡多個伺服器上的負載這些方面還沒那麼強大。

為了最有效地使用Node.js,你需要快取靜態內容,代理和平衡多個應用程式伺服器負載,並管理客戶端、Node.js和助手——如執行Socket.IO的伺服器——之間的埠競爭。 NGINX可用於解決這些問題,從而使得它成為了一個Node.js效能優化的偉大工具。

使用這些技巧可以提高Node.js應用效能:

  1. 實現反向代理伺服器
  2. 快取靜態檔案
  3. 多伺服器的負載均衡
  4. 代理WebSocket連線
  5. 實現SSL / TLS和HTTP / 2

注:Node.js應用效能的快速解決辦法是修改你的Node.js配置,以充分利用現代多核伺服器的優勢。你也可以讀一讀另一篇關於如何讓Node.js生成單獨子程式的文章

1.實現反向代理伺服器

我們在NGINX.Inc的時候,如果看到有應用程式伺服器直接接觸傳入的訪問流量,用於高效能網站核心的時候,總會不自覺地有點擔憂。這包括許多基於WordPress的網站,也包括Node.js網站。

Node.js專為可擴充套件性而設計,它比大多數應用伺服器更易於擴充套件,它的web伺服器端可以處理好大量的訪問流量。但是web服務並不是Node.js存在的理由——Node.js並不是因為這個目的而被構建的。

如果你有一個大流量網站,提高應用程式效能的第一步是在你的Node.js伺服器前放一個反向代理伺服器。這樣可以保護Node.js伺服器直接接觸外部訪問流量,還能讓你靈活使用多個應用程式伺服器,平衡負載伺服器,快取內容。

在現有的伺服器設定前放NGINX作為一個反向代理伺服器,是NGINX的核心用例,全世界各地已經有數以千萬計的網站實施了。

使用NGINX作為Node.js的反向代理伺服器還有一些特定的優勢,其中包括:

  • 簡化操作許可權和埠分配
  • 更有效地服務於靜態影像(見第二個小竅門)
  • 成功管理Node.js崩潰的情況
  • 減輕DoS攻擊

注意:這些教程介紹瞭如何使用NGINX作為在Ubuntu 14.04或CentOS環境中的反向代理伺服器,而且可以總覽NGINX置於node.js之前的整體情況。

2.快取靜態檔案

隨著基於Node.js的網站的使用量的增長,伺服器的壓力開始越來越大。這時候你要做這兩件事情:

  1. 充分利用Node.js伺服器。
  2. 使得新增應用程式伺服器和負載均衡變得容易。

這其實是很容易做到的。一開始就實施NGINX作為反向代理伺服器,就像第一點技巧中所描述的那樣。這樣就能輕易實現快取記憶體、負載平衡(如果有多個Node.js伺服器的話)等。

針對Modulus,一個應用程式容器平臺,有一篇非常有用的關於利用NGINX增壓Node.js應用程式效能的文章。由於Node.js都是靠自己完成所有的工作的,所以我們的網站平均每秒只能服務將近900個請求。使用NGINX作為反向代理伺服器,提供靜態內容,一個站點每秒可服務超過1600個請求——效能提升了近2倍。

效能的提升能讓你有時間採取額外措施以適應進訪問量的增長,如審查(或提高)網站設計,優化程式程式碼,部署更多的應用程式伺服器。

以下配置程式碼適用執行於Modulus的網站:

server {
  listen 80;
  server_name static-test-47242.onmodulus.net;
  root /mnt/app;
  index index.html index.htm;
  location /static/ {
   try_files $uri $uri/ =404;
  }
  location /api/ {
   proxy_pass http://node-test-45750.onmodulus.net;
  }
}

例如,在Nginx位置塊中,你可能不想要快取某些內容。例如,你通常不會想要快取部落格平臺的管理介面的。以下就是禁用[或免除]快取Ghost管理介面的配置程式碼:

location ~ ^/(?:ghost|signout) { 
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://ghost_upstream;
        add_header Cache-Control "no-cache, private, no-store,
        must-revalidate, max-stale=0, post-check=0, pre-check=0";
    }

快取NGINX伺服器上的靜態檔案可以顯著減輕Node.js應用程式伺服器的負載,讓它能夠達到更佳效能。

3.實現Node.js負載平衡

Node.js應用高效能的真正關鍵執行多個應用程式伺服器和平衡負載。

Node.js負載平衡可能特別棘手,因為Node.js允許執行在web瀏覽器上的JavaScript程式碼和執行在Node.js應用伺服器上的JavaScript程式碼做高水平的互動,同時使用JSON物件作為資料交換的介質。這意味著,一個給定的客戶會話會持續執行在特定的應用程式伺服器上,並且會話永續性用多個應用程式伺服器天然地難以實現。

Internet和web的主要優點之一就是高度無國界,其中包括通過任意伺服器訪問請求檔案來滿足客戶端請求。Node.js顛覆了無國界,並且在有狀態的環境中——同一伺服器始終如一地響應來自任意特定客戶端的請求——效果最好。

通過NGINX Plus,而非開源NGINX軟體,可以最好地滿足這個需求。NGINX的兩個版本頗為相似,但一個主要區別就是它們對負載平衡演算法的支援不同。

NGINX支援無狀態的負載均衡方法:

  • 迴圈。新的請求會去往列表中的下一個伺服器。
  • 最少的連線。新的請求會去到活躍連線最少的伺服器。
  • IP Hash。新的請求會去往雜湊分配客戶端IP地址的伺服器。

只是這些方法中的一種,IP Hash,可靠地傳送指定客戶端請求到同一伺服器,有利於Node.js應用程式。然而,IP Hash很容易導致某臺伺服器收到的請求數量不成比例,在犧牲其他伺服器的代價下,正如這一篇部落格中描述的負載均衡技術那樣。此方法支援的有狀態是以犧牲潛在不理想的跨伺服器資源的請求分配為代價的。

不同於NGINX,NGINX Plus支援會話永續性。在使用會話永續性的時候,同一伺服器還能可靠地接收來自指定客戶端的所有請求。 Node.js的優勢——在客戶端和伺服器之間有狀態的通訊,以及NGINX Plus的優勢——高階負載均衡能力,都達到最大化。

所以,你可以使用NGINX或NGINX Plus來支援多個Node.js伺服器的負載均衡。只有NGINX才有可能讓你最大化地實現負載均衡效能和友好的Node.js有狀態性。內建於NGINX的應用健康檢查以及監控功能也很有用。

NGINX Plus還支援會話維持,因此允許應用程式伺服器在它採取停止服務的請求之後,還能優雅地完成當前會話。

4.代理WebSocket連線

HTTP,在所有版本里,是專為“pull”通訊——來自於伺服器的客戶端請求檔案設計的。WebSocket是一個允許“push”和“push/pull”通訊的工具,即伺服器可以主動傳送客戶端沒有請求的檔案。

WebSocket協議可以更容易地支援客戶端和伺服器之間更堅固的相互作用,同時減少傳輸的資料量並最小化等待時間。當需要時,可以實現全雙工傳輸連線,也就是說根據需要客戶和伺服器都可以發起並接收請求。

WebSocket協議具有強大的JavaScript介面,因此非常適合作為應用伺服器的Node.js——而且,對於事務量不多的web應用程式,也可以作為web伺服器。當事務量增加,那麼在客戶端和Node.js web伺服器之間,多個應用伺服器之間使用NGINX或NGINX Plus插入NGINX就有必要了。

Node.js通常與Socket.IO聯合使用,Socket.IO是一個WebSocket API,它在Node.js應用程式中很受歡迎。這可能會導致port 80(對於HTTP)或port 443(對於HTTPS)變得相當擁擠,而解決方法就是代理Socket.IO伺服器。你可以使用NGINX作為代理伺服器中,就像前面說的那樣,並且還獲得其他的功能,例如靜態檔案快取,負載均衡等。

以下就是作為server.js node應用程式檔案監聽port 5000的程式碼。它擔當了代理伺服器(而不是web伺服器)的角色,並路由請求到正確的埠:

var io = require('socket.io').listen(5000);
io.sockets.on('connection', function (socket) {
  socket.on('set nickname', function (name) {
    socket.set('nickname', name, function () {
      socket.emit('ready');
    });
  });
  socket.on('msg', function () {
    socket.get('nickname', function (err, name) {
      console.log('Chat message by ', name);
    });
  });
});

var socket = io(); // 這是你的初始化程式碼。

有關完整的介紹,包括NGINX配置,請參閱此部落格文章。有關這一類更深入的web應用程式潛在架構和基礎設施問題,請參閱此部落格文章

5.實現SSL / TLS和HTTP / 2

越來越多的網站使用SSL / TLS來保護網站上所有使用者的互動。你可以決定是否以及何時做出這個舉動,但如果你選擇了這麼做,那麼NGINX有兩種方式來支援這個轉變:

  1. 你可以在NGINX裡終止SSL / TLS連線到客戶端,如果你設定了NGINX作為反向代理的話。 Node.js伺服器使用Nginx反向代理伺服器來來回回地傳送和接收未加密的請求和內容。
  2. 早期跡象表明,使用HTTP / 2——新的HTTP協議的新版本——可以在很大程度上或完全抵消使用SSL / TLS強加的效能損失。 NGINX支援HTTP / 2,你可以終止HTTP / 2和SSL,而在Node.js應用伺服器中無需做任何改變。

在你採取這些實現步驟的時候,你還需要更新在Node.js配置檔案中的URL,建立和完善在NGINX配置中的安全連線,必要時還可以使用SPDY或HTTP / 2。新增HTTP / 2支援意味著瀏覽器版本使用新的協議支援HTTP / 2與應用程式進行通訊:老版本的瀏覽器使用HTTP / 1.x。

下面的配置程式碼適用於使用SPDY的Ghost部落格。它包括一些高階功能,如OCSP stapling。使用NGINX用於SSL終端,包括OCSP stapling選項,看這裡。對於同一主題的概述,看這裡。

你需要做的輕微改動就是配置Node.js應用程式,從SPDY升級到HTTP / 2,時間可以是現在,也可以是2016年初SPDY支援消失的時候。

server {
   server_name domain.com;
   listen 443 ssl spdy;
   spdy_headers_comp 6;
   spdy_keepalive_timeout 300;
   keepalive_timeout 300;
   ssl_certificate_key /etc/nginx/ssl/domain.key;
   ssl_certificate /etc/nginx/ssl/domain.crt;
   ssl_session_cache shared:SSL:10m;  
   ssl_session_timeout 24h;           
   ssl_buffer_size 1400;              
   ssl_stapling on;
   ssl_stapling_verify on;
   ssl_trusted_certificate /etc/nginx/ssl/trust.crt;
   resolver 8.8.8.8 8.8.4.4 valid=300s;
   add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';
   add_header X-Cache $upstream_cache_status;
   location / {
        proxy_cache STATIC;
        proxy_cache_valid 200 30m;
        proxy_cache_valid 404 1m;
        proxy_pass http://ghost_upstream;
        proxy_ignore_headers X-Accel-Expires Expires Cache-Control;
        proxy_ignore_headers Set-Cookie;
        proxy_hide_header Set-Cookie;
        proxy_hide_header X-powered-by;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header Host $http_host;
        expires 10m;
    }
    location /content/images {
        alias /path/to/ghost/content/images;
        access_log off;
        expires max;
    }
    location /assets {
        alias /path/to/ghost/themes/uno-master/assets;
        access_log off;
        expires max;
    }
    location /public {
        alias /path/to/ghost/built/public;
        access_log off;
        expires max;
    }
    location /ghost/scripts {
        alias /path/to/ghost/core/built/scripts;
        access_log off;
        expires max;
    }
    location ~ ^/(?:ghost|signout) { 
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://ghost_upstream;
        add_header Cache-Control "no-cache, private, no-store,
        must-revalidate, max-stale=0, post-check=0, pre-check=0";
        proxy_set_header X-Forwarded-Proto https;
    }
}

結論

本文介紹了一些最重要的可以在Node.js應用程式佈置的效能改進。它著重於新增到應用程式的NGINX以及Node.js——通過使用NGINX作為反向代理伺服器,快取靜態檔案,負載均衡,代理WebSocket連線,並終止SSL / TLS和HTTP / 2協議。

NGINX和Node.js的結合,被廣泛認為是一種建立新的微服務型應用程式或增加靈活性和效能到現有的基於SOA的使用Java或Microsoft .NET應用的方法。這篇文章可以幫助你優化Node.js應用程式,讓Node.js和NGINX的夥伴關係為你所用。

譯文連結:http://www.codeceo.com/article/5-tips-nodejs-performance.html
英文原文:5 Tips to Increase Node.js Application Performance
翻譯作者:碼農網 – 小峰
轉載必須在正文中標註並保留原文連結、譯文連結和譯者等資訊。]

相關文章