注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

anqiang专栏

不要问细节是怎么搞的,源码说明一切

 
 
 

日志

 
 

Directory源码分析  

2009-11-30 10:12:23|  分类: Lucene |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

 

大家都知道一般的检索系统都会分为两个基本部分:

1.Index 建立索引部分

2.Search 检索部分

但是在Lucene中有一部分比较重要的东西,在没有理解这部分之前,就无从谈起以上的两部分。这部分就是Lucene的存储org.apache.lucene.store,这个包里实现了Lucene的底层存储机制。因此我们的分析就从这个包开始。

这个类是一个抽象类,它相当于文件系统中文件夹的概念。包含着delete(),listAll(),等等一些与普通文件系统相同的功能,事实上Lucene通过这一层的抽象,将底层的读写操作的细节都屏蔽掉了,我们在操作一个Direcotry对象或者进行读写操作时,不知道是在对内存操作还是在硬盘介质操作。

现在我们来看看源码:

package org.apache.lucene.store;

/**

 * Licensed to the Apache Software Foundation (ASF) under one or more

 * contributor license agreements.  See the NOTICE file distributed with

 * this work for additional information regarding copyright ownership.

 * The ASF licenses this file to You under the Apache License, Version 2.0

 * (the "License"); you may not use this file except in compliance with

 * the License.  You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

import java.io.IOException;

import org.apache.lucene.index.IndexFileNameFilter;

/** A Directory is a flat list of files.  Files may be written once, when they

 * are created.  Once a file is created it may only be opened for read, or

 * deleted.  Random access is permitted both when reading and writing.

 *

 * <p> Java's i/o APIs not used directly, but rather all i/o is

 * through this API.  This permits things such as: <ul>

 * <li> implementation of RAM-based indices;

 * <li> implementation indices stored in a database, via JDBC;

 * <li> implementation of an index as a single file;

 * </ul>

 *

 * Directory locking is implemented by an instance of {@link

 * LockFactory}, and can be changed for each Directory

 * instance using {@link #setLockFactory}.

 *

 */

public abstract class Directory {

  volatile protected boolean isOpen = true;

  /** Holds the LockFactory instance (implements locking for

   * this Directory instance). */

  protected LockFactory lockFactory;

  /** List the files in the directory.

   * 

   *  @deprecated For some Directory implementations ({@link

   *  FSDirectory}, and its subclasses), this method

   *  silently filters its results to include only index

   *  files.  Please use {@link #listAll} instead, which

   *  does no filtering. */

  public abstract String[] list()

       throws IOException;

  /** Returns an array of strings, one for each file in the

   *  directory.  Unlike {@link #list} this method does no

   *  filtering of the contents in a directory, and it will

   *  never return null (throws IOException instead).

   *

   *  Currently this method simply falls back to {@link

   *  #list} for Directory impls outside of Lucene's core &

   *  contrib, but in 3.0 that method will be removed and

   *  this method will become abstract. */

  public String[] listAll()

    throws IOException

  {

    return list();

  }

  /** Returns true iff a file with the given name exists. */

  public abstract boolean fileExists(String name)

       throws IOException;

  /** Returns the time the named file was last modified. */

  public abstract long fileModified(String name)

       throws IOException;

  /** Set the modified time of an existing file to now. */

  /**

   * 更新文件modified时间

   */

  public abstract void touchFile(String name)

       throws IOException;

  /** Removes an existing file in the directory. */

  /**

   * 负责删除一个文件

   */

  public abstract void deleteFile(String name)

       throws IOException;

  /** Renames an existing file in the directory.

   * If a file already exists with the new name, then it is replaced.

   * This replacement is not guaranteed to be atomic.

   * @deprecated 

   */

  public abstract void renameFile(String from, String to)

       throws IOException;

  /** Returns the length of a file in the directory. */

  public abstract long fileLength(String name)

       throws IOException;

  /** Creates a new, empty file in the directory with the given name.

      Returns a stream writing this file. */

  /**

   * 创建一个输出流,这个流可能是写入内存(RAMDirectory)或者硬盘的一个流

   */

