重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
本篇文章给大家分享的是有关Node中怎么处理HTTP请求,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。
目前成都创新互联公司已为成百上千家的企业提供了网站建设、域名、雅安服务器托管、网站托管维护、企业网站设计、阎良网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。
首先使用express generator快速搭建一个express项目,命令:
express analysis_http
按照提示进入项目安装依赖,然后使用npm start可以启动express项目。那究竟我们项目是如何创建http服务器并且进行启动的呢?express创建成功会在bin文件夹下生成www文件,里面有必须的启动配置。我们可以看看www文件:
我们初步可以看到,主要调用了http.createServer()和 server.listen()两个方法。我们现在可能会有一系列疑问:
接口使用的req和res参数从何而来?createServer()如何创建服务器?listen()具体是进行了什么样的操作?
接下来,我们通过源码来具体分析这些问题。首先,从gitHub拉取一份NodeJS源码,地址:
https://github.com/nodejs/node.git
我们先来查看lib/http.js文件关键代码:
我们可以看到createServer()方法返回的是Server的一个实例。而参数requestListener我们我们接口中的传入的回调函数:
function(req, res, next) { res.send('respond with a resource');}
在文件顶部可以看到Server引用的_http_server.js。所以我们去_http_server.js中看看Server这个构造函数:
由于Server继承net.Server,而net.Server继承自events.EventEmitter所以可以使用on等方法。我们可以看到在Server构造函数中设置了request和connection事件的回调函数:
request使用了createServer中设置的回调方法requestListener。connection则使用了回调方法:connectionListener。
那我们什么时候会触发connection事件呢?我们看下connectionListener关键源码:
这里比较需要注意的有parser对象以及parseOnIncoming()。我们先来看看parser对象,parser来自parsers.alloc():
const parser = parsers.alloc();
从文件顶部可以看出parsers来自_http_common.js文件。我们可以看看源码:
我们可以看到,为了尽可能增加对parser进行重用,减少不断调用构造函数的消耗,parser采用了FreeList的数据结构,FreeList池中设有上限1000,parser是基于事件,使用了http-parser库。然后可以看到两个比较重要的方法:parseOnHeadersComplete和parserOnMessageComplete。
parseOnHeadersComplete:请求头解析完成则触发本方法。parserOnMessageComplete:接收body完成后触发本方法,数据接收完成会触发end事件。
我们再来看看FreeList的源码:
http默认创建了1000个http_parser实例,每次有http请求时,都会从数组中去除一个http_parser分配给当前的socket。如果1000个http_parser全部分配完毕,则会分配新的http_parser。我们解析完请求头会触发parseOnHeadersComplete方法,如果不是udp类型请求,就会触发request事件。
讲完了parser对象,我们接着回到刚才说的parseOnInComing()方法。parseOnInComing()方法使用bind,并传入参数parser,socket,state。
parser.onIncoming = parserOnIncoming.bind(undefined, server, socket, state);
我们先看看parseOnInComing()的源码:
里面有个重要的判断为sockket._httpMessage。如果结果为true,说明有其他请求在占用socket。而parserOnInComing()方法用来处理解析完毕的请求,所以到这里代表解析请求头和请求体已经完成了。而刚才已经讲过:请求头解析完毕会执行parserOnHeadersComplete()方法,我们看看parserOnHeadersComplete()方法的源码:
我们可以看到里面调用了parser.incoming,parser.incoming则是ParserInComingMessage(socket)的一个实例。ParserInComingMessage继承自Stream.Readable。而Stream是NodeJS另一个尤其重要的知识点,不过本篇文章不进行深入讲解。
Object.setPrototypeOf(IncomingMessage.prototype, Stream.Readable.prototype);Object.setPrototypeOf(IncomingMessage, Stream.Readable);
所以整体的逻辑应该为:
1.解析请求头,就会触发request事件。2.请求头解析完毕执行parserOnHeadersComplete()方法。3.在parserOnHeadersComplete()方法中执行了parseOnIncoming()方法。4.最后server.emit('request', req, res)。
在触发request事件的时候,传入req, res参数。因为一开始我们说过了request绑定了回调方法:
function(req, res, next) { res.end('respond with a resource');}
所以触发request的时候回调方法被执行。但是body数据不会被解析,而body数据会一直存放在stream中,直到用户触发data事件来接收body中的数据。回调方法中会触发res.end()事件。那究竟listen()是做了什么操作呢?
因为只有connection事件被触发,才会触发listen()事件。所以先看下onconnection()源码:
我们接着查看调用onconnection()方法的源码:
setupListenHandle(address, port, addressType, backlog, fd)
可以看到底部使用了_listen2。我们继续查看调用_listen2源码:
可以看到内部调用了server._listen2。我们再次查看调用listenInCluster的源码:
我们是使用递推,由下往上推出调用的方法,所以整体的流程应该是:
1.listen()调用listenInCluster(this, pipeName, -1, -1, backlog, undefined, options.exclusive);2.在listenInCluste()中调用server._listen2(address, port, addressType, backlog, fd, flags);3.接着调用了setupListenHandle(address, port, addressType, backlog, fd, flags);4.在setupListenHandle()中调用了onconnection()。5.最终回到listen()方法并且self.emit('connection', socket);
以上就是Node中怎么处理HTTP请求,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注创新互联行业资讯频道。