Node的簡介

  從開始學習node到如今已經有半年多了,中間沒有作過什麼實際工做中的項目,因此感受本身的知識有些匱乏,可是我仍是要寫這些文章,由於工做中的須要用node來開發後臺環境,再加上我對這些知識記得很少,都是來看之前寫的源碼抄過來,本身根本記不住一些繁瑣的代碼,想借此機會來鞏固一下我所學到的東西,等之後慢慢來補充我如今所寫的文章。javascript

  能夠去百度node.js中文網,上面所寫的第一句話就是這麼一句歸納了node.js全部的話吧。前端

  node.js是一個基於Chorme V8引擎的javascript運行環境。node.js使用了一個事件驅動,非阻塞式I/O模型,使其輕量又高效。java

 

 什麼是Chorme V8引擎,它是怎麼來的?此處直達廖雪峯大佬的文章

  Node.js是目前很是火熱的技術,可是它的誕生經歷卻很奇特。node

  衆所周知,在Netscape設計出JavaScript後的短短几個月,JavaScript事實上已是前端開發的惟一標準。程序員

後來,微軟經過IE擊敗了Netscape後一統桌面,結果幾年時間,瀏覽器毫無進步。(2001年推出的古老的IE 6到今天仍然有人在使用!)數據庫

沒有競爭就沒有發展。微軟認爲IE6瀏覽器已經很是完善,幾乎沒有可改進之處,而後解散了IE6開發團隊!而Google卻認爲支持現代Web應用的新一代瀏覽器纔剛剛起步,尤爲是瀏覽器負責運行JavaScript的引擎性能還可提高10倍。編程

先是Mozilla藉助已壯烈犧牲的Netscape遺產在2002年推出了Firefox瀏覽器,緊接着Apple於2003年在開源的KHTML瀏覽器的基礎上推出了WebKit內核的Safari瀏覽器,不過僅限於Mac平臺。後端

  隨後,Google也開始建立自家的瀏覽器。他們也看中了WebKit內核,因而基於WebKit內核推出了Chrome瀏覽器。瀏覽器

Chrome瀏覽器是跨Windows和Mac平臺的,而且,Google認爲要運行現代Web應用,瀏覽器必須有一個性能很是強勁的JavaScript引擎,因而Google本身開發了一個高性能JavaScript引擎,名字叫V8,以BSD許可證開源。服務器

現代瀏覽器大戰讓微軟的IE瀏覽器遠遠地落後了,由於他們解散了最有經驗、戰鬥力最強的瀏覽器團隊!回過頭再追趕卻發現,支持HTML5的WebKit已經成爲手機端的標準了,IE瀏覽器今後與主流移動端設備絕緣。

瀏覽器大戰和Node有何關係?

  話說有個叫Ryan Dahl的歪果仁,他的工做是用C/C++寫高性能Web服務。對於高性能,異步IO、事件驅動是基本原則,可是用C/C++寫就太痛苦了。因而這位仁兄開始設想用高級語言開發Web服務。他評估了不少種高級語言,發現不少語言雖然同時提供了同步IO和異步IO,可是開發人員一旦用了同步IO,他們就再也懶得寫異步IO了,因此,最終,Ryan瞄向了JavaScript。

  由於JavaScript是單線程執行,根本不能進行同步IO操做,因此,JavaScript的這一「缺陷」致使了它只能使用異步IO。

選定了開發語言,還要有運行時引擎。這位仁兄曾考慮過本身寫一個,不過明智地放棄了,由於V8就是開源的JavaScript引擎。讓Google投資去優化V8,咱只負責改造一下拿來用,還不用付錢,這個買賣很划算。

  因而在2009年,Ryan正式推出了基於JavaScript語言和V8引擎的開源Web服務器項目,命名爲Node.js。雖然名字很土,可是,Node第一次把JavaScript帶入到後端服務器開發,加上世界上已經有無數的JavaScript開發人員,因此Node一會兒就火了起來。

