偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Node.js中的stream模塊詳解

開發(fā) 前端
stream不是node.js獨(dú)有的概念,而是一個(gè)操作系統(tǒng)最基本的操作方式,只不過node.js有API支持這種操作方式。linux命令的|就是stream。

什么是stream

定義

流的英文stream,流(Stream)是一個(gè)抽象的數(shù)據(jù)接口,Node.js中很多對(duì)象都實(shí)現(xiàn)了流,流是EventEmitter對(duì)象的一個(gè)實(shí)例,總之它是會(huì)冒數(shù)據(jù)(以 Buffer 為單位),或者能夠吸收數(shù)據(jù)的東西,它的本質(zhì)就是讓數(shù)據(jù)流動(dòng)起來??赡芸匆粡垐D會(huì)更直觀:

水桶管道流轉(zhuǎn)圖

注意:stream不是node.js獨(dú)有的概念,而是一個(gè)操作系統(tǒng)最基本的操作方式,只不過node.js有API支持這種操作方式。linux命令的|就是stream。

為什么要學(xué)習(xí)stream

視頻播放例子

小伙伴們肯定都在線看過電影,對(duì)比定義中的圖-水桶管道流轉(zhuǎn)圖,source就是服務(wù)器端的視頻,dest就是你自己的播放器(或者瀏覽器中的flash和h5 video)。大家想一下,看電影的方式就如同上面的圖管道換水一樣,一點(diǎn)點(diǎn)從服務(wù)端將視頻流動(dòng)到本地播放器,一邊流動(dòng)一邊播放,最后流動(dòng)完了也就播放完了。

說明:視頻播放的這個(gè)例子,如果我們不使用管道和流動(dòng)的方式,直接先從服務(wù)端加載完視頻文件,然后再播放。會(huì)造成很多問題

  1.  因內(nèi)存占有太多而導(dǎo)致系統(tǒng)卡頓或者崩潰
  2.  因?yàn)槲覀兊木W(wǎng)速 內(nèi)存 cpu運(yùn)算速度都是有限的,而且還要有多個(gè)程序共享使用,一個(gè)視頻文件加載完可能有幾個(gè)g那么大。

讀取大文件data的例子

有一個(gè)這樣的需求,想要讀取大文件data的例子

使用文件讀取 

  1. const http = require('http');  
  2. const fs = require('fs');  
  3. const path = require('path');  
  4. const server = http.createServer(function (req, res) {  
  5.     const fileName = path.resolve(__dirname, 'data.txt');  
  6.     fs.readFile(fileName, function (err, data) {  
  7.         res.end(data);  
  8.     });  
  9. });  
  10. server.listen(8000); 

使用文件讀取這段代碼語法上并沒有什么問題,但是如果data.txt文件非常大的話,到了幾百M(fèi),在響應(yīng)大量用戶并發(fā)請(qǐng)求的時(shí)候,程序可能會(huì)消耗大量的內(nèi)存,這樣可能造成用戶連接緩慢的問題。而且并發(fā)請(qǐng)求過大的話,服務(wù)器內(nèi)存開銷也會(huì)很大。這時(shí)候我們來看一下用stream實(shí)現(xiàn)。 

  1. const http = require('http');  
  2. const fs = require('fs');  
  3. const path = require('path');  
  4. const server = http.createServer(function (req, res) {  
  5.     const fileName = path.resolve(__dirname, 'data.txt');  
  6.     let stream = fs.createReadStream(fileName);  // 這一行有改動(dòng)  
  7.     stream.pipe(res); // 這一行有改動(dòng)  
  8. });  
  9. server.listen(8000); 

使用stream就可以不需要把文件全部讀取了再返回,而是一邊讀取一邊返回,數(shù)據(jù)通過管道流動(dòng)給客戶端,真的減輕了服務(wù)器的壓力。

