Oct 15, 2019
应用内文件锁
写了一个 NodeJS 版的 fs-lockfile 用来管理应用层上的文件读取和写入。
这样应用内的所有磁盘 IO 都被应用程序统一接管,如果需要做比如权限管理、Bookmark、文件缓存、虚拟文件系统等,这些都可以做一个中间层,也有一定的使用场景。
设计接口
首先设计了六个方法,获取读锁、释放读锁、获取写锁、释放写锁、读文件、写文件。读文件里面封装了读锁的获取和释放,写文件里面封装了写锁的获取和释放。接口的方法如下:
- obtainReadLock(filePath): Object
- obtainWriteLock(filePath): Object
- releaseReadLock(lockMeta): undefined
- releaseWriteLock(lockMeta): undefined
- read(filePath): Promise
- write(filePath, content): Promise
读文件操作的方法: read
展示一下 read 简单封装了 obtainReadLock ,并且在执行读文件操作后,调用 releaseReadLock 释放读锁。
1 | async function read(fp) { |
简要介绍 obtainReadLock, obtainWriteLock 的执行流程
obtainReadLock
- 获取 文件路径的 lockMeta ,然后要等待 lockMeta 中的写锁队列全部释放完成
- 之后生成一个读锁,返回 lockMeta
obtainWriteLock
- 获取文件路径的 lockMeta, 获取 lockMeta 中 writeQueue 最后一个写锁 prevLock。
- 生成一个新的写锁,并且放进 lockMeta 中的 writeQueue
- 等待所有读锁释放
- 等待 prevLock 释放
注意:要先把新生成的写锁放在 writeQueue 中,因为其它方法可能会访问这个写队列
另外两个方法也是类似的,这样一个完整的应用层的锁就写好了。
锁是怎么生成的
是一个简单的 Promise 锁。构造一个方法,生成一个 Promise 实例,然后把里面的 resolve 方法引用到一个对象中,当需要释放锁的时候,调用 resolve 方法就能继续执行。如下面的例子:
1 | function generateLock() { |
后续问题
这样的设计后,还是有两个扩展的问题
- 读锁要不要直接访问之前的文件内容,这样让写锁能不与读锁互斥
- 文件只有一个读锁,如果有一个 readQueue 存储多个读锁,可以对读锁增加更多的控制,但相应的复杂度就会增加不少
以及多线程操作的问题
- 文件锁是基于线程的,如果是多线程或者多进程操作文件,最好是把所有的锁请求都通过 ipc 传递到一个线程中集中管理
多线程进程的解决方法也好处理:前文说了,read & write 封装是应对一般的需求,如果要做 ipc 通信,或者一些权限处理,或者缓存文件等,可以自行封装和完善。
这里是 npm package 的地址: fs-lockfile 。