在Node上運行的JavaScript相比其餘後端開發語言有何優點?

  最大的優點是藉助JavaScript天生的事件驅動機制加V8高性能引擎,使編寫高性能Web服務垂手可得。

  其次,JavaScript語言自己是完善的函數式語言,在前端開發時,開發人員每每寫得比較隨意,讓人感受JavaScript就是個「玩具語言」。可是,在Node環境下,經過模塊化的JavaScript代碼,加上函數式編程,而且無需考慮瀏覽器兼容性問題,直接使用最新的ECMAScript 6標準,能夠徹底知足工程上的需求。

 

  node是啥?

  傳統意義上的javascript運行在瀏覽器上,這是由於瀏覽器內核實際上分爲兩個部分,渲染引擎和javaScript引擎。前者主要負責渲染HTML+CSS,後者主要負責運行javaScript。Chorme使用的javascript引擎是V8,它的運行速度很是快。

  

  node 跟 chorme 有什麼區別?

  架構同樣,都是基於事件驅動的異步架構!

  瀏覽器主要是經過事件驅動來服務頁面交互。

  

  node 主要是經過事件驅動來服務 I/O

  node 沒有HTML,WebKit和顯卡等等的UI技術支持

 

  爲何要用node.js?

  總的來講,Node.js 適合如下場景:

  1. 實時性應用,好比在線多人協做工具,網頁聊天應用等。
  2. 以 I/O 爲主的高併發應用,好比爲客戶端提供 API,讀取數據庫。
  3. 流式應用,好比客戶端常常上傳文件。
  4. 先後端分離。

  實際上前二者能夠歸結爲一種,即客戶端普遍使用長鏈接,雖然併發數較高,但其中大部分是空閒鏈接。

  Node.js 也有它的侷限性,它並不適合 CPU 密集型的任務,好比人工智能方面的計算,視頻、圖片的處理等。

固然,以上缺點不是信口開河,或者死記硬背,更不是人云亦云,須要咱們對 Node.js 的原理有必定的瞭解,才能作出正確的判斷。

   

  基礎概念

  併發 下面的這些是參考的他人的文章

  與客戶端不一樣,服務端開發者很是關心的一項數據是併發數,也就是這臺服務器最多能支持多少個客戶端的併發請求。早年的 C10K 問題就是討論如何利用單臺服務器支持 10K 併發數。固然隨着軟硬件性能的提升,目前 C10K 已經再也不是問題,咱們開始嘗試解決 C10M 問題,即單臺服務器如何處理百萬級的併發。

  在 C10K 提出時,咱們還在使用 Apache 服務器,它的工做原理是每當有一個網絡請求到達,就 fork 出一個子進程並在子進程中運行 PHP 腳本。執行完腳本後再把結果發回客戶端。

  這樣能夠確保不一樣進程之間互不干擾,即便一個進程出問題也不影響整個服務器,可是缺點也很明顯:進程是一個比較重的概念,擁有本身的堆和棧,佔用內存較多,一臺服務器能運行的進程數量有上限,大約也就在幾千左右。

雖然 Apache 後來使用了 FastCGI,但本質上只是一個進程池,它減小了建立進程的開銷,但沒法有效提升併發數。

  Java 的 Servlet 使用了線程池,即每一個 Servlet 運行在一個線程上。線程雖然比進程輕量,但也是相對的。 有人測試過 ,每一個線程獨享的棧的大小是 1M,依然不夠高效。除此之外,多線程編程會帶來各類麻煩,這一點想必程序員們都深有體會。

  若是不使用線程,還有兩種解決方案,分別是使用協程(coroutine)和非阻塞 I/O。協程比線程更加輕量,多個協程能夠運行在同一個線程中,並由程序員本身負責調度,這種技術在 Go 語言中被普遍使用。而非阻塞 I/O 則被 Node.js 用來處理高併發的場景。

  

  非阻塞 I/O

  這裏所說的 I/O 能夠分爲兩種: 網絡 I/O 和文件 I/O,實際上二者高度相似。 I/O 能夠分爲兩個步驟,首先把文件(網絡)中的內容拷貝到緩衝區,這個緩衝區位於操做系統獨佔的內存區域中。隨後再把緩衝區中的內容拷貝到用戶程序的內存區域中。

  對於阻塞 I/O 來講,從發起讀請求,到緩衝區就緒,再到用戶進程獲取數據,這兩個步驟都是阻塞的。

  非阻塞 I/O 其實是向內核輪詢,緩衝區是否就緒,若是沒有則繼續執行其餘操做。當緩衝區就緒時,講緩衝區內容拷貝到用戶進程,這一步實際上仍是阻塞的。

I/O 多路複用技術是指利用單個線程處理多個網絡 I/O,咱們常說的 select 、 epoll 就是用來輪詢全部 socket 的函數。好比 Apache 採用了前者,而 Nginx 和 Node.js 使用了後者,區別在於後者效率更高。因爲 I/O 多路複用實際上仍是單線程的輪詢,所以它也是一種非阻塞 I/O 的方案。

  異步 I/O 是最理想的 I/O 模型,然而惋惜的是真正的異步 I/O 並不存在。 Linux 上的 AIO 經過信號和回調來傳遞數據,可是存在缺陷。現有的 libeio 以及 Windows 上的 IOCP,本質上都是利用線程池與阻塞 I/O 來模擬異步 I/O。

  Node.js 線程模型

