背景

去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。

没有计划的阅读,收效甚微。

新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。

这个“玩法”虽然常见且板正,但是有效。

已读完书籍《架构简洁之道》。

当前阅读周书籍《深入浅出的Node.js》

理解Buffer

Buffer结构

模块结构

Buffer是一个典型的JavaScript与C++结合的模块,它将性能相关部分用C++实现,将非性能相关的部分用JavaScript实现。

阅读周·深入浅出的Node.js | 文件与网络的二进制数据处理,从理解Buffer开始_node.js

Buffer对象

Buffer对象类似于数组,它的元素为16进制的两位数,即0到255的数值。

var str = "深入浅出node.js";
var buf = new Buffer(str, 'utf-8');
console.log(buf); // <Buffer e6 b7 b1 e5 85 a5 e6 b5 85 e5 87 ba 6e 6f 64 65 2e 6a 73>

Buffer内存分配

Buffer对象的内存分配不是在V8的堆内存中,而是在Node的C++层面实现内存的申请的。

为了高效地使用申请来的内存,Node采用了slab分配机制,slab是一种动态内存管理机制。

slab具有如下3种状态:

  • full:完全分配状态。
  • partial:部分分配状态。
  • empty:没有被分配状态。

Buffer的转换

Buffer对象可以与字符串之间相互转换。目前支持的字符串编码类型有:ASCII、UTF-8、UTF-16LE/UCS-2、Base64、Binary、Hex。

字符串转Buffer

字符串转Buffer对象主要是通过构造函数完成。

语法如下:

new Buffer(str, [encoding]);

通过构造函数转换的Buffer对象,存储的只能是一种编码类型。encoding参数不传递时,默认按UTF-8编码进行转码和存储。

const buf = new Buffer('node.js');
console.log(buf); // <Buffer 6e 6f 64 65 2e 6a 73>

Buffer转字符串

Buffer对象的toString()可以将Buffer对象转换为字符串。

语法如下:

buf.toString([encoding], [start], [end])

可以设置encoding(默认为UTF-8)、start、end这3个参数实现整体或局部的转换。

const str = buf.toString();
console.log(str); // node.js

Buffer不支持的编码类型

Node的Buffer对象支持的编码类型有限,只有少数的几种编码类型可以在字符串和Buffer之间转换。为此,Buffer提供了一个isEncoding()函数来判断编码是否支持转换。

语法如下:

Buffer.isEncoding(encoding)

我们来挑几个类型测试一下:

console.log(Buffer.isEncoding('utf8')) // true
console.log(Buffer.isEncoding('hex')) // true
console.log(Buffer.isEncoding('UTF-8')) // true
console.log(Buffer.isEncoding('Base64')) // true
console.log(Buffer.isEncoding('utf/8')) // false

Buffer的拼接

Buffer在使用场景中,通常是以一段一段的方式传输。想要进行拼接,可以用一个数组来存储接收到的所有Buffer片段并记录下所有片段的总长度,然后调用Buffer.concat()方法生成一个合并的Buffer对象。

Buffer.concat()方法封装了从小Buffer对象向大Buffer对象的复制过程。

const buf1 = Buffer.from('node');
const buf2 = Buffer.from('.');
const buf3 = Buffer.from('js');
const arr = [buf1, buf2, buf3];
const bufres = Buffer.concat(arr);
console.log(bufres.toString()); // node.js

Buffer与性能

在Web应用中,字符串转换到Buffer是时时刻刻发生的,提高字符串到Buffer的转换效率,可以很大程度地提高网络吞吐率。

在Node构建的Web应用中,可以选择将页面中的动态内容和静态内容分离,静态内容部分可以通过预先转换为Buffer的方式,可以有效地减少CPU的重复使用,节省服务器资源,使性能得到提升。

由于文件自身是二进制数据,所以在不需要改变内容的场景下,尽量只读取Buffer,然后直接传输,不做额外的转换,避免损耗。

总结

我们来总结一下本篇的主要内容:

  • Buffer是二进制数据,字符串与Buffer之间存在编码关系。
  • Buffer对象的真正的内存是在Node的C++层面提供的,JavaScript层面只是使用它。
  • 当进行小而频繁的Buffer操作时,采用slab的机制进行预先申请和事后分配,使得JavaScript到操作系统之间不必有过多的内存申请方面的系统调用。对于大块的Buffer而言,则直接使用C++层面提供的内存,而无需细腻的分配操作。
  • 通过预先转换静态内容为Buffer对象,可以有效地减少CPU的重复使用,节省服务器资源,提升性能。
  • 由于文件自身是二进制数据,所以在不需要改变内容的场景下,尽量只读取Buffer,然后直接传输,不做额外的转换,避免损耗。

作者介绍非职业「传道授业解惑」的开发者叶一一。《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。