  public abstract IndexOutput createOutput(String name) throws IOException;

  /** Ensure that any writes to this file are moved to

   *  stable storage.  Lucene uses this to properly commit

   *  changes to the index, to prevent a machine/OS crash

   *  from corrupting the index. */

  public void sync(String name) throws IOException {}

  /** Returns a stream reading an existing file. */

  /**

   * 创建一个输入流,这个流将信息读入都内存中

   */

  public abstract IndexInput openInput(String name)

    throws IOException;

  /** Returns a stream reading an existing file, with the

   * specified read buffer size.  The particular Directory

   * implementation may ignore the buffer size.  Currently

   * the only Directory implementations that respect this

   * parameter are {@link FSDirectory} and {@link

   * org.apache.lucene.index.CompoundFileReader}.

   * 

   *创建一个输入流,这里为输入流设定特定的bufferSize

   * 默认的bufferSize都是1KB  

   */

  public IndexInput openInput(String name, int bufferSize) throws IOException {

    return openInput(name);

  }

  /** Construct a {@link Lock}.

   * @param name the name of the lock file

   * 加锁功能

   */

  public Lock makeLock(String name) {

      return lockFactory.makeLock(name);

  }

  /**

   * Attempt to clear (forcefully unlock and remove) the

   * specified lock.  Only call this at a time when you are

   * certain this lock is no longer in use.

   * @param name name of the lock to be cleared.

   */

  public void clearLock(String name) throws IOException {

    if (lockFactory != null) {

      lockFactory.clearLock(name);

    }

  }

  /** Closes the store. */

  public abstract void close()

       throws IOException;

  /**

   * Set the LockFactory that this Directory instance should

   * use for its locking implementation.  Each * instance of

   * LockFactory should only be used for one directory (ie,

   * do not share a single instance across multiple

   * Directories).

   *

   * @param lockFactory instance of {@link LockFactory}.

   */

  public void setLockFactory(LockFactory lockFactory) {

      this.lockFactory = lockFactory;

      lockFactory.setLockPrefix(this.getLockID());

  }

  /**

   * Get the LockFactory that this Directory instance is

   * using for its locking implementation.  Note that this

   * may be null for Directory implementations that provide

   * their own locking implementation.

   */

  public LockFactory getLockFactory() {

      return this.lockFactory;

  }

  /**

   * Return a string identifier that uniquely differentiates

   * this Directory instance from other Directory instances.

   * This ID should be the same if two Directory instances

   * (even in different JVMs and/or on different machines)

   * are considered "the same index".  This is how locking

   * "scopes" to the right index.

   */

  public String getLockID() {

      return this.toString();

  }

  /**

   * Copy contents of a directory src to a directory dest.

   * If a file in src already exists in dest then the

   * one in dest will be blindly overwritten.

   *

   * <p><b>NOTE:</b> the source directory cannot change

   * while this method is running.  Otherwise the results

   * are undefined and you could easily hit a

   * FileNotFoundException.

   *

   * <p><b>NOTE:</b> this method only copies files that look

   * like index files (ie, have extensions matching the

   * known extensions of index files).

   *

   * @param src source directory

   * @param dest destination directory

   * @param closeDirSrc if <code>true</code>, call {@link #close()} method on source directory

   * closeDirSrc表明在拷贝完毕后是否关闭已经打开的srcDirectory

   * @throws IOException

   */

