基于lucene的案例开发:实时索引管理类IndexManager

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/44015983

http://www.llwjy.com/blogd.php?id=5757ce8c007754704b563dd6a47ca1ca

个人的博客小站也搭建成功,网址:www.llwjy.com/blog.php ,欢迎大家来吐槽~

在前一篇博客中,对实时索引的实现原理做了一些简单的介绍,这里就介绍下,如何利用Lucene来实现索引的管理(Lucene中已经实现了大部分的功能,我们只需要对其再次封装即可)。

逐个击破

在Lucene4.3.1中,实现实时索引时,需要将IndexWrite的相关操作委托给TrackingIndexWriter来处理,具体代码实现如下:

ps:关于如何创建索引这里就不再介绍了,可以参照之前的博客或者该博客后面的完整代码。

this.trackingIndexWriter = new TrackingIndexWriter(this.indexWriter);

同时初始化索引管理对象,代码如下:

this.nrtManager = new NRTManager(this.trackingIndexWriter, new SearcherFactory());	

到这里还需要开启两个守护线程:内存索引重读线程和内存数据commit线程。内存索引重读线程执行的频率也就是实时索引的时差,由于内存中的数据不会太多,所以这个延时一般也就是在十几毫秒左右;内存数据commit线程是将内存中的数据写到磁盘上,不至于数据丢失,如果研究过Lucene源码的童鞋也许会发现,即使你不执行commit操作,到内存中的数据达到一定的程度,也会将一部分数据写到磁盘上,只不过重启服务这部分数据就丢失了同时还会造成一系列的问题,http://bbs.csdn.net/topics/390677902
这个地址下就是commit线程死掉之后造成的一系列问题,感兴趣的童鞋可以了解下。

内存重读线程我们只需要配置下参数启动即可,代码如下:

this.nrtManagerReopenThread = new NRTManagerReopenThread(this.nrtManager, indexReopenMaxStaleSec, indexReopenMinStaleSec);
this.nrtManagerReopenThread.setName("NRTManager Reopen Thread");
this.nrtManagerReopenThread.setPriority(Math.min(Thread.currentThread().getPriority()+2, Thread.MAX_PRIORITY));
this.nrtManagerReopenThread.setDaemon(true);
this.nrtManagerReopenThread.start();

内存数据commit线程需要自己写代码实现,然后启动该线程即可,代码如下:

private class IndexCommitThread extends Thread{
	private boolean flag;
	public IndexCommitThread(String name){
		super(name);
	}