看了兩個(gè)例子我想小伙伴們應(yīng)該知道為什么要使用stream了吧!因?yàn)橐淮涡宰x取,操作大文件,內(nèi)存和網(wǎng)絡(luò)是吃不消的,因此要讓數(shù)據(jù)流動(dòng)起來,一點(diǎn)點(diǎn)的進(jìn)行操作。

stream流轉(zhuǎn)過程

再次看這張水桶管道流轉(zhuǎn)圖

圖中可以看出,stream整個(gè)流轉(zhuǎn)過程包括source,dest,還有連接二者的管道pipe(stream的核心),分別介紹三者來帶領(lǐng)大家搞懂stream流轉(zhuǎn)過程。

stream從哪里來-soucre

stream的常見來源方式有三種:

  1.  從控制臺(tái)輸入
  2.  http請(qǐng)求中的request
  3.  讀取文件

這里先說一下從控制臺(tái)輸入這種方式,2和3兩種方式stream應(yīng)用場(chǎng)景章節(jié)會(huì)有詳細(xì)的講解。

看一段process.stdin的代碼 

  1. process.stdin.on('data', function (chunk) {  
  2.     console.log('stream by stdin', chunk)  
  3.     console.log('stream by stdin', chunk.toString())  
  4. })  
  5. //控制臺(tái)輸入koalakoala后輸出結(jié)果  
  6. stream by stdin <Buffer 6b 6f 616c 616b 6f 616c 610a>  
  7. stream by stdin koalakoala 

運(yùn)行上面代碼:然后從控制臺(tái)輸入任何內(nèi)容都會(huì)被data 事件監(jiān)聽到,process.stdin就是一個(gè)stream對(duì)象,data 是stream對(duì)象用來監(jiān)聽數(shù)據(jù)傳入的一個(gè)自定義函數(shù),通過輸出結(jié)果可看出process.stdin是一個(gè)stream對(duì)象。

說明:stream對(duì)象可以監(jiān)聽"data","end","opne","close","error"等事件。node.js中監(jiān)聽自定義事件使用.on方法,例如process.stdin.on(‘data’,…), req.on(‘data’,…),通過這種方式,能很直觀的監(jiān)聽到stream數(shù)據(jù)的傳入和結(jié)束

連接水桶的管道-pipe

從水桶管道流轉(zhuǎn)圖中可以看到,在source和dest之間有一個(gè)連接的管道pipe,它的基本語法是source.pipe(dest),source和dest就是通過pipe連接,讓數(shù)據(jù)從source流向了dest。

stream到哪里去-dest

stream的常見輸出方式有三種:

  1.  輸出控制臺(tái)
  2.  http請(qǐng)求中的response
  3.  寫入文件

stream應(yīng)用場(chǎng)景

stream的應(yīng)用場(chǎng)景主要就是處理IO操作,而http請(qǐng)求和文件操作都屬于IO操作。這里再提一下stream的本質(zhì)——由于一次性IO操作過大,硬件開銷太多,影響軟件運(yùn)行效率,因此將IO分批分段進(jìn)行操作,讓數(shù)據(jù)像水管一樣流動(dòng)起來,直到流動(dòng)完成,也就是操作完成。下面對(duì)幾個(gè)常用的應(yīng)用場(chǎng)景分別進(jìn)行介紹

介紹一個(gè)壓力測(cè)試的小工具

一個(gè)對(duì)網(wǎng)絡(luò)請(qǐng)求做壓力測(cè)試的工具ab,ab 全稱 Apache bench ,是 Apache 自帶的一個(gè)工具,因此使用 ab 必須要安裝 Apache 。mac os 系統(tǒng)自帶 Apache ,windows用戶視自己的情況進(jìn)行安裝。運(yùn)行ab 之前先啟動(dòng) Apache ,mac os 啟動(dòng)方式是 sudo apachectl start 。

Apache bench對(duì)應(yīng)參數(shù)的詳細(xì)學(xué)習(xí)地址,有興趣的可以看一下Apache bench對(duì)應(yīng)參數(shù)的詳細(xì)學(xué)習(xí)地址

