第一次更新 查漏補(bǔ)缺昨晚零時寫的 辣雞輸入法導(dǎo)致錯別字太多。
Node.js的到底是用來做什么的
在闡述之前我想放一個鏈接,這是國外的一個大神,對于node.js非常好的一篇介紹的文章,英文比較好的朋友可以直接去閱讀,本文也很大程度上參考了這篇文章,也同時感謝知乎用戶廠長對于本文的翻譯!原文題目為Node.js is the New Black
Node.js的簡介
根據(jù)維基百科對于Node.js的介紹,我們可以知道一些基本關(guān)于Node.js的情況。Node.js是瑞安·達(dá)爾(Ryan Dahl)在2009年發(fā)明出來的一種一個能夠在服務(wù)器端運(yùn)行JavaScript,并且可以開放源代碼,以及跨平臺運(yùn)行JavaScript的一種運(yùn)行環(huán)境。
Node.js采用了Google公司的V8引擎。Node.js允許通過JavaScript和一系列模塊來編寫服務(wù)器端應(yīng)用和網(wǎng)絡(luò)相關(guān)的應(yīng)用。核心模塊包括文件系統(tǒng)I/O、網(wǎng)絡(luò)(HTTP、TCP、UDP、DNS、TLS/SSL等)、二進(jìn)制數(shù)據(jù)流、加密算法、數(shù)據(jù)流等等。Node模塊的API形式簡單,降低了編程的復(fù)雜度。
Node.js主要用于編寫像Web服務(wù)器一樣的網(wǎng)絡(luò)應(yīng)用,這和PHP和Python是類似的。但是Node.js與其他語言最大的不同之處在于,PHP等語言是阻塞的(只有前一條命令執(zhí)行完畢才會執(zhí)行后面的命令),而Node.js是非阻塞的(多條命令可以同時被運(yùn)行,通過回調(diào)函數(shù)得知命令已結(jié)束運(yùn)行)。
Node.js是事件驅(qū)動的。開發(fā)者可以在不使用線程的情況下開發(fā)出一個能夠承載高并發(fā)的服務(wù)器。其他服務(wù)器端語言難以開發(fā)高并發(fā)應(yīng)用,而且即使開發(fā)出來,性能也不盡人意。Node.js正是在這個前提下被創(chuàng)造出來。Node.js把JavaScript的易學(xué)易用和Unix網(wǎng)絡(luò)編程的強(qiáng)大結(jié)合到了一起。
Node.js使用Google V8 JavaScript 引擎,因為
- V8是基于BSD許可證的開源軟件
- V8速度非常快
- V8專注于網(wǎng)絡(luò)功能,在HTTP、DNS、TCP等方面更加成熟
Node.js已經(jīng)有數(shù)十萬模塊,它們可以通過一個名為npm的管理器免費(fèi)下載。Node.js開發(fā)社區(qū)主要有兩個郵件列表、一個在freenode的名為#node.js的IRC頻道。社區(qū)集中在NodeConf。
雜敘
在學(xué)習(xí)Web前端的朋友看來,Node.js是一個經(jīng)常會聽到而且無法回避的問題,然而國內(nèi)的資料相對較少,國外的資料大多數(shù)是以英文為主要藍(lán)本闡述的,所以友好度并不高,我結(jié)合最近查詢到的資料,結(jié)合自己淺薄的見解,做出一點(diǎn)點(diǎn)介紹和綜述,希望可以幫到大家。如有不正,多請斧正!
1. Node.js到底是什么
Node.js在維基百科中就已經(jīng)很明確的說明了,它是一個運(yùn)行環(huán)境,并不是其他的什么比如軟件庫,簡而言之,和C#所需要的編譯環(huán)境一樣,Node.js就是JavaScript的編譯環(huán)境,它存在的目的就是為了讓JavaScript可以和其他的后端語言一樣能夠在瀏覽器上運(yùn)行,換種說法就是,可以讓前端語言JavaScript在寫完之后交給Node.js進(jìn)行編譯和解釋,它的存在對于JavaScript有了質(zhì)的飛躍,對于一個前端來說利用JavaScript就可以編譯后臺代碼是一件多么爽飛天的事情。
簡單的Node.js命令就是
#node hello.js
2. V8引擎
我們都知道計算機(jī)處理器智能識別機(jī)器語言,而JavaScript是一門高級語言,計算機(jī)并不能直接讀懂。所以我們需要所謂的引擎來將其轉(zhuǎn)化成計算機(jī)所能理解的語言。v8引擎是由Google推出的,為其瀏覽器Chrome所設(shè)計的開源JavaScript引擎。得益于JIT,編譯模式的改變與編譯階段的優(yōu)化,JavaScript的性能得到了一個飛躍。其源代碼是用c++寫的,感除了對JavaScript性能的大幅提升,v8引擎也提供了“嵌入”的功能,使得開發(fā)者也可以在自己的c++程序中使用“嵌入”的v8引擎,從而高效地編譯JavaScript,并加入c++的feature。要知道,作為一個底層得多的語言,c++可以實現(xiàn)的feature可要比JavaScript多得多。舉例說明,JavaScript本身并沒有read這么一個function。然而通過v8,我們可以將其綁定到一個用c++寫的read callback上,從而通過JavaScript我們也可以直接加載文件了。
于是,借助于v8種種便利的功能,Node.js誕生了。
3. 數(shù)據(jù)的請求和處理(表述的不太準(zhǔn)確望海涵)
首先我們要注意的是瀏覽器給網(wǎng)站發(fā)請求的過程一直沒怎么變過。當(dāng)瀏覽器給網(wǎng)站發(fā)了請求。服務(wù)器收到了請求,然后開始搜尋被請求的資源。如果有需要,服務(wù)器還會查詢一下數(shù)據(jù)庫,最后把響應(yīng)結(jié)果傳回瀏覽器。不過,在傳統(tǒng)的web服務(wù)器中,每一個請求都會讓服務(wù)器創(chuàng)建一個新的進(jìn)程來處理這個請求。后來有了Ajax,我們就不用每次都請求一個完整的新頁面了,取而代之的是,每次只請求需要的部分頁面信息就可以了。這顯然是一個進(jìn)步。但是比如你要建一個類似微博的社交網(wǎng)站,導(dǎo)致的結(jié)果是你的好友會隨時的推送新的狀態(tài),然后你的新鮮事會實時自動刷新。要達(dá)成這個需求,我們需要讓用戶一直與服務(wù)器保持一個有效連接。目前最簡單的實現(xiàn)方法,就是讓用戶和服務(wù)器之間保持長輪訓(xùn)(long polling)。
HTTP請求不是持續(xù)的連接,你請求一次,服務(wù)器響應(yīng)一次,然后就完了。長輪訓(xùn)是一種利用HTTP模擬持續(xù)連接的技巧。具體來說,只要頁面載入了,不管你需不需要服務(wù)器給你響應(yīng)信息,你都會給服務(wù)器發(fā)一個Ajax請求。這個請求不同于一般的Ajax請求,服務(wù)器不會直接給你返回信息,而是它要等著,直到服務(wù)器覺得該給你發(fā)信息了,它才會響應(yīng)。比如,你的好友發(fā)了一條新鮮事,服務(wù)器就會把這個新鮮事當(dāng)做響應(yīng)發(fā)給你的瀏覽器,然后你的瀏覽器就刷新頁面了。瀏覽器收到響應(yīng)刷新完之后,再發(fā)送一條新的請求給服務(wù)器,這個請求依然不會立即被響應(yīng)。于是就開始重復(fù)以上步驟。利用這個方法,可以讓瀏覽器始終保持等待響應(yīng)的狀態(tài)。雖然以上過程依然只有非持續(xù)的HTTP參與,但是我們模擬出了一個看似持續(xù)的連接狀態(tài)我們再看傳統(tǒng)的服務(wù)器。每次一個新用戶連到你的網(wǎng)站上,你的服務(wù)器就得開一個連接。每個連接都需要占一個進(jìn)程,這些進(jìn)程大部分時間都是閑著的(比如等著你好友發(fā)新鮮事,等好友發(fā)完才給用戶響應(yīng)信息?;蛘叩戎鴶?shù)據(jù)庫返回查詢結(jié)果什么的)。雖然這些進(jìn)程閑著,但是照樣占用內(nèi)存。這意味著,如果用戶連接數(shù)的增長到一定規(guī)模,你服務(wù)器沒準(zhǔn)就要耗光內(nèi)存直接癱了。這種情況怎么解決?解決方法就是剛才上邊說的:非阻塞和事件驅(qū)動。這些概念在我們談的這個情景里面其實沒那么難理解。你把非阻塞的服務(wù)器想象成一個loop循環(huán),這個loop會一直跑下去。一個新請求來了,這個loop就接了這個請求,把這個請求傳給其他的進(jìn)程(比如傳給一個搞數(shù)據(jù)庫查詢的進(jìn)程),然后響應(yīng)一個回調(diào)(callback)。完事了這loop就接著跑,接其他的請求。這樣下來。服務(wù)器就不會像之前那樣傻等著數(shù)據(jù)庫返回結(jié)果了。如果數(shù)據(jù)庫把結(jié)果返回來了,loop就把結(jié)果傳回用戶的瀏覽器,接著繼續(xù)跑。在這種方式下,你的服務(wù)器的進(jìn)程就不會閑著等著。從而在理論上說,同一時刻的數(shù)據(jù)庫查詢數(shù)量,以及用戶的請求數(shù)量就沒有限制了。服務(wù)器只在用戶那邊有事件發(fā)生的時候才響應(yīng),這就是事件驅(qū)動。FriendFeed是用基于Python的非阻塞框架Tornado (知乎也用了這個框架) 來實現(xiàn)上面說的新鮮事功能的。不過,Node.js就比前者更妙了。Node.js的應(yīng)用是通過javascript開發(fā)的,然后直接在Google的變態(tài)V8引擎上跑。
用了Node.js,你就不用擔(dān)心用戶端的請求會在服務(wù)器里跑了一段能夠造成阻塞的代碼了。因為javascript本身就是事件驅(qū)動的腳本語言。你回想一下,在給前端寫javascript的時候,更多時候你都是在搞事件處理和回調(diào)函數(shù)。所以javascript本身就是給事件處理量身定制的語言。
4. 使用Node.js的優(yōu)劣(很重要)
在使用Node.js的時候我們可以知道,Node.js解決了很多JavaScript的痛點(diǎn)。當(dāng)然Node.js作為一個語言也有很多都缺陷。
優(yōu)勢
- 采用事件驅(qū)動、異步編程,為網(wǎng)絡(luò)服務(wù)而設(shè)計。其實Javascript的匿名函數(shù)和閉包特性非常適合事件驅(qū)動、異步編程。而且JavaScript也簡單易學(xué),很多前端設(shè)計人員可以很快上手做后端設(shè)計。
- Node.js非阻塞模式的IO處理給Node.js帶來在相對低系統(tǒng)資源耗用下的高性能與出眾的負(fù)載能力,非常適合用作依賴其它IO資源的中間層服務(wù)。
- Node.js輕量高效,可以認(rèn)為是數(shù)據(jù)密集型分布式部署環(huán)境下的實時應(yīng)用系統(tǒng)的完美解決方案。Node非常適合如下情況:在響應(yīng)客戶端之前,您預(yù)計可能有很高的流量,但所需的服務(wù)器端邏輯和處理不一定很多。
缺點(diǎn)
可靠性低。
單進(jìn)程,單線程,只支持單核CPU,不能充分的利用多核CPU服務(wù)器。一旦這個進(jìn)程崩掉,那么整個web服務(wù)就崩掉了。
當(dāng)然對于這些缺點(diǎn)也喲普很多解決辦法:
- 開啟多個進(jìn)程,每個進(jìn)程綁定不同的端口,用反向代理服務(wù)器如 Nginx 做負(fù)載均衡,好處是我們可以借助強(qiáng)大的 Nginx 做一些過濾檢查之類的操作,同時能夠?qū)崿F(xiàn)比較好的均衡策略,但壞處也是顯而易見——我們引入了一個間接層。
- 多進(jìn)程綁定在同一個端口偵聽。在Node.js中,提供了進(jìn)程間發(fā)送“文件句柄” 的功能。
- 一個進(jìn)程負(fù)責(zé)監(jiān)聽、接收連接,然后把接收到的連接平均發(fā)送到子進(jìn)程中去處理。
5. 適用場景
SON APIs——構(gòu)建一個Rest/JSON API服務(wù),Node.js可以充分發(fā)揮其非阻塞IO模型以及JavaScript對JSON的功能支持(如JSON.stringfy函數(shù))單頁面、多Ajax請求應(yīng)用——如Gmail,前端有大量的異步請求,需要服務(wù)后端有極高的響應(yīng)速度基于Node.js開發(fā)Unix命令行工具——Node.js可以大量生產(chǎn)子進(jìn)程,并以流的方式輸出,這使得它非常適合做Unix命令行工具流式數(shù)據(jù)——傳統(tǒng)的Web應(yīng)用,通常會將HTTP請求和響應(yīng)看成是原子事件。而Node.js會充分利用流式數(shù)據(jù)這個特點(diǎn),構(gòu)建非??岬膽?yīng)用。如實時文件上傳系統(tǒng)transloadit準(zhǔn)實時應(yīng)用系統(tǒng)——如聊天系統(tǒng)、微博系統(tǒng),但Javascript是有垃圾回收機(jī)制的,這就意味著,系統(tǒng)的響應(yīng)時間是不平滑的(GC垃圾回收會導(dǎo)致系統(tǒng)這一時刻停止工作)。如果想要構(gòu)建硬實時應(yīng)用系統(tǒng),Erlang是個不錯的選擇。
結(jié)語
Node.js是一個對于前端工作者不可或缺的工具。尤其是對于JavaScript有著巨大的提升,現(xiàn)階段Node.js的應(yīng)用已經(jīng)有了非常蓬勃的發(fā)展。對于Node.js的學(xué)習(xí)和熟練運(yùn)用,必不可少!以上。