NodeJS研究笔记,利用目录来实现跨平台文件锁

来源:互联网 发布:大数据分析系统开发 编辑:程序博客网 时间:2024/05/16 10:09

文件加锁是多线程或多进程开发时,确保文件资源不被并发读写所污染的有效措施,NodeJS提供了一些办法确保在竞争性环境下,文件数据的一致性。

NodeJS保证文件读取一致性的办法有两种,一是读取文件时设置隔离标志,隔离标志是指,当使用fs模块打开文件时,在打开模式中,添加一个x标记。例如下面的代码将以独占的方式去读写一个文件:

fs.open('config.loc', 'wx', function(err) {    if (err) return console.error(err);    //这里开始可以放心的读写文件});

在open调用中,第二个参数中所包含的’x’,指的就是要以独占的方式打开指定文件, 如果指定文件已经被别的进程或线程打开了,那么if(err)的判断成立,函数就会直接打印错误信息并返回。

利用隔离标志实现文件的原子操作有一个缺陷,并不是所有的平台都会支持该标志,例如要读取网络硬盘上的文件,那么该标志很可能会被忽略掉。因此使用隔离标志读取跨平台的文件时,隔离作用很可能会失效。

为了弥补这个缺陷,更好的办法是使用目录来实现文件锁。mkdir是标准的POSIX系统调用,根据标准,它的实现必须是原子性的。任何支持该调用的平台都必须保证它执行的原子性,利用这个特性来构建文件锁的话,代码就能得到很好的跨平台支持,即使是读取网络文件,也能保证文件的一致性不会被破坏。接下来我们看看,如何在NodeJS中使用mkdir实现文件锁。

var fs = require('fs');var hasLock = false;var lockDir = 'config.lock';exports.lock = function(cb) {    if (hasLock)  return cb();    fs.mkdir(lockDir, function(err) {        if (err) return cb(err);        fs.writeFile(lockDir + '/' + process.pid, function(err) {            if (err) console.error(err);            hasLock = true;            return cb();        });    });}

lockDir 表示目录名,lock是一个导出函数,当外部模块要想获取文件锁,那么调用函数lock即可。lock的实现原理是,调用mkdir生成一个目录,如果有多个线程或进程同时调用lock,那么在lock中,将同时调用mkdir去生成同一个目录,但是由于mkdir的原子性是由实现该调用的平台必须保证的,所以即使多个进程同时调用lock,那么也只有一个进程可能成功,其他的都会失败。一旦失败,调用进程的回调函数会被lock调用。成功的进程,它的进程pid会被写入到目录里,以方便调试,同时把hasLock标志位设置为true, 一旦lock返回成功,进程就可以放心的读取文件了。

释放文件锁也简单,只要把目录给删除就可以了,代码如下:

exports.unlock = function(cb) {    if (!hasLock)  return cb();    fs.unlink(lockDir + '/' + process.pid, function(err) {        if (err) return cb(err);        fs.rmdir(lockDir, function(err) {            if (err) return cb(err);            hasLock = false;            cb();        }) ;    });}

利用unlink解除进程对目录的获取权限,然后调用rmdir将目录删除,执行unlock后,其他线程或进程才有机会通过调用 lock获取文件锁。

我们把上面的文件锁实现成一个单独的模块lock.js,如下:

var fs = require('fs');var hasLock = false;var lockDir = 'config.lock';exports.lock = function(cb) {    if (hasLock)  return cb();    fs.mkdir(lockDir, function(err) {        if (err) return cb(err);        fs.writeFile(lockDir + '/' + process.pid, function(err) {            if (err) console.error(err);            hasLock = true;            return cb();        });    });}exports.unlock = function(cb) {    if (!hasLock)  return cb();    fs.unlink(lockDir + '/' + process.pid, function(err) {        if (err) return cb(err);        fs.rmdir(lockDir, function(err) {            if (err) return cb(err);            hasLock = false;            cb();        }) ;    });}process.on('exit', function() {    if (hasLock) {        fs.unlinkSync(lockDir + '/' + process.pid);        fs.rmdirSync(lockDir);        console.log('removed lock');    }});

lock.js的使用方式如下:

var locker = require('./lock');locker.lock(function(err) {    if (err) throw err;    /*    这里可以放心的读取相关文件    */    locker.unlock(function() {});})
0 0