Node.js 受益於它的事件驅動和異步的特徵,已經很快了。但是,在現代網絡中只是快是不行的。如果你打算用Node.js 開發你的下一個Web 應用的話,那麼你就應該無所不用其極,讓你的應用更快,異常的快。本文將介紹10 條,經過檢驗得知可大大提高Node 應用的技巧。廢話不多說,讓我們逐條來看看。
1. 並行
創建Web 應用的時候,你可能要多次調用內部API 來獲取各種數據。比如說,假設在Dashboard 頁面上,你要執行下面這幾個調用:
用戶信息-getUserProfile().
當前活動-getRecentActivity().
訂閱內容-getSubscriptions().
通知內容-getNotifications().
為了拿到這些信息,你應該會為每個方法創建獨立的中間件,然後將它們鏈接到Dashboard 路由上。不過問題是,這些方法的執行是線性的,上一個沒結束之前下一個不會開始。可行解決案是並行調用它們。
如你所知由於異步性,Node.js 非常擅長並行調用多個方法。我們不能暴殄天物。我上面提到的那些方法沒有依賴性,所以我們可以並行執行它們。這樣我們可以削減中間件數量,大幅提高速度。
我們可以用async.js來處理並行,它是一個專門用來調教JavaScript 異步的Node 模塊。下面代碼演示怎樣用async.js 並行調用多個方法的:
複製代碼代碼如下:
function runInParallel() {
async.parallel([
getUserProfile,
getRecentActivity,
getSubscriptions,
getNotifications
], function(err, results) {
//This callback runs when all the functions complete
});
}
如果你想更深入了解async.js ,請移步它的GitHub 頁面。
2. 異步
根據設計Node.js 是單線程的。基於這點,同步代碼會堵塞整個應用。比如說,多數的文件系統API 都有它們的同步版本。下面代碼演示了文件讀取的同步和異步兩種操作:
複製代碼代碼如下:
// Asynchronous
fs.readFile('file.txt', function(err, buffer) {
var content = buffer.toString();
});
// Synchronous
var content = fs.readFileSync('file.txt').toString();
不過要是你執行那種長時間的阻塞操作,主線程就會被阻塞到這些操作完成為止。這大大降低你應用的性能。所以,最好確保你的代碼裡用的都是異步版本API,最起碼你應該在性能節點異步。而且,你在選用第三方模塊的時候也要很小心。因為當你想方設法把同步操作從你代碼中剔除之後,一個外部庫的同步調用會讓你前功盡棄,降低你的應用性能。
3. 緩存
如果你用到一些不經常變化的數據,你應該把它們緩存起來,改善性能。比如說,下面的代碼是獲取最新帖子並顯示的例子:
複製代碼代碼如下:
var router = express.Router();
router.route('/latestPosts').get(function(req, res) {
Post.getLatest(function(err, posts) {
if (err) {
throw err;
}
res.render('posts', { posts: posts });
});
});
如果你不經常發貼的話,你可以把帖子列表緩存起來,然後一段時間之後再把它們清理掉。比如,我們可以用Redis模塊來達到這個目的。當然,你必須在你的服務器上裝Redis。然後你可以用叫做node_redis的客戶端來保存鍵/值對。下面的例子演示我們怎麼緩存帖子:
複製代碼代碼如下:
var redis = require('redis'),
client = redis.createClient(null, null, { detect_buffers: true }),
router = express.Router();
router.route('/latestPosts').get(function(req,res){
client.get('posts', function (err, posts) {
if (posts) {
return res.render('posts', { posts: JSON.parse(posts) });
}
Post.getLatest(function(err, posts) {
if (err) {
throw err;
}
client.set('posts', JSON.stringify(posts));
res.render('posts', { posts: posts });
});
});
});
看到了吧,我們首先檢查Redis 緩存,看看是否有帖子。如果有,我們從緩存中拿這些帖子列表。否則我們就檢索數據庫內容,然後把結果緩存。此外,一定時間之後,我們可以清理Redis 緩存,這樣就可以更新內容了。
4. gzip 壓縮
開啟gzip 壓縮對你的Web 應用會產生巨大影響。當一個gzip 壓縮瀏覽器請求某些資源的時候,服務器會在響應返回給瀏覽器之前進行壓縮。如果你不用gzip 壓縮你的靜態資源,瀏覽器拿到它們可能會花費更長時間。
在Express 應用中,我們可以用內建express.static() 中間件來處理靜態內容。此外,還可以用compression 中間件壓縮和處理靜態內容。下面是使用例:
複製代碼代碼如下:
var compression = require('compression');
app.use(compression()); //use compression
app.use(express.static(path.join(__dirname, 'public')));
5. 如果可以,在用客戶端渲染
現在有超多功能強勁的客戶端MVC/MVVM 框架,比如說AngularJS,Ember,Meteor, 等等,構建一個單頁面應用變得非常簡單。基本上,你只要公開一個API,返回JSON響應給客戶端就可以了,而不需要在服務端渲染頁面。在客戶端,你可以用框架來組織JSON 然後把它們顯示在UI 上。服務端只發送JSON 響應可以節省帶寬,改善性能,因為你不需要在每個響應裡面都返回佈局標記了,對吧,你只需要返回純JSON,然後在客戶端渲染它們。
看下我的這個教程,它是關於怎樣用Express 4 公開一個RESTful APIs的。我還寫了另一篇教程,演示了怎樣把這些APIs 和AngularJS 結合起來。
6. 不要在Sessions 存儲太多數據
典型的Express頁面應用, Session 數據默認是保存在內存中的。當你把太多數據保存在Session 的時候,會導致服務器開銷顯著增大。所以,要么你切換到別的儲存方式來保存Session 數據,要么盡量減少存儲在Session 中的數據量。
比如說,當用戶登錄到你的應用的時候,你可以只在Session 中保存他們的ID 而不是整個用戶數據對象。還有,對於那些你能夠從id 拿到對象的查詢,你應該會喜歡用MongoDB或者Redis來存儲session 數據。
7. 優化查詢
假設你有個博客,你要在主頁上顯示最新帖子。你可能會通過Mongoose這樣取數據:
複製代碼代碼如下:
Post.find().limit(10).exec(function(err, posts) {
//send posts to client
});
不過問題是Mongoose 的find() 方法會把對象的所有字段都查詢出來,而許多字段在主頁上並不要求。比如說,commentsis 保存的是特定帖子的回复。我們不需要顯示文章回复,所以我們可以在查詢的時候把它給剔除掉。這無疑會提高速度。可以像這樣優化上面那條查詢:
複製代碼代碼如下:
Post.find().limit(10).exclude('comments').exec(function(err, posts) {
//send posts to client
});
8. 用標準的V8 方法
集合上的一些操作,比如map,reduce,和forEach 不一定支持所有瀏覽器。我們可以通過前台的庫解決部分瀏覽器兼容性問題。但對於Node.js,你要確切知道Google 的V8 JavaScript 引擎支持哪些操作。這樣,你就可以在服務端直接用這些內建方法來操作集合了。
9. 在Node 前面用Nginx
Nginx是個微小型輕量Web 服務器,用它可以降低你的Node.js服務器的負載。你可以把靜態資源配置到nginx 上,而不是在Node 上。你可以在nginx 上用gzip 壓縮響應,讓所有的響應都變得更小。所以,如果你有個正在營運的產品,我覺得你應該會想用nginx 來改善運行速度的。
10. 打包JavaScript
最後,你還可以大大提高頁面應用速度,通過把多個JS 文件打包。當瀏覽器在頁面渲染中碰到<script> 元素的時候會被堵塞,直到拿到這個腳本才繼續運行(除非設置了異步屬性)。比如,如果你的頁面有五個JavaScript 文件,瀏覽器會發出五個獨立的HTTP 請求來獲取他們。如果把這五個文件壓縮打包成一個,整體性能將可以大幅提升。 CSS 文件也是一樣。你可以用諸如Grunt/Gulp 這樣的編譯工具來打包你的資源文件。
結論
上面10 條技巧肯定可以提高你的Web 應用的速度的。不過,我知道還有改善和優化的空間。如果你有任何改善性能的技巧的話,在回复裡告訴我。
謝謝閱讀!