介紹這個(gè)小工具的目的是對(duì)下面幾個(gè)場(chǎng)景可以進(jìn)行直觀的測(cè)試,看出使用stream帶來了哪些性能的提升。

get請(qǐng)求中應(yīng)用stream

這樣一個(gè)需求:

使用node.js實(shí)現(xiàn)一個(gè)http請(qǐng)求,讀取data.txt文件,創(chuàng)建一個(gè)服務(wù),監(jiān)聽8000端口,讀取文件后返回給客戶端,講get請(qǐng)求的時(shí)候用一個(gè)常規(guī)文件讀取與其做對(duì)比,請(qǐng)看下面的例子。

  •  常規(guī)使用文件讀取返回給客戶端response例子 ,文件命名為getTest1.js 
  1. // getTest.js  
  2. const http = require('http');  
  3. const fs = require('fs');  
  4. const path = require('path');  
  5. const server = http.createServer(function (req, res) {  
  6.     const method = req.method; // 獲取請(qǐng)求方法  
  7.     if (method === 'GET') { // get 請(qǐng)求方法判斷  
  8.         const fileName = path.resolve(__dirname, 'data.txt');  
  9.         fs.readFile(fileName, function (err, data) {  
  10.             res.end(data);  
  11.         });  
  12.     }  
  13. });  
  14. server.listen(8000); 
  •  使用stream返回給客戶端response 將上面代碼做部分修改,文件命名為getTest2.js 
  1. // getTest2.js  
  2. // 主要展示改動(dòng)的部分  
  3. const server = http.createServer(function (req, res) {  
  4.     const method = req.method; // 獲取請(qǐng)求方法  
  5.     if (method === 'GET') { // get 請(qǐng)求  
  6.         const fileName = path.resolve(__dirname, 'data.txt');  
  7.         let stream = fs.createReadStream(fileName);  
  8.         stream.pipe(res); // 將 res 作為 stream 的 dest  
  9.     }  
  10. });  
  11. server.listen(8000); 

對(duì)于下面get請(qǐng)求中使用stream的例子,會(huì)不會(huì)有些小伙伴提出質(zhì)疑,難道response也是一個(gè)stream對(duì)象,是的沒錯(cuò),對(duì)于那張水桶管道流轉(zhuǎn)圖,response就是一個(gè)dest。

雖然get請(qǐng)求中可以使用stream,但是相比直接file文件讀取·res.end(data)有什么好處呢?這時(shí)候我們剛才推薦的壓力測(cè)試小工具就用到了。getTest1和getTest2兩段代碼,將data.txt內(nèi)容增加大一些,使用ab工具進(jìn)行測(cè)試,運(yùn)行命令ab -n 100 -c 100 http://localhost:8000/,其中-n 100表示先后發(fā)送100次請(qǐng)求,-c 100表示一次性發(fā)送的請(qǐng)求數(shù)目為100個(gè)。對(duì)比結(jié)果分析使用stream后,有非常大的性能提升,小伙伴們可以自己實(shí)際操作看一下。

post中使用stream

