提升 Node.js 應(yīng)用性能的 5 個(gè)技巧
“如果nginx沒(méi)有在你的節(jié)點(diǎn)服務(wù)器之前,那么你可能就錯(cuò)了。”Bryan Hughes在Twitter上說(shuō)
Node.js是全球領(lǐng)先的用JavaScript——世界上最流行的編程語(yǔ)言創(chuàng)建服務(wù)器應(yīng)用程序的工具。提供web服務(wù)器和應(yīng)用服務(wù)器的功能,Node.js被認(rèn)為是各種以微服務(wù)為基礎(chǔ)的開(kāi)發(fā)和交付的關(guān)鍵工具。 (下載關(guān)于Node.js和NGINX的免費(fèi)Forrester報(bào)告。)
Node.js可以替代或增強(qiáng)Java和.NET用于后端應(yīng)用程序的開(kāi)發(fā)。
Node.js是單線程的,并且使用非阻塞I / O,允許它擴(kuò)展并支持?jǐn)?shù)以萬(wàn)計(jì)的并行操作。它和NGINX共享這些架構(gòu)特性,并解決C10K問(wèn)題——支持超過(guò)10000個(gè)并發(fā)連接——NGINX也可以解 決并行操作問(wèn)題。 Node.js以它的高性能和高開(kāi)發(fā)效率享譽(yù)全球。
那么,哪里會(huì)出問(wèn)題呢?
Node.js有一些薄弱環(huán)節(jié)和漏洞,這些薄弱環(huán)節(jié)和漏洞會(huì)使得基于Node的系統(tǒng)容易出現(xiàn)性能不佳,甚至崩潰的現(xiàn)象。尤其是當(dāng)基于Node.js的web應(yīng)用程序碰到訪問(wèn)量高速增長(zhǎng)的時(shí)候,問(wèn)題就會(huì)出現(xiàn)得更加頻繁。
此外,Node.js是用于創(chuàng)建和運(yùn)行產(chǎn)生核心可變網(wǎng)頁(yè)內(nèi)容邏輯的強(qiáng)大工具。但它在服務(wù)靜態(tài)內(nèi)容——例如圖像和JavaScript文件——以及平衡多個(gè)服務(wù)器上的負(fù)載這些方面還沒(méi)那么強(qiáng)大。
為了最有效地使用Node.js,你需要緩存靜態(tài)內(nèi)容,代理和平衡多個(gè)應(yīng)用程序服務(wù)器負(fù)載,并管理客戶端、Node.js和助手——如運(yùn)行 Socket.IO的服務(wù)器——之間的端口競(jìng)爭(zhēng)。 NGINX可用于解決這些問(wèn)題,從而使得它成為了一個(gè)Node.js性能優(yōu)化的偉大工具。
使用這些技巧可以提高Node.js應(yīng)用性能:
-
實(shí)現(xiàn)反向代理服務(wù)器
-
緩存靜態(tài)文件
-
多服務(wù)器的負(fù)載均衡
-
代理WebSocket連接
-
實(shí)現(xiàn)SSL / TLS和HTTP / 2
注:Node.js應(yīng)用性能的快速解決辦法是修改你的Node.js配置,以充分利用現(xiàn)代多核服務(wù)器的優(yōu)勢(shì)。你也可以讀一讀另一篇關(guān)于如何讓Node.js生成單獨(dú)子進(jìn)程的文章。
1.實(shí)現(xiàn)反向代理服務(wù)器
我們?cè)贜GINX.Inc的時(shí)候,如果看到有應(yīng)用程序服務(wù)器直接接觸傳入的訪問(wèn)流量,用于高性能網(wǎng)站核心的時(shí)候,總會(huì)不自覺(jué)地有點(diǎn)擔(dān)憂。這包括許多基于WordPress的網(wǎng)站,也包括Node.js網(wǎng)站。
Node.js專(zhuān)為可擴(kuò)展性而設(shè)計(jì),它比大多數(shù)應(yīng)用服務(wù)器更易于擴(kuò)展,它的web服務(wù)器端可以處理好大量的訪問(wèn)流量。但是web服務(wù)并不是Node.js存在的理由——Node.js并不是因?yàn)檫@個(gè)目的而被構(gòu)建的。
如果你有一個(gè)大流量網(wǎng)站,提高應(yīng)用程序性能的第一步是在你的Node.js服務(wù)器前放一個(gè)反向代理服務(wù)器。這樣可以保護(hù)Node.js服務(wù)器直接接觸外部訪問(wèn)流量,還能讓你靈活使用多個(gè)應(yīng)用程序服務(wù)器,平衡負(fù)載服務(wù)器,緩存內(nèi)容。
在現(xiàn)有的服務(wù)器設(shè)置前放NGINX作為一個(gè)反向代理服務(wù)器,是NGINX的核心用例,全世界各地已經(jīng)有數(shù)以千萬(wàn)計(jì)的網(wǎng)站實(shí)施了。
使用NGINX作為Node.js的反向代理服務(wù)器還有一些特定的優(yōu)勢(shì),其中包括:
-
簡(jiǎn)化操作權(quán)限和端口分配
-
更有效地服務(wù)于靜態(tài)圖像(見(jiàn)第二個(gè)小竅門(mén))
-
成功管理Node.js崩潰的情況
-
減輕DoS攻擊
注意:這些教程介紹了如何使用NGINX作為在Ubuntu 14.04或CentOS環(huán)境中的反向代理服務(wù)器,而且可以總覽NGINX置于node.js之前的整體情況。
2.緩存靜態(tài)文件
隨著基于Node.js的網(wǎng)站的使用量的增長(zhǎng),服務(wù)器的壓力開(kāi)始越來(lái)越大。這時(shí)候你要做這兩件事情:
-
充分利用Node.js服務(wù)器。
-
使得添加應(yīng)用程序服務(wù)器和負(fù)載均衡變得容易。
這其實(shí)是很容易做到的。一開(kāi)始就實(shí)施NGINX作為反向代理服務(wù)器,就像第一點(diǎn)技巧中所描述的那樣。這樣就能輕易實(shí)現(xiàn)高速緩存、負(fù)載平衡(如果有多個(gè)Node.js服務(wù)器的話)等。
針對(duì)Modulus,一個(gè)應(yīng)用程序容器平臺(tái),有一篇非常有用的關(guān)于利用NGINX增壓Node.js應(yīng)用程序性能的文章。由于Node.js都是靠 自己完成所有的工作的,所以我們的網(wǎng)站平均每秒只能服務(wù)將近900個(gè)請(qǐng)求。使用NGINX作為反向代理服務(wù)器,提供靜態(tài)內(nèi)容,一個(gè)站點(diǎn)每秒可服務(wù)超過(guò) 1600個(gè)請(qǐng)求——性能提升了近2倍。
性能的提升能讓你有時(shí)間采取額外措施以適應(yīng)進(jìn)訪問(wèn)量的增長(zhǎng),如審查(或提高)網(wǎng)站設(shè)計(jì),優(yōu)化程序代碼,部署更多的應(yīng)用程序服務(wù)器。
以下配置代碼適用運(yùn)行于Modulus的網(wǎng)站:
- 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位置塊中,你可能不想要緩存某些內(nèi)容。例如,你通常不會(huì)想要緩存博客平臺(tái)的管理界面的。以下就是禁用[或免除]緩存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服務(wù)器上的靜態(tài)文件可以顯著減輕Node.js應(yīng)用程序服務(wù)器的負(fù)載,讓它能夠達(dá)到更佳性能。
3.實(shí)現(xiàn)Node.js負(fù)載平衡
Node.js應(yīng)用高性能的真正關(guān)鍵運(yùn)行多個(gè)應(yīng)用程序服務(wù)器和平衡負(fù)載。
Node.js負(fù)載平衡可能特別棘手,因?yàn)镹ode.js允許運(yùn)行在web瀏覽器上的JavaScript代碼和運(yùn)行在Node.js應(yīng)用服務(wù)器上 的JavaScript代碼做高水平的交互,同時(shí)使用JSON對(duì)象作為數(shù)據(jù)交換的介質(zhì)。這意味著,一個(gè)給定的客戶會(huì)話會(huì)持續(xù)運(yùn)行在特定的應(yīng)用程序服務(wù)器 上,并且會(huì)話持久性用多個(gè)應(yīng)用程序服務(wù)器天然地難以實(shí)現(xiàn)。
Internet和web的主要優(yōu)點(diǎn)之一就是高度無(wú)國(guó)界,其中包括通過(guò)任意服務(wù)器訪問(wèn)請(qǐng)求文件來(lái)滿足客戶端請(qǐng)求。Node.js顛覆了無(wú)國(guó)界,并且在有狀態(tài)的環(huán)境中——同一服務(wù)器始終如一地響應(yīng)來(lái)自任意特定客戶端的請(qǐng)求——效果最好。
通過(guò)NGINX Plus,而非開(kāi)源NGINX軟件,可以最好地滿足這個(gè)需求。NGINX的兩個(gè)版本頗為相似,但一個(gè)主要區(qū)別就是它們對(duì)負(fù)載平衡算法的支持不同。
NGINX支持無(wú)狀態(tài)的負(fù)載均衡方法:
-
循環(huán)。新的請(qǐng)求會(huì)去往列表中的下一個(gè)服務(wù)器。
-
最少的連接。新的請(qǐng)求會(huì)去到活躍連接最少的服務(wù)器。
-
IP Hash。新的請(qǐng)求會(huì)去往哈希分配客戶端IP地址的服務(wù)器。
只是這些方法中的一種,IP Hash,可靠地發(fā)送指定客戶端請(qǐng)求到同一服務(wù)器,有利于Node.js應(yīng)用程序。然而,IP Hash很容易導(dǎo)致某 臺(tái)服務(wù)器收到的請(qǐng)求數(shù)量不成比例,在犧牲其他服務(wù)器的代價(jià)下,正如這一篇博客中描述的負(fù)載均衡技術(shù)那樣。此方法支持的有狀態(tài)是以犧牲潛在不理想的跨服務(wù)器 資源的請(qǐng)求分配為代價(jià)的。
不同于NGINX,NGINX Plus支持會(huì)話持久性。在使用會(huì)話持久性的時(shí)候,同一服務(wù)器還能可靠地接收來(lái)自指定客戶端的所有請(qǐng)求。 Node.js的優(yōu)勢(shì)——在客戶端和服務(wù)器之間有狀態(tài)的通信,以及NGINX Plus的優(yōu)勢(shì)——高級(jí)負(fù)載均衡能力,都達(dá)到最大化。
所以,你可以使用NGINX或NGINX Plus來(lái)支持多個(gè)Node.js服務(wù)器的負(fù)載均衡。只有NGINX才有可能讓你最大化地實(shí)現(xiàn)負(fù)載均衡性能和友好的Node.js有狀態(tài)性。內(nèi)置于NGINX的應(yīng)用健康檢查以及監(jiān)控功能也很有用。
NGINX Plus還支持會(huì)話維持,因此允許應(yīng)用程序服務(wù)器在它采取停止服務(wù)的請(qǐng)求之后,還能優(yōu)雅地完成當(dāng)前會(huì)話。
4.代理WebSocket連接
HTTP,在所有版本里,是專(zhuān)為“pull”通信——來(lái)自于服務(wù)器的客戶端請(qǐng)求文件設(shè)計(jì)的。WebSocket是一個(gè)允許“push”和“push/pull”通信的工具,即服務(wù)器可以主動(dòng)發(fā)送客戶端沒(méi)有請(qǐng)求的文件。
WebSocket協(xié)議可以更容易地支持客戶端和服務(wù)器之間更堅(jiān)固的相互作用,同時(shí)減少傳輸?shù)臄?shù)據(jù)量并最小化等待時(shí)間。當(dāng)需要時(shí),可以實(shí)現(xiàn)全雙工傳輸連接,也就是說(shuō)根據(jù)需要客戶和服務(wù)器都可以發(fā)起并接收請(qǐng)求。
WebSocket協(xié)議具有強(qiáng)大的JavaScript接口,因此非常適合作為應(yīng)用服務(wù)器的Node.js——而且,對(duì)于事務(wù)量不多的web應(yīng)用程 序,也可以作為web服務(wù)器。當(dāng)事務(wù)量增加,那么在客戶端和Node.js web服務(wù)器之間,多個(gè)應(yīng)用服務(wù)器之間使用NGINX或NGINX Plus插入NGINX就有必要了。
Node.js通常與Socket.IO聯(lián)合使用,Socket.IO是一個(gè)WebSocket API,它在Node.js應(yīng)用程序中很受歡迎。這可能會(huì)導(dǎo)致port 80(對(duì)于HTTP)或port 443(對(duì)于HTTPS)變得相當(dāng)擁擠,而解決方法就是代理Socket.IO服務(wù)器。你可以使用NGINX作為代理服務(wù)器中,就像前面說(shuō)的那樣,并且還 獲得其他的功能,例如靜態(tài)文件緩存,負(fù)載均衡等。
以下就是作為server.js node應(yīng)用程序文件監(jiān)聽(tīng)port 5000的代碼。它擔(dān)當(dāng)了代理服務(wù)器(而不是web服務(wù)器)的角色,并路由請(qǐng)求到正確的端口:
- 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(); // 這是你的初始化代碼。
有關(guān)完整的介紹,包括NGINX配置,請(qǐng)參閱此博客文章。有關(guān)這一類(lèi)更深入的web應(yīng)用程序潛在架構(gòu)和基礎(chǔ)設(shè)施問(wèn)題,請(qǐng)參閱此博客文章。
5.實(shí)現(xiàn)SSL / TLS和HTTP / 2
越來(lái)越多的網(wǎng)站使用SSL / TLS來(lái)保護(hù)網(wǎng)站上所有用戶的交互。你可以決定是否以及何時(shí)做出這個(gè)舉動(dòng),但如果你選擇了這么做,那么NGINX有兩種方式來(lái)支持這個(gè)轉(zhuǎn)變:
-
你可以在NGINX里終止SSL / TLS連接到客戶端,如果你設(shè)置了NGINX作為反向代理的話。 Node.js服務(wù)器使用Nginx反向代理服務(wù)器來(lái)來(lái)回回地發(fā)送和接收未加密的請(qǐng)求和內(nèi)容。
-
早期跡象表明,使用HTTP / 2——新的HTTP協(xié)議的新版本——可以在很大程度上或完全抵消使用SSL / TLS強(qiáng)加的性能損失。 NGINX支持HTTP / 2,你可以終止HTTP / 2和SSL,而在Node.js應(yīng)用服務(wù)器中無(wú)需做任何改變。
在你采取這些實(shí)現(xiàn)步驟的時(shí)候,你還需要更新在Node.js配置文件中的URL,建立和完善在NGINX配置中的安全連接,必要時(shí)還可以使用 SPDY或HTTP / 2。添加HTTP / 2支持意味著瀏覽器版本使用新的協(xié)議支持HTTP / 2與應(yīng)用程序進(jìn)行通信:老版本的瀏覽器使用HTTP / 1.x。
下面的配置代碼適用于使用SPDY的Ghost博客。它包括一些高級(jí)功能,如OCSP stapling。使用NGINX用于SSL終端,包括OCSP stapling選項(xiàng),看這里。對(duì)于同一主題的概述,看這里。
你需要做的輕微改動(dòng)就是配置Node.js應(yīng)用程序,從SPDY升級(jí)到HTTP / 2,時(shí)間可以是現(xiàn)在,也可以是2016年初SPDY支持消失的時(shí)候。
- 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;
- }
- }
結(jié)論
本文介紹了一些最重要的可以在Node.js應(yīng)用程序布置的性能改進(jìn)。它著重于添加到應(yīng)用程序的NGINX以及Node.js——通過(guò)使用 NGINX作為反向代理服務(wù)器,緩存靜態(tài)文件,負(fù)載均衡,代理WebSocket連接,并終止SSL / TLS和HTTP / 2協(xié)議。
NGINX和Node.js的結(jié)合,被廣泛認(rèn)為是一種創(chuàng)建新的微服務(wù)型應(yīng)用程序或增加靈活性和性能到現(xiàn)有的基于SOA的使用Java或 Microsoft .NET應(yīng)用的方法。這篇文章可以幫助你優(yōu)化Node.js應(yīng)用程序,讓Node.js和NGINX的伙伴關(guān)系為你所用。
譯文鏈接:http://www.codeceo.com/article/5-tips-nodejs-performance.html
英文原文:5 Tips to Increase Node.js Application Performance