1. 程式人生 > >淺析 Node.js 單執行緒模型

淺析 Node.js 單執行緒模型

總結筆記:對於每個使用者請求,由主執行緒接收並存放於一個事件佇列中(不做任何處理),當無請求發生時,即主執行緒空閒,主執行緒開始迴圈處理事件佇列中的任務:

對於非阻塞JS程式:

1、若某事件需要I/O操作,則主執行緒發出I/O請求,然後繼續執行,由底層的程式實現I/O並返回I/O資料(底層程式是多執行緒的,JS是單執行緒的),底層I/O執行緒處理完後將該事件重新放入事件佇列並釋放當前執行緒;

2、某事件不需要I/O操作,則主執行緒直接處理;(由其他執行緒處理後放入的事件此時也被主執行緒直接處理掉);

對於阻塞JS程式:

1、若某事件需要I/O操作,則主執行緒發出I/O請求,然後等待I/O結束,由底層的程式實現I/O並返回I/O資料,主執行緒獲得該事件所需資料後繼續處理該事件;

2、某事件不需要I/O操作,則主執行緒直接處理;

綜上可知,node.js由js解釋程式和底層程式碼實現,JS程式碼是主執行緒,是單執行緒執行,而底層程式碼是多執行緒,可同時處理多個I/O請求,js中的阻塞與非阻塞程式碼只決定js在I/O時繼不繼續執行(當然,若阻塞執行,底層多執行緒也沒啥用了),而底層會為每一個I/O請求建立一個執行緒;

注意:這只是對Node.js的一個分析,用來理解nodejs的執行緒模型而已,實際使用要具體問題具體分析,建議結合http://www.runoob.com/nodejs/nodejs-callback.html中的阻塞與非阻塞來學習,阻塞即只要一個主執行緒執行所有操作,當事件需要I/O操作則主執行緒等待I/O完成再繼續執行,而非阻塞,即對事件處理使用了事件回撥,此時,主執行緒將繼續執行下一步的程式碼而不用等待該事件I/O完成,當I/O完成時主執行緒再針對該事件執行相應的回撥函式;

例如:1、

var http =require('http');

http.createServer(function(request, response){// 傳送 HTTP 頭部 // HTTP 狀態值: 200 : OK// 內容型別: text/plain
    response.writeHead(200,{'Content-Type':'text/plain'});// 傳送響應資料 "Hello World"
    response.end('Hello World\n');}).listen(8888);// 終端列印如下資訊
console.log('Server running at http://127.0.0.1:8888/'
);
該主執行緒只做三件事:1、偵聽8888埠(偵聽操作也可以理解為是I/O操作,因而應當也是由底層程式實現,即底層程式監聽埠,若有事件,則放入事件佇列,繼續偵聽埠); 2、JS主執行緒處理並生成返回資料; 3、返回處理結果(此步驟是I/O操作,由執行緒池處理);

2、

var fs =require("fs");

fs.readFile('input.txt',function(err, data){if(err)return console.error(err);
    console.log(data.toString());});

console.log("程式執行結束!");
該主執行緒在執行I/O時不等待I/O完成,直接繼續執行,執行緒池執行緒執行完後將結果返還給主執行緒,主執行緒執行回撥函式並處理事件;

正文

Node.js 採用事件驅動和非同步 I/O 的方式,實現了一個單執行緒、高併發的 JavaScript 執行時環境,而單執行緒就意味著同一時間只能做一件事,那麼 Node.js 如何通過單執行緒來實現高併發和非同步 I/O?本文將圍繞這個問題來探討 Node.js 的單執行緒模型 。

1、高併發策略

一般來說,高併發的解決方案就是提供多執行緒模型,伺服器為每個客戶端請求分配一個執行緒,使用同步 I/O,系統通過執行緒切換來彌補同步 I/O 呼叫的時間開銷。比如 Apache 就是這種策略,由於 I/O 一般都是耗時操作,因此這種策略很難實現高效能,但非常簡單,可以實現複雜的互動邏輯。

而事實上,大多數網站的伺服器端都不會做太多的計算,它們接收到請求以後,把請求交給其它服務來處理(比如讀取資料庫),然後等著結果返回,最後再把結果發給客戶端。因此,Node.js 針對這一事實採用了單執行緒模型來處理,它不會為每個接入請求分配一個執行緒,而是用一個主執行緒處理所有的請求,然後對 I/O 操作進行非同步處理,避開了建立、銷燬執行緒以及線上程間切換所需的開銷和複雜性。

2、事件迴圈

Node.js 在主執行緒裡維護了一個事件佇列,當接到請求後,就將該請求作為一個事件放入這個佇列中,然後繼續接收其他請求。當主執行緒空閒時(沒有請求接入時),就開始迴圈事件佇列,檢查佇列中是否有要處理的事件,這時要分兩種情況:如果是非 I/O 任務,就親自處理,並通過回撥函式返回到上層呼叫;如果是 I/O 任務,就從 執行緒池 中拿出一個執行緒來處理這個事件,並指定回撥函式,然後繼續迴圈佇列中的其他事件。

