node.js - NodeJs:缓慢的 req.pipe
问题描述
我发现与 go实现(https : //github.com/tus/tusd)。
在这里您可以找到不同实现之间的比较(在本地运行,相同的机器,相同的输入)
节点:
[2019-01-31 16:22:45,578] INFO Uploading 52428800 bytes chunk from offset: 104857600
[2019-01-31 16:22:47,329] INFO Total bytes sent: 157286400 (kb/s: 29930)
去:
[2019-01-31 16:26:31,894] INFO Uploading 52428800 bytes chunk from offset: 104857600
[2019-01-31 16:26:32,348] INFO Total bytes sent: 209715200 (kb/s: 115639)
我探索了 tus-node-server 代码库,然后构建了一个非常简化的服务器实现(我试图减少可能的开销)。
这是代码:
const fs = require('fs');
const express = require('express');
const app = express();
let offset = 0;
let len = Math.pow(2,30);
app.post('/files',(req,res) => {
console.log("post received");
res.set({
'Location': 'http://localhost:8888/files/test',
'Tus-Resumable': '1.0.0',
});
res.status(201).end();
});
app.options('/files',(req,res) => {
console.log("options received");
res.set({
'Location': 'http://localhost:8888/files/test',
'Tus-Resumable': '1.0.0',
'Tus-Version': '1.0.0,0.2.2,0.2.1'
});
res.status(200).end();
});
app.head('/files/test',(req,res) => {
console.log("options received");
res.set({
'Upload-Offset': offset,
'Upload-Length': len
});
res.status(200).end();
});
app.patch('/files/test',(req, res) => {
let localOffset = parseInt(req.get('Upload-Offset'), 10);
// the file is pre-created
const path = `./file.tmp`;
const options = {
flags: 'r+',
start: localOffset
};
const stream = fs.createWriteStream(path, options);
let new_offset = 0;
req.on('data', (buffer) => {
new_offset += buffer.length;
});
return req.pipe(stream).on('finish', () => {
localOffset += new_offset;
offset = localOffset;
res.set({
'Upload-Offset': offset,
'Upload-Length': len
});
res.status(204).end();
});
});
const host = 'localhost';
const port = 8888;
app.listen(port, host, (err, resp) => {
if(err) {
console.error(err);
return
}
console.log('listening')
});
我认为性能不佳是由于以下代码块:
const stream = fs.createWriteStream(path, options);
req.pipe(stream)
我还使用管道检查了文件副本,并且性能良好(类似于 go 实现)
const fs = require('fs');
const path = require('path');
const from = path.normalize(process.argv[2]);
const to = path.normalize(process.argv[3]);
const readOpts = {}; // {highWaterMark: Math.pow(2,16)};
const writeOpts ={}; // {highWaterMark: Math.pow(2,16)};
const startTs = Date.now();
const source = fs.createReadStream(from, readOpts);
const dest = fs.createWriteStream(to, writeOpts);
let offset = 0;
source.on('data', (buffer) => {
offset += buffer.length;
});
dest.on('error', (e) => {
console.log('[FileStore] write: Error', e);
});
source.pipe(dest).on('finish',() => {
const endTs = Date.now();
const kbs = (offset / (endTs - startTs)) / 1000;
console.log("SPEED: ", kbs, offset);
});
所以瓶颈似乎是处理请求和管道。
请你帮我理解发生了什么以及为什么与 go 版本相比如此缓慢
解决方案
我觉得你这里有highWaterMark
问题。
您的测试之间的差异是由于:
req
具有 16 kb 的 highWaterMarkcreateReadStream
具有64 kb 的 highWaterMark
您可以看到增值:
console.log('readableHighWaterMark', req.readableHighWaterMark);
相反,假设您的网络延迟可以忽略不计(因为您是本地主机),您可以尝试writeStream
使用更大的水印创建:
const options = {
flags: 'w',
start: localOffset,
highWaterMark: 1048576
};
const stream = fs.createWriteStream(path, options);
这应该会加快写入速度,但会花费更多 RAM。
推荐阅读
- java - Springboot @ConfigurationProperties 不注入
- c++ - 在基类数组中使用子类方法的默认值
- java - 如何获得持有附魔物品的实体的生命值
- python - 根据列的最小值选择两列值
- python - 在 django 中编写自定义重定向装饰器
- kdb - kdb:如何在控制台中显示整个列表?
- python - 如何替换字符串中的空格逗号?
- assembly - 使用动态链接 libc 开始调试 32 位 ARM 程序集时 gdb 挂起
- android - Android Preferences DataStore 与现有 Room 实现
- apache - 如何让 Apache 将 Web 应用程序作为根文档提供服务?