likes
comments
collection
share

node 类库:graceful-fs

作者站长头像
站长
· 阅读数 15

研究 hexo,我发现文件底层读写用的是 hexo-fs 一个它自己封装的库,然后我好奇点开,发现是对 graceful-fs 的又一层封装,好奇又打开了 graceful-fs,想看看这里到底有什么。一起看看~

什么是

"graceful-fs" 作为 "fs" 模块的直接替代品,对其进行了多种改进。

这些改进旨在使不同平台和环境下的行为标准化,并使文件系统访问更能抵御错误。

核心特性

  1. openreaddir 的调用排入队列,并且如果由于文件描述符过多而出现 EMFILE 错误,一旦某些文件关闭就重试这些调用。
  2. 为 0.6.2 之前的 Node 版本修复 lchmod
  3. 如果可能,实现 fs.lutimes。否则,它将成为无操作(noop)。
  4. 如果用户不是 root,忽略 chownfchownlchown 中的 EINVAL 和 EPERM 错误。
  5. 如果 lchmodlchown 不可用,使它们成为无操作。
  6. 如果读取文件结果出现 EAGAIN 错误,则重试读取文件。

在 Windows 上,如果出现 EACCESS 或 EPERM 错误,可能是因为杀毒软件锁定了目录,它会重试重命名文件长达一秒钟。

用法

// use just like fs
var fs = require('graceful-fs')

// now go and do stuff with it...
fs.readFile('some-file-or-whatever', (err, data) => {
  // Do stuff here.
})

上面展示了异步 API 的调用方法。

同步 API

此模块无法拦截或处理来自同步方法的 EMFILE 或 ENFILE 错误。如果您使用打开文件描述符的同步方法,那么您需要自行处理任何错误。

这是一个已知的限制,而不是一个错误。

扩展 fs

// Make sure to read the caveat below.
var realFs = require('fs')
var gracefulFs = require('graceful-fs')
gracefulFs.gracefulify(realFs)

如果你要扩展 fs 模块,你可以这样做,不过注意:

这只应该在顶层应用层完成,以便延迟处理任何使用 fs 的依赖项导致的 EMFILE 错误。您不应该在库中这样做,因为它可能会在程序的其他部分造成意外的延迟。

变更

这个模块现在相当稳定,并被许多东西使用。话虽如此,因为它在 node API 的核心部分实现了微妙的行为变化,即使是适度的更改也可能极具破坏性,因此在有疑问的情况下,版本控制倾向于提升主版本号。

主要版本之间的主要变化是在提供完全修补的 fs 模块与对 node 核心内置进行猴子补丁(monkey-patching)之间切换,以及创建未经猴子补丁的 fs 的方法。

目标是将 EMFILE 错误转变为更慢的 fs 操作。因此,如果您尝试打开大量文件,而不是崩溃,打开操作将被排队等待其他操作关闭。

每种方法都有其优势。对 fs 进行猴子补丁意味着在您的应用程序中任何地方都不可能发生 EMFILE 错误,因为一切都在使用同一个已经打过补丁的核心 fs 模块。然而,它也显然可能导致不希望的副作用,特别是如果该模块被多次加载的话。

实现一个单独但相同的打过补丁的 fs 模块更为精确(并且不会冒多次打补丁的风险),但也带来了与核心模块保持同步的挑战。

当前的方法是加载 fs 模块,然后创建一个看起来相同的对象,拥有所有相同的方法,除了一些经过修补的方法。它可以安全地用于从 0.8 到 7.0 的所有 Node 版本。

原理

graceful-fs,是怎么实现它声称的这些呢?它在类库里顶一个了数组,当做队列,然后每次调用 readFile,writeFile 等等 API 的时候,都会捕获错误,如果错误是 EEMFIL 就将这个任务的上下文加入到队列中,调用了队列的 enqueue 方法,然后,这个方法再将当前任务入队列的时候,会触发一次 retry 方法,该方法弹出队列头,尝试去执行它。

另外,在调用 fs.close 的时候,又会触发一次 retry 尝试去消耗队列,整体来说,非常巧妙。

附录

什么是猴子补丁?

"Monkey-patching" 在软件开发中是一个术语,特别是在类库(库)的上下文中。它指的是在运行时修改或扩展代码,特别是修改或扩展第三方代码,而不是修改源代码。这种做法通常用于:

  1. 修复错误:如果一个库有一个错误,并且等待官方修复不切实际,开发者可能会使用猴子补丁来临时修复这个问题。
  2. 添加功能:在不修改原始库的情况下,给库添加新的功能或方法。
  3. 改变行为:改变库中某个功能的行为,以符合特定的需求或工作流程。

尽管猴子补丁看起来是一种方便的解决方案,但它有其缺点和风险:

  • 维护困难:猴子补丁可能会使代码的行为变得难以预测,尤其是当涉及到复杂的依赖和多个补丁时。
  • 兼容性问题:如果底层库更新,猴子补丁可能会停止工作,或者导致新的问题。
  • 性能影响:某些类型的猴子补丁可能会对性能产生负面影响。
  • 调试难度:由于它们改变了原始行为,因此可能使调试和跟踪问题变得更加困难。

由于这些原因,许多开发者建议仅在必要时且没有其他选择的情况下才使用猴子补丁,并且在使用时应该非常谨慎。

什么是 EMFILE 错误?

在 Node.js 开发中,EMFILE 错误是一个常见的错误类型,它代表 "Error: Too Many Open Files"。这个错误发生在当一个进程试图打开超过操作系统允许的文件描述符数量的文件时。每个操作系统都有对可以同时打开的文件数量的限制,这是为了防止资源耗尽。

什么时候发生?

EMFILE 错误通常在以下情况发生:

  1. 高并发情况下:当应用程序尝试同时打开大量文件时,例如在处理大量的并发网络连接或文件操作。
  2. 资源限制:操作系统对于可以打开的文件数量有限制,这个限制可以是全局的也可以是针对单个进程的。
  3. 长时间运行的应用程序:长时间运行且不正确关闭文件描述符的应用程序可能会导致文件描述符耗尽。

影响

EMFILE 错误的影响包括:

  • 应用程序性能下降:无法打开新文件或建立新的网络连接,可能导致性能显著下降。
  • 功能受限:某些功能可能无法正常工作,因为它们需要打开新的文件或资源。
  • 系统稳定性问题:在极端情况下,可能导致应用程序或整个系统崩溃。

处理和应对

程序员通常采用以下方法来处理和应对 EMFILE 错误:

  1. 增加文件描述符限制:可以临时或永久性地增加操作系统允许打开的文件描述符数量。
  2. 使用队列或限流机制:限制同时打开的文件数量,使用队列来管理文件操作。
  3. 正确关闭文件描述符:确保在不需要时及时关闭文件描述符。
  4. 使用专门的库:例如 graceful-fs 在 Node.js 中可以作为 fs 模块的替代品,它在内部处理 EMFILE 错误,通过排队请求和重试机制来减轻这个问题。
  5. 代码优化和资源管理:优化应用程序以减少同时打开的文件数量,更好地管理资源。
  6. 监控和日志记录:实施监控和日志记录来识别并解决导致 EMFILE 错误的根本原因。

通过这些方法,开发者可以更有效地管理资源,避免 EMFILE 错误,确保应用程序的健壮性和可靠性。

-- END --