本文的主要內容是對nodejs提供的一些重要模塊,結合官方API進行介紹,遇到精彩的文章,我會附在文中並標明瞭出處。主要包括以下8個模塊html
轉載請註明出處,多謝支持~node
在node.js中,一個字符串的長度與根據該字符串所建立的緩存區的長度並不相同,由於在計算字符串的長度時,是以文字做爲一個單位,而在計算緩存區的長度時,是以字節做爲一個單位。git
好比針對 」我喜好編程」這個字符串,該字符串對象的length屬性值與根據該字符串建立的buffer對象的length屬性值並不相同。由於字符串對象的length屬性值獲取的是文字個數,而buffer對象的length屬性值獲取的是緩存區的長度,即緩存區中的字節。github
var str = '勇士隊加油';
console.log(str.length);//5
var buf = new Buffer(str);
console.log(buf.length);//15
複製代碼
另外,可使用0開始的序號來取出字符串對象或緩存區中的數據。可是,在獲取數據時,字符串對象是以文字做爲一個單位,而緩存區對象是以字節做爲一個單位。好比,針對一個引用了字符串對象的str變量來講,str2獲取的是第三個文字,而針對一個引用了緩存區對象的buf對象來講,buf2獲取的是緩存區中的第三個字節數據轉換爲整數後的數值。以下:web
console.log(str[2]);//隊
console.log(buf[2]);//135
複製代碼
從上文中能夠看出,若是讀取文件內容是,剛好不是一個完整文字時,可能會輸出錯誤信息shell
var fs = require('fs');
var rs = fs.createReadStream('testdata.md', {bufferSize: 11});
var data = '';
rs.on("data", function (trunk){
data += trunk;
});
rs.on("end", function () {
console.log(data);
});
複製代碼
可能會輸出以下的內容編程
事件循���和請求���象構成了Node.js���異步I/O模型的���個基本���素,這也是典���的消費���生產者場景。
複製代碼
形成這個問題的根源在於data += trunk
語句裏隱藏的錯誤,在默認的狀況下,trunk是一個Buffer對象。這句話的實質是隱藏了toString的變換的:segmentfault
data = data.toString() + trunk.toString();
複製代碼
因爲漢字不是用一個字節來存儲的,致使有被截破的漢字的存在,因而出現亂碼。解決這個問題有一個簡單的方案,是設置編碼集:數組
var rs = fs.createReadStream('testdata.md', {encoding: 'utf-8', bufferSize: 11});
複製代碼
下面展現一個正確讀取文件,並鏈接buffer對象的方法緩存
var buffers = [];
var nread = 0;
readStream.on('data', function (chunk) {
buffers.push(chunk);
nread += chunk.length;
});
readStream.on('end', function () {
var buffer = null;
switch(buffers.length) {
case 0: buffer = new Buffer(0);
break;
case 1: buffer = buffers[0];
break;
default:
buffer = new Buffer(nread);
for (var i = 0, pos = 0, l = buffers.length; i < l; i++) {
var chunk = buffers[i];
// 把chunk複製到buffer對象從pos位置開始的地方
chunk.copy(buffer, pos);
pos += chunk.length;
}
break;
}
});
複製代碼
buf.copy(targetBuffer,[targetStart],[sourceStart],[sourceEnd]);
在Buffer對象的copy方法中,使用四個參數,第一個參數爲必須指定的參數,其他三個參數均爲可選參數。第一個參數用於指定複製的目標Buffer對象。第二個參數用於指定目標Buffer對象中從第幾個字節開始寫入數據,參數值爲一個小於目標的Buffer對象長度的整數值,默認值爲0(從開始處寫入數據)。第三個參數用於指定從複製源Buffer對象中獲取數據時的開始位置,默認值爲0,即從複製源Buffer對象中的第一個字節開始獲取數據,第四個參數用於指定從複製源Buffer對象中獲取數據時的結束位置,默認值爲複製源Buffer對象的長度,即一直獲取完畢複製源Buffer對象中的全部剩餘數據。
粉絲日誌 - nodejs buffer對象 淺析 nodejs buffer 對象
根據域名解析ip地址,設置參數能夠返回一個域名對應的多個IP
var dns = require('dns');
dns.lookup('www.baid.com', {all: true} function(err, address, family){
console.log(address)
})
[ { address: '122.10.91.48', family: 4 } ]
複製代碼
注意:lookup函數會受本地host的影響。如,在host文件中配置了
127.0.0.1 www.baidu.com
使用dns.lookup()查詢www.baidu.com這個域名時,會返回 127.0.0.1。此時能夠考慮使用dns.resolve4()方法來替代解析域名。
使用dns.lookupService(address, port, callback)方法,該方法依賴getnameinfo底層函數。 callback函數有三個參數(err, hostname, service),service是protocol,爲http或https,使用以下所示:
dns.lookupService('127.0.0.1',80,(err,hostname,service)=>{
if(err) console.log(err);
console.log('該IP對應的主機爲:'+hostname+' 協議爲:'+service);
});
// 該IP對應的主機爲:localhost 協議爲:http
複製代碼
process是一個全局內置對象,能夠在代碼中的任何位置訪問此對象,這個對象表明咱們的node.js代碼宿主的操做系統進程對象。使用process對象能夠截獲進程的異常、退出等事件,也能夠獲取進程的當前目錄、環境變量、內存佔用等信息,還能夠執行進程退出、工做目錄切換等操做。
Process模塊提供了訪問正在運行的進程。child_process模塊能夠建立子進程,並與他們通訊。cluster模塊提供了實現共享相同端口的集羣服務能力,容許多個請求同時處理。
process實現了EventEmitter接口,exit方法會在當進程退出的時候執行。由於進程退出以後將再也不執行事件循環,全部只有那些沒有回調函數的代碼纔會被執行。在下面例子中,setTimeout裏面的語句是沒有辦法執行到的。
process.on('exit', function () {
setTimeout(function () {
console.log('This will not run');
}, 100);
console.log('Bye.');
});
複製代碼
process.pid:當前進程的進程號。
process.version:Node的版本,好比v0.10.18。
process.platform:當前系統平臺,好比Linux。
process.title:默認值爲「node」,能夠自定義該值。
process.argv:當前進程的命令行參數數組。
console.log("argv: ",process.argv.slice(2));
//node test.js a b c
//argv: [ 'a', 'b', 'c' ]
複製代碼
process.env:指向當前shell的環境變量,好比process.env.HOME。
process.execPath:運行當前進程的可執行文件的絕對路徑。
process.memoryUsage():node進程內存的使用狀況,rss表明ram的使用狀況,vsize表明總內存的使用大小,包括ram和swap;
process.heapTotal,process.heapUsed:分別表明v8引擎內存分配和正在使用的大小。
process.stdout:指向標準輸出。
process.stdin:指向標準輸入。
process.stderr:指向標準錯誤。
process.exit():退出當前進程。
process.cwd():返回運行當前腳本的工做目錄的路徑。_
process.chdir():改變工做目錄。
process.nextTick():將一個回調函數放在下次事件循環的頂部。 process.nextTick()的例子,指定下次事件循環首先運行的任務。
process.nextTick(function () {
console.log('Next event loop!');
});
複製代碼
上面代碼能夠用setTimeout改寫,可是nextTick回調的優先級更高,會被放在事件隊列的最前面,而settimeout是放在最後面.
setTimeout(function () {
console.log('Next event loop!');
}, 0)
複製代碼
exit事件
當前進程退出時,會觸發exit事件,能夠對該事件指定回調函數。這一個用來定時檢查模塊的狀態的好鉤子(hook)(例如單元測試),當主事件循環在執行完’exit’的回調函數後將再也不執行,因此在exit事件中定義的定時器可能不會被加入事件列表.
process.on('exit', function () {
fs.writeFileSync('/tmp/myfile', 'This MUST be saved on exit.');
});
複製代碼
uncaughtException事件
在你接觸node以後,你就會發現那些影響了主事件循環的異常會把整個node進程宕掉的。這會是至關嚴重的問題,因此process提供了另一個有用的事件uncaughtException
來解決這個問題,當前進程拋出一個沒有被捕捉的意外時,會觸發uncaughtException事件。
process.on('uncaughtException', function (err) {
console.log('Caught exception: ' + err);
});
setTimeout(function () {
console.log('This will still run.');
}, 2000);
// Intentionally cause an exception, but don't catch it.
nonexistentFunc();
console.log('This will not run.');
複製代碼
咱們來看上面的例子,咱們註冊了uncaughtException事件來捕捉系統異常。執行到nonexistentFunc()時,由於該函數沒有定義因此會拋出異常。
Caught exception: ReferenceError: nonexistentFunc is not defined
This will still run.
複製代碼
再看一個例子
var http = require('http');
var server = http.createServer(function(req,res) {
res.writeHead(200, {});
res.end('response');
badLoggingCall('sent response');
console.log('sent response');
});
process.on('uncaughtException', function(e) {
console.log(e);
});
server.listen(8080);
複製代碼
在這裏例子中咱們建立了一個web服務器,當處理完請求以後,咱們會執行badLoggingCall()方法。由於這個方法不存在,因此會有異常拋出。可是咱們註冊的uncaughtException事件會對異常作出處理,這樣服務器不會受到影響得以繼續運行。咱們會在服務器端記錄錯誤日誌
[ReferenceError: badLoggingCall is not defined]
複製代碼
但常規不建議使用該粗略的異常捕獲處理,建議使用 domains
child_process是Node.js的一個十分重要的模塊,經過它能夠實現建立多進程,以利用單機的多核計算資源。雖然,Nodejs天生是單線程單進程的,可是有了child_process模塊,能夠在程序中直接建立子進程,並使用主進程和子進程之間實現通訊,等到子進程運行結束之後,主進程再用回調函數讀取子進程的運行結果。
try catch
沒法捕獲異步中的異常。因此咱們能作的只能是
app.get('/index', function (req, res) {
// 業務邏輯
});
process.on('uncaughtException', function (err) {
logger.error(err);
});
複製代碼
這個時候,雖然咱們能夠記錄下這個錯誤的日誌,且進程也不會異常退出,可是咱們是沒有辦法對發現錯誤的請求友好返回的,只可以讓它超時返回。
這個時候 domain模塊就出現了,它能夠捕捉異步錯誤。而咱們爲了讓 domain 模塊來接管全部的http請求中的異常,因此把它寫成一箇中間件是很是方便的。
app.use(function (req, res, next) {
var reqDomain = domain.create();
reqDomain.on('error', function (err) { // 下面拋出的異常在這裏被捕獲,觸發此事件
console.log('捕獲到錯誤');
res.send(500, err.stack); // 成功給用戶返回了 500
});
reqDomain.run(next);
});
複製代碼
app.use(function(req,res,next){ .....})
這是一箇中間件,用來接收全部http請求,這裏你能夠捕獲request
和 response
對象用來作一些過濾,邏輯判斷等等,最後經過 next 來放行本次請求,那麼這個中間件就完成了他的一次使命.
而後咱們在 process 上將未處理的異常捕捉一下,作到萬無一失.
process.on('uncaughtException', function (err) {
console.error("uncaughtException ERROR");
if (typeof err === 'object') {
if (err.message) {
console.error('ERROR: ' + err.message)
}
if (err.stack) {
console.error(err.stack);
}
} else {
console.error('argument is not an object');
}
});
複製代碼
而後拋出錯誤實踐一下,是否能被捕捉
app.get('/err', function (req, res) {
//throw new Error('exception');
setTimeout(function () {
throw new Error('exception'); // 拋出一個異步異常
}, 1000);
})
複製代碼
每次 domian 捕獲到錯誤後,我都在控制檯輸出了一行提示信息 "捕獲到錯誤" 當前進程並無由於異常而掛掉,這就是咱們要的效果.
咱們之因此想到用 setTimeout 就是想模擬一個異步的回調,若是你直接 throw new Error('exception');這樣就不是異步了,直接會被 process 上的 uncaughtException 來接管.
## cluster nodejs最大的特色就是單進程、無阻塞運行,而且是異步事件驅動的。Nodejs的這些特性可以很好的解決一些問題,例如在服務器開發中,併發的請求處理是個大問題,阻塞式的函數會致使資源浪費和時間延遲。經過事件註冊、異步函數,開發人員能夠提升資源的利用率,性能也會改善。既 然Node.js採用單進程、單線程模式,那麼在現在多核硬件流行的環境中,單核性能出色的Nodejs如何利用多核CPU呢?
cluster是一個nodejs內置的模塊,用於nodejs多核處理。cluster模塊,能夠幫助咱們簡化多進程並行化程序的開發難度,輕鬆構建一個用於負載均衡的集羣。
fork其實就是建立子進程的方法,新建立的進程被認爲是子進程,而調用fork的進程則是父進程。 子進程和父進程原本是在獨立的內存空間中的。但當你使用了fork以後,二者就處在同一個做用域內了。 可是,內存的讀寫,文件的map,都不會影響對方。也就是說,你建立的進程其實能夠相互通訊,而且被master進程 管理。
var util = require("util");
複製代碼
util.inherits(constructor, superConstructor)
是一個實現對象間原型繼承的方法。JavaScript 的面向對象特性是基於原型的繼承,與常見的基於類的不一樣,JavaScript 沒有提供對象繼承的語言級別特性,而是經過原型鏈複製來實現的。inherits方法能夠將父類原型鏈上的方法複製到子類中,實現原型式繼承。
var events = require("events");
//MyStream構造函數,在構造函數將this指向本對象
function MyStream() {
events.EventEmitter.call(this);
}
//複製父對象上全部的方法
util.inherits(MyStream, events.EventEmitter);
//對MyStream類添加原型方法
MyStream.prototype.write = function(data) {
this.emit("data", data);
}
var stream = new MyStream();
//因爲MyStream繼承自EventEmitter,因此其實例stream是MyStream類的實例也是EventEmitter類的實例
console.log(stream instanceof events.EventEmitter); // true
console.log(MyStream.super_ === events.EventEmitter); // true
//父類中的方法調用
stream.on("data", function(data) {
console.log('Received data: "' + data + '"');
})
//子類中的方法調用
stream.write("It works!"); // Received data: "It works!"
複製代碼
此模塊是一個核心模塊,直接引用
var events = require('events');
複製代碼
events模塊只提供了一個對象,events.EventEmitter,核心是 事件發射 和 事件監聽 功能。每一個事件由一個事件名(用於標識事件),和多個參數組成。事件名:字符串,一般表達必定的語義;事件被髮射時,監聽該事件的函數被依次調用。
var events = require("events");
var emitter = new events.EventEmitter();
emitter.on("/click", function () {
console.log("first event");
})
emitter.on("/click", function () {
console.log("second event");
})
emitter.emit("/click");
複製代碼
注意
var nn = emitter.emit("/click1");
emitter.on("/click1", function () {
console.log("first event");
})
console.log(nn);// false
複製代碼
EventEmitter.once(事件名, 回調函數)
,即把上面的on替換爲once便可,而後這個只監聽一次就失效;
EventEmitter.removeListener(事件名, 回調函數名)
var events = require("events");
var emitter = new events.EventEmitter();
var first = function () {
console.log("first event");
}
var second = function () {
console.log("second event");
}
emitter.on("/click", first)
emitter.on("/click", second)
emitter.emit("/click");
emitter.removeListener("/click", first);
console.log("————移除完成————");
emitter.emit("/click");
輸出
// first event
// second event
// --移除完成--
// second event
複製代碼
var events = require("events");
var emitter = new events.EventEmitter();
var first = function () {
console.log("first event");
}
var second = function () {
console.log("second event");
}
emitter.on("/click", first)
emitter.on("/click", second)
emitter.emit("/click");
emitter.removeAllListeners("/click");
console.log("————移除完成————");
emitter.emit("/click");
輸出
// first event
// second event
// --移除完成--
複製代碼
當碰見異常時會發射error事件,EventEmitter規定,若是沒有監聽其的監聽器,Node.js會把其當成異常,退出程序並打印調用棧。所以須要設置監聽其的監聽器,避免碰見錯誤後整個程序崩潰。
var events = require("events");
var emitter = new events.EventEmitter();
var first = function () {
console.log("first event");
}
var error = function (error) {
console.log(error);
}
emitter.on("/click", first)
emitter.on("error", error) //若是沒有這一行代碼,下面在發射error時會出錯而後退出程序
emitter.emit("/click");
emitter.emit("error", error)
console.log("————移除完成————");
emitter.emit("/click");
輸出
// first event
// [Function]
// --移除完成--
// first event
複製代碼
任何類型若是繼承了該類就是一個事件觸發體,繼承該類的任何類型都是事件的一個實例(給事件綁定一個函數後,一旦觸發事件,立刻執行事件綁定函數.
下面例子演示經過繼承給對象綁定一個事件,來自一介布衣_events模塊的精彩示例
var util = require('util');
var events = require('events');
var Anythin = function (name) {
this.name = name;
}
util.inherits(Anythin, events.EventEmitter);
//建立一隻貓
var cat = new Anythin('黑貓');
//綁定事件
cat.on("activity", function (activity) {
console.log(this.name + activity);
});
//建立一隻老鼠
var mouse = new Anythin('老鼠');
//綁定事件
mouse.on("activity", function (activity) {
console.log(this.name + activity);
});
//建立屋子的主人
var people = new Anythin('主人');
//綁定事件
people.on("activity", function (activity) {
console.log(this.name + activity);
});
//建立主人的孩子
var child = new Anythin('嬰兒');
//綁定事件
child.on("activity", function (activity) {
console.log(this.name + activity);
});
console.log('靜靜的夜晚,主人一家正在酣睡......');
console.log('黑貓緊盯着黑暗的角落.....');
setTimeout(function(){
console.log('黑貓再也堅持不住了......');
cat.emit("activity",'睡着了');
mouse.emit("activity",'爬出洞口');
people.emit("activity",'聞聲而起');
child.emit("activity",'開始哭哭啼啼');
},3000);
複製代碼
上面的例子就是一個萬能的造物主,能夠建立宇宙中的萬事萬物.上面建立的一個事件引起體 "黑貓" 因爲晚上'上班' 太辛苦,而偷偷去睡覺,這一事件誘因直接致使囂張的'老鼠'從洞裏出來覓食,因爲老鼠覓食動做不當而吵醒作夢的'主人'及正在酣睡的'嬰兒' 造物主製造的各類角色時已天生有個綁定事件的活動 activity ,這個功勞要歸功於:
util.inherits(Anythin, events.EventEmitter);
複製代碼
util 模塊也是node.js 中的核心模塊,他構建了一些經常使用的代碼, inherits 就是其中之一,讓前面的類型繼承後面的的類型.因此上面的代碼是 Anythin 類型繼承了 EventEmitter 類型,因此EventEmitter 類型實現的方法屬性能夠直接使用(固然包括綁定事件觸發函數的方法,註冊事件的方法 等)