·眾所周知
NodeJS三大神器"事件驅動,V8,回調函數"。
·事件驅動,故名思議:等快遞和收快遞區別。NodeJS將原先大牛們掌握的神神秘秘的EPOLL走向大眾化,這點是最大的貢獻。當然事件驅動最早最有發言權的還是FLASH擁護者,在那個學校只教授順序編程的年代里,一些廣為流傳的FLASH作品都源自事件驅動。
·V8:初聞該名比較深奧。網上有不少介紹不再累述,個人理解快在JIT和HASH定位對象上,跳過了CLASS代碼。
·事件回調:這些年比較流行的風格,熟悉之后如沐清風,不熟悉的時候死活想不明白。
僅接着,圍繞着三項,無論是官方還是民間立即給NodeJS貼上了高性能、高並發、非阻塞I/O的標簽。一篇繁榮景象的面前,要想真正將此落地,仍然有許多功課需要完成。
·最大的硬傷"單線程"
NodeJS有句話就"除了代碼,所有一切都是並行執行的"。這句話乍一看不是很明白,下面這個例子之后,
req.on('end',function(){ console.log(j+":"+body); // sleep(11); //BLOCK /* //FORK exec("systeminfo",function(err,stdot,stderr){ res.end("aa"+'\n'); }); */ j++; res.write("ccc"+"\n"); });
就會發現如果出現了要想完成SLEEP這件事,光靠Node本身一個進程一個線程根本不可能實現。但在其他高級語言C/JAVA中,SLEEP並不是一件什么大事,多起一個Thread就解決了。Node要想實現,只可能再Fork一個進程來分擔老爹進程的活。所以,市面上所謂的Node多線程模塊都是一種曲線救國,實際上是多進程,包括以后能不能火起來的H5-WebWorker。所以,批注中的取systeminfo,這個比較耗時的動作也只能依靠Fork來繞開,已達到表面上的高並發高性能。
那是不是通過神器"事件回調就能繞開"了呢?答案也是否定的,即便是在單線程模式下最接近異步調用的eventproxy也無法徹底解決這一難題。如下代碼,但這不妨礙eventproxy成為一件大眾佳作。
function sleep(milliSeconds) { var startTime = new Date().getTime(); while (new Date().getTime() < startTime + milliSeconds); } function test300(obj){ sleep(2000); obj.trigger("v3",300); } var EventProxy = require("eventproxy").EventProxy; var obj = new EventProxy(); var counter = 0; var add= function (v1, v2, v3){ console.log(v1+v2+v3+''); }; obj.assign("v1", "v2", "v3", add); obj.trigger("v1",100); test300(obj); obj.trigger("v2",200); console.log("I'm Done");
很多時候,有人甚至把NodeJS這一特性作為賣點,個人絕對這個賣點用不好就會啃人,如果真要說賣點,倒是可以往業務邏輯可讀性上去扯開。但是事件回調又給代碼可讀性脫了不少后退。所以,現在想想老外的那句話真是一語中的,絕對算是非常負責的一句話。
·如果要做到"高、高、非"有辦法嗎?
老實說,就官網API來說難度真是不小,很多的例子太過理想化了。所以多少是要借助其他高級語言的輔助,其中取巧的方法就是EPOLL+多線程,當然這前提得要有足夠時間思考,因為這個水非常深,並且要步步為營。在沒有強大的技術基礎上,還是推薦下面一些技巧,希望對大家有所借鑒。
- 首先,充分評估要實現的功能,如果業務量少的阻塞,可以借fork的力(child_process模塊)。
- 當你是一個服務端,如果有點良心的話,把settimeout留好,別外面人給拖死。寫代碼的都不容易。
- 當你是一個客戶端,記得保護好自己,要設超時,NodeJS的http沒有現成的timeout事件和模塊,這個得要重寫socket和streaming模塊,很麻煩。
- 針對短連接,調優操作系統TIME_WAIT參數的CD時間。並且棄用Fork,想盡辦法優化自身業務邏輯。
·萬事不離"能量守恆定律"
沒有絕對的高性能服務器,就像世界上沒有永動機一樣,開門關窗、開窗關門。C/JAVA的Thread是以犧牲CPU為交換代價,Node的事件已自我承擔單線程風險為代價。把復雜的運算成本留給誰,這是值得認真思考的一件事情。