node 特點
- 單線程
傳統(tǒng)的java是為每個連接建一個線程绝编,每個線程需要耗費大約2MB內(nèi)存僻澎。如果需要處理大量的并發(fā)就需要大量的機器。而node只是有一個線程瓮增,當有客戶端連接了怎棱,就觸發(fā)一個內(nèi)部事件,通過非阻塞I/O绷跑、事件驅(qū)動機制拳恋,讓Node.js程序宏觀上也是并行的。 - 非阻塞I/O
理論上單線程處理砸捏,在執(zhí)行I/O操作時谬运,整個線程會掛起,阻塞垦藏,等到結(jié)果返回后梆暖,才能執(zhí)行后面的代碼,而在node中I/O是非 阻塞的掂骏,當執(zhí)行I/O時轰驳,只是把I/O丟給libuv處理,繼續(xù)執(zhí)行后面的代碼弟灼,當某個I/O執(zhí)行完畢時级解,將以事件的形式通知node線程,線程執(zhí)行這個事件的回調(diào)函數(shù)田绑。 - 事件驅(qū)動
客戶端請求建立連接勤哗,會觸發(fā)相應的事件。在Node中掩驱,在一個時刻芒划,只能執(zhí)行一個事件回調(diào)函數(shù)冬竟,但是在執(zhí)行一個事件回調(diào)函數(shù)的中途,可以轉(zhuǎn)而處理其他事件(比如民逼,又有新用戶連接了)泵殴,然后返回繼續(xù)執(zhí)行原事件的回調(diào)函數(shù),這種處理機制缴挖,稱為“事件循環(huán)”袋狞。
創(chuàng)建一個Server
'use strict';
const http = require('http');
http.createServer(function(req, res){
res.writeHead(200, {'Content-Type':'text/html; charset= UTF-8; '});
res.end('Hello World!');
}).listen(8080);
? ~ curl 127.0.0.1:8080
Hello World!%
這就是一個最簡單的helloWorld,那我們客戶端請求到server是如何運行的呢映屋?
1.http模塊在node 中http模塊有2個一個是http-client苟鸯,一個是http-server
2.http模塊是繼承與net模塊,net模塊是繼承與events
3.events只提供了 events.EventEmitter棚点。EventEmitter 的核心就是事件觸發(fā)與事件監(jiān)聽器功能的封裝
4.此時的http就擁有了事件的監(jiān)聽與觸發(fā)功能
實現(xiàn)原理
我們通過代碼http.createServer方法
// http.js
function createServer(opts, requestListener) {
return new Server(opts, requestListener);
}
//http-server.js
function Server(options, requestListener) {
/*省略代碼*/
if (requestListener) {
this.on('request', requestListener);
}
this.on('connection', connectionListener);
}
核心代碼是注冊了2個方法一個request早处,一個connection方法
connection 方法是在tcp的3次握手會被調(diào)用,在client-server建立連接成功之后瘫析,
function connectionListenerInternal(server, socket) {
debug('SERVER new http connection');
/**省略代碼**/
parser.onIncoming = parserOnIncoming.bind(undefined, server, socket, state);
// We are consuming socket, so it won't get any actual data
socket.on('resume', onSocketResume);
socket.on('pause', onSocketPause);
// Override on to unconsume on `data`, `readable` listeners
socket.on = socketOnWrap;
// We only consume the socket if it has never been consumed before.
if (socket._handle) {
var external = socket._handle._externalStream;
if (!socket._handle._consumed && external) {
parser._consumed = true;
socket._handle._consumed = true;
parser.consume(external);
}
}
parser[kOnExecute] =
onParserExecute.bind(undefined, server, socket, parser, state);
socket._paused = false;
}
tcp建立成功砌梆,然后server就需要解析數(shù)據(jù),生成一個req與res贬循,同時要emit觸發(fā)request方法咸包,這樣就回到了我們的http.createServer(callback)方法
function parserOnIncoming(server, socket, state, req, keepAlive) {
resetSocketTimeout(server, socket, state);
if (req.upgrade) {
req.upgrade = req.method === 'CONNECT' ||
server.listenerCount('upgrade') > 0;
if (req.upgrade)
return 2;
}
state.incoming.push(req);
// If the writable end isn't consuming, then stop reading
// so that we don't become overwhelmed by a flood of
// pipelined requests that may never be resolved.
if (!socket._paused) {
var ws = socket._writableState;
if (ws.needDrain || state.outgoingData >= socket.writableHighWaterMark) {
socket._paused = true;
// We also need to pause the parser, but don't do that until after
// the call to execute, because we may still be processing the last
// chunk.
socket.pause();
}
}
var res = new server[kServerResponse](req);
res._onPendingData = updateOutgoingData.bind(undefined, socket, state);
res.shouldKeepAlive = keepAlive;
DTRACE_HTTP_SERVER_REQUEST(req, socket);
COUNTER_HTTP_SERVER_REQUEST();
if (socket._httpMessage) {
// There are already pending outgoing res, append.
state.outgoing.push(res);
} else {
res.assignSocket(socket);
}
// When we're finished writing the response, check if this is the last
// response, if so destroy the socket.
res.on('finish',
resOnFinish.bind(undefined, req, res, socket, state, server));
if (req.headers.expect !== undefined &&
(req.httpVersionMajor === 1 && req.httpVersionMinor === 1)) {
if (continueExpression.test(req.headers.expect)) {
res._expect_continue = true;
if (server.listenerCount('checkContinue') > 0) {
server.emit('checkContinue', req, res);
} else {
res.writeContinue();
server.emit('request', req, res);
}
} else if (server.listenerCount('checkExpectation') > 0) {
server.emit('checkExpectation', req, res);
} else {
res.writeHead(417);
res.end();
}
} else {
server.emit('request', req, res);
}
return 0; // No special treatment.
}
關鍵代碼 server.emit('request', req, res);
這樣我們就監(jiān)聽到了客戶端的請求。