nodejs中實(shí)現(xiàn)阻塞實(shí)例
node.js中與生俱來(lái)的單線程編程、回調(diào)函數(shù)異步式風(fēng)格讓我們有時(shí)喜有時(shí)憂。先說(shuō)單線程,很多人會(huì)費(fèi)解于node.js的單線程如何能做到高并發(fā)?這個(gè)問(wèn)題不是本文重點(diǎn),點(diǎn)到為止。澄清一點(diǎn),node.js的單線程僅僅指javascript引擎是單線程的,無(wú)論如何我們沒(méi)有辦法在javascript中實(shí)現(xiàn)多線程和阻塞(本文用到的方法同樣不是通過(guò)V8引擎實(shí)現(xiàn)同步的);但對(duì)于node.js的其他方面不代表不能多線程,例如IO。如果現(xiàn)在node.js遭受大量請(qǐng)求,而這些請(qǐng)求都是IO密集型的,那么此時(shí)node每接受一個(gè)請(qǐng)求,在遇到耗時(shí)較長(zhǎng)的IO操作時(shí),javascript線程并不會(huì)一直在此等待,而是交出控制,在回調(diào)堆棧里添加IO操作完成后要執(zhí)行的操作(當(dāng)回調(diào)層級(jí)過(guò)多,訪問(wèn)數(shù)量過(guò)大,大量的回調(diào)鏈可能會(huì)爆棧)。而在這段時(shí)間內(nèi),node.js又可以處理其他請(qǐng)求了。所以對(duì)于node.js而言,雖然javascript是單線程的,每次只能處理一個(gè)請(qǐng)求,但javascript處理一個(gè)請(qǐng)求的時(shí)間往往較短(對(duì)于IO密集型應(yīng)用而言),只要可以異步處理,那么在處理的過(guò)程中,此次請(qǐng)求都會(huì)釋放控制,使node.js能處理其他請(qǐng)求。這并發(fā)請(qǐng)求的同時(shí),IO其實(shí)一直處于并發(fā)狀態(tài),減少處理請(qǐng)求的線程數(shù),節(jié)約資源以增加IO的線程數(shù),對(duì)于通常耗時(shí)很長(zhǎng)的IO密集型請(qǐng)求來(lái)說(shuō),無(wú)疑能帶來(lái)性能上的提升。
前面啰啰嗦嗦地一直在強(qiáng)調(diào)IO密集型,其實(shí)是在強(qiáng)調(diào)node.js的強(qiáng)項(xiàng)。相應(yīng)的,它的短板就是CPU密集型的請(qǐng)求。道理很簡(jiǎn)單,javascript不會(huì)并發(fā),只能一個(gè)請(qǐng)求完成后才能處理其他請(qǐng)求。一個(gè)請(qǐng)求處理的時(shí)間越長(zhǎng),其他請(qǐng)求等待的時(shí)間越長(zhǎng)。同一時(shí)間只會(huì)有一個(gè)請(qǐng)求被處理,并發(fā)性能很低。
話說(shuō)到這兒,我想申明一點(diǎn):node.js不應(yīng)該被阻塞;能異步處理的方法異步處理(如使用fs.readFile(),而非fs.syncReadFile()fs.readFileSync()方法)。
node中不能阻塞,并不代表node外不能阻塞。前面我們有講到fibers,現(xiàn)在,我們就來(lái)嘗試在fibers中實(shí)現(xiàn)阻塞。就以處理一個(gè)http請(qǐng)求為例吧:
var Fiber = require('fibers');
var http = require("http");
Fiber(function () {
var httpFiber = Fiber.current;
var html = "";
http.get("http://www.baidu.com", function (res) {
var dataFiber = Fiber.current;
res.on("data", function (data) {
html += data;
});
res.on("end", function (data) {
httpFiber.run();
});
});
Fiber.yield();
console.log(html);
}).run();
yield()、 run()這兩個(gè)方法還不了解的同學(xué),請(qǐng)自行查閱《fibers in node》。
fibers的運(yùn)行并不在node進(jìn)程中,所以在fibers內(nèi)部實(shí)現(xiàn)阻塞對(duì)node整體的性能并沒(méi)有影響。而且實(shí)現(xiàn)起來(lái)也是相當(dāng)容易,只需要在想阻塞的時(shí)候,把fiber yield掉。需要繼續(xù)運(yùn)行,則執(zhí)行 run()恢復(fù)fiber。在上面的例子中,我們希望當(dāng)http.get請(qǐng)求發(fā)起時(shí)阻塞當(dāng)前程序,當(dāng)所有數(shù)據(jù)接收完成時(shí),恢復(fù)程序。于是我們?cè)谡{(diào)用http.get后使用 Fiber.yield()中斷此fiber。在對(duì)response的監(jiān)聽(tīng)中,如果觸發(fā) end事件表明數(shù)據(jù)傳輸完成,于是在 end的回調(diào)函數(shù)中,調(diào)用 Fiber.current.run()恢復(fù)fiber,這樣,后續(xù)的代碼就以同步的方式拿到http.get請(qǐng)求的數(shù)據(jù)。
上面的示例只是提供一種思路。如果對(duì)這種思路進(jìn)行一些抽象封裝,比如說(shuō),對(duì)有接受回調(diào)函數(shù)為參數(shù)的異步方法進(jìn)行一步柯里化,在調(diào)用后中斷,并劫持回調(diào)函數(shù),以恢復(fù)程序的代碼為回調(diào)函數(shù)。獲取異步數(shù)據(jù)后,再程序觸發(fā)預(yù)定的回調(diào)函數(shù),這樣基本能實(shí)現(xiàn)異步方法同步化。這段說(shuō)得比較亂,基本上就是 fibers/future的實(shí)現(xiàn)思路,如果有興趣,請(qǐng)參考其源代碼。
- 輕松創(chuàng)建nodejs服務(wù)器(7):阻塞操作的實(shí)現(xiàn)
- 輕松創(chuàng)建nodejs服務(wù)器(8):非阻塞是如何實(shí)現(xiàn)的
- 輕松創(chuàng)建nodejs服務(wù)器(9):實(shí)現(xiàn)非阻塞操作
- NodeJs中的非阻塞方法介紹
- Nodejs極簡(jiǎn)入門教程(三):進(jìn)程
- Nodejs進(jìn)程管理模塊forever詳解
- 利用NodeJS的子進(jìn)程(child_process)調(diào)用系統(tǒng)命令的方法分享
- 防止Node.js中錯(cuò)誤導(dǎo)致進(jìn)程阻塞的辦法
相關(guān)文章
Node.js利用debug模塊打印出調(diào)試日志的方法
debug日志打印模塊主要實(shí)現(xiàn)功能是帶命名空間(模塊名)、時(shí)間戳、色彩輸出日志;將日志寫入文件;瀏覽器端使用;格式化函數(shù);支持自定義方法。下面這篇文章主要介紹了Node.js利用debug模塊打印出調(diào)試日志的方法,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-04-04學(xué)習(xí)使用grunt來(lái)打包JavaScript和CSS程序的教程
這篇文章主要介紹了學(xué)習(xí)使用grunt來(lái)打包JavaScript和CSS程序的教程,grunt基于node.js和需要的朋友可以參考下2016-01-01在Linux系統(tǒng)中搭建Node.js開發(fā)環(huán)境的簡(jiǎn)單步驟講解
這篇文章主要介紹了在Linux系統(tǒng)中搭建Node.js開發(fā)環(huán)境的步驟,Node使得JavaScript程序可以在本地操作系統(tǒng)環(huán)境中解釋運(yùn)行,需要的朋友可以參考下2016-01-01淺談Node.js輕量級(jí)Web框架Express4.x使用指南
本篇文章主要介紹了淺談Node.js輕量級(jí)Web框架Express4.x使用指南,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05如何使用nvm實(shí)現(xiàn)nodejs版本管理(版本刪除,版本切換及版本添加)
這篇文章主要給大家介紹了關(guān)于如何使用nvm實(shí)現(xiàn)nodejs版本管理(版本刪除,版本切換及版本添加)的相關(guān)資料,nvm是一個(gè)node版本管理工具,通過(guò)它可以安裝多種node版本并且可以快速、簡(jiǎn)單的切換node版本,需要的朋友可以參考下2023-10-10