您好,登錄后才能下訂單哦!
這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)什么是Node.js呢,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
Node不是萬(wàn)能藥!但的確能解決一些關(guān)鍵問(wèn)題
學(xué)習(xí)Node不是一件輕松事兒,但你所收到的回報(bào)是對(duì)得起你的付出的。因?yàn)楫?dāng)下Web應(yīng)用開發(fā)中的諸多難題唯有JavaScript才能解決。
“你夠酷嗎?來(lái)用我吧!” Node.js 為最新潮的編程語(yǔ)言提供了一系列很酷的API和工具箱,它可以直接應(yīng)用于傳統(tǒng)的Rails、Ajax、Hadoop、甚至可以某種程度上用于iPhone開發(fā)和HTML5。如果你參加過(guò)一些大型技術(shù)會(huì)議,你總是會(huì)聽到一些關(guān)于Node.js的主題演講,盡管這些話題對(duì)普通的開發(fā)者來(lái)說(shuō)依然有些難以企及。
你可能已經(jīng)聽說(shuō)Node.js(有時(shí)我們將其簡(jiǎn)稱為“Node”)是一個(gè)服務(wù)器端的解決方案,它可以運(yùn)行JavaScript,并可以作為Web服務(wù)來(lái)處理HTTP請(qǐng)求。如果這些東東還不至于讓你暈頭轉(zhuǎn)向的話,轉(zhuǎn)眼間關(guān)于端口、sockets和線程的討論就又成了當(dāng)下最熱門的話題,你會(huì)覺(jué)得這些東西讓你眼花繚亂。這些內(nèi)容真的屬于JavaScript的范疇嗎?為什么世界上那么多人寧愿將JavaScript脫離瀏覽器而運(yùn)行,更不用說(shuō)將JavaScript運(yùn)行于服務(wù)器端了?
好消息是,你所聽到的(所想到的)關(guān)于Node的一切都是正確的。Node的的確確是屬于網(wǎng)絡(luò)編程的范疇,用以處理服務(wù)器端的請(qǐng)求和響應(yīng)。壞消息是和之前的Rails、Ajax和Hadoop一樣,真正實(shí)用的技術(shù)資料實(shí)在太少。等到基于Node的“優(yōu)秀的”框架成熟之后,技術(shù)資料一定會(huì)跟得上的,但何必要等到技術(shù)書籍和教程都出來(lái)之后再去嘗試使用Node呢?現(xiàn)在就使用Node,說(shuō)不定會(huì)給你的代碼帶來(lái)意想不到的改觀,甚至讓你的程序變得更易實(shí)現(xiàn)。
專家門的警告!
和大多數(shù)技術(shù)一樣,Node也是新瓶裝舊酒:它看起來(lái)不透明而且很怪異,但獨(dú)受小開發(fā)團(tuán)隊(duì)的青睞。如果你沒(méi)有接觸過(guò)Node,則需要學(xué)習(xí)一些很容易上手的服務(wù)器端腳本。你需要化時(shí)間來(lái)搞清楚Node,因?yàn)榧幢闶沁\(yùn)行于服務(wù)器端的JavaScript,它和客戶端JavaScript也極為不同。實(shí)際情況是,你不得不自己給自己洗腦,以便重新學(xué)習(xí)理解圍繞JavaScript的事件處理機(jī)制、異步IO和一些網(wǎng)絡(luò)基礎(chǔ)知識(shí)。
不幸的是,這意味著如果你已經(jīng)用Node作開發(fā)超過(guò)兩年時(shí)間的話,你會(huì)覺(jué)得這篇文章內(nèi)容很單調(diào)乏而且過(guò)于簡(jiǎn)單。你會(huì)開始尋找新的“刺激”,比如將Node運(yùn)行于客戶端,或者開始嘗試事件I/O、反射器模式和npm。你會(huì)發(fā)現(xiàn)Node的世界是如此有趣,甚至很多Node高級(jí)技術(shù)具有某種史詩(shī)般的美感,而這些東西對(duì)于初學(xué)者來(lái)說(shuō)依然是難于企及的。因此,或許你應(yīng)該將你掌握的知識(shí)分享給你的同伴,尤其是對(duì)于那些不了解Node的同學(xué),當(dāng)他們開始對(duì)Node感興趣時(shí),給他們分享傳授Node高級(jí)技術(shù)。
Node:幾個(gè)小例子
首先,你應(yīng)當(dāng)意識(shí)到Node是用于運(yùn)行獨(dú)立的JavaScript程序的,而不是運(yùn)行于瀏覽器中的某個(gè)HTML片段里。它是存放在文件系統(tǒng)中的真實(shí)存在的文件,由Node程序執(zhí)行,以一種守護(hù)進(jìn)程的模式運(yùn)行,同時(shí)打開對(duì)某些端口的監(jiān)聽。
跳過(guò) hello world
最經(jīng)典的例子當(dāng)然是“Hello World“,在Node官網(wǎng)(http://nodejs.org/docs/latest )上有源碼。幾乎每個(gè)人都是從Hello World開始接觸Node的?,F(xiàn)在讓我們跳過(guò)這個(gè)最簡(jiǎn)單的例子,來(lái)看一些更有趣的例子:實(shí)現(xiàn)一個(gè)可以從服務(wù)器發(fā)送文件到客戶端的程序(而不僅僅是發(fā)送一段文本到客戶端)。
var sys = require("sys"), http = require("http"), url = require("url"), path = require("path"), fs = require("fs"); http.createServer(function(request, response) { var uri = url.parse(request.url).pathname; var filename = path.join(process.cwd(), uri); path.exists(filename, function(exists) { if(!exists) { response.writeHead(404, {"Content-Type": "text/plain"}); response.end("404 Not Found\n"); return; } fs.readFile(filename, "binary", function(err, file) { if(err) { response.writeHead(500, {"Content-Type": "text/plain"}); response.end(err + "\n"); return; } response.writeHead(200); response.end(file, "binary"); }); }); }).listen(8080); console.log("Server running at http://localhost:8080/");
感謝Mike Amundsen,他給出了這段代碼的相似的實(shí)現(xiàn)。這個(gè)例子是由Devon Govett在Nettuts+上提交的一段代碼,盡管已經(jīng)根據(jù)新版本的Node作了更新,但Devon的整個(gè)帖子是一個(gè)非常好的入門學(xué)習(xí)教材,對(duì)于初學(xué)者來(lái)說(shuō)更是如此。
如果你是一個(gè)新手,你可以將上述代碼保存到一個(gè)文本文件中,命名為NodeFileServer.js。在運(yùn)行之前你需要一個(gè)Node運(yùn)行環(huán)境,最新的Node版本可以從官網(wǎng)下載這個(gè)文件或者從github上將源碼取下來(lái)。你需要編譯源碼,如果你沒(méi)有用過(guò)Unix、對(duì)make和configure不甚熟悉,則需要查閱在線編譯手冊(cè)來(lái)尋求幫助。
Node不是JavaScript,Node可以運(yùn)行JavaScript
剛剛你將NodeFileServer.js存成了某個(gè)文件,別擔(dān)心,我們等下會(huì)回過(guò)頭來(lái)運(yùn)行它的?,F(xiàn)在,讓我們移步到現(xiàn)實(shí)當(dāng)中來(lái),在Unix中執(zhí)行典型的配置和編譯命令:
./configure make make install
這讓我們確信一個(gè)事實(shí):Node不是JavaScript,Node是一個(gè)可以運(yùn)行JavaScript的程序,但Node絕對(duì)不是JavaScript。實(shí)際上,Node是基于C寫的程序??梢酝ㄟ^(guò)ls來(lái)查看Node/src目錄中的文件,可以看到Node的源碼:
大多數(shù)人會(huì)以為,JavaScript是一門糟糕的語(yǔ)言,更不用說(shuō)用它來(lái)實(shí)現(xiàn)服務(wù)器端的功能了,其實(shí)你只對(duì)了一半。不錯(cuò),對(duì)于操作系統(tǒng)級(jí)別的Socket和網(wǎng)絡(luò)編程來(lái)說(shuō),JavaScript可能并不能勝任。但Node并不是JavaScript實(shí)現(xiàn)的,它是基于C實(shí)現(xiàn)的。C語(yǔ)言是可以完美的勝任任意量級(jí)的網(wǎng)絡(luò)編程的。而JavaScript則完全有能力將指令傳遞給C程序,然后由C程序來(lái)操控操作系統(tǒng)“地下城”。實(shí)際上,和C語(yǔ)言相比,JavaScript更容易被開發(fā)者們接觸到,這是值得引起注意的地方,如果你想用Node進(jìn)行一些嚴(yán)肅的編程的話,這個(gè)原因會(huì)被一再提及。
Node的基本用法進(jìn)一步反映出了Node是如何和JavaScript一起工作的,Node不是JavaScript。你可以通過(guò)命令行來(lái)運(yùn)行它:
— (bdm0509@Bretts-MacBook-Pro Sun, 29 May 11) — — — — — — — — — — (/Users/bdm0509/tmp/Node/src) — — (09:09 $)-> export PATH=$HOME/local/Node/bin:$PATH — (bdm0509@Bretts-MacBook-Pro Sun, 29 May 11) — — — — — — — — — — (/Users/bdm0509/tmp/Node/src) — — (09:09 $)-> cd ~/examples — (bdm0509@Bretts-MacBook-Pro Sun, 29 May 11) — — — — — — — — — — — — (/Users/bdm0509/examples) — — (09:09 $)-> Node NodeFileServer.js Server running at http://127.0.0.1:1337/
現(xiàn)在你肯定對(duì)Node有個(gè)大概的了解了吧。對(duì)于這段命令行,的確還有很多知識(shí)點(diǎn)需要進(jìn)一步解釋說(shuō)明,比如在端口1337到底發(fā)生了什么?但這里你只需知道,Node只是一個(gè)可以讓你運(yùn)行JavaScript的程序。讀者不必糾結(jié)于Node如何和JavaScript協(xié)同工作,這里也不會(huì)對(duì)此做過(guò)多介紹,只要知道Node可以運(yùn)行JavaScript,這就足夠了。而且你只需學(xué)習(xí)JavaScript這一門編程語(yǔ)言即可,不用擔(dān)心自己不懂C語(yǔ)言。記住這是最最重要的一點(diǎn),不必了解C也可寫出Node可運(yùn)行的程序。
和Node服務(wù)器的交互
剛才我們?cè)贜ode上運(yùn)行了NodeFileServer.js。這時(shí)你可以訪問(wèn)你本機(jī)的1337端口,可以看到正常的輸出。
沒(méi)錯(cuò),輸出結(jié)果不足為奇。但應(yīng)當(dāng)意識(shí)到我們只用短短20行代碼就實(shí)現(xiàn)了一個(gè)文件服務(wù)器。輸出結(jié)果是你剛剛保存的腳本源文件的文本,并沒(méi)有以二進(jìn)制的形式輸出。這個(gè)文件服務(wù)器可以輸出它上面的任何文件。如果在同樣目錄下放入一張圖片,在URL后綴中寫上圖片文件名,就像這樣:http://localhost:8080/my_image.png。
Node也可以展示出二進(jìn)制的圖片文件。當(dāng)你回頭再看這段短小的程序時(shí),一定覺(jué)得這太不可思議了。用JavaScript輕易就能寫出一個(gè)你想要的服務(wù)程序難道不讓人感到愜意嗎?不僅如此,假設(shè)你想寫一個(gè)可以處理多個(gè)請(qǐng)求的服務(wù)(這是一個(gè)提示,同時(shí)打開四個(gè)五個(gè)甚至十個(gè)瀏覽器訪問(wèn)服務(wù)器),這也是很容易做到的。Node讓人著迷的地方在于,你完全可以用很簡(jiǎn)單而且很不起眼的JavaScript程序來(lái)完成你想要的這些結(jié)果。
快速入門手冊(cè)
圍繞Node的話題總是會(huì)比純粹運(yùn)行在服務(wù)器端的代碼更值得花點(diǎn)時(shí)間來(lái)討論。不管怎樣,我們還是從一段代碼開始我們的話題,概覽一下NodeFileServer.js文件,觀察代碼:
var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(1337, "127.0.0.1"); console.log('Server running at http://127.0.0.1:1337/');
首先調(diào)用了函數(shù)require(),require()是程序員最常用的函數(shù)之一。實(shí)際上,在CommonJS規(guī)范中也有提到這個(gè)函數(shù),在討論到關(guān)于JavaScript模塊概念的時(shí)候有提及,此外,Davd Flanagan在2009年的一個(gè)很酷的實(shí)現(xiàn)中也有提到。換句話說(shuō),require()對(duì)于你來(lái)說(shuō)可能是個(gè)新鮮事物,但它不是Node隨意添加的一個(gè)函數(shù),他是使用JavaScript進(jìn)行模塊化編程的核心概念,Node將這一特性發(fā)揮的淋漓盡致。
接下來(lái),http變量用以創(chuàng)建一個(gè)服務(wù)器。這個(gè)服務(wù)使用一個(gè)回調(diào)函數(shù)來(lái)處理當(dāng)產(chǎn)生一個(gè)連接時(shí)的動(dòng)作。這里的回調(diào)函數(shù)并未對(duì)請(qǐng)求作過(guò)多修飾,僅僅以text/plain格式輸出一個(gè)字符串“Hello World”作為請(qǐng)求響應(yīng)。這個(gè)邏輯非常簡(jiǎn)單。
實(shí)際上,這里展示了使用Node的標(biāo)準(zhǔn)模式:
定義交互類型,并獲得一個(gè)用以處理這個(gè)交互的變量(通過(guò)require())。
創(chuàng)建一個(gè)新的服務(wù)(通過(guò)createServer())。
給服務(wù)綁定一個(gè)回調(diào),用以處理請(qǐng)求。包括處理請(qǐng)求的函數(shù)應(yīng)當(dāng)包括一個(gè)請(qǐng)求…,以及一個(gè)響應(yīng)
通知服務(wù)器啟動(dòng)服務(wù),這里需要指定IP和端口(通過(guò)listen)。
解釋器之惑
盡管通過(guò)這種方法可以使用JavaScript輕易的實(shí)現(xiàn)一個(gè)服務(wù)(不管運(yùn)行代碼的虛機(jī)實(shí)際上跑的是C程序還是其他什么程序),這種做法回避了一個(gè)問(wèn)題:你需要使用JavaScript寫出一個(gè)服務(wù)器嗎?為了找到這個(gè)問(wèn)題的答案,我們來(lái)考慮一個(gè)非常典型的場(chǎng)景。
JSON的處理
這是一種非常典型的web應(yīng)用,前臺(tái)使用HTML和CSS,JavaScript用來(lái)作數(shù)據(jù)驗(yàn)證,并和后臺(tái)進(jìn)行數(shù)據(jù)交互。由于你處于web交互的最頂端,你使用Ajax提交數(shù)據(jù)到后臺(tái)并從后臺(tái)獲取數(shù)據(jù),而不是單單依靠表單提交來(lái)實(shí)現(xiàn)。如果你是這樣做的話,那么你同樣會(huì)非常喜歡使用JSON的。JSON是如今最流行的傳輸數(shù)據(jù)的格式。
因此,這個(gè)Ajax也可以比作“把在線拍賣網(wǎng)站里的某些吉他的信息發(fā)給我”。這個(gè)請(qǐng)求通過(guò)網(wǎng)絡(luò)到達(dá)一個(gè)運(yùn)行PHP程序的服務(wù)器。PHP服務(wù)器不得不給JavaScript返回很多信息,而且這些信息必須以某種形式的數(shù)據(jù)包發(fā)給客戶端,而且這個(gè)數(shù)據(jù)包是可以被JavaScript解析的。因此數(shù)據(jù)可以打包成數(shù)組,然后轉(zhuǎn)換為JSON,就像這樣:
$itemGuitar = array( 'id' => 'itemGuitar', 'description' => 'Pete Townshend once played this guitar while his own axe ' . was in the shop having bits of drumkit removed from it.', 'price' => 5695.99, 'urls' => array('http://www.thewho.com', 'http://en.wikipedia.com/wiki/Pete_Townshend') ); $output = json_encode($itemGuitar); print($output);
回到客戶端,JavaScript得到這個(gè)返回的數(shù)據(jù)包,由于經(jīng)過(guò)轉(zhuǎn)換,數(shù)據(jù)編程了JSON格式。就像這樣:
{ "id": "itemGuitar", "description": "Pete Townshend once played this guitar...", "price": 5695.99, "urls": ["http://www.thewho.com", "http://en.wikipedia.com/wiki/Pete_Townshend"] }
這種轉(zhuǎn)換是標(biāo)準(zhǔn)的,轉(zhuǎn)換前后也是相互等價(jià)的。接下來(lái),就可以將這個(gè)字符串轉(zhuǎn)換為JavaScript對(duì)象,可以調(diào)用eval(),就像這樣:
var itemDetails = eval('(' + jsonDataString + ')');
計(jì)算結(jié)果是一個(gè)普通的JavaScript對(duì)象,這個(gè)對(duì)象的屬性和JSON數(shù)組的數(shù)據(jù)結(jié)構(gòu)保持一致。當(dāng)然,由于jsonDataString通常是由服務(wù)器返回的,通常需要這樣來(lái)解析返回結(jié)果:
var itemDetails = eval('(' + request.responseText + ')');
這就是最最典型的JSON處理,但存在一個(gè)非常嚴(yán)重的問(wèn)題。
對(duì)實(shí)體代碼微妙的破壞性
(譯注:這個(gè)小標(biāo)題著實(shí)讓人費(fèi)解,作者這里拐彎抹角的解釋了Node的一個(gè)好處,就是前端和后端都采用同樣的語(yǔ)言JavaScript,在作JSON解析時(shí)是無(wú)障礙的,而當(dāng)前端使用JavaScript作JSON編碼,后臺(tái)用PHP作JSON解碼時(shí),多少會(huì)因?yàn)槎喾N語(yǔ)言的JSON解析的實(shí)現(xiàn)不同而帶來(lái)一些兼容性問(wèn)題)
首先,這類代碼的一個(gè)主要問(wèn)題是,它對(duì)解釋器的依賴比較嚴(yán)重。在上個(gè)例子中,解釋器就是指內(nèi)置的JSON解析器或者實(shí)現(xiàn)解析JSON的代碼,這實(shí)際上依賴了兩樣?xùn)|西:和eval()解析響應(yīng)文本的操作一樣的基于Java的JSON解析器,以及基于PHP的JSON解析器。在PHP5.2.0中已經(jīng)包含了JSON解析器,但卻是以外部依賴的形式給出的,并不是內(nèi)置于PHP的內(nèi)核中。
但這并不是大肆宣揚(yáng)解釋器的種種。畢竟解釋器本身還存在很多問(wèn)題,比如將“I”解析成了“i”,數(shù)組中的元素1解釋成了2。當(dāng)然,在JSON工具正式發(fā)布之前會(huì)有大量的測(cè)試,以保證在各種復(fù)雜場(chǎng)景中都不會(huì)出現(xiàn)錯(cuò)誤,包括在客戶端的解析結(jié)果和在服務(wù)器端的解析結(jié)果完全一致。無(wú)論如何,這都需要大量的測(cè)試才行。
不管怎樣,JSON依然存在很多實(shí)際的問(wèn)題。
基于某種語(yǔ)言(基于JavaScript或者PHP)的JSON解析器選擇是一個(gè)很大的問(wèn)題。換句話說(shuō),問(wèn)題不是在于“翻譯”(translation)而在于“翻譯器”(translator)(譯注:作者的意思是說(shuō)JSON本身的規(guī)則沒(méi)有問(wèn)題,反倒是各種語(yǔ)言的JSON實(shí)現(xiàn)的質(zhì)量參差不齊,甚至有很多bug)。當(dāng)一個(gè)語(yǔ)言的版本比較穩(wěn)定時(shí),基于這門語(yǔ)言的JSON解析器的運(yùn)用和推廣會(huì)比較快。結(jié)果是,JSON解析器變的越來(lái)越強(qiáng)大,以至于可以解析任意復(fù)雜的數(shù)據(jù)結(jié)構(gòu),即便這么復(fù)雜的數(shù)據(jù)結(jié)構(gòu)根本不會(huì)實(shí)際用到。反之,每次迭代中(每次計(jì)算迭代的路徑和數(shù)據(jù)類型的組合),也很有可能出現(xiàn)JSON解釋器無(wú)法解析的數(shù)據(jù)結(jié)構(gòu)(或者很深的JSON路徑)的情況。
下圖就是可選的JSON解釋器
這并不是說(shuō)JSON本身很糟糕,實(shí)際上,我們認(rèn)為JSON的流行正是得益于其在新領(lǐng)域中的應(yīng)用(譯注:作者的言外之意是,在新領(lǐng)域中的初次JSON實(shí)現(xiàn)往往伴隨很多問(wèn)題)。對(duì)于新的領(lǐng)域,我們不禁要問(wèn):“這個(gè)新東東支持JSON嗎?” 因此,JSON需要不斷進(jìn)化,需要不斷的測(cè)試,不斷的兼容新的平臺(tái)。而作為程序員的,可能需要重新組織你的數(shù)據(jù)結(jié)構(gòu),或者等待新的版本出現(xiàn)以滿足你的需求,或者干脆直接hack JSON。而這些正是我們所說(shuō)的編程資源的浪費(fèi)。
假設(shè)你可以自己動(dòng)手豐衣足食實(shí)現(xiàn)一個(gè)解釋器,即便這樣,你也沒(méi)有通過(guò)“抄近道”揀到便宜,而是用JavaScript重復(fù)造輪子而已。
而Node則規(guī)避了此類問(wèn)題,剛剛你讀到的文字——關(guān)于內(nèi)嵌JSON的PHP5.2.0、關(guān)于將對(duì)象轉(zhuǎn)換為數(shù)組、關(guān)于采用新的結(jié)構(gòu)組織數(shù)據(jù)的方式、關(guān)于JSON中新特性的實(shí)現(xiàn)——這一切擾人的問(wèn)題在Node中都將不復(fù)存在,因?yàn)榍岸送ㄟ^(guò)JavaScript作JSON編碼,后臺(tái)使用JavaScript作JSON解碼,永遠(yuǎn)不會(huì)出問(wèn)題。
JavaScript中eval()的潛在隱患
正如我們不用將Node當(dāng)作一門新的語(yǔ)言來(lái)對(duì)待一樣,在Node中通過(guò)eval()來(lái)執(zhí)行一段代碼也和JavaScript中的eval()一樣(不被推薦)。眾所周知eval()是非常危險(xiǎn)的。eval()用以執(zhí)行一段文本表示的代碼邏輯,可以理解為在文本框中“直接敲入SQL代碼來(lái)執(zhí)行查詢”,這是不安全的,這實(shí)際上是惡意SQL注入。當(dāng)每次eval()執(zhí)行一段字符串的時(shí)候,(美國(guó))中西部的一只小狗都會(huì)瑟瑟發(fā)抖,東部海灘上的某位母親的腳趾會(huì)被刺傷并受到詛咒。eval()非常危險(xiǎn)。網(wǎng)上有很多關(guān)于此的資料,這里不再贅述。可以用google查詢“eval JavaScript evil”或者“eval JavaScript injection”獲取更多信息。
當(dāng)然,如果沒(méi)有任何其他上下文的約束,在Node中也是允許使用eval()的,因此eval()的隱患在Node依然存在。畢竟Node的目的并不是完全解決eval()的問(wèn)題。Node被稱之為基于事件的JavaScript或基于事件的I/O,這里所說(shuō)的“基于事件”是Node中非常重要的概念。但要徹底理解什么是基于事件,以及為什么基于事件能讓你規(guī)避eval()的危險(xiǎn),則需要理解JSON在應(yīng)用之中是如何工作的,此外還要搞清楚適應(yīng)于web應(yīng)用典型架構(gòu)的特有數(shù)據(jù)結(jié)構(gòu)。
基于事件的Web應(yīng)用
傳統(tǒng)的Web表單提交就是典型的基于事件的模式。換句話說(shuō),在Web表單里輸入了很多數(shù)據(jù)(用戶輸入文本框,點(diǎn)選復(fù)選框,從列表中選中某些項(xiàng)等等),之后這些數(shù)據(jù)提交給服務(wù)器。這個(gè)場(chǎng)景中實(shí)際是一個(gè)單一的程序事件:使用POST方式將表單數(shù)據(jù)提交。這也是基于Ajax的Web應(yīng)用的工作原理。
一次性發(fā)送大量數(shù)據(jù)
對(duì)于Ajax來(lái)說(shuō),是可以和基于事件編程扯上一點(diǎn)關(guān)系??蛻舳撕头?wù)器端之間有些交互可以認(rèn)為是基于事件的。典型的場(chǎng)景是輸入一個(gè)省市代碼,發(fā)送請(qǐng)求到服務(wù)器獲得城市和省的名稱。這里通過(guò)XmlHttpRequest的Ajax并不需要將很多數(shù)據(jù)一次性扔給服務(wù)器。但這并不能改變大部分web應(yīng)用都是基于頁(yè)面刷新這種模式的現(xiàn)狀。Ajax已經(jīng)更廣泛的用于很多有意思的視覺(jué)相關(guān)的交互,快速的作表單驗(yàn)證,無(wú)刷新提交數(shù)據(jù),這樣就可以避免重新載入頁(yè)面。因此,盡管并未通過(guò)提交表單來(lái)發(fā)起一個(gè)真正的POST請(qǐng)求,通過(guò)Ajax可以模擬POST表單提交。
坦率的講,這種傳統(tǒng)的Ajax交互方式也阻礙了Ajax程序員的創(chuàng)新。每次發(fā)送一個(gè)請(qǐng)求時(shí)(不管請(qǐng)求的數(shù)據(jù)多么小),都會(huì)在網(wǎng)絡(luò)里走一個(gè)來(lái)回。服務(wù)器必須針對(duì)這個(gè)請(qǐng)求作出響應(yīng),通常是開辟一個(gè)新的進(jìn)程。因此,如果你真正置身于一個(gè)事件模型的環(huán)境中作開發(fā),你可能需要通過(guò)發(fā)起10到15個(gè)單獨(dú)的小請(qǐng)求來(lái)保持你的頁(yè)面和服務(wù)器之間的聯(lián)系,服務(wù)器也會(huì)為之創(chuàng)建10到15個(gè)線程(可能更少,這取決于服務(wù)器處理新請(qǐng)求時(shí)分配線程池的策略),當(dāng)這個(gè)數(shù)量乘以1000或者10000或者100000時(shí)(譯注:每個(gè)頁(yè)面需要10個(gè)請(qǐng)求,那么越多用戶訪問(wèn)這個(gè)頁(yè)面,所發(fā)起的請(qǐng)求個(gè)數(shù)就會(huì)越來(lái)越多),就會(huì)出現(xiàn)內(nèi)存溢出、邏輯交錯(cuò)帶來(lái)的沖突、網(wǎng)絡(luò)癱瘓、系統(tǒng)崩潰這些問(wèn)題。
結(jié)果是,在大多數(shù)場(chǎng)景中,Web應(yīng)用需要保持對(duì)事件的最小依賴。有一個(gè)折衷方案,就是服務(wù)器端程序的響應(yīng)返回的不是一個(gè)微小的數(shù)據(jù)片段,而是帶有更多冗余數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)包,通常是JSON數(shù)據(jù),這時(shí)就又遇到了eval()的問(wèn)題。問(wèn)題當(dāng)然出在eval()身上,但這也和Web本身和服務(wù)器線程控制、包括頁(yè)面和服務(wù)器之間的HTTP請(qǐng)求和響應(yīng)策略(至少在這個(gè)場(chǎng)景下)有密不可分的關(guān)系。
或許有些人對(duì)上文提到的問(wèn)題不以為然,因?yàn)槟阒烙泻芏喾椒▉?lái)規(guī)避直接eval()帶來(lái)的問(wèn)題,你會(huì)使用諸如JSON.parse()來(lái)代替eval()。同樣有很多令人信服的論據(jù)鼓勵(lì)我們小心的使用eval()。這些東東都是值得進(jìn)一步討論的。但不管怎樣,看一看eval()帶來(lái)了太多類似棧溢出(Stack Overflow)這類的問(wèn)題吧,你會(huì)發(fā)現(xiàn)大部分程序員并未正確或者安全的使用eval()。這著實(shí)是一個(gè)問(wèn)題。因?yàn)樘嗖锁B程序員似乎根本沒(méi)有意識(shí)到eval()的問(wèn)題有多嚴(yán)重。
不斷的發(fā)送少量的數(shù)據(jù)
Node帶來(lái)了架構(gòu)應(yīng)用的新思路,我們可以基于Node采用事件模型來(lái)架構(gòu)Web應(yīng)用,或者說(shuō)“小型的”事件模型。換句話說(shuō),你應(yīng)當(dāng)基于大量的事件發(fā)送大量的請(qǐng)求,每個(gè)請(qǐng)求的數(shù)據(jù)包都很小,或者根據(jù)需要從后臺(tái)抓取少量數(shù)據(jù),而不是發(fā)送很少的請(qǐng)求,每次請(qǐng)求都帶有大量的數(shù)據(jù)。在很多場(chǎng)景中,大多數(shù)情況下你需要喚醒GUI程序(Java Swing程序員的GUI知識(shí)儲(chǔ)備可以派上用場(chǎng)了)。因此,當(dāng)用戶輸入姓氏和名字后,移步到下一個(gè)輸入框,這時(shí)就已經(jīng)發(fā)起了一個(gè)請(qǐng)求來(lái)驗(yàn)證輸入的用戶名是否已經(jīng)存在。省市代碼、地址和電話號(hào)碼的驗(yàn)證也是同理。頁(yè)面上每發(fā)生一個(gè)時(shí)間,都會(huì)產(chǎn)生一個(gè)請(qǐng)求和響應(yīng)。
這有什么不同嗎?為什么Node可以做到,并規(guī)避了已有的線程問(wèn)題?其實(shí)Node并沒(méi)有這么神秘,Node官網(wǎng)充分解釋了其哲學(xué):
Node的目標(biāo)是提供一種構(gòu)建可伸縮的網(wǎng)絡(luò)應(yīng)用的方案,在hello world例子中,服務(wù)器可以同時(shí)處理很多客戶端連接。Node和操作系統(tǒng)有一種約定,如果創(chuàng)建了新的鏈接,操作系統(tǒng)就將通知Node,然后進(jìn)入休眠。如果有人創(chuàng)建了新的鏈接,那么它(Node)執(zhí)行一個(gè)回調(diào),每一個(gè)鏈接只占用了非常小的(內(nèi)存)堆棧開銷。
Node是無(wú)阻塞的,不會(huì)出現(xiàn)同源競(jìng)爭(zhēng)線程的情況(Node非常樂(lè)于處理即時(shí)的請(qǐng)求,發(fā)生了什么事情,那就讓他發(fā)生吧),新請(qǐng)求到達(dá)服務(wù)器時(shí),不需要為這個(gè)請(qǐng)求單獨(dú)作什么事情。Node僅僅是悠閑的坐在那里等待(請(qǐng)求的發(fā)生),有請(qǐng)求就處理請(qǐng)求。用非常簡(jiǎn)單的代碼就可以實(shí)現(xiàn),而不用花費(fèi)程序員寶貴的精力去實(shí)現(xiàn)一整套服務(wù)器端邏輯。
沒(méi)錯(cuò),混亂不可避免
值得一提的是,非阻塞系統(tǒng)帶來(lái)的問(wèn)題也會(huì)出現(xiàn)在這種編程模式中:一個(gè)進(jìn)程(非線程)等待一個(gè)數(shù)據(jù)存儲(chǔ)操作,這時(shí)產(chǎn)生了另外一個(gè)抓取與之無(wú)關(guān)的數(shù)據(jù)的操作,這個(gè)意外的操作會(huì)對(duì)現(xiàn)有的等待造成影響(譯注:作者的意思是說(shuō)多個(gè)操作同時(shí)發(fā)生或者沒(méi)有按照預(yù)定順序發(fā)生時(shí),會(huì)產(chǎn)生混亂,也就是說(shuō),操作本身并不是原子性的)。但要注意,大多數(shù)基于事件的web編程模式都是“只讀的”!你大概也沒(méi)有遇到過(guò)通過(guò)“微請(qǐng)求”來(lái)修改數(shù)據(jù)的情況,或者說(shuō)非常罕見。相反,通過(guò)這種請(qǐng)求來(lái)驗(yàn)證數(shù)據(jù)合法性、查詢數(shù)據(jù)的情形則非常常見。這種情況下,最好直接根據(jù)請(qǐng)求作響應(yīng)。數(shù)據(jù)庫(kù)本身會(huì)作加鎖操作,一般來(lái)講,一個(gè)優(yōu)秀的數(shù)據(jù)庫(kù)完全可以高效的做到數(shù)據(jù)操作的加鎖解鎖,而不用服務(wù)器端的程序代碼去多做什么。而Node又比操作系統(tǒng)處理線程的保持和釋放更加高效,使得服務(wù)器不必單獨(dú)為“web響應(yīng)”開辟一個(gè)進(jìn)程。
此外,Node也計(jì)劃實(shí)現(xiàn)“進(jìn)程分支”(process forking),HTML5 Web Workers API為更復(fù)雜的進(jìn)程控制提供了引擎(規(guī)范)支持。同樣,如果你采用基于事件的模型來(lái)架構(gòu)web應(yīng)用,你的程序可能至少有100多個(gè)場(chǎng)景需要線程的支持。最終你會(huì)發(fā)現(xiàn),你的編程思路和思考問(wèn)題的方式發(fā)生了改變,你的注意力將放在服務(wù)器端處理請(qǐng)求的邏輯上,而不必在乎Node如何工作。
Node的用武之地
這里我們討論另外一種web開發(fā)模式,不管是不是采用了Node、或者是不是采用了基于事件的編程模式,這都無(wú)關(guān)緊要,因?yàn)檫@種模式實(shí)在太重要了。簡(jiǎn)言之:對(duì)癥下藥!概括講就是,針對(duì)不同的問(wèn)題采取不同的解決方案,而不管這種解決方案是否解決其他問(wèn)題。
思維定勢(shì)
不止在web設(shè)計(jì)領(lǐng)域,在所有編程之中都存在某種思維定勢(shì)??梢赃@么描述這種思維定勢(shì):你學(xué)到的、掌握的越多,你能解決的問(wèn)題就越多,你所掌握的技能的應(yīng)用場(chǎng)景也就越多。這看起來(lái)理所當(dāng)然,除非你在技術(shù)上鉆研的更深。沒(méi)錯(cuò),學(xué)習(xí)新的語(yǔ)言和新的工具并廣泛使用它總不是壞事。但往往會(huì)進(jìn)入一個(gè)誤區(qū),就是,因?yàn)槟懔私馑阅闶褂盟?,而不是因?yàn)槟闼莆盏募寄芎凸ぞ呤恰白钸m合”你的業(yè)務(wù)的。
我們來(lái)看一下Ajax,關(guān)于Ajax已經(jīng)有太多太多的討論了。我們知道,Ajax為無(wú)刷新的快速查詢請(qǐng)求提供了可靠的解決方案。而如今因?yàn)锳jax的濫用以至于過(guò)分替代了傳統(tǒng)的表單提交。我們遇到一個(gè)新技術(shù)、學(xué)習(xí)它、掌握它、應(yīng)用它,然后“濫用它”。畢竟很多業(yè)務(wù)場(chǎng)景僅僅需要傳統(tǒng)的表單提交,而不需要Ajax。說(shuō)起來(lái)簡(jiǎn)單,實(shí)際上還有成千上萬(wàn)的濫用Ajax的案例場(chǎng)景,僅僅因?yàn)槟硞€(gè)應(yīng)用的開發(fā)工程師對(duì)Ajax的盲目尊崇。
同樣的,Node也面臨這樣一個(gè)問(wèn)題。當(dāng)你初識(shí)Node發(fā)現(xiàn)它的種種好處,就想到處使用它。就會(huì)一股腦的將PHP或Perl程序換成Node。結(jié)果呢?糟透了。其實(shí)你已經(jīng)害上了強(qiáng)迫癥,總是想將Node用于有違其設(shè)計(jì)初衷的場(chǎng)景中:使用JavaScript提交大量數(shù)據(jù)給Node,或者通過(guò)Node返回給JavaScript大量的JSON數(shù)據(jù),交給前端去作eval(),或者干脆使用Node作一個(gè)文件服務(wù)器用以返回HTML頁(yè)面或做HTTP重定向。
但這些場(chǎng)景均不是Node所擅長(zhǎng)的。Node更擅長(zhǎng)處理體積小的請(qǐng)求以及基于事件的I/O,使用Node解決客戶端和服務(wù)器之間的快速溝通,使用表單提交將大量的數(shù)據(jù)發(fā)送給服務(wù)器,使用PHP和Perl來(lái)處理重型數(shù)據(jù)庫(kù)操作以及動(dòng)態(tài)HTML頁(yè)面的生成。使用Node運(yùn)行于服務(wù)器端來(lái)處理體積不大的請(qǐng)求。不管是采用Rails還是Spring以及各式各樣的服務(wù)端容器,只要按需索取即可。一定要明白你需要解決的問(wèn)題是什么,基于此采取最佳解決方案,而不是基于你當(dāng)下所掌握的技能來(lái)解決遇到的問(wèn)題。
Node的簡(jiǎn)單的初衷
還有最后一點(diǎn)需要注意,當(dāng)你越來(lái)越深入你的編程時(shí),你會(huì)發(fā)現(xiàn)你不必每個(gè)工具、API和所使用的框架都達(dá)到精通。將刀用在刀刃上,不要將錘子當(dāng)成鉆頭來(lái)使用。了解每個(gè)工具所適用的場(chǎng)景和能解決的問(wèn)題,然后找到這個(gè)工具的最適合的應(yīng)用場(chǎng)景。如果你想變成超人式的通才(程序員往往什么都想知道),你離“專家”也就越來(lái)越遠(yuǎn),所謂專家,就是指在一兩個(gè)方面達(dá)到非常精通。當(dāng)然,每個(gè)老板都希望能找到超人式的通才,但這種人往往可遇不可求。
學(xué)習(xí)Node可能會(huì)有些吃力,但是非常值得的。為什么?因?yàn)槟阏趯で蠡贘avaScript的web應(yīng)用的解決方案。這意味著你已有的JavaScript編程技能不會(huì)丟掉,當(dāng)你需要使用PHP或者Perl時(shí),你必須重新學(xué)習(xí)一門新的語(yǔ)言,而Node不必如此大動(dòng)干戈。學(xué)習(xí)新語(yǔ)言帶來(lái)的問(wèn)題比學(xué)習(xí)他們帶來(lái)的好處要大的多。
學(xué)習(xí)Node所面臨的挑戰(zhàn)是,你需要更加活躍思維,將程序拆成低耦合的小片段,然后像組裝數(shù)組一樣的組裝他們。但Node和基于事件的I/O并不能解決所有問(wèn)題,但確定的是,很多關(guān)鍵問(wèn)題,只能依靠Node來(lái)解決。
上述就是小編為大家分享的什么是Node.js呢了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。