[Исходный адрес][1]В моем блоге
Буфер и поток в узле приведут к путанице в передние инженеры, которые новые для узла, потому что интерфейс не имеет аналогичной концепции (или мы не осознавали его). Однако на бэкэнде в узле, буфер и поток везде. Буфер означает буфер, поток означает поток. В компьютере буфер - это зона хранения, которая хранит промежуточные переменные и удобно для CPU для чтения данных; поток аналогичен потоку воды для описания потока данных. Буфер и поток, как правило, являются операциями на уровне байтов. Эта статья представит конкретные детали этих двух модулей, а затем введут файловый модуль, чтобы дать читателям более четкое понимание.
текст
Двоичный буфер буфер
// 创建一个长度为 10、且用 30 填充的 Buffer。
const buf1 = Buffer.alloc(10, 30)
console.log(buf1)// <Buffer 1e 1e 1e 1e 1e 1e 1e 1e 1e 1e>
// 字符串转Buffer
const buf2 = Buffer.from('javascript')
console.log(buf2)// <Buffer 6a 61 76 61 73 63 72 69 70 74>
// 字符串转 buffer
console.log(buf2.toString())// javascript
console.log(buf2.toString('hex')) //6a617661736372697074
Буфер аналогичен массиву целых чисел, которые могут принимать подписки, иметь атрибут длины и вырезать и копировать операции и т. Д. Многие API также похожи на массивы, но размер буфера определяется при его создании и не может быть скорректирован. Буферные сделки с байтами, двузначным шестнадцатеричным, поэтому целочисленный диапазон составляет от 0 до 255.
Как видите, Buffer можно преобразовать в строку и из нее, а также установить кодировку набора символов. Буфер используется для обработки двоичных данных, передаваемых файловым вводом-выводом и сетевым вводом-выводом, а строка используется для представления. При обработке двоичных данных, передаваемых файловым вводом-выводом и сетевым вводом-выводом, вы должны попытаться передать их напрямую в форме буфера, и скорость будет значительно улучшена, но работа со строками все еще намного быстрее, чем работа с буферами.
Распределение буферной памяти и оптимизация производительности
Buffer — типичный модуль, объединяющий javascript и C++, производительность реализована на C++, а javascript отвечает за подключение и предоставление интерфейсов. Память, занимаемая Buffer, не выделяется V8, но не зависит от памяти кучи V8. Приложение памяти и javascript выделяют память через уровень C++. Стоит отметить, что всякий раз, когда мы используемBuffer.alloc(size)
При запросе буферной памяти буфер будет ограничен 8 кБ, чтобы определить распределение больших объектов или небольших объектов, небольших объектов в оставшийся пул памяти, достаточно, чтобы подать заявку на 8 КБ пула памяти; большие объекты непосредственно из памяти приложения уровня C ++ Отказ Поэтому для объектов большого размера примените большое количество памяти приложения намного быстрее, чем маленький пул памяти.
Поток Поток
Как упоминалось ранее, потоки аналогичны потокам воды для описания потока данных. Передачу данных в файловом вводе-выводе и сетевом вводе-выводе можно назвать потоками. Потоки — это модели, которые могут единообразно описывать все общие типы ввода и вывода, и являются последовательным чтением и записью.Абстрактное представление последовательности байтов. Поток данных из конца A в конец B отличается от потока из конца B в конец A. Следовательно, поток является направленным. Терминал вводит данные в терминал B, для B это входной поток, и полученный объект является потоком для чтения, для A это выходной конец, а полученный объект является записываемым потоком. Некоторые потоки могут быть прочитаны и записаны, такие как соединения TCP, соединения Socket и т. д., которые называются потоками чтения-записи (Duplex). Существует также поток чтения и записи, который может изменять и преобразовывать данные во время чтения и записи.Transformпоток.
В узле данные в этих потоках являются объектом Buffer, а потоки с возможностью чтения и записи будут хранить данные ввнутреннийв кеше, ожидая использования;Duplexа такжеTransformвсе поддерживаетсядва независимыхКэш используется как для чтения, так и для записи. При поддержании достаточно эффективного потока данных чтение и запись могут выполняться независимо, не влияя друг на друга.
В узле все эти четыре потока являются экземплярами EventEmitter, все они имеют события закрытия и ошибки, читаемые потоки имеют события данных, которые отслеживают поступление данных и т. д., а записываемые потоки имеют события завершения, которые отслеживают передачу данных в нижний уровень. -уровневая система и т.п. ,Duplexа такжеTransformоба достигнуты одновременноReadableа такжеWritableСобытия и интерфейсы.
Стоит отметить, что событие слива для записи, это событие представляет缓存的数据被排空了
. Почему это событие? Причина в том, что запись потока, доступного для записи, и чтение потока, доступного для чтения, будут иметь область буфера для буферизации данных записи/чтения.Область буфера имеет размер.Как только записанное содержимое превысит этот размер, метод записи вернет false. Указывает, что запись остановлена. В это время, если вы продолжите чтение данных в области буфера, а область буфера опустеет, будет запущено событие слива. Вы можете предотвратить разрыв области буфера следующим образом:
var rs = fs.createReadStream(src);
var ws = fs.createWriteStream(dst);
rs.on('data', function (chunk) {
if (ws.write(chunk) === false) {
rs.pause();
}
});
rs.on('end', function () {
ws.end();
});
ws.on('drain', function () {
rs.resume();
});
Некоторые общие классификации потоков:
- Написание: HTTP-запросы, на клиенте, Ответы HTTP, на сервере, FS пикирует потоки, потоки ZLIB, Crypto Street, TCP-розетки, детский процесс stdin, Process.Stout, Process.Stderr
трубопроводКонцепция также очень яркая: поток воды из одного конца в другой требует труб в качестве канала или среды. То же самое верно и для потоков, и для передачи данных между концами также требуются каналы, которые в узле такие:
// 将 readable 中的所有数据通过管道传递给名为 file.txt 的文件
const readable = getReadableStreamSomehow();
const writable = getWritableStreamSomehow('file.txt');
// readable 中的所有数据都传给了 'file.txt'
readable.pipe(writable);
// 对流进行链式地管道操作
const r = fs.createReadStream('file.txt');
const z = zlib.createGzip();
const w = fs.createWriteStream('file.txt.gz');
r.pipe(z).pipe(w);
Обратите внимание, что только потоки, доступные для чтения, поддерживают канал, а потоки, доступные для записи, являются пунктами назначения.
Труба может использоваться не только как канал, но также может очень хорошо контролировать поток в конвейере, контролировать баланс чтения и записи и предотвращать чрезмерную работу любой из сторон. Кроме того, канал может прослушивать данные и конечные события читаемого потока, чтобы можно было построить быстрый ответ:
// 一个文件下载的例子,使用回调函数的话需要等到服务器读取完文件才能向浏览器发送数据
var http = require('http') ;
var fs = require('fs') ;
var server = http.createServer(function (req, res) {
fs.readFile(__dirname + '/data.txt', function (err, data) {
res.end(data);
}) ;
}) ;
server.listen(8888) ;
// 而采用流的方式,只要建立连接,就会接受到数据,不用等到服务器缓存完data.txt
var http = require('http')
var fs = require('fs')
var server = http.createServer(function (req, res) {
var stream = fs.createReadStream(__dirname + '/data.txt')
stream.pipe(res)
})
server.listen(8888)
Таким образом, использование трубы может решить вышеуказанную проблему ликвидации.
файловый модуль fs
Файловый модуль fs — это высокоуровневый модуль, который наследует низкоуровневые модули, такие как EventEmitter, stream и path, и обеспечивает операции с файлами, включая чтение, запись, переименование, удаление, обход каталогов и связывание с файловыми системами POSIX. . В отличие от идеи дизайна узла и других модулей, все операции в модуле fs обеспечиваютАсинхронный и синхронныйдве версии. Модуль fs в основном состоит из следующих частей:
- Инкапсуляция базовой файловой системы POSIX, соответствующая собственным файловым операциям операционной системы.
- Файловые потоки, наследующие Stream fs.createReadStream и fs.createWriteStream
- Синхронные методы работы с файлами, такие как fs.readFileSync, fs.writeFileSync
- Асинхронные методы работы с файлами, fs.readFile и fs.writeFile.
Архитектура API модуля выглядит следующим образом:
Операции чтения и записи:
const fs = require('fs'); // 引入fs模块
/* 读文件 */
// 使用流
const read = fs.createReadStream('sam.js',{encoding:'utf8'});
read.on('data',(str)=>{
console.log(str);
})
// 使用readFile
fs.readFile('test.txt', {}, function(err, data) {
if (err) {
throw err;
}
console.log(data);
});
// open + read
fs.open('test.txt','r',(err, fd) => {
fs.fstat(fd,(err,stat)=>{
var len = stat.size; //检测文件长度
var buf = new Buffer(len);
fs.read(fd,buf,0,len,0,(err,bw,buf)=>{
console.log(buf.toString('utf8'));
fs.close(fd);
})
});
});
/* 写文件与读取文件API形式类似 */
Есть три способа чтения/записи файлов, так в чем же разница?
- createReadStream/createWriteStream создает объект ReadStream, который считывает содержимое файла как потоковые данные, основная цель этого метода — прочитать данные в поток и получить читаемый поток, что удобно для работы с потоками
- readfile / writefile: node.js будет лечить содержимое файла в целом, выделите область буфера для него и считывать / записывать содержимое файла в область буфера одновременно. В этот период Node.js не сможет выполнять Любая другая обработка. Поэтому при чтении и записи больших файлов может привести к тому, что область кэша «взорвана»
- чтение/запись чтения/записи содержимого файла заключается в непрерывном чтении/записи небольшого фрагмента содержимого файла в область буфера и, наконец, чтении содержимого файла из области буфера.
Синхронизация API, а также. Наиболее часто используется readFile, чтение больших файлов выполняется с помощью read, чтобы предоставить более подробную информацию об основной работе, и read, чтобы открыть.
fs.stat('eda.txt', (err, stat) => {
if (err)
throw err
console.log(stat)
})
/*
Stats {
dev: 16777220,
mode: 33279,
nlink: 1,
uid: 501,
gid: 20,
rdev: 0,
blksize: 4194304,
ino: 4298136825,
size: 0,
blocks: 0,
atimeMs: 1510317983760.94, - 文件数据最近被访问的时间
mtimeMs: 1510317983760.94, - 文件数据最近被修改的时间。
ctimeMs: 1510317983777.8538, - 文件状态最近更改的时间
birthtimeMs: 1509537398000,
atime: 2017-11-10T12:46:23.761Z,
mtime: 2017-11-10T12:46:23.761Z,
ctime: 2017-11-10T12:46:23.778Z,
birthtime: 2017-11-01T11:56:38.000Z
}*/
const FSWatcher = fs.watch('eda.txt', (eventType, filename) => {
console.log(`${eventType}`)
})
FSWatcher.on('change', (eventType, filename) => {
console.log(`${filename}`)
})
// watch和返回的FSWatcher实例的回调函数都绑定在了 change 事件上
fs.watchFile('message.text', (curr, prev) => {
console.log(`the current mtime is: ${curr.mtime}`);
console.log(`the previous mtime was: ${prev.mtime}`);
})
Есть еще два способа прослушивания файлов:
- watch вызывает базовый API для мониторинга файлов, что является быстрым и надежным
- watchFile выполняется путем постоянного опроса
fs.Stat
(Файл статистики) Для получения файла отслеживания изменений медленнее, менее надежное, другие параметры функции обратного вызоваfs.Stat
Пример
Так что используйте watch как можно чаще, watchFile используется для сценариев, где вам нужно получить больше информации о файле.
разное
Создание, удаление, копирование, перемещение, переименование, проверка файлов, изменение разрешений...
Суммировать
От Buffer к Stream, а затем к файловому модулю fs, соединив их вместе, можно получить более четкое представление обо всем, а также более глубокое понимание механизма и реализации интерфейсных инструментов автоматизации, таких как webpack и gulp для строить рабочие процессы. То же самое верно и для изучения других знаний — зная все входы и выходы, зная, почему оно существует, и зная связь между ними, вы можете соединить разрозненные знания, придать им смысл и позволить себе «подняться по коридору, спуститься вниз». на кухню "".
Ссылаться на: