本文主要過下http生成服務和處理請求的主要流程,其餘功能並未涉及。node
const http = require('http'); http.createServer((req, res) => { res.end('hello word'); }).listen(8080);
例子中從生成服務,到接收請求,最後響應請求,其中主要的工做有4部分,分別是:git
http.createServer
來生成一個服務listen
函數監聽端口req
和res
對象res.end
響應請求// lib/http.js function createServer(opts, requestListener) { return new Server(opts, requestListener); } // lib/_http_server.js function Server(options, requestListener) { if (typeof options === 'function') { requestListener = options; options = {}; } // ... if (requestListener) { // 當req和res對象都生成好之後,就會觸發request事件,讓業務函數對請求進行處理 this.on('request', requestListener); } // connection事件能夠在net Server類中看到,當三次握手完成後,就會觸發這個事件 this.on('connection', connectionListener); } ObjectSetPrototypeOf(Server.prototype, net.Server.prototype); ObjectSetPrototypeOf(Server, net.Server); function connectionListener(socket) { // 這裏就是執行connectionListenerInternal函數並傳入this和socket參數 defaultTriggerAsyncIdScope( getOrSetAsyncId(socket), connectionListenerInternal, this, socket ); } // connection事件觸發後的回調函數,這個函數將在「解析生成req、res對象」板塊進行講解 function connectionListenerInternal(server, socket) { // ... }
調用http.createServer
函數時,會返回一個Server
實例,Server
是從net Server
類繼承而來的。所以,http Server
實例也就具有監聽端口生成服務,與客戶端通訊的能力。前面例子中調用的listen
函數,實際上就是net Server
中的listen
。github
在實例Server
對象的過程當中,會分別監聽request
和connection
這兩個事件。數據結構
connection
:這裏監聽的就是net
中的connection
事件,當客戶端發起請求,TCP三次握手鍊接成功時,服務端就會觸發connection
事件。connection
事件的回調函數connectionListenerInternal
將在下一個板塊進行講解。request
:當req
和res
對象都初始成功之後,就會發布request
事件,前面代碼中咱們能夠看到request
事件的回調函數requestListener
就是開發者調用http.createServer
時傳入的回調函數,這個回調函數會接收req
和res
兩個對象。當客戶端TCP請求與服務端鏈接成功後,服務端就會觸發connection
事件,此時就會實例一個http-parser用來解析客戶端請求,當客戶端數據解析成功後,就會生成一個req
對象,接下來咱們先來看下req
對象生成過程。socket
// lib/_http_server.js function Server(options, requestListener) { // ... // 客戶端與服務端三次握手完成,觸發connection事件 this.on('connection', connectionListener); } function connectionListener(socket) { // 這裏就是執行connectionListenerInternal函數並傳入this和socket參數 defaultTriggerAsyncIdScope( getOrSetAsyncId(socket), connectionListenerInternal, this, socket ); } /** * @param {http Server} server * @param {net Socket} socket */ function connectionListenerInternal(server, socket) { // ... // parsers.alloc函數執行會使用返回一個free list分配的HTTPParser對象 const parser = parsers.alloc(); // 請求解析器初始化工做 parser.initialize( HTTPParser.REQUEST, new HTTPServerAsyncResource('HTTPINCOMINGMESSAGE', socket), server.maxHeaderSize || 0, server.insecureHTTPParser === undefined ? isLenient() : server.insecureHTTPParser, server.headersTimeout || 0, ); parser.socket = socket; socket.parser = parser; // ... } // lib/_http_common.js const parsers = new FreeList('parsers', 1000, function parsersCb() { // 這裏使用http-parser庫來做爲請求解析器 const parser = new HTTPParser(); cleanParser(parser); // ... return parser; });
http Server
中使用http-parser實例來做爲客戶端請求的解析器。值得注意的是,這裏使用了free list數據結構來分配parser
對象。函數
// lib/internal/freelist.js class FreeList { constructor(name, max, ctor) { this.name = name; this.ctor = ctor; this.max = max; this.list = []; } // 須要對象,分配一個對象 alloc() { return this.list.length > 0 ? this.list.pop() : // 這裏的ctor是實例FreeList對象時,傳入的統一新增對象的方法 ReflectApply(this.ctor, this, arguments); } // 對象用完,釋放對象 free(obj) { if (this.list.length < this.max) { this.list.push(obj); return true; } return false; } }
這部分運用到free list數據結構。使用該數據結構目的是減小對象新建銷燬所帶來的性能消耗,它會維護一個長度固定的隊列,隊列中的全部對象大小都相同。當須要使用對象的時候,會優先從隊列中獲取空閒的對象,若是隊列中已經沒有可用的對象,就會新建一個與隊列中存放的對象大小相同的對象,供程序使用。對象使用完後,不會直接銷燬,而是會將對象壓入隊列中,直到後面被推出使用。性能
瞭解free list
後,咱們繼續來看下客戶端請求的解析。ui
// lib/_http_common.js const parsers = new FreeList('parsers', 1000, function parsersCb() { const parser = new HTTPParser(); cleanParser(parser); // 爲這些事件綁定回調函數 parser[kOnHeaders] = parserOnHeaders; parser[kOnHeadersComplete] = parserOnHeadersComplete; parser[kOnBody] = parserOnBody; parser[kOnMessageComplete] = parserOnMessageComplete; return parser; });
http-parser在解析客戶端請求也是基於事件來對數據進行處理:this
kOnHeaders
:不斷解析請求頭kOnHeadersComplete
:請求頭解析完成kOnBody
:不斷解析請求體kOnMessageComplete
:請求體解析完成TCP在進行數據傳輸的過程當中,會將超出緩衝區剩餘空間大小的數據進行拆包,使得同一個請求數據包可能分屢次發送給服務端。這裏kOnHeaders
和kOnBody
就是用於拼接被拆分的數據,組合同一個請求的數據。url
當請求頭解析完成之後,會執行kOnHeadersComplete
回調函數,在這個回調函數中會生成req
對象。
// lib/_http_common.js const { IncomingMessage } = require('_http_incoming'); // 請求頭解析完成後執行的回調函數 function parserOnHeadersComplete(versionMajor, versionMinor, headers, method, url, statusCode, statusMessage, upgrade, shouldKeepAlive) { const parser = this; const { socket } = parser; // ... // 絕大多數狀況下socket.server[kIncomingMessage]等於IncomingMessage const ParserIncomingMessage = (socket && socket.server && socket.server[kIncomingMessage]) || IncomingMessage; const incoming = parser.incoming = new ParserIncomingMessage(socket); // ... return parser.onIncoming(incoming, shouldKeepAlive); } // lib/_http_incoming.js function IncomingMessage(socket) { // ... }
kOnHeadersComplete
回調中實例出來的IncomingMessage
對象就是req
對象。回調最後會執行parser.onIncoming
函數,生成res
對象。
// lib/_http_server.js function connectionListenerInternal(server, socket) { // ... // 這個就是kOnHeadersComplete回調最後執行的函數 parser.onIncoming = FunctionPrototypeBind(parserOnIncoming, undefined, server, socket, state); // ... } // 第四個參數就是req對象,req對象是在parser.onIncoming(incoming, shouldKeepAlive)函數執行的時候傳入的incoming對象 function parserOnIncoming(server, socket, state, req, keepAlive) { // ... ArrayPrototypePush(state.incoming, req); // 實例res對象 const res = new server[kServerResponse](req); if (socket._httpMessage) { ArrayPrototypePush(state.outgoing, res); } // ... // 這個事件會在調用res.end的時候觸發 res.on('finish', FunctionPrototypeBind(resOnFinish, undefined, req, res, socket, state, server)); // ... server.emit('request', req, res); // 發佈request事件,執行createServer函數調用傳入的業務處理函數 // ... } // 這裏的ServerResponse繼承於OutgoingMessage類,後續將會介紹到 this[kServerResponse] = options.ServerResponse || ServerResponse;
當req
和res
對象都初始成功並存放後,就會執行createServer函數調用傳入的業務處理函數。
當req
生成後,邊會執行parserOnIncoming
生成res
對象,同時會在res
對象中註冊finish
事件,當業務代碼執行res.end
的時候,就會觸發這個事件。當req
和res
對象都準備好後,就會發布request
事件,同時將req
和res
對象傳入。request
事件的回調函數就是業務代碼調用http.createServer
時傳入的回調函數。
const http = require('http'); http.createServer((req, res) => { res.end('hello word'); }).listen(8080);
當業務處理完成後,業務代碼中主動調用res.end()
函數,響應客戶端請求,接下來咱們看下。
// lib/_http_server.js function ServerResponse(req) { FunctionPrototypeCall(OutgoingMessage, this); // ... } ObjectSetPrototypeOf(ServerResponse.prototype, OutgoingMessage.prototype); ObjectSetPrototypeOf(ServerResponse, OutgoingMessage);
ServerResponse
類是從OutgoingMessage
類繼承的。業務中使用的res.end
方法也是在OutgoingMessage
中進行定義的,下面咱們看下OutgoingMessage
類實現。
// lib/_http_outgoing.js function OutgoingMessage() { // ... this._header = null; // ... } OutgoingMessage.prototype.end = function end(chunk, encoding, callback) { //... if (chunk) { // ... write_(this, chunk, encoding, null, true); } // 訂閱finish事件,回調函數是res.end調用時傳入的callback if (typeof callback === 'function') this.once('finish', callback); // ... // 使用write_將響應數據寫入響應請求的內容中,而後執行_send綁定finish函數,當數據響應完成後,就會觸發執行這個finish函數 const finish = FunctionPrototypeBind(onFinish, undefined, this); this._send('', 'latin1', finish); } function write_(msg, chunk, encoding, callback, fromEnd) { // ... len = Buffer.byteLength(chunk, encoding); // ... if (!msg._header) { if (fromEnd) { msg._contentLength = len; } } //... // 業務代碼中調用res.end,_header爲null,_implicitHeader函數在lib/_http_server.js中被重寫,_implicitHeader執行會將一個header+CRLF賦值給msg._header if (!msg._header) { msg._implicitHeader(); } // ... ret = msg._send(chunk, encoding, callback); // ... } OutgoingMessage.prototype._send = function _send(data, encoding, callback) { if (!this._headerSent) { if (typeof data === 'string' && (encoding === 'utf8' || encoding === 'latin1' || !encoding)) { // _implicitHeader函數生成爲_header賦值響應頭+CRLF,所以這裏的data最終的值爲響應頭+CRLF+響應體 data = this._header + data; } else { const header = this._header; ArrayPrototypeUnshift(this.outputData, { data: header, encoding: 'latin1', callback: null }); } this._headerSent = true; } return this._writeRaw(data, encoding, callback); }; OutgoingMessage.prototype._writeRaw = _writeRaw; function _writeRaw(data, encoding, callback) { const conn = this.socket; // ... if (conn && conn._httpMessage === this && conn.writable) { // ... // 將響應的內容添加到響應緩衝區,並寫出返回給用戶,當寫出成功之後執行回調函數 return conn.write(data, encoding, callback); } // ... }
res.end
在執行的時候,主要流程有兩個:
write_
函數,首先會生成響應頭,而後將響應頭存放到_header
中,後續再生成響應內容,將響應內容(響應頭+CRLF+響應體)經過socket寫出響應給用戶。res._send
,向socket.write
中寫入finish
回調函數,當服務端的響應內容徹底寫出的時候執行finish
函數,finish
函數內部會發布finish
事件。程序中有兩處監聽了finish
事件:
parserOnIncoming
函數中生成res
對象後,會在上面監聽finish
事件;res.end
函數中訂閱了一次finish
事件,這裏的回調函數主要是業務代碼調用res.end
時傳入的回調函數。// 響應頭內容處理 // lib/_http_server.js ServerResponse.prototype._implicitHeader = function _implicitHeader() { this.writeHead(this.statusCode); }; ServerResponse.prototype.writeHead = writeHead; function writeHead(statusCode, reason, obj) { // ... this._storeHeader(statusLine, headers); // ... } // lib/_http_outgoing.js OutgoingMessage.prototype._storeHeader = _storeHeader; function _storeHeader(firstLine, headers) { // ... this._last = true; // ... this._header = header + CRLF; this._headerSent = false; // ... }
_implicitHeader
執行會將響應頭+CRLF內容存放到res._header
中,此時響應頭已經處理完,等到須要使用socket.write
響應請求的時候,再取出來同響應體一同返回給客戶端。
// lib/_http_server.js function parserOnIncoming(server, socket, state, req, keepAlive) { // 注意這裏也訂閱了res對象中的finish事件 res.on('finish', FunctionPrototypeBind(resOnFinish, undefined, req, res, socket, state, server)); } function resOnFinish(req, res, socket, state, server) { // 清除state中存放的req對象 ArrayPrototypeShift(state.incoming); clearRequestTimeout(req); clearIncoming(req); // 關閉res process.nextTick(emitCloseNT, res); // 關閉socket鏈接 if (res._last) { if (typeof socket.destroySoon === 'function') { socket.destroySoon(); } else { socket.end(); // socket斷開鏈接 } } } function emitCloseNT(self) { self.destroyed = true; self._closed = true; self.emit('close'); }
當finish
事件觸發,程序會首先將緩衝的req
和res
對象刪除,而後關閉socket
鏈接,至此這個客戶端請求就處理完成了。