一個(gè)通過post請(qǐng)求微信小程序的地址生成二維碼的需求。 

  1. /*  
  2. * 微信生成二維碼接口  
  3. * params src 微信url / 其他圖片請(qǐng)求鏈接  
  4. * params localFilePath: 本地路徑  
  5. * params data: 微信請(qǐng)求參數(shù)  
  6. * */  
  7. const downloadFile=async (src, localFilePath, data)=> {  
  8.     try{  
  9.         const ws = fs.createWriteStream(localFilePath);  
  10.         returnnewPromise((resolve, reject) => {  
  11.             ws.on('finish', () => {  
  12.                 resolve(localFilePath);  
  13.             });  
  14.             if (data) {  
  15.                 request({  
  16.                     method: 'POST',  
  17.                     uri: src,  
  18.                     json: true,  
  19.                     body: data  
  20.                 }).pipe(ws);  
  21.             } else {  
  22.                 request(src).pipe(ws);  
  23.             }  
  24.         });  
  25.     }catch (e){  
  26.         logger.error('wxdownloadFile error: ',e);  
  27.         throw e;  
  28.     }  

看這段使用了stream的代碼,為本地文件對(duì)應(yīng)的路徑創(chuàng)建一個(gè)stream對(duì)象,然后直接.pipe(ws),將post請(qǐng)求的數(shù)據(jù)流轉(zhuǎn)到這個(gè)本地文件中,這種stream的應(yīng)用在node后端開發(fā)過程中還是比較常用的。

post與get使用stream總結(jié)

request和reponse一樣,都是stream對(duì)象,可以使用stream的特性,二者的區(qū)別在于,我們?cè)倏匆幌滤肮艿懒鬓D(zhuǎn)圖,

request是source類型,是圖中的源頭,而response是dest類型,是圖中的目的地。

在文件操作中使用stream

一個(gè)文件拷貝的例子 

  1. const fs = require('fs')  
  2. const path = require('path')  
  3. // 兩個(gè)文件名  
  4. const fileName1 = path.resolve(__dirname, 'data.txt')  
  5. const fileName2 = path.resolve(__dirname, 'data-bak.txt')  
  6. // 讀取文件的 stream 對(duì)象  
  7. const readStream = fs.createReadStream(fileName1)  
  8. // 寫入文件的 stream 對(duì)象  
  9. const writeStream = fs.createWriteStream(fileName2)  
  10. // 通過 pipe執(zhí)行拷貝,數(shù)據(jù)流轉(zhuǎn)  
  11. readStream.pipe(writeStream)  
  12. // 數(shù)據(jù)讀取完成監(jiān)聽,即拷貝完成  
  13. readStream.on('end', function () {  
  14.     console.log('拷貝完成')  
  15. }) 

看了這段代碼,發(fā)現(xiàn)是不是拷貝好像很簡(jiǎn)單,創(chuàng)建一個(gè)可讀數(shù)據(jù)流readStream,一個(gè)可寫數(shù)據(jù)流writeStream,然后直接通過pipe管道把數(shù)據(jù)流轉(zhuǎn)過去。這種使用stream的拷貝相比存文件的讀寫實(shí)現(xiàn)拷貝,性能要增加很多,所以小伙伴們?cè)谟龅轿募僮鞯男枨蟮臅r(shí)候,盡量先評(píng)估一下是否需要使用stream實(shí)現(xiàn)。

前端一些打包工具的底層實(shí)現(xiàn)

目前一些比較火的前端打包構(gòu)建工具,都是通過node.js編寫的,打包和構(gòu)建的過程肯定是文件頻繁操作的過程,離不來stream,例如現(xiàn)在比較火的gulp,有興趣的小伙伴可以去看一下源碼。

stream的種類

  •  Readable Stream 可讀數(shù)據(jù)流
  •  Writeable Stream 可寫數(shù)據(jù)流
  •  Duplex Stream 雙向數(shù)據(jù)流,可以同時(shí)讀和寫
  •  Transform Stream 轉(zhuǎn)換數(shù)據(jù)流,可讀可寫,同時(shí)可以轉(zhuǎn)換(處理)數(shù)據(jù)(不常用)

之前的文章都是圍繞前兩種可讀數(shù)據(jù)流和可寫數(shù)據(jù)流,第四種流不太常用,需要的小伙伴網(wǎng)上搜索一下,接下來對(duì)第三種數(shù)據(jù)流Duplex Stream 說明一下。

Duplex Stream 雙向的,既可讀,又可寫。Duplex streams同時(shí)實(shí)現(xiàn)了 Readable和Writable 接口。Duplex streams的例子包括

  •  tcp sockets
  •  zlib streams
  •  crypto streams我在項(xiàng)目中還未使用過雙工流,一些Duplex Stream的內(nèi)容可以參考這篇文章NodeJS Stream 雙工流

