本文次要过下http生成服务和解决申请的次要流程,其余性能并未波及。

应用例子

const http = require('http');http.createServer((req, res) => {  res.end('hello word');}).listen(8080);

例子中从生成服务,到接管申请,最初响应申请,其中次要的工作有4局部,别离是:

  • 调用http.createServer来生成一个服务
  • 调用listen函数监听端口
  • 接管申请,生成reqres对象
  • 执行业务函数,执行res.end响应申请

http.createServer和listen

// lib/http.jsfunction createServer(opts, requestListener) {  return new Server(opts, requestListener);}// lib/_http_server.jsfunction 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

在实例Server对象的过程中,会别离监听requestconnection这两个事件。

  • connection:这里监听的就是net中的connection事件,当客户端发动申请,TCP三次握手连贯胜利时,服务端就会触发connection事件。connection事件的回调函数connectionListenerInternal将在下一个板块进行解说。
  • request:当reqres对象都初始胜利当前,就会公布request事件,后面代码中咱们能够看到request事件的回调函数requestListener就是开发者调用http.createServer时传入的回调函数,这个回调函数会接管reqres两个对象。

生成req、res对象

当客户端TCP申请与服务端连贯胜利后,服务端就会触发connection事件,此时就会实例一个http-parser用来解析客户端申请,当客户端数据解析胜利后,就会生成一个req对象,接下来咱们先来看下req对象生成过程。

// lib/_http_server.jsfunction 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.jsconst 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.jsclass 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后,咱们持续来看下客户端申请的解析。

// lib/_http_common.jsconst 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在解析客户端申请也是基于事件来对数据进行解决:

  • kOnHeaders:一直解析申请头
  • kOnHeadersComplete:申请头解析实现
  • kOnBody:一直解析申请体
  • kOnMessageComplete:申请体解析实现

TCP在进行数据传输的过程中,会将超出缓冲区残余空间大小的数据进行拆包,使得同一个申请数据包可能分屡次发送给服务端。这里kOnHeaderskOnBody就是用于拼接被拆分的数据,组合同一个申请的数据。

当申请头解析实现当前,会执行kOnHeadersComplete回调函数,在这个回调函数中会生成req对象。

// lib/_http_common.jsconst { 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.jsfunction IncomingMessage(socket) {  // ...}

kOnHeadersComplete回调中实例进去的IncomingMessage对象就是req对象。回调最初会执行parser.onIncoming函数,生成res对象。

// lib/_http_server.jsfunction 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;

reqres对象都初始胜利并寄存后,就会执行createServer函数调用传入的业务处理函数。
req生成后,边会执行parserOnIncoming生成res对象,同时会在res对象中注册finish事件,当业务代码执行res.end的时候,就会触发这个事件。当reqres对象都筹备好后,就会公布request事件,同时将reqres对象传入。request事件的回调函数就是业务代码调用http.createServer时传入的回调函数。

res.end执行

const http = require('http');http.createServer((req, res) => {  res.end('hello word');}).listen(8080);

当业务解决实现后,业务代码中被动调用res.end()函数,响应客户端申请,接下来咱们看下。

// lib/_http_server.jsfunction ServerResponse(req) {  FunctionPrototypeCall(OutgoingMessage, this);  // ...}ObjectSetPrototypeOf(ServerResponse.prototype, OutgoingMessage.prototype);ObjectSetPrototypeOf(ServerResponse, OutgoingMessage);

ServerResponse类是从OutgoingMessage类继承的。业务中应用的res.end办法也是在OutgoingMessage中进行定义的,上面咱们看下OutgoingMessage类实现。

// lib/_http_outgoing.jsfunction 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.jsServerResponse.prototype._implicitHeader = function _implicitHeader() {  this.writeHead(this.statusCode);};ServerResponse.prototype.writeHead = writeHead;function writeHead(statusCode, reason, obj) {  // ...  this._storeHeader(statusLine, headers);  // ...}// lib/_http_outgoing.jsOutgoingMessage.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.jsfunction 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事件触发,程序会首先将缓冲的reqres对象删除,而后敞开socket连贯,至此这个客户端申请就解决实现了。