JavaScript 起初为浏览器而设计, 没有读取或操作二进制数据流的机制. Buffer 类的引入, 则让 NodeJS 拥有操作文件流或网络二进制流的能力.
Buffer 基本概念
Buffer 对象的内存分配不是在 V8 的堆内存中, 而是 Node 在 C++ 层面进行内存申请, 可以理解为在内存中单独开辟了一部分空间, 但是使用时分配内存则是由 Node 层面完成的, 释放也是由 Node 中 v8 的 gc 机制自动控制. Buffer 基本操作, 这里不在赘述, 官方文档很详细.
Buffer 性能对比
通常, 网络传输中, 都需要将数据转换为 Buffer. 下面做一个性能对比实验.
1. 使用纯字符串返回给客户端
- const http = require('http');
- let hello = ''
- for (var i = 0; i <10240; i++) {
- hello += "a";
- }
- console.log(`Hello:${hello.length}`)
- // hello = Buffer.from(hello);
- http.createServer((req, res) => {
- res.writeHead(200);
- res.end(hello);
- }).listen(8001);
复制代码
使用
ab -c 200 -t 100 http://127.0.0.1:8001/
命令来进行性能测试, 发起 200 个并发客户端
使用字符串, QPS 可以达到 4019.70, 传输率为 40491.45KB 每秒.
2. 使用 Buffer. 将字符串转换为 Buffer 对象, 再发给客户端.
- const http = require('http');
- let hello = ''
- for (var i = 0; i <10240; i++) {
- hello += "a";
- }
- console.log(`Hello:${hello.length}`)
- hello = Buffer.from(hello);
- http.createServer((req, res) => {
- res.writeHead(200);
- res.end(hello);
- }).listen(8001);
复制代码
取消 Buffer 转换的注释, 同样使用
ab -c 200 -t 100 http://127.0.0.1:8001/
测试, 同样发起 200 个并发客户端.
使用 Buffer,QPS 达到 7130.05, 传输率为 71822.74KB 每秒. 性能是原来的 177%, 极大的节省了服务器资源. 上面这个对比示例参考于深入浅出 Node JS.
那么问题来了, 为什么会有这么大的性能提升呢?
道理其实很简单, 在 NodeJS 中, 进行 http 传输时, 若返回的类型为 string, 则会将 string 类型的参数, 转换为 Buffer, 通过 NodeJS 中的 Stream 流, 一点点的返回给客户端. 如果我们直接返回 Buffer 类型, 就没有了转换操作, 直接返回, 减少了 CPU 的重复使用率. 这一部分逻辑见 Node 源码 https://github.com/nodejs/node/blob/v10.9.0/lib/_http_outgoing.js#L612
在上面性能对比示例中, 返回 string 时, 每次请求都需要将 string 装换成 Buffer 返回; 而直接返回 Buffer 时, 这个 Buffer 是我们启动服务时就存放在内存中的, 每次请求直接返回内存中的 Buffer 即可, 因此 Buffer 使用前后 QPS 提升了很多.
因此, 我们在写业务代码时, 部分资源可以预先转换为 Buffer 类型 (如 js,CSS 等静态资源文件), 直接返回 buffer 给客户端, 再比如一些文件转发的场景, 将获取到的内容储存为 Buffer 直接转发, 避免额外的转换操作.
参考资料:
- http://nodejs.cn/api/buffer.html
- https://book.douban.com/subject/25768396/
来源: https://juejin.im/post/5b76e6a86fb9a019fe684018