FSLockFactory(lucene8.0的文件锁相关)

1.概述

lucene作为一个开源的搜索框架,为我们提供了一个非常好的了解全文检索的窗口。在生产中,多使用基于lucene的solrElasticSearch两个分布式搜索框架,原因必然与其不支持分布式和官方并未提供相关的使用文档相关,所以我们可以认将lucene 视为一个搜索框架的内核。本人在此之前,在前辈指导下尝试用lucene实现过几个简单的商品搜索接口,出于好奇尝试阅读源码,现在lucene已经到8.0以上版本,网络上对于lucene源码的分析并不多见,而较为经典的资料如lucene in action等多停滞在3.0版本。图个时髦,尝试阅读8.0版本源码,就从文件锁开始,能力有限,错误的地方请指正。

2.FSLockFactory文件系统锁工厂

其继承关系如下:
在这里插入图片描述
其父类LockFactory是一个抽象类,内部仅定义了一个obtainLock方法,该方法在抽象类.FSLockFactory中实现,其内部仅调用了定义的新的抽象方法obtainFSLock,该方法的实现在其两个子类NativeFSLockFactory和SimpleFSLockFactory中实现。
FSLockFactory还提供了返回默认锁工厂的方法getDefault,返回的是NativeFSLockFactory单例。

3.NativeFSLockFactory(本地文件系统锁工厂)

3.1属性和内部类

属性

/**
   * Singleton instance
   * 恶汉单例
   */
  public static final NativeFSLockFactory INSTANCE = new NativeFSLockFactory();

  //.lock文件集合(索引文件夹不只一个)
  private static final Set<String> LOCK_HELD = Collections.synchronizedSet(new HashSet<String>());

NativeFSLockFactory是单例模式。
内部类:

static final class NativeFSLock extends Lock {
	//文件锁,nio获得
	final FileLock lock;
	//文件通道
    final FileChannel channel;
    //被锁的文件路径
    final Path path;
    //文件创建时间
    final FileTime creationTime;
    //锁是否关闭
    volatile boolean closed;
    
    NativeFSLock(FileLock lock, FileChannel channel, Path path, FileTime creationTime) {
      this.lock = lock;
      this.channel = channel;
      this.path = path;
      this.creationTime = creationTime;
    }
...
}

内部类NativeFSLock继承自抽象类Lock,并对Lock中定义的两个抽象方法ensureValid和close进行实现,可以认为,其封装了nio部分的文件锁。

3.2核心方法

protected Lock obtainFSLock(FSDirectory dir, String lockName) throws IOException {
    //待锁文件夹
    Path lockDir = dir.getDirectory();
    //保证存在并且是文件夹(如果是符号链接会失败)
    Files.createDirectories(lockDir);
    //待锁完整的路径,文件夹+文件名
    Path lockFile = lockDir.resolve(lockName);
    IOException creationException = null;//异常
    try {
      //保证文件存在
      Files.createFile(lockFile);
    } catch (IOException ignore) {//文件已经存在
      //仅保证文件存在,至于是不是已经被创建我们不关心。
      creationException = ignore;
    }
    
    final Path realPath;
    try {
      //获得文件的,绝对路径
      realPath = lockFile.toRealPath();
    } catch (IOException e) {
      // if we couldn't resolve the lock file, it might be because we couldn't create it.
      // so append any exception from createFile as a suppressed exception, in case its useful
      if (creationException != null) {
        e.addSuppressed(creationException);
      }
      throw e;
    }
    
    // used as a best-effort check, to see if the underlying file has changed
    //nio读取文件创建时间
    final FileTime creationTime = Files.readAttributes(realPath, BasicFileAttributes.class).creationTime();
    //将.lock文件加入set集合
    if (LOCK_HELD.add(realPath.toString())) {
      FileChannel channel = null;
      FileLock lock = null;
      try {
        //以写方式打开lock文件通道,没有就创建
        channel = FileChannel.open(realPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
        //尝试获得进程锁
        lock = channel.tryLock();
        if (lock != null) {
          //将系统进程锁进封装
          return new NativeFSLock(lock, channel, realPath, creationTime);
        } else {
          throw new LockObtainFailedException("Lock held by another program: " + realPath);
        }
      } finally {
        if (lock == null) { //获取锁失败则清空,不影响下次重新加锁
          IOUtils.closeWhileHandlingException(channel); // TODO: addSuppressed
          clearLockHeld(realPath);  // clear LOCK_HELD last 
        }
      }
    } else {//已经获得
      throw new LockObtainFailedException("Lock held by this virtual machine: " + realPath);
    }
  }

在Debug过程中,发现在创建IndexWriter对象时该方法会被调用,传入的是我们指定的索引文件夹和一个write.lock文件。在这个方法的效果就是对索引文件夹下的.lock文件加锁,加的是进程级别的锁(进程不共享,单一进程不可重复获得,释放后可以)。
下附一张StandardOpenOption枚举表:

枚举值含义
READ以读取方式打开文件
WRITE以写入方式打开文件
CREATE如果文件不存在,创建
CREATE_NEW如果文件不存在,创建;若存在,异常
APPEND在文件的尾部追加
DELETE_ON_CLOSE当流关闭的时候删除文件
TRUNCATE_EXISTING把文件设置为0字节
SPARSE文件不够时创建新的文件
SYNC同步文件的内容和元数据信息随着底层存储设备
DSYNC同步文件的内容随着底层存储设备

4.思考

为什么是对索引文件夹下的.lock文件加锁,不是对整个文件夹加锁?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值