您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關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請求,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。