當執行緒中的 I/O 任務完成以後,就執行指定的回撥函式,並把這個完成的事件放到事件佇列的尾部,等待事件迴圈,當主執行緒再次迴圈到該事件時,就直接處理並返回給上層呼叫。 這個過程就叫 事件迴圈 (Event Loop),其執行原理如下圖所示:

這個圖是整個 Node.js 的執行原理,從左到右,從上到下,Node.js 被分為了四層,分別是 應用層V8引擎層Node API層 和 LIBUV層。

  • 應用層:   即 JavaScript 互動層,常見的就是 Node.js 的模組,比如 http,fs
  • V8引擎層:  即利用 V8 引擎來解析JavaScript 語法,進而和下層 API 互動
  • NodeAPI層:  為上層模組提供系統呼叫,一般是由 C 語言來實現,和作業系統進行互動 。
  • LIBUV層: 是跨平臺的底層封裝,實現了 事件迴圈、檔案操作等,是 Node.js 實現非同步的核心 。

無論是 Linux 平臺還是 Windows 平臺,Node.js 內部都是通過 執行緒池 來完成非同步 I/O 操作的,而 LIBUV 針對不同平臺的差異性實現了統一呼叫。因此,Node.js 的單執行緒僅僅是指 JavaScript 執行在單執行緒中,而並非 Node.js 是單執行緒。

3、事件驅動模型

Node.js 實現非同步的核心是事件驅動,也就是說,它把每一個任務都當成 事件 來處理,然後通過 Event Loop 模擬了非同步的效果,為了更具體、更清晰的理解和接受這個事實,下面我們用虛擬碼來描述一下這個實現過程 。

【1】定義事件佇列

既然是佇列,那就是一個先進先出 (FIFO) 的資料結構,我們用JS陣列來描述,如下:

1 2 3 4 5 6 7 /** * 定義事件佇列 * 入隊:push() * 出隊:shift() * 空佇列:length == 0 */ globalEventQueue: []

我們利用陣列來模擬佇列結構:陣列的第一個元素是佇列的頭部,陣列的最後一個元素是佇列的尾部,push() 就是在佇列尾部插入一個元素,shift() 就是從佇列頭部彈出一個元素。這樣就實現了一個簡單的事件佇列。

【2】定義接收請求入口

