好玩的Sendfile---探索Node.Js中更快的数据传输方式

本文转载自微信公众号「编程杂技」,作者theanarkh。转载本文请联系编程杂技公众号。

创新互联公司专业为企业提供石阡网站建设、石阡做网站、石阡网站设计、石阡网站制作等企业网站建设、网页设计与制作、石阡企业网站模板建站服务,10多年石阡做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。

在Node.js中,当我们给前端返回一个静态文件的时候,我们通常会把文件先读进内容,然后通过socket接口写到底层,从而返回给前端。无论是一次性读取到内存还是使用流式的方式,都不可避免地要把数据从内核复制到用户层,再把数据复制到内核,这是一种低效的方式,因为多了无效的复制。在nginx中,可以通过sendfile指令提供效率。Node.js的copyFile底层使用了sendfile系统调用,但是网络IO的时候,没有使用该API。因为Node.js通过队列的方式,控制数据的写入。那么是否可以实现sendfile的方式来提供这网络IO的效率。首先我们看一下sendfile的好处是什么。

  • sendfile() copies data between one file descriptor and another. Because this copying is done within the kernel, sendfile() is more efficient than the combination of read(2) and write(2), which would require transferring data to and from user space.

我们看到sendfile通过把内核完成数据的传输,减少了内核和用户层的数据复制,从而提高了效率。下面我们通过napi写一个addon来实现这个功能。

 
 
 
 
  1. #include   
  2. #include   
  3. #include  
  4. #include  
  5. #include  
  6. static napi_value copyFile(napi_env env, napi_callback_info info) { 
  7.   size_t argc = 3; 
  8.   napi_value args[3]; 
  9.   // 拿到js层的入参,这里是三个 
  10.   napi_get_cb_info(env, info, &argc, args, NULL, NULL); 
  11.   int fd1; 
  12.   int fd2; 
  13.   int len; 
  14.   // js传入的是一个数字,v8转成了对象,这里再次把入参转成int型 
  15.   napi_get_value_int32(env, args[0], &fd1); 
  16.   napi_get_value_int32(env, args[1], &fd2); 
  17.   napi_get_value_int32(env, args[2], &len); 
  18.   int writed = sendfile(fd2, fd1, 0,len); 
  19.   napi_value ret; 
  20.   napi_create_int32(env, writed, &ret); 
  21.   return ret; 
  22.  
  23. napi_value Init(napi_env env, napi_value exports) { 
  24.   napi_value func; 
  25.   // 创建一个函数并且设置为exports对象的getArray属性的值 
  26.   napi_create_function(env, 
  27.                       NULL, 
  28.                       NAPI_AUTO_LENGTH, 
  29.                       copyFile, 
  30.                       NULL, 
  31.                       &func); 
  32.   napi_set_named_property(env, exports, "copyFile", func); 
  33.   return exports; 
  34. NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) 

下面我们看看怎么使用。首先用这个addon来复制文件,类似Node.js的copyyFile

 
 
 
 
  1. const fs= require('fs'); 
  2. const { copyFile } = require('./build/Release/sendfile.node'); 
  3. const { 
  4.   O_WRONLY, 
  5.   O_CREAT, 
  6. } = fs.constants; 
  7. async function test() { 
  8.   const [fd1, fd2] = await Promise.all([openFile('1.txt', 'r'), openFile('2.txt', O_WRONLY | O_CREAT)]); 
  9.   const { size } = await getFileInfo(fd1); 
  10.   console.log(copyFile(fd1, fd2, size)); 
  11.   fs.close(fd1, () => {}); 
  12.   fs.close(fd2, () => {}); 
  13. function openFile(filename, mode) { 
  14.   return new Promise((resolve, reject) => { 
  15.     fs.open(filename, mode, (err, fd) => { 
  16.       if (err) { 
  17.         reject(err); 
  18.       } else { 
  19.         resolve(fd); 
  20.       } 
  21.     }); 
  22.   })} 
  23.  
  24. function getFileInfo(fd) { 
  25.   return new Promise((resolve, reject) => { 
  26.     fs.fstat(fd, (err, stat) => { 
  27.       if (err) { 
  28.         reject(err) 
  29.       }else { 
  30.         resolve(stat); 
  31.       } 
  32.     }); 
  33.   }) 
  34. test(); 

执行上面代码,我们可以看到文件会成功复制2.txt。接着我们再来试一下网络IO的场景。

 
 
 
 
  1. const fs= require('fs'); 
  2. const http = require('http'); 
  3. const { copyFile } = require('./build/Release/sendfile.node'); 
  4. const server = http.createServer(async (req, res) => { 
  5.   const fd = await openFile('1.txt', 'r'); 
  6.   const { size } = await getFileInfo(fd); 
  7.   const ret = copyFile(fd, res.socket._handle.fd, size); 
  8.   res.socket.end(); 
  9. }).listen(8002); 
  10.  
  11. const { 
  12.   O_WRONLY, 
  13.   O_CREAT, 
  14. } = fs.constants; 
  15.  
  16. function openFile(filename, mode) { 
  17.   return new Promise((resolve, reject) => { 
  18.     fs.open(filename, mode, (err, fd) => { 
  19.       if (err) { 
  20.         reject(err); 
  21.       } else { 
  22.         resolve(fd); 
  23.       } 
  24.     }); 
  25.   })} 
  26.  
  27. function getFileInfo(fd) { 
  28.   return new Promise((resolve, reject) => { 
  29.     fs.fstat(fd, (err, stat) => { 
  30.       if (err) { 
  31.         reject(err) 
  32.       }else { 
  33.         resolve(stat); 
  34.       } 
  35.     }); 
  36.   })} 

以上代码首先启动一个http服务器,然后收到请求的时候,通过addon调用sendfile给前端返回对应的内容,最后关闭连接。结果如下。

sendfile似乎在网络IO中可以应用了,但只是一个demo的思路,后续有时间继续研究分析。

网页标题:好玩的Sendfile---探索Node.Js中更快的数据传输方式
网站URL:http://www.hantingmc.com/qtweb/news4/395104.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联