5

http server源码解析

 3 years ago
source link: http://www.cnblogs.com/helloxiaoduan/p/14442557.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本文主要过下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.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

在实例 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.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 后,我们继续来看下客户端请求的解析。

// 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 在解析客户端请求也是基于事件来对数据进行处理:

kOnHeaders
kOnHeadersComplete
kOnBody
kOnMessageComplete

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

当请求头解析完成以后,会执行 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;

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.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 事件触发,程序会首先将缓冲的 reqres 对象删除,然后关闭 socket 连接,至此这个客户端请求就处理完成了。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK