从源码角度分析nodejs如何处理一个HTTP请求

本文从源码层面探讨Node.js处理HTTP请求的细节,包括createServer、listen方法的工作原理。分析了req/res的来源、头部信息创建、connection事件触发、http_parser解析过程及请求事件的触发。阐述了Node.js如何封装复杂流程,使其对外显得简单。
摘要由CSDN通过智能技术生成

使用nodejs创建一个http服务器是非常简单的。突然想到同学的一句话,“经济基础决定上层建筑”。这句话似乎是个万金油,在任何事情上都可以评论一二。所以这种简单其实是因为nodejs在内部帮我们封装了很多,隐藏了非常多的细节。本文旨在深入到这些细节,拨开这个面纱。

为了行文方便,每段代码都加了标志CS

开始

CS1

var http = require('http');
http.createServer(function(req, res) {
   
  res.end('ok');
}).listen(3000);

创建一个http服务器就这么简单。http模块是nodejs内置的模块。这段代码执行了两个函数,分别是http.createServer.listen。那么问题来了。

  • 问题1:req/res是从哪里来的
  • 问题2:相应头部信息是如何创建的
  • 问题3:listen函数发生了什么

带着这些问题来看源码到底发生了什么。我看的源码是https://github.com/nodejs/node/tree/master/lib

createServer

CS2

/lib/http.js

const server = require('_http_server');

const Server = server.Server;
const ClientRequest = client.ClientRequest;

function createServer(requestListener) {
   
  return new Server(requestListener);
}

这里的requestListener就是我们代码中传入的回调函数。

function(req, res) {
  res.end('ok');
}

createServer函数返回的是Server的一个实例。所以需要到_http_server文件中去找Server这个构造函数。

CS3

/lib/_http_server.js

function Server(requestListener) {
  if (!(this instanceof Server)) return new Server(requestListener);
  net.Server.call(this, { allowHalfOpen: true });

  if (requestListener) {
    this.on('request', requestListener);
  }

  // ...
  this.on('connection', connectionListener);
  // ...

}
util.inherits(Server, net.Server);

可以看到,Server借用了net.Server的构造函数。这是由于Server继承自net.Server。后者的构造函数设置了一些属性并继承自EventEmitter。所以可以使用emit/on等。另外,从nodejs官方文档看到net.Server可以创建一个TCP或者IPC服务器。

Server构造函数中设置了requestconnection事件的回调函数(重要)。不要忘了requestListener哦,这是我们在createServer中设置的回调。然后还有个connectionListener。这是触发connection事件时的回调。

  • 问题4:什么时候触发connection事件

CS4

/lib/_http_server.js

function connectionListener(socket) {
  debug('SERVER new http connection');

  httpSocketSetup(socket);

  // ...
  var parser = parsers.alloc();
  parser.reinitialize(HTTPParser.REQUEST);
  parser.socket = socket;
  socket.parser = parser;
  parser.incoming = null;

  var state = {
    // ...
  };

  parser.onIncoming = parserOnIncoming.bind(undefined, this, socket, state);

  parser[kOnExecute] =
    onParserExecute.bind(undefined, this, socket, parser, state);
  // ...
}

这里需要关注的是parser这个对象和parserOnIncoming函数。后者使用了bind,并预先传入了三个参数(parser, socket, state)parser来自于parsers.alloc()。parser

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值