不少文章都提到 Node.js 是單線程的,然而這樣的說法並不嚴謹,甚至能夠說很不負責,由於咱們至少會想到如下幾個問題:

  1. Node.js 在一個線程中如何處理併發請求?
  2. Node.js 在一個線程中如何進行文件的異步 I/O?
  3. Node.js 如何重複利用服務器上的多個 CPU 的處理能力?

  

  網絡 I/O

  Node.js 確實能夠在單線程中處理大量的併發請求,但這須要必定的編程技巧。總之,在利用 Node.js 編程時,任何耗時操做必定要使用異步來完成,避免阻塞當前函數。由於你在爲客戶端提供服務,而全部代碼老是單線程、順序執行。

 

  文件 I/O

  我在以前的文章中也強調過,異步是爲了優化體驗,避免卡頓。而真正節省處理時間,利用 CPU 多核性能,仍是要靠多線程並行處理。

  實際上 Node.js 在底層維護了一個線程池。以前在基礎概念部分也提到過,不存在真正的異步文件 I/O,一般是經過線程池來模擬。線程池中默認有四個線程,用來進行文件 I/O。

  須要注意的是,咱們沒法直接操做底層的線程池,實際上也不須要關心它們的存在。線程池的做用僅僅是完成 I/O 操做,而非用來執行 CPU 密集型的操做,好比圖像、視頻處理,大規模計算等。

  若是有少許 CPU 密集型的任務須要處理,咱們能夠啓動多個 Node.js 進程並利用 IPC 機制進行進程間通信,或者調用外部的 C++/Java 程序。若是有大量 CPU 密集型任務,那隻能說明選擇 Node.js 是一個錯誤的決定。

  事件循環

  在 Node.js 中存在一個事件循環(Event Loop)。

  一次完整的 Event Loop 也能夠分爲多個階段(phase),依次是 poll、check、close callbacks、timers、I/O callbacks 、Idle。

因爲 Node.js 是事件驅動的,每一個事件的回調函數會被註冊到 Event Loop 的不一樣階段。好比 fs.readFile 的回調函數被添加到 I/O callbacks, setImmediate 的回調被添加到下一次 Loop 的 poll 階段結束後, process.nextTick() 的回調被添加到當前 phase 結束後,下一個 phase 開始前。

  不一樣異步方法的回調會在不一樣的 phase 被執行,掌握這一點很重要,不然就會由於調用順序問題產生邏輯錯誤。

  Event Loop 不斷的循環,每個階段內都會同步執行全部在該階段註冊的回調函數。這也正是爲何我在網絡 I/O 部分提到,不要在回調函數中調用阻塞方法,老是用異步的思想來進行耗時操做。一個耗時過久的回調函數可能會讓 Event Loop 卡在某個階段好久,新來的網絡請求就沒法被及時響應。

因爲本文的目的是對 Node.js 有一個初步的,全面的認識。就不詳細介紹 Event Loop 的每一個階段了。

  數據流

  使用數據流的好處很明顯,生活中也有真實寫照。舉個例子,老師佈置了暑假做業,若是學生天天都作一點(做業流),就能夠比較輕鬆的完成任務。若是積壓在一塊兒,到了最後一天,面對堆成小山的做業本,就會感到力不從心。

  Server 開發也是這樣,假設用戶上傳 1G 文件,或者讀取本地 1G 的文件。若是沒有數據流的概念,咱們須要開闢 1G 大小的緩衝區,而後在緩衝區滿後一次性集中處理。

若是是採用數據流的方式,咱們能夠定義很小的一塊緩衝區,好比大小是 1Mb。當緩衝區滿後就執行回調函數,對這一小塊數據進行處理,從而避免出現積壓。

實際上 request 和 fs 模塊的文件讀取都是一個可讀數據流:

  總結

  對於高併發的長鏈接,事件驅動模型比線程輕量得多,多個 Node.js 進程配合負載均衡能夠方便的進行拓展。所以 Node.js 很是適合爲 I/O 密集型應用提供服務。但這種方式的缺陷就是不擅長處理 CPU 密集型任務。

   原本此次是想本身寫一篇文章看看的,可是看到他人寫的文章太好了~~~

  對不起,此次的文章後半部分都是參考的他人的文章

  參考文章: 爲何要用node.js

          廖雪峯的javaScript教程