stream

160 阅读2分钟

平时会用到很多流,request,response,file, socket 都是stream。那么stream是怎么实现的呢,它解决了什么问题呢。

众所周知的生产消费问题,消费者消费生产者生产的数据,如果他们两个直接交互,由于生产者的生产速度和消费者的消费速度不固定,很容易产生摩擦(请自行脑补一下)。于是有了缓冲池,生产者向缓冲池注入,消费者从缓冲池抽取。虽然也会有缓冲池水位过高不能再注入的情况,但是问题还是简化了不少。stream就是这个解决方案的实现。

stream 的流动状态

_readableState.flow = null,暂时没有消费者过来

_readableState.flow = false,主动触发了 .pause()

_readableState.flow = true,流动模式

消费者怎么抽取数据

当readStream 监听了readable event 之后,开始主动索取数据read(). 每次数据push到缓冲池,都会触发readable事件。可以通过 _readableState.buffer 来查看缓存池到底缓存了多少,buffer 大小也是有上限的,默认设置为 16kb,也就是 16384 个字节长度,它最大可设置为 8Mb。 各个stream之间的流动用pipe函数。

const myReadable = new MyReadable(dataSource);
myReadable.setEncoding('utf8');
myReadable.on('readable', () => {
  console.log(myReadable._readableState.buffer.length);
  let chunk;
  while (null !== (chunk = myReadable.read())) {
    console.log(`Received ${chunk.length} bytes of data.`);
  }
});
import fs from 'fs'

// 读取流
let data = ''
const readStream = fs.createReadStream('shell.sh', {
  encoding: 'utf-8'
})

readStream.on('data', (chunk) => {
  data += chunk
})

readStream.on('end', () => {
  console.log(data)
})

readStream.on('error', (error) => {
  console.log(error)
})


// 写入流
const writeStream = fs.createWriteStream('test.txt', {
  encoding: 'utf-8'
})

writeStream.write("我是测试stream", 'utf-8')


writeStream.on('finish', () => {
  console.log('写入完成')
})

// 管道流
writeStream.write('')
readStream.pipe(writeStream).on('finish', () => {
  console.log('管道流完成')
})

Duplex Stream

一般的stream都是readable 或者 writable stream,在上述生产者,缓冲池,消费者三个概念中占用两个。

duplex是双工的stream,既能读也能写。他的输入和输出是分开的。输入是单独的 write stream, 输出是 read stream。缓冲池也是两个。

Transform Stream

Transform Stream 集成了 Duplex Stream,它同样具备 Readable 和 Writable 的能力,只不过它的输入和输出是存在相互关联的,中间做了一次转换处理。常见的处理有 Gzip 压缩、解压等。

Transform 的处理就是通过 _transform 函数将 Duplex 的 Readable 连接到 Writable,由于 Readable 的生产效率与 Writable 的消费效率是一样的,所以这里 Transform 内部不存在「背压」问题,背压问题的源头是外部的生产者和消费者速度差造成的。

参考链接:www.barretlee.com/blog/2017/0…