原文:Node.js:深刻淺出 http 與 streamhtml
stream(流)是Node.js提供的又一個僅在服務區端可用的模塊,流是一種抽象的數據結構。Stream 是一個抽象接口,Node 中有不少對象實現了這個接口。例如,對http 服務器發起請求的request 對象就是一個 Stream,還有stdout(標準輸出流)。git
經過本文,你會知道 stream 是什麼,以及 strem 在 http 服務中發揮着什麼做用。github
假設樓上有一桶水,想倒往樓下的水桶。直接往下倒,確定會灑出來,那麼在兩個水桶間加一根管子(pipe),就可讓樓上的水,逐漸地流到樓下的水桶內:web
看了前面稍微瞭解 Node.js 的同窗可能就要問了,流的做用不就是傳遞數據麼,也就是把一個地方數據拷貝到另外一個地方,不用流也能夠這樣實現:api
var water = fs.readFileSync('a.txt', {encoding: 'utf8'});
fs.writeFileSync('b.txt', water);複製代碼
但這樣作有個致命問題:瀏覽器
處理數據量較大的文件時不能分塊處理,致使速度慢,內存容易爆滿。bash
const rs = fs.createReadStream('a.mp4')
const ws = fs.createWriteStream('b.mp4')
rs.pipe(ws) // pipe自動調用了 data, end 等事件複製代碼
其實 stream 不只能夠用來處理文件,它能夠處理任何一種數據提供源。下面來看看 stream 在 http 服務中,扮演着什麼樣的角色。服務器
先來幾個靈魂拷問:數據結構
下面一步步來深刻了解 Node.js 的 http 服務。app
Node 能夠不須要 Apache、Nginx、IIS,自身就能夠搭建可靠的 http 服務。
建立一個簡單的 http 服務:
const http = require('http');
const server = http.createServer(function (req, res) {
res.writeHead(200, {
"Content-Type": "text/html;charset=UTF-8"
})
res.end("歡迎來到推啊!!!")
})
server.listen(3000, function () {
console.log('listening port 3000')
})複製代碼
Node 是如何監聽 http 請求的,內部的處理機制是什麼。
http.createServer() 方法返回的是 http 模塊封裝的一個基於事件的 http 服務器。
http.createServer() 封裝了 http.Server()
const http = require('http');
const server = new http.Server();
server.on('request', function (req, res) {
res.writeHead(200, {
"Content-Type": "text/html;charset=UTF-8"
})
res.end("歡迎來到推啊!!!")
})
server.listen(3000, function () {
console.log('listening port 3000');
});複製代碼
Koa 框架的本質是在 http 模塊的上層,封裝了本身的 ctx 對象,以及實現了洋蔥模型的中間件體系。
Node.js 中的 HTTP 接口旨在支持傳統上難以使用的協議的許多特性。 特別是,大塊的、可能塊編碼的消息。 接口永遠不會緩衝整個請求或響應,用戶可以流式傳輸數據。
爲了支持全部可能的 HTTP 應用程序,Node.js 的 HTTP API 都很是底層。 它僅進行流處理和消息解析。
http.Server() 是基於事件的,主要事件有:
那麼,http 模塊是如何監聽獲取瀏覽器發來的請求呢?
這就須要進一步看看 Node.js 中,http 模塊是如何實現的了。
http.Server 繼承自: <net.Server>
net 模塊用於建立基於流的 TCP 或 IPC 的服務器(net.createServer())與客戶端(net.createConnection())
建立 TCP 服務:
const net = require('net');
const server = net.createServer((c) => {
// 'connection' 監聽器。
console.log('客戶端已鏈接');
c.on('end', () => {
console.log('客戶端已斷開鏈接');
});
c.write('你好\r\n');
c.pipe(c);
});
server.on('error', (err) => {
throw err;
});
server.listen(8124, () => {
console.log('服務器已啓動');
});複製代碼
telnet 進行測試:
須要注意的是,net 模塊建立出來的是一個 TCP 服務,而它監聽接受到的數據,是一個 stream(流)
net 模塊接收到的內容是無法直接打印的,須要經過 strema 的方式來處理。
下面代碼接受並打印瀏覽器請求:
const net = require('net');
const fs = require('fs');
const server = net.createServer((c) => {
let stream = fs.createWriteStream('test.txt');
c.pipe(stream).on('finish', () => {
console.log('Done');
});
c.on('error', (err) => {
console.log(err);
});
})
server.listen('4000', () => {
console.log('服務器已啓動');
});複製代碼
在瀏覽器輸入 localhost:4000 後,在服務端目錄下生成 test.txt 文件:
GET / HTTP/1.1
Host: localhost:4000
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7,ko;q=0.6
Cookie: UM_distinctid=16e3ab5c769669-0aa68fde01b9b3-37647e05-13c680-16e3ab5c76a1ec; _9755xjdesxxd_=32; gdxidpyhxdE=6EK5fbKotSpp2Uiam1adlQW2uSUV%2FlvMMzX0Lo2iqcBIdSnV4Gf2CIYG2LZevagi2DhNAVlVmPjg4WyCs0EXamAO%2B3tQHgmnyrEj14hrmaV6Ev2MHAdWnIR9giTvXoIlRicy1MhUZ007j%5C%5C84xvU4PmLl0sEbHlkQ4Tvefuj9Ri%2B6D%2FZ%3A1574856606158; YD00636840014594%3AWM_NI=dKC1nbSYkvmP3bFoHMIDoTTp82bhdKlE9PKhaaJmK%2FO5hJLvRckcK9ONax49iBl7ML0AK8sRz90Qd%2Bexn2ABVSxcFOebaImloWcpuWVLQ8eRrrbgDEaIMdym983xRZqnV1c%3D; YD00636840014594%3AWM_NIKE=9ca17ae2e6ffcda170e2e6eeafd83a8aea9dd7ed5ca9b88ba3d44a828e8faaf23d8ab3ff9bec7c86b7a7d0b12af0fea7c3b92aa8958dd2ed6a9187a597f05cf1be8a90cb478b9ca1b5d77cfceeae89e741b09bf7b1ca64a3bf868eca258deba8abcf7e92bda584ca338d92af99b733ab9ea4d8d048f49f85d9f25abae700b2e721a7a8a2d3c44a968db884c545a7beafaaf068ad89ad91f85d96949fd9e85ab2aeae88c74fa3978385fc67fbe8fd99d152b3b29ad2e237e2a3; YD00636840014594%3AWM_TID=PZ3AtyPUGXFABQFEABZp7I3hmitUyn7z; CNZZDATA1278176396=591860064-1572943020-%7C1574943751; _yapi_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjI4LCJpYXQiOjE1NzY0NjQzMjIsImV4cCI6MTU3NzA2OTEyMn0.EEdBvJMRnnu2-_qli_Jqc2D6CtxocEzZjEz_hWv4qEk; _yapi_uid=28複製代碼
梳理一下 http 服務的整個調用鏈路:
在深刻了解 http 模塊內部的基本原理後,能夠想一想咱們在應用場景中,能夠利用 stream(流)作到哪些事情
能夠嘗試本身實現一下平時接觸到的一些應用,如:
有問題歡迎留言...