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文件加锁,不是对整个文件夹加锁?

发布了28 篇原创文章 · 获赞 11 · 访问量 1531

猜你喜欢

转载自blog.csdn.net/weixin_42881755/article/details/98508073