从asp.net web应用程序使用Lucene.Net线程安全

Lel*_*son 10 asp.net singleton lucene.net

所以我一直在研究从Web应用程序中实现Lucene.Net索引搜索和写入的最佳方法.我提出了以下要求:

  • 需要允许并发搜索和访问索引(查询并行运行)
  • 会有多个索引
  • 使索引搜索完全是最新的("实时")不是必需的
  • 运行作业以在某个频率上更新索引(每个索引的频率不同)
  • 很明显,我希望以一种遵循lucene"最佳实践"并且能够很好地执行和扩展的方式来完成所有这些工作

我找到了一些有用的资源,这里有几个很好的问题就像这样

在该帖子作为指导后,我决定尝试一个单例模式,其中包含一个用于管理索引的包装器的并发字典.

为了简单起见,我假装我只管理一个索引,在这种情况下,包装器可以成为单例.最终看起来像这样:

public sealed class SingleIndexManager
{
    private const string IndexDirectory = "C:\\IndexDirectory\\";
    private const string IndexName = "test-index";
    private static readonly Version _version = Version.LUCENE_29;

    #region Singleton Behavior
    private static volatile SingleIndexManager _instance;
    private static object syncRoot = new Object();

    public static SingleIndexManager Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (syncRoot)
                {
                    if (_instance == null)
                        _instance = new SingleIndexManager();
                }
            }

            return _instance;
        }
    }
    #endregion

    private IndexWriter _writer;
    private IndexSearcher _searcher;

    private int _activeSearches = 0;
    private int _activeWrites = 0;

    private SingleIndexManager()
    {
        lock(syncRoot)
        {
            _writer = CreateWriter(); //hidden for sake of brevity
            _searcher = new IndexSearcher(_writer.GetReader());
        }
    }

    public List<Document> Search(Func<IndexSearcher,List<Document>> searchMethod)
    {
        lock(syncRoot)
        {
            if(_searcher != null && !_searcher.GetIndexReader().IsCurrent() && _activeSearches == 0)
            {
                _searcher.Close();
                _searcher = null;
            }
            if(_searcher == null)
            {
                _searcher = new IndexSearcher((_writer ?? (_writer = CreateWriter())).GetReader());
            }
        }
        List<Document> results;
        Interlocked.Increment(ref _activeSearches);
        try
        {
            results = searchMethod(_searcher);
        } 
        finally
        {
            Interlocked.Decrement(ref _activeSearches);
        }
        return results;
    }

    public void Write(List<Document> docs)
    {
        lock(syncRoot)
        {
            if(_writer == null)
            {
                _writer = CreateWriter();
            }
        }
        try
        {
            Interlocked.Increment(ref _activeWrites);
            foreach (Document document in docs)
            {
                _writer.AddDocument(document, new StandardAnalyzer(_version));
            }

        } 
        finally
        {
            lock(syncRoot)
            {
                int writers = Interlocked.Decrement(ref _activeWrites);
                if(writers == 0)
                {
                    _writer.Close();
                    _writer = null;
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

从理论上讲,这应该允许一个线程安全的单例实例用于索引(此处命名为"index-test"),其中我有两个公开公开的方法,Search()并且Write()可以从ASP.NET Web应用程序中调用,而不需要考虑线程安全?(如果这不正确,请告诉我).

现在有一件事让我有点麻烦:

如何Application_End在Global.asax.cs文件中优雅地关闭这些实例,以便如果我想在IIS中重新启动我的Web应用程序,我不会得到一堆write.lock失败等?

到目前为止,我所能想到的只有:

public void Close()
{
    lock(syncRoot)
    {
        _searcher.Close();
        _searcher.Dispose();
        _searcher = null;

        _writer.Close();
        _writer.Dispose();
        _writer = null;
    }
}
Run Code Online (Sandbox Code Playgroud)

并称之为Application_End,但如果我有任何活跃的搜索者或作者,这是否会导致腐败索引?

任何帮助或建议都非常感谢.谢谢.

cas*_*One 11

Lucene.NET 非常安全.我可以肯定地说,IndexWriterIndexReader类上的所有方法都是线程安全的,你可以使用它们而不必担心同步.您可以删除涉及围绕这些类的实例进行同步的所有代码.

也就是说,更大的问题是使用ASP.NET中的Lucene.NET. ASP.NET会出于多种原因回收应用程序池,但是,在关闭一个应用程序域时,它会引发另一个应用程序池来处理对该站点的新请求.

如果您尝试使用FSDirectory不同的IndexWriter/ 来访问相同的物理文件(假设您使用的是基于文件系统)IndexReader,那么您将收到错误,因为文件上的锁尚未被具有'的应用程序域释放已经关闭了.

为此,建议的最佳实践是控制处理Lucene.NET访问的进程; 这通常意味着创建一个服务,您可以通过Remoting或WCF(最好是后者)公开您的操作.

这样做更多(因为你必须创建代表你的操作的所有抽象),但你获得了以下好处:

  • 服务进程将始终处于启动状态,这意味着客户端(ASP.NET应用程序)不必担心争用所需的文件FSDirectory.他们只需要打电话给服务.

  • 您正在更高层次上抽象搜索操作.您不是直接访问Lucene.NET,而是定义这些操作所需的操作和类型.一旦你完成了抽象,如果你决定从Lucene.NET转移到其他搜索机制(比如RavenDB),那么就需要改变合同的实现.