本文主要过下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
链接,至此这个客户端请求就处理完成了。