Итак, я делал некоторые исследования по наилучшему способу реализации поиска Lucene.Net и записи из веб-приложения. Я изложил следующие требования:
- Необходимо разрешить параллельный поиск и доступ к индексу (запросы выполняются параллельно)
- будет несколько индексов
- имеющий индексный поиск, полностью обновлен ( "в режиме реального времени" ) НЕ является обязательным требованием
- запускать задания для обновления индексов на некоторой частоте (частота для каждого индекса различна)
- очевидно, хотел бы сделать все это таким образом, который следует за "лучшими практиками" lucene и может хорошо выполнять и масштабировать.
Я нашел полезные ресурсы и пару хороших вопросов здесь, как этот
После этой публикации в качестве руководства я решил попробовать шаблон singleton с параллельным словарем оболочки, созданной для управления индексом.
Чтобы сделать вещи проще, я буду притворяться, что я управляю только одним индексом, и в этом случае оболочка может стать синглом. Это выглядит следующим образом:
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;
}
}
}
}
}
Теоретически это должно позволить потокобезопасный экземпляр singleton для индекса (здесь называется "index-test" ), где у меня есть два открытых метода, Search()
и Write()
, которые могут быть вызваны изнутри Веб-приложение ASP.NET без каких-либо проблем с безопасностью потоков? (если это неверно, сообщите мне).
Была одна вещь, которая сейчас немного беспокоит меня:
Как изящно закрыть эти экземпляры на Application_End
в файле Global.asax.cs, чтобы, если я хочу перезапустить мое веб-приложение в IIS, я не собираюсь получать кучу ошибок write.lock и т.д.
Все, что я могу догадываться до сих пор, это:
public void Close()
{
lock(syncRoot)
{
_searcher.Close();
_searcher.Dispose();
_searcher = null;
_writer.Close();
_writer.Dispose();
_writer = null;
}
}
и вызывая это в Application_End
, но если у меня есть активные поисковые машины или писатели, это приведет к поврежденному индексу?
Любая помощь или предложения очень ценятся. спасибо.