  public static void copy(Directory src, Directory dest, boolean closeDirSrc) throws IOException {

    final String[] files = src.listAll();

    IndexFileNameFilter filter = IndexFileNameFilter.getFilter();

    byte[] buf = new byte[BufferedIndexOutput.BUFFER_SIZE];

    /**

     * 将所有的index索引文件全部重新拷贝一份到新的Directory中去

     */

    for (int i = 0; i < files.length; i++) {

      if (!filter.accept(null, files[i]))

        continue;

      IndexOutput os = null;

      IndexInput is = null;

      try {

        // create file in dest directory

        os = dest.createOutput(files[i]);

        // read current file

        is = src.openInput(files[i]);

        // and copy to dest directory

        long len = is.length();

        long readCount = 0;

        while (readCount < len) {

         /**

          * 得到一个读入内存的byte信息数量 

          * 这个最大值是 BufferedIndexOutput.BUFFER_SIZE 

          * 大概是16KB

          * 这是拷贝时的默认大小

          * 

          * 但是我们知道IndexInput的默认缓存大小是1KB

          * BufferedIndexOut的默认缓存大小16KB 一般是对硬盘的操作

          * RAMOutputStream默认缓存大小为1KB 也是对内存操作

          * 这里可能是由于对硬盘的读操作要明显快于对硬盘的写操作

          * 所以在内存消耗和时间效率上的综合考虑

             * 拷贝的过程:

          * 1.测试需要读入的内容是否大于BufferedIndexOutput.BUFFER_SIZE

          * 2.如果大于,则仅仅拷贝BUFFER_SIZE大小的内容

          * 3.如果小于,则将所有剩下的内容

          * 这样整个的拷贝次数是 Len/BUFFER_SIZE 次

          */

          int toRead = readCount + BufferedIndexOutput.BUFFER_SIZE > len ? (int)(len - readCount) : BufferedIndexOutput.BUFFER_SIZE;

          is.readBytes(buf, 0, toRead);

          os.writeBytes(buf, toRead);

          readCount += toRead;

        }

      } finally {

        // graceful cleanup

        try {

          if (os != null)

            os.close();

        } finally {

          if (is != null)

            is.close();

        }

      }

    }

    if(closeDirSrc)

      src.close();

  }

  /**

   * @throws AlreadyClosedException if this Directory is closed

   */

  protected final void ensureOpen() throws AlreadyClosedException {

    if (!isOpen)

      throw new AlreadyClosedException("this Directory is closed");

  }

}

public abstract IndexOutput createOutput(String name) throws IOException;

这个函数是为了打开一个Output流,这个流文件会实现写入的一般操作,这个与java中提供的output流相似,不过Lucene由于特殊的需求,有一些特定的实现,例如在内存中的流 RAMOutputStream流,这个流实现向内存文件RAMFile的写入操作。BufferedIndexOutput流,这个流实现向硬盘介质写入的操作,当然BufferedIndexOutput类是一个抽象类,不同的子类会对它的一些操作进行实现。

 public abstract IndexInput openInput(String name)

throws IOException;

这个函数实现创建一个输入流,实现将File信息读入内存的操作。与openOutput相似,输入流也有几类,RAMInputStream流,实现将RAMFile中的内容读入到内存的操作。事实上它是一个将一块儿内存信息拷贝到另一块儿的操作。BufferedIndexInput流,是实现将硬盘介质中的信息读入到内存的操作。

public static void copy(Directory src, Directory dest, boolean closeDirSrc) throws IOException

这个函数实现了将src这个Directory拷贝到dest这个Directory的操作。这个函数在我们希望备份一个Directory中的内容时比较有用。

它的操作原理是,

1.得到src中的所有文件;

2.打开src中的第一个文件,假设文件名为FileName1

3.dest中创建一个名称FileName1的文件;

4.srcFileName1信息写入到dest的同名文件中;

5.返回2,直到所有文件拷贝结束

至于读入写入的细节我们会在后面的内容中介绍。

Diretory 包含RAMDirectoryFSDirectory两个子类,FSDirectory中又包含很三个子类,因此我会沿着这条主线进行介绍,在介绍的过程中牵扯到的相关类,我会一并进行介绍。

因为Directory涉及到IndexInputIndexOutput,因此我会有一条主线分析它们。

具体是IndexInputIndexOutputRAMInputSteamRAMOutputStreamBufferedIndexInputBufferedIndexOutput,对于更细节的不同Directory中对BufferedIndexInput BufferedIndexOutput的实现,我们会一并介绍。

由于对于加锁机制和多线程机制不是很了解,因此在讲解过程中,会忽略对这些部分的分析。

PS:现在我分析的是2.9.0版本Lucene,与现在发布的3.0.0版本的Lucene区别不是很大。

  评论这张
 
阅读(499)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017