NodeJs IO流

是什么?

把一个东西从 A 搬到 B 该怎么搬呢?

抬起来,移动到目的地,放下不就行了么。

那如果这个东西有一吨重呢?

那就一部分一部分的搬。

其实 IO 也就是搬东西,包括网络的 IO、文件的 IO,如果数据量少,那么直接传送全部内容就行了,但如果内容特别多,一次性加载到内存会崩溃,而且速度也慢,这时候就可以一部分一部分的处理,这就是流的思想。

nodejs 四种流

可写流,可读流,双工流,转换流, 下文只描述了 可写流和可读流 的使用。

流的直观感受

从一个地方流到另一个地方,显然有流出的一方和流入的一方,流出的一方就是可读流(readable),而流入的一方就是可写流(writable)。

背压

但是 read 和 write 都是异步的,如果两者速率不一致呢?

如果 Readable 读入数据的速率大于 Writable 写入速度的速率,这样就会积累一些数据在缓冲区,如果缓冲的数据过多,就会爆掉,会丢失数据。

而如果 Readable 读入数据的速率小于 Writable 写入速度的速率呢?那没关系,最多就是中间有段空闲时期。

这种读入速率大于写入速率的现象叫做"背压",或者"负压"。也很好理解,写入段压力比较大,写不进去了,会爆缓冲区,导致数据丢失。

如何解决

当调用 writable stream 的 write 方法的时候会返回一个 boolean 值代表是写入了目标还是放在了缓冲区:

  • true: 数据已经写入目标
  • false:目标不可写入,暂时放在缓冲区

我们可以判断返回 false 的时候就 pause,然后等缓冲区清空了就 resume:

js 复制代码
const rs = fs.createReadStream(srcFilename);
const ws = fs.createWriteStream(dstFilename);

rs.on('data', function (chunk) {
    if (ws.write(chunk) === false) {
        rs.pause();
    }
});

rs.on('end', function () {
    ws.end();
});

ws.on('drain', function () {
    rs.resume();
});

这样就能达到根据写入速率暂停和恢复读入速率的功能,解决了背压问题。

drain钩子官方解析

Event: 'drain'#

Added in: v0.9.4

If a call to stream.write(chunk) returns false, the 'drain' event will be emitted when it is appropriate to resume writing data to the stream.

也可以用pipe 管道

ini 复制代码
const rs = fs.createReadStream(src);
const ws = fs.createWriteStream(dst);

rs.pipe(ws);

参考链接

神说要有光-# 彻底掌握 Node.js 四大流,解决爆缓冲区的"背压"问题

相关推荐
在逃牛马5 小时前
【Uni-App+SSM 宠物项目实战】Day12:宠物信息添加
后端
muchan925 小时前
为什么“它”在业务逻辑上是最简单的?
前端·后端·面试
SimonKing6 小时前
告别繁琐配置!Retrofit-Spring-Boot-Starter让HTTP调用更优雅
java·后端·程序员
kele_z6 小时前
PostgreSQL执行计划的使用与查看
后端
往事随风去6 小时前
别再纠结了!IM场景下WebSocket和MQTT的正确选择姿势,一文讲透!
后端·websocket·架构
咖啡Beans6 小时前
Docker安装ELK(Elasticsearch + Logstash + Kibana)
后端·elasticsearch·docker
过分不让我用liberty6 小时前
在java项目中项目里集成ES
后端
Python私教6 小时前
Django全栈班v1.04 Python基础语法 20250912 下午
后端·python·django
爱读源码的大都督6 小时前
为什么Spring 6中要把synchronized替换为ReentrantLock?
java·后端·架构