每一個請求都會被攔截並進入處理函式,如下所示: 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 /**

相關推薦

淺析 Node.js 執行模型

總結筆記:對於每個使用者請求,由主執行緒接收並存放於一個事件佇列中(不做任何處理),當無請求發生時,即主執行緒空閒,主執行緒開始迴圈處理事件佇列中的任務: 對於非阻塞JS程式: 1、若某事件需要I/O操作,則主執行緒發出I/O請求,然後繼續執行,由底層

02-node.js 執行,‘ 非同步’非阻塞io

1、基本概念     同步:多個任務順序執行     非同步:多個任務並排執行 2、node的併發實現原理     Node JS是單執行緒應用程式,但它通過事件和回撥概念,支援併發。 由於Node JS每一個API是非同步的,作為一個單獨的執行緒,它使用非同步函

Redis之執行模型

Redis客戶端對服務端的每次呼叫都經歷了傳送命令,執行命令,返回結果三個過程。其中執行命令階段,由於Redis是單執行緒來處理命令的,所有每一條到達服務端的命令不會立刻執行,所有的命令都會進入一個佇列中,然後逐個被執行。並且多個客戶端傳送的命令的執行順序是不確定的。但是可以確定的是不會有兩條命

ES6非同步操作概述- 執行模型

單執行緒模型 單執行緒模型指的是,JavaScript 只在一個執行緒上執行。也就是說,JavaScript 同時只能執行一個任務,其他任務都必須在後面排隊等待。 注意,JavaScript 只在一個執行緒上執行,不代表 JavaScript 引擎只有一個執行緒。事實上,

執行模型

單執行緒模型指的是,JavaScript只在一個執行緒上執行。也就是說 JavaScript同時只能執行一個任務,其他任務都必須在後面排隊等待。 注意,JavaScript只在一個執行緒上執行,不代表JavaScript引擎只有一個執行緒。時實上,  JavaScript引擎有

redis執行模型分析

redis原理 redis採用自己實現的事件分離器,效率比較高,內部採用非阻塞的執行方式,吞吐能力比較大。 不過,因為一般的記憶體操作都是簡單存取操作,執行緒佔用時間相對較短,主要問題在io上,因此,redis這種模型是合適的,但是如果某一個執行緒出現問題導致執行緒佔用很長時間,那麼reids的單

多程序執行模型程序多執行模型之爭

伺服器,事件 多程序單執行緒模型典型代表:nginx 單程序多執行緒模型典型代表:memcached 另外redis, mongodb也可以說是走的“多程序單執行緒模”模型(叢集),只不過作為資料庫伺服器,需要進行防寫,只提供了讀同步。 原因很簡單,因為伺服器的發展大部分都

js執行與java多執行、同步與非同步

       寫這篇部落格源於想對比一下單執行緒js和多執行緒java兩種語言的區別。       定義區:            單執行緒:只能執行一個任務,只有在完成執行後,才能繼續執行其他的任務。            多執行緒:有多個執行緒,可以同時執行多個任務。

Android執行模型中Message、Handler、Message Queue、Looper之間的關係

四者之間的關係: Handler獲取當前執行緒中的looper物件,looper用來從存放Message的MessageQueue中取出Message,再有Handler進行Message的分發和處理. 簡單定義: 1、Message Queue(訊息佇列): 用來存放通過Handler釋出的訊息,通常附屬

理解js執行非同步機制

1.最近在維護一個專案,用到的主要語言是JS。因此在寫需求功能時對JS的非同步有了進一步的理解。JS是單執行緒的,但是JS是可以非同步的。setTimeout(function(){console.log();},1000)  執行setTimeout時,會把裡面的匿名函式放

js執行和非同步並不衝突

眾所周知,js是單執行緒的,但是我們又經常說js非同步,這是為什麼呢? 實際上瀏覽器的渲染過程是多執行緒的,它不只有js一個執行緒,它還有GUI渲染執行緒、事件觸發執行緒、定時觸發器執行緒、非同步http請求執行緒等執行緒 js一次只能執行一個任務,當他有許

Redis 網路架構及執行模型

最近略有閒暇時間,於是對Redis進行了一些學習,學習途徑除了官方文件還有Redis原始碼,我看的版本是2.8.13,Redis原始碼總行數不到5W行,不同元件拆分非常細緻,閱讀起來也很清晰。這篇部落格主要介紹我對Redis網路層架構以及執行緒模型的一些瞭解,希望能對大家

用故事解析setTimeout和setInterval(內含js執行和任務佇列)

區別: setTimeout(fn,t): 延遲呼叫,超過了時間就呼叫回撥函式,返回一個id,使用clearTimeout(id)取消執行。 注意:取消了裡面的回撥函式就不執行了哦,而不是取消的時候就立即執行,下面有原始碼可以自己cv試一下。 setInterval(fn,t): 迴圈呼叫,有周期性的呼叫回撥

原始碼--Node.js 探祕:初識執行Node.js

  -- 高階理解 Nodejs, 從原始碼方向理解   // Node.js 探祕:初識單執行緒的 Node.js http://taobaofed.org/blog/2015/10/29/deep-into-node-1/ // 深入理解 Nod

node執行非同步模型

提到nodejs都知道單執行緒非同步I/O,但是能說清楚為什麼單執行緒非同步I/O,為什麼能增加網路吞吐量,怎麼充分利用cpu資源嗯,這個知道的就不多了。 首先要說的是I/O,I/O是計算機的抽象概念,指的是鍵盤,滑鼠,印表機,套接字等和記憶體之間的資料交換,I/O的速度是很慢的,知道計算機存貯模型的都知道

node裡只有js引擎是執行,不代表整個node都是執行

這裡是摘要一下關鍵的話: 我們可以用Ajax和Web Worker迴應這個誤解(javascript是單執行緒的,無法很好利用多核CPU)。當Ajax請求傳送之後,除非是同步請求,否則其餘的JavaScript程式碼會很快被執行到。在Ajax傳送完成,直到接收到響應的這段

node.js的非同步I/O、事件驅動、執行

nodejs的特點總共有以下幾點 非同步I/O(非阻塞I/O) 事件驅動 單執行緒 擅長I/O密集型,不擅長CPU密集型 高併發 下面是一道很經典的面試題,描述了node的整體執行機制,相信很多人都碰到了。這道題背後的原理就是nodejs程式碼執行順序 setTimeout(function()

效能追擊:萬字長文30+圖揭祕8大主流伺服器程式執行模型 | Node.js,Apache,Nginx,Netty,Redis,Tomcat,MySQL,Zuul

> 本文為《[高效能網路程式設計遊記](https://www.itzhai.com/articles/decrypt-the-threading-model-of-common-server-programs.html)》的第六篇“效能追擊:萬字長文30+圖揭祕8大主流伺服器程式執行緒模型”。 ![ima

JS執行

JS的單執行緒 JS語言的特點之一就是單執行緒,簡而言之就是同一時間只能做一件事,假設JS是多執行緒,一個執行緒在某個節點上新增內容,另一個執行緒刪除了這個節點,這時就看到了問題所在了,但是H5提出了Web Worker標準,允許js指令碼建立多個執行緒,但是子執行緒完全受主執行緒的控制,而且你

JavaScript——從setTimeout()的執行了解js執行和非同步

眾所周知,JavaScript是單執行緒的,那麼到底什麼是單執行緒呢?今天我們就用setTimeout()舉例,看看單執行緒到底是什麼樣的。 單執行緒,從名字就能知道,它只有一個主執行緒。單執行緒就意味著,所有任務需要排隊,前一個任務結束,才會執行後一個任務。如果前一個任務