最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 让 Node.js 变“懒”的 COW 技术

    正文概述 掘金(zxg_神说要有光)   2021-08-24   370

    COW 不是奶牛,是 Copy-On-Write 的缩写,这是一种是复制但也不完全是复制的技术。

    一般来说复制就是创建出完全相同的两份,两份是独立的:

    让 Node.js 变“懒”的 COW 技术

    但是,有的时候复制这件事没多大必要,完全可以复用之前的,这时候可以只是引用之前的那份,在写内容的时候才去复制对应的一部分内容。这样如果内容用于读的话,就免去了复制,而如果需要写,才会真正复制部分内容来做修改。

    让 Node.js 变“懒”的 COW 技术

    这就叫做“写时复制”,也就是 Copy-On-Write。

    原理很简单,但是在操作系统的内存管理和文件系统中却很常见,Node.js 里面也因为这种技术变“懒”了。

    本文我们来探究下 Copy-On-Write 在 Node.js 的进程创建和文件复制的应用:

    文件复制

    文件复制这件事最常见的思路就是完全写一份相同的文件内容到另一个位置,但是这样有两个问题:

    • 完全写一份相同的内容,如果同样的文件复制了几百次,那么也创建相同的内容几百次么?太浪费硬盘空间了
    • 如果写到一半断电了怎么办?覆盖的内容如何恢复?

    怎么办呢?这时候操作系统设计者就想到了 COW 技术。

    用 COW 技术实现文件复制以后完美解决了上面两个问题:

    • 复制只是添加一个引用到之前的内容,如果不修改并不会真正复制,只有到第一次修改内容的时候才去真正复制对应的数据块,这样就避免了大量硬盘空间的浪费。
    • 写文件时会先在另一个空闲磁盘块做修改,等修改完之后才会复制到目标位置,这样就不会有断电无法回滚的问题

    在 Node.js 的 fs.copyFile 的 api 就可以使用 Copy-On-Write 模式:

    默认情况下,copyFile 会写入目标文件,覆盖原内容

    const fsPromises = require('fs').promises;
    
    (async function() {
      try {
        await fsPromises.copyFile('source.txt', 'destination.txt');
      } catch(e) {
        console.log(e.message);
      }
    })();
    

    但是可以通过第三个参数指定复制的策略:

    const fs = require('fs');
    const fsPromises = fs.promises;
    const { COPYFILE_EXCL, COPYFILE_FICLONE, COPYFILE_FICLONE_FORCE} = fs.constants;
    
    (async function() {
      try {
        await fsPromises.copyFile('source.txt', 'destination.txt', COPYFILE_FICLONE);
      } catch(e) {
        console.log(e.message);
      }
    })();
    

    支持的 flag 有 3 个:

    • COPYFILE_EXCL: 如果目标文件已存在,会报错(默认是覆盖)
    • COPYFILE_FICLONE: 以 copy-on-write 模式复制,如果操作系统不支持就转为真正的复制(默认是直接复制)
    • COPYFILE_FICLONE_FORCE:以 copy-on-write 模式复制,如果操作系统不支持就报错

    这3个常量分别是 1,2,4,可以通过按位或把它们合并之后传入:

    const flags = COPYFILE_FICLONE | COPYFILE_EXCL;
    fsPromises.copyFile('source.txt', 'destination.txt', flags);
    

    Node.js 支持操作系统的 copy-on-write 技术,在一些场景下可以提升性能,建议使用 COPYFILE_FICLONE 的方式,会比默认的方式好一些。

    进程创建

    fork 是常见的创建进程的方式,而它的实现就是一种 copy-on-write 技术。

    我们知道,进程在内存中分为代码段、数据段、堆栈段这 3 部分:

    • 代码段:存放要执行的代码
    • 数据段:存放一些全局数据
    • 堆栈段:存放执行的状态

    如果基于该进程创建一个新的进程,那么要复制这 3 部分内存。而如果这三部分内存是一样的内容,那就浪费了内存空间。

    所以 fork 并不会真正的复制内存,而是创建一个新的进程,引用父进程的内存,当做数据的修改的时候,才会真正复制该部分的内存。

    让 Node.js 变“懒”的 COW 技术

    这也是为什么把进程创建叫做 fork,也就是分叉,因为不完全是独立的,只是某部分做了分叉,成了两份,但是大部分还是一样的。

    但如果要执行的代码不一样怎么办呢,这时候就要用 exec 了,它会创建新的代码段、数据段、堆栈段、执行新的代码。

    Node.js 里面同样可以用 fork 和 exec 的 api:

    fork:

    const cluster = require('cluster');
    
    if (cluster.isMaster) {
      console.log('I am master');
      cluster.fork();
      cluster.fork();
    } else if (cluster.isWorker) {
      console.log(`I am worker #${cluster.worker.id}`);
    }
    

    exec:

    const { exec } = require('child_process');
    exec('my.bat', (err, stdout, stderr) => {
      if (err) {
        console.error(err);
        return;
      }
      console.log(stdout);
    });
    

    fork 是 linux 进程创建的基础,由此可见 copy-on-write 技术多么重要了。

    总结

    复制同样的内容多份无疑比较浪费空间,所以操作系统在做文件复制、进程创建时的内存复制的时候都采用了 Copy-On-Write 技术,只有真正修改的时候才会去做复制。

    Node.js 支持了 fs.copyFile 的 flags 的设置,可以指定 COPYFILE_FICLONE 来使用 Copy-On-Write 的方式做文件复制,也建议大家使用这种方式来节省硬盘空间,提高文件复制的性能。

    进程的 fork 也是 Copy-On-Write 的实现,并不会直接复制进程的代码段、数据段、堆栈段到新的内容,而是引用之前的,只有在修改的时候才会做真正的内存复制。

    除此以外,Copy-On-Write 在 Immutable 的实现,在分布式的读写分离等领域都有很多应用。

    COW 让 Node.js 变“懒”了,但性能却更高了。


    起源地下载网 » 让 Node.js 变“懒”的 COW 技术

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元