	@SuppressWarnings("deprecation")
	public void run(){
		flag = true;
		while(flag) {
			try {
				indexWriter.commit();
				if (bprint) {
					System.out.println(new Date().toLocaleString() + "\t" + IndexManagerName + "\tcommit");
				}
				TimeUnit.SECONDS.sleep(indexCommitSeconds);
			} catch (IOException e) {
				e.printStackTrace();
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
		}
	}
}
this.indexCommitThread = new IndexCommitThread(IndexManagerName + "Index Commit Thread");
this.indexCommitThread.setDaemon(true);
this.indexCommitThread.start();

那又如何像普通的索引那样使用IndexSearcher呢?当然NrtManager类也提供了相关的方法,可以获取最新可用的IndexSearcher,代码如下:

public IndexSearcher getIndexSearcher(){
	try {
		return this.nrtManager.acquire();
	} catch (IOException e) {
		e.printStackTrace();
		return null;
	}
}

当然在使用之后别忘记释放,代码如下:

public void release(IndexSearcher searcher){
	try {
		nrtManager.release(searcher);
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}

另类单例模式

在之前的博客中,我也多次提到,加载索引是一个相当消耗资源的事情,所以我们不可能每一次索引操作都加载一次索引,所以我们就必须使用单例模式来实现IndexManager类。这里的单例模式又和我们常见的单例模式有所区别,普通的单例模式该类只有一个对象,这里的单例模式是该类有多个对象,下面就简单的介绍下此处另类的单例模式。

通过前一篇博客最后,你也许会注意到,系统中关于索引的配置信息是存在HashSet对象中,这也就是说这里IndexManager类会实例化多少次取决于HashSet对象,也就是你配置文件让他实例化多少次就会实例化多少次。既然这样,怎么还能叫单例模式呢?这里的单例是索引的单例,也就是说一个索引只有一个IndexManager对象,不会存在两个IndexManager对象去操作同一个索引的情况。具体代码实现如下:

/**
 * Initialization on Demand Holder式初始化IndexManager
 */
private static class LazyLoadIndexManager {
	private static final HashMap<String, IndexManager> indexManager = new HashMap<String, IndexManager>();

	static {
		for (ConfigBean configBean : IndexConfig.getConfigBean()) {
			indexManager.put(configBean.getIndexName(), new IndexManager(configBean));
		}
	}
}

/**
 *@Description: IndexManager私有构造方法
 *@Author: lulei
 *@Version: 1.1.0
 */
private IndexManager(ConfigBean configBean){
	//...
}
public static IndexManager getIndexManager(String indexName){
	return LazyLoadIndexManager.indexManager.get(indexName);
}

这样我们就可以通过索引名获取到该索引的IndexManager对象。

庐山真面目

说了这么多,下面就把IndexManager的源码附在最后,感兴趣的童鞋可以试试(里面还有一些其他的方法,相信不用介绍也都可以看的懂)

 /**
 *@Description: 索引管理类
 */
package com.lulei.lucene.index.manager;  

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.NRTManager;
import org.apache.lucene.search.NRTManager.TrackingIndexWriter;
import org.apache.lucene.search.NRTManagerReopenThread;
import org.apache.lucene.search.SearcherFactory;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.NIOFSDirectory;
import org.apache.lucene.util.Version;

import com.lulei.lucene.index.model.ConfigBean;
import com.lulei.lucene.index.model.IndexConfig;

public class IndexManager {

	private IndexWriter indexWriter;
	//更新索引文件的IndexWriter
	private TrackingIndexWriter trackingIndexWriter;
	//索引文件采用的分词器
	private Analyzer analyzer;
	//索引管理对象
	private NRTManager nrtManager;
	//索引重读线程
	private NRTManagerReopenThread nrtManagerReopenThread;
	//索引写入磁盘线程
	private IndexCommitThread indexCommitThread;

	//索引地址
	private String indexPath;
	//索引重读最大、最小时间间隔
	private double indexReopenMaxStaleSec;
	private double indexReopenMinStaleSec;
	//索引commit时间
	private int indexCommitSeconds;
	//索引名
	private String IndexManagerName;
	//commit时是否输出相关信息
	private boolean bprint = true;

	/**
	 * Initialization on Demand Holder式初始化IndexManager
	 */
	private static class LazyLoadIndexManager {
		private static final HashMap<String, IndexManager> indexManager = new HashMap<String, IndexManager>();

		static {
			for (ConfigBean configBean : IndexConfig.getConfigBean()) {
				indexManager.put(configBean.getIndexName(), new IndexManager(configBean));
			}
		}
	}

	/**
	 *@Description: IndexManager私有构造方法
	 *@Author: lulei
	 *@Version: 1.1.0
	 */
	private IndexManager(ConfigBean configBean){
		//设置相关属性
		analyzer = configBean.getAnalyzer();
		indexPath = configBean.getIndexPath();
		IndexManagerName = configBean.getIndexName();
		indexReopenMaxStaleSec = configBean.getIndexReopenMaxStaleSec();
		indexReopenMinStaleSec = configBean.getIndexReopenMinStaleSec();
		indexCommitSeconds = configBean.getIndexCommitSeconds();
		bprint = configBean.isBprint();
		String indexFile = indexPath + IndexManagerName + "/";
		//创建或打开索引
		IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_43, analyzer);
		indexWriterConfig.setOpenMode(OpenMode.CREATE_OR_APPEND);
		Directory directory = null;
		try {
			directory = NIOFSDirectory.open(new File(indexFile));
			if (IndexWriter.isLocked(directory)){
				IndexWriter.unlock(directory);
			}
			this.indexWriter = new IndexWriter(directory, indexWriterConfig);
			this.trackingIndexWriter = new TrackingIndexWriter(this.indexWriter);
			this.nrtManager = new NRTManager(this.trackingIndexWriter, new SearcherFactory());
		} catch(IOException e){
			e.printStackTrace();
		}
		//开启守护进程
		this.setThread();
	}
	/**
	 * @Author: lulei
	 * @Description: 创建索引管理线程
	 */
	private void setThread(){
		this.nrtManagerReopenThread = new NRTManagerReopenThread(this.nrtManager, indexReopenMaxStaleSec, indexReopenMinStaleSec);
		this.nrtManagerReopenThread.setName("NRTManager Reopen Thread");
		this.nrtManagerReopenThread.setPriority(Math.min(Thread.currentThread().getPriority()+2, Thread.MAX_PRIORITY));
		this.nrtManagerReopenThread.setDaemon(true);
		this.nrtManagerReopenThread.start();

		this.indexCommitThread = new IndexCommitThread(IndexManagerName + "Index Commit Thread");
		this.indexCommitThread.setDaemon(true);
		this.indexCommitThread.start();
	}

	/**
	 * @return
	 * @Author:lulei
	 * @Description: 重启索引commit线程
	 */
	public String setCommitThread() {
		try {
			if (this.indexCommitThread.isAlive()){
				return "is alive";
			}
			this.indexCommitThread = new IndexCommitThread(IndexManagerName + "Index Commit Thread");
			this.indexCommitThread.setDaemon(true);
			this.indexCommitThread.start();
		} catch (Exception e) {
			e.printStackTrace();
			return "failed";
		}
		return "reload";
	}

	/**
	 *@Description: 索引commit线程
	 *@Author: lulei
	 *@Version: 1.1.0
	 */
	private class IndexCommitThread extends Thread{
		private boolean flag;
		public IndexCommitThread(String name){
			super(name);
		}

		@SuppressWarnings("deprecation")
		public void run(){
			flag = true;
			while(flag) {
				try {
					indexWriter.commit();
					if (bprint) {
						System.out.println(new Date().toLocaleString() + "\t" + IndexManagerName + "\tcommit");
					}
					TimeUnit.SECONDS.sleep(indexCommitSeconds);
				} catch (IOException e) {
					e.printStackTrace();
				} catch (InterruptedException e1) {
					e1.printStackTrace();
				}
			}
		}
	}

	/**
	 * @return IndexManager
	 * @Author: lulei
	 * @Description: 获取索引管理类
	 */
	public static IndexManager getIndexManager(String indexName){
		return LazyLoadIndexManager.indexManager.get(indexName);
	}

	/**
	 * @@Description:释放IndexSearcher资源
	 * @param searcher
	 */
	public void release(IndexSearcher searcher){
		try {
			nrtManager.release(searcher);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * @return IndexSearcher
	 * @Author: lulei
	 * @Description: 返回IndexSearcher对象,使用完之后,调用release方法进行释放
	 */
	public IndexSearcher getIndexSearcher(){
		try {
			return this.nrtManager.acquire();
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}

	public NRTManager getNRTManager(){
		return this.nrtManager;
	}

	public IndexWriter getIndexWriter(){
		return this.indexWriter;
	}

	public TrackingIndexWriter getTrackingIndexWriter(){
		return this.trackingIndexWriter;
	}

	public Analyzer getAnalyzer(){
		return analyzer;
	}

	/**
	 * @return
	 * @Author: lulei
	 * @Description: 获取索引中的记录条数
	 */
	public int getIndexNum(){
		return indexWriter.numDocs();
	}
}

ps:最近发现其他网站可能会对博客转载,上面并没有源链接,如想查看更多关于 基于lucene的案例开发点击这里。或访问网址http://blog.csdn.net/xiaojimanman/article/category/2841877 或
http://www.llwjy.com/blog.php

时间: 2024-08-04 21:32:14

基于lucene的案例开发:实时索引管理类IndexManager的相关文章

基于lucene的案例开发:索引文件结构

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/42836309 前面我们已经介绍了非结构数据的检索过程以及lucene的数学模型,这篇博客就主要介绍一下lucene索引的文件结构,下图是lucene生成的索引实例: lucene索引结构是层次结构,主要有以下几个层次: 索引(Index) 在lucene中,一个索引是放在一个文件夹中的,上述实例中的所有文件就组成了lucene索引 段(Segment) 一个索引中可以有很多段,

基于lucene的案例开发:索引数学模型

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/42818185 通过前一篇博客,对所以也许有了一个大致的了解,知道检索是一个怎样的过程,这篇博客就简单的介绍一下lucene实现的数学模型. 前面我们已经提到lucene实现的索引是一种反向索引,有词典和倒排表组成(实际的结构要比这个复杂很多),那索引的数学模型又是怎样的呢?在开始这个之前,还是先熟悉下几个名词.       文档(Document):上篇博客中的索引创建过程中列

基于lucene的案例开发:创建索引

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/42872711 从这篇博客开始,不论是API介绍还是后面的案例开发,都是基于 lucene4.3.1 这个版本,Lucene4.3.1 下载请点击这里, Lucene其他版本下载请点击这里,Lucene4.3.1官方API文档请点击这里. 创建索引demo 在开始介绍之前,先看一个简单的索引创建demo程序: /** *@Description: 索引创建demo */ pack

基于lucene的案例开发:实时索引的修改

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/44280311 http://www.llwjy.com/blogdetail/e42fa5c3097f4964fca0fdfe7cd7a9a2.html 个人的博客小站已经上线了,网址 www.llwjy.com,欢迎大家来吐槽~ 上一篇博客已经介绍了实时索引的检索功能,这个就相当于数据的的查询功能,我们都知道数据库的增删改查是最常用的四个功能,实时索引也是一样,他也有增删改查

基于lucene的案例开发:实时索引的检索

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/44279753 http://www.llwjy.com/blogdetail/31bb705106379feaf6d31b58dd777be6.html 个人博客小站搭建成功,网址 www.llwjy.com,欢迎大家来吐槽~ 在前面的博客中,我们已经介绍了IndexSearcher中的检索方法,也介绍了如何基于lucene中的NRT*类去创建实时索引,在这篇博客中我们就重点介

基于lucene的案例开发:实现实时索引基本原理

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/43982653 http://www.llwjy.com/blogd.php?id=63d4c488a2cccb5851c0498d374951c9 个人的博客小站也搭建成功,网址:www.llwjy.com/blog.php ,欢迎大家来吐槽~ 基本原理 在前面的博客中也说过,程序初始话索引文件是十分消耗系统资源的,因此要想实现实时索引就不能实时的去修改索引文件.重新加载索引文

基于lucene的案例开发:纵横小说分布式采集

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/46812645 http://www.llwjy.com/blogdetail/9df464b20cca5405c7ce07e2fb2d768f.html 个人博客站已经上线了,网址 www.llwjy.com ~欢迎各位吐槽~ ----------------------------------------------------------------------------

基于lucene的案例开发:数据库连接池

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/43272993 通过java程序去连接数据库时,使用的协议是TCP/IP协议,TCP/IP协议需要进行3次握手.如果每一次数据库操作都需要创建一个新的连接,都要进行3次握手,这是十分浪费资源的,程序的效率也不是很高.为了解决这个问题,我们想可不可以自己维护一些数据库连接,需要数据库操作的时候,直接使用这其中的一个连接,用完了,在还给它,这样的话就不需要每次数据库操作都创建一个新的

基于lucene的案例开发:案例初识

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/43192055 首先抱歉,这几天在准备案例的整体框架设计,所以更新就断了几天,还请原谅. 案例整体介绍 在我们开始正式的案例开发介绍之前,我们先看一下整体的案例demo介绍,明白案例是做什么的. 从上图中,我们可以看出,这个案例主要是通过爬虫程序去采集纵横小说上的资源,然后将资源存储到自己的数据库中,将数据库中的需要检索的数据通过lucene建立索引文件,最后通过web服务展示数