這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)Node.js中的單線(xiàn)程模型是什么,文章內(nèi)容豐富且以專(zhuān)業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),豐潤(rùn)企業(yè)網(wǎng)站建設(shè),豐潤(rùn)品牌網(wǎng)站建設(shè),網(wǎng)站定制,豐潤(rùn)網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷(xiāo),網(wǎng)絡(luò)優(yōu)化,豐潤(rùn)網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力。可充分滿(mǎn)足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專(zhuān)業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶(hù)成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
1、高并發(fā)
一般來(lái)說(shuō),高并發(fā)的解決方案就是多線(xiàn)程模型,服務(wù)器為每個(gè)客戶(hù)端請(qǐng)求分配一個(gè)線(xiàn)程,使用同步I/O,系統(tǒng)通過(guò)線(xiàn)程切換來(lái)彌補(bǔ)同步I/O調(diào)用的時(shí)間開(kāi)銷(xiāo),比如Apache就是這種策略,由于I/O一般都是耗時(shí)操作,因此這種策略很難實(shí)現(xiàn)高性能,但非常簡(jiǎn)單,可以實(shí)現(xiàn)復(fù)雜的交互邏輯。
而事實(shí)上,大多數(shù)網(wǎng)站的服務(wù)器端都不會(huì)做太多的計(jì)算,它們只是接收請(qǐng)求,交給其它服務(wù)(比如從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)),然后等著結(jié)果返回再發(fā)給客戶(hù)端。因此,Node.js針對(duì)這一事實(shí)采用了單線(xiàn)程模型來(lái)處理,它不會(huì)為每個(gè)接入請(qǐng)求分配一個(gè)線(xiàn)程,而是用一個(gè)主線(xiàn)程處理所有的請(qǐng)求,然后對(duì)I/O操作進(jìn)行異步處理,避開(kāi)了創(chuàng)建、銷(xiāo)毀線(xiàn)程以及在線(xiàn)程間切換所需的開(kāi)銷(xiāo)和復(fù)雜性。
2、事件循環(huán)
Node.js 在主線(xiàn)程中維護(hù)了一個(gè)事件隊(duì)列,當(dāng)接收到請(qǐng)求后,就將請(qǐng)求作為一個(gè)事件放入該隊(duì)列中,然后繼續(xù)接收其他請(qǐng)求。當(dāng)主線(xiàn)程空閑時(shí)(沒(méi)有請(qǐng)求接入時(shí)),就開(kāi)始循環(huán)事件隊(duì)列,檢查隊(duì)列中是否有要處理的事件,這時(shí)要分兩種情況:如果是非I/O任務(wù),就親自處理,并通過(guò)回調(diào)函數(shù)返回到上層調(diào)用;如果是I/O任務(wù),就從線(xiàn)程池中拿出一個(gè)線(xiàn)程來(lái)執(zhí)行這個(gè)事件,并指定回調(diào)函數(shù),然后繼續(xù)循環(huán)隊(duì)列中的其他事件。當(dāng)線(xiàn)程中的I/O任務(wù)完成后,就執(zhí)行指定的回調(diào)函數(shù),并把這個(gè)完成的事件放到事件隊(duì)列的尾部,等待事件循環(huán),當(dāng)主線(xiàn)程再次循環(huán)到該事件時(shí),就直接處理并返回給上層調(diào)用。 這個(gè)過(guò)程就叫事件循環(huán)(Event Loop),如下圖所示:
這個(gè)圖是整個(gè)Node.js的運(yùn)行原理,從左到右,從上到下,Node.js被分成了四層,分別是應(yīng)用層、V8引擎層、Node API層 和 LIBUV層,
應(yīng)用層: 即Javascript交互層,常見(jiàn)的就是Node.js的模塊,比如 http,fs
V8引擎層: 即利用V8引擎來(lái)解析Javascript語(yǔ)法,進(jìn)而和下層API交互
NodeAPI層: 為上層模塊提供系統(tǒng)調(diào)用,一般是由C語(yǔ)言來(lái)實(shí)現(xiàn),和操作系統(tǒng)進(jìn)行交互
LIBUV層: 即Event Loop,是Node.js實(shí)現(xiàn)異步的核心,由LIBUV庫(kù)來(lái)實(shí)現(xiàn),而LIBUV中的線(xiàn)程池是由操作系統(tǒng)內(nèi)核接受管理的。
從上述理解來(lái)看,Node.js的單線(xiàn)程僅僅是指Javascript運(yùn)行在單線(xiàn)程中,而并非Node.js是單線(xiàn)程,在Node中,無(wú)論是Linux平臺(tái)還是Windows平臺(tái),內(nèi)部都是通過(guò)線(xiàn)程池來(lái)完成IO操作,而LIBUV就是針對(duì)不同平臺(tái)的差異性實(shí)現(xiàn)了統(tǒng)一調(diào)用。
3、事件驅(qū)動(dòng)
總結(jié)上面的過(guò)程可以發(fā)現(xiàn),Node.js的核心是使用事件驅(qū)動(dòng)模式實(shí)現(xiàn)了異步I/O,為了更具體、更清晰的理解和接受這個(gè)事實(shí),我們用代碼來(lái)描述Node.js的事件驅(qū)動(dòng)模型:
3.1、事件隊(duì)列
首先,我們需要定義一個(gè)事件隊(duì)列,既然是隊(duì)列,那就是一個(gè)先進(jìn)先出(FIFO)的數(shù)據(jù)結(jié)構(gòu),我們用JS的數(shù)組來(lái)描述,如下:
/** * 定義事件隊(duì)列 * 入隊(duì):unshfit() * 出隊(duì):pop() * 空隊(duì)列:length == 0 */ eventQueue:[],
為了方便理解,我們規(guī)定:數(shù)組的第一個(gè)元素是隊(duì)列的尾部,數(shù)組的最后一個(gè)元素是隊(duì)列的頭部, unshfit 就是在尾部插入一個(gè)元素,pop就是從頭部彈出一個(gè)元素,這樣就實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的隊(duì)列。
3.2、接收請(qǐng)求
定義一個(gè)總的入口來(lái)接收用戶(hù)請(qǐng)求,如下所示:
/** * 接收用戶(hù)請(qǐng)求 * 每一個(gè)請(qǐng)求都會(huì)進(jìn)入到該函數(shù) * 傳遞參數(shù)request和response */ processHttpRequest:function(request,response){ //定義一個(gè)事件對(duì)象 var event = createEvent({ params:request.params, //傳遞請(qǐng)求參數(shù) result:null, //存放請(qǐng)求結(jié)果 callback:function(){} //指定回調(diào)函數(shù) }); //在隊(duì)列的尾部添加該事件 eventQueue.unshift(event); },
這個(gè)函數(shù)很簡(jiǎn)單,就是把用戶(hù)的請(qǐng)求包裝成事件,放到隊(duì)列里,然后繼續(xù)接收其他請(qǐng)求。
3.3、事件循環(huán)
當(dāng)主線(xiàn)程處于空閑時(shí)就開(kāi)始循環(huán)事件隊(duì)列,所以,我們?cè)俣x一個(gè)事件循環(huán)的函數(shù):
/** * 事件循環(huán)主體,主線(xiàn)程擇機(jī)執(zhí)行 * 循環(huán)遍歷事件隊(duì)列 * 處理事件 * 執(zhí)行回調(diào),返回給上層 */ eventLoop:function(){ //如果隊(duì)列不為空,就繼續(xù)循環(huán) while(this.eventQueue.length > 0){ //從隊(duì)列的頭部拿出一個(gè)事件 var event = this.eventQueue.pop(); //如果是IO任務(wù) if(isIOTask(event)){ //從線(xiàn)程池里拿出一個(gè)線(xiàn)程 var thread = getThreadFromThreadPool(); //交給線(xiàn)程處理 thread.handleIOTask(event) }else { //非IO任務(wù)處理后,直接返回結(jié)果 var result = handleEvent(event); //最終通過(guò)回調(diào)函數(shù)返回給V8,再由V8返回給應(yīng)用程序 event.callback.call(null,result); } } },
主線(xiàn)程不停的檢測(cè)事件隊(duì)列,對(duì)于IO任務(wù)就交給線(xiàn)程池來(lái)處理,非IO任務(wù)就自己處理并返回。
3.4、線(xiàn)程池
線(xiàn)程池接到任務(wù)以后,直接處理IO操作,比如讀取數(shù)據(jù)庫(kù):
當(dāng)IO
/** * 處理IO任務(wù) * 完成后將事件添加到隊(duì)列尾部 * 釋放線(xiàn)程 */ handleIOTask:function(event){ //當(dāng)前線(xiàn)程 var curThread = this; //操作數(shù)據(jù)庫(kù) var optDatabase = function(params,callback){ var result = readDataFromDb(params); callback.call(null,result) }; //執(zhí)行IO任務(wù) optDatabase(event.params,function(result){ //返回結(jié)果存入事件對(duì)象中 event.result = result; //IO完成后,將不再是耗時(shí)任務(wù) event.isIOTask = false; //將該事件重新添加到隊(duì)列的尾部 this.eventQueue.unshift(event); //釋放當(dāng)前線(xiàn)程 releaseThread(curThread) }) }
任務(wù)完成以后就執(zhí)行回調(diào),把請(qǐng)求結(jié)果存入事件中,并將該事件重新放入隊(duì)列中,等待循環(huán),最后釋放線(xiàn)程。當(dāng)主線(xiàn)程再次循環(huán)到該事件時(shí),就直接處理了。
4、Node.js軟肋
以上四步簡(jiǎn)單描述了Node.js事件驅(qū)動(dòng)模型,至此,我們對(duì)Node.js應(yīng)該有了一個(gè)簡(jiǎn)單而又清晰的認(rèn)識(shí),但Node.js 并不是什么都能做。
上面提到,如果是I/O任務(wù),Nodejs就把任務(wù)交給線(xiàn)程池來(lái)異步處理,高效簡(jiǎn)單,因此Node.js適合處理I/O密集型任務(wù),但不是所有的任務(wù)都是I/O密集型任務(wù),當(dāng)碰到CPU密集型任務(wù)時(shí),就是只用CPU計(jì)算的操作,比如要對(duì)數(shù)據(jù)加解密(node.bcrypt.js),數(shù)據(jù)壓縮和解壓(node-tar),這時(shí)Node.js就會(huì)親自處理,一個(gè)一個(gè)的計(jì)算,前面的任務(wù)沒(méi)有執(zhí)行完,后面的任務(wù)只能干等著,如下圖所示:
在事件隊(duì)列中,如果前面的CPU計(jì)算任務(wù)沒(méi)有完成,那么后面的任務(wù)就會(huì)被阻塞,出現(xiàn)響應(yīng)緩慢的情況,如果操作系統(tǒng)本身就是單核,那也就算了,但現(xiàn)在大部分服務(wù)器都是多CPU或多核的,而Node.js只有一個(gè)EventLoop,也只占用一個(gè)CPU/內(nèi)核,當(dāng)Node.js被CPU密集型任務(wù)占用,導(dǎo)致其他任務(wù)被阻塞時(shí),卻還有CPU/內(nèi)核處理閑置狀態(tài),造成資源浪費(fèi)。因此Node.js不適合CPU密集型任務(wù)。
5、Node.js適用場(chǎng)景
5.1、RESTful API
這是適合 Node 的理想情況,因?yàn)槟梢詷?gòu)建它來(lái)處理數(shù)萬(wàn)條連接。它仍然不需要大量邏輯;它本質(zhì)上只是從某個(gè)數(shù)據(jù)庫(kù)中查找一些值并將它們組成一個(gè)響應(yīng)。由于響應(yīng)是少量文本,入站請(qǐng)求也是少量的文本,因此流量不高,一臺(tái)機(jī)器甚至也可以處理最繁忙的公司的 API 需求。
5.2、實(shí)時(shí)程序
比如聊天服務(wù),聊天應(yīng)用程序是最能體現(xiàn) Node.js 優(yōu)點(diǎn)的例子:輕量級(jí)、高流量并且能良好的應(yīng)對(duì)跨平臺(tái)設(shè)備上運(yùn)行密集型數(shù)據(jù)(雖然計(jì)算能力低)。同時(shí),聊天也是一個(gè)非常值得學(xué)習(xí)的用例,因?yàn)樗芎?jiǎn)單,并且涵蓋了目前為止一個(gè)典型的 Node.js 會(huì)用到的大部分解決方案。
上述就是小編為大家分享的Node.js中的單線(xiàn)程模型是什么了,如果剛好有類(lèi)似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
分享題目:Node.js中的單線(xiàn)程模型是什么
文章網(wǎng)址:http://www.chinadenli.net/article8/piihip.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供Google、企業(yè)建站、、營(yíng)銷(xiāo)型網(wǎng)站建設(shè)、App開(kāi)發(fā)、小程序開(kāi)發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)