stream有什么弊端

  •  用 rs.pipe(ws) 的方式來寫文件并不是把 rs 的內(nèi)容 append 到 ws 后面,而是直接用 rs 的內(nèi)容覆蓋 ws 原有的內(nèi)容
  •  已結(jié)束/關(guān)閉的流不能重復(fù)使用,必須重新創(chuàng)建數(shù)據(jù)流
  •  pipe 方法返回的是目標(biāo)數(shù)據(jù)流,如 a.pipe(b) 返回的是 b,因此監(jiān)聽事件的時(shí)候請(qǐng)注意你監(jiān)聽的對(duì)象是否正確
  •  如果你要監(jiān)聽多個(gè)數(shù)據(jù)流,同時(shí)你又使用了 pipe 方法來串聯(lián)數(shù)據(jù)流的話,你就要寫成:代碼實(shí)例: 
  1. data  
  2.        .on('end', function() {  
  3.            console.log('data end');  
  4.        })  
  5.        .pipe(a)  
  6.        .on('end', function() {  
  7.            console.log('a end');  
  8.        }) 
  9.        .pipe(b)  
  10.        .on('end', function() {  
  11.            console.log('b end');  
  12.        }); 

stream的常見類庫

  •  event-stream 用起來有函數(shù)式編程的感覺
  •  awesome-nodejs#streams也是一個(gè)不錯(cuò)的第三方stream庫,有興趣的小伙伴可以github看一下

總結(jié)

本篇文章屬于進(jìn)階路線【Node必知必會(huì)系列】,看完了這篇文章是不是對(duì)stream有了一定的了解,并且知道了node對(duì)于文件處理還是有完美的解決方案的。本文中三次展示了水桶管道流轉(zhuǎn)圖,總要的事情說三遍希望小伙伴們記住它,除了以上內(nèi)容小伙伴們會(huì)不會(huì)有一些思考,比如

  1.  stream數(shù)據(jù)流轉(zhuǎn)具體內(nèi)容是什么呢?二進(jìn)制還是string類型還是其他類型,該類型為stream帶來了什么好處?
  2.  水桶管道流轉(zhuǎn)圖中的水管,也就是pipe函數(shù)什么時(shí)候觸發(fā)的呢?在什么情況下觸流轉(zhuǎn)發(fā)?底層機(jī)制是什么?上面的疑問(由于篇幅過長(zhǎng)拆分為兩篇)會(huì)在我stream的第二篇文章為大家詳細(xì)講解 

 

責(zé)任編輯:龐桂玉 來源: 前端大全
相關(guān)推薦

2021-09-26 05:06:04

Node.js模塊機(jī)制

2021-03-01 08:03:26

Node.jsStream模塊

2025-05-26 00:31:31

2019-12-17 11:40:44

Node.js模塊前端

2023-06-30 23:25:46

HTTP模塊內(nèi)存

2012-03-09 09:11:29

Node.js

2011-12-09 11:16:48

Node.js

2021-05-21 09:36:42

開發(fā)技能代碼

2016-08-11 14:02:02

NodeJS前端

2024-01-05 08:49:15

Node.js異步編程

2015-03-10 10:59:18

Node.js開發(fā)指南基礎(chǔ)介紹

2013-11-01 09:34:56

Node.js技術(shù)

2011-09-08 14:07:28

Node.js

2023-06-20 19:35:00

Node.js工具

2017-03-19 16:40:28

漏洞Node.js內(nèi)存泄漏

2017-03-20 13:43:51

Node.js內(nèi)存泄漏

2021-12-25 22:29:57

Node.js 微任務(wù)處理事件循環(huán)

2020-05-29 15:33:28

Node.js框架JavaScript

2020-08-31 15:00:17

Node.jsrequire前端

2021-01-26 08:07:44

Node.js模塊 Async
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)