仿照博客园搜索功能 找找看的实现 发现问题 杂谈

我记得在园子里有园友提出博客园的搜索功能是采用的Lucene.net,具体是不是我也不确定,但是宁可信其是,所以我在仿照博客园 搜索功能的时候采用的也是Lucene.net,有园友给我提意见,就是我以前的博客中提到的那些框架,例如log4net,Quartz.net等都是片面的讲解一个框架结构,只能作为练习用,而不能真正的在项目中使用,具体原因就是在实战中会遇到各种问题,没对应的策略,所以今天我就来把我这个仿照系统的搜索功能的实现简单的描述一下,重点说明的是在项目中使用这些框架应该注意的地方,这样才会更贴切实战,也更有用一些。

利用Lucene.net 进行搜索,应该分为三步走战略

  1. 利用IndexWriter进行创建索引
  2. 利用Analyzer对索引进行分词
  3. 利用IndexReader对索引进行提取

总的来说就是这三步,下面我们来具体分析一下这三步在实战中都应该采用什么策略。

首先,创建索引,因为博客会不断的被添加,所以我们在创建索引的时候要指定索引为增量添加(在IndexWriter实例化的时候有一个属性设置为false就可以)

1  Lucene.Net.Store.Directory dir = FSDirectory.Open("d:/index");
2  IndexWriter writer = new IndexWriter(dir, new PanGuAnalyzer(), false, IndexWriter.MaxFieldLength.LIMITED);

 

设置了创建索引为增量索引以后,我们还需要指定其中的某些参数,让lucene.net在特定的计算机上工作最大化。

现在有一个问题出现了,我们不可能手动的去创建索引,所以我们要使用定时器去自动的创建索引,这就用到了Quartz.net定时器,

还有一个问题就是我们创建索引因为是自动创建,所以不需要界面显示,并且需要在后台运行,所以我们创建索引最好是采用Windows 服务形式来承载。

当然了,在程序运行过程中会遇到各种问题,需要我们记录成粗错误发生的位置以及时间,这就会用到log4net 日志管理框架。

 

问题一个一个的出现了,现在我们就要把这些问题逐个的解决,首先就是创建Windows 服务,这个在Vs中很容易的就可以实现。现在我把仿照博客园实现搜索的Windows服务的部分代码贴出来分析一下,

 1  public partial class Service1 : ServiceBase
 2     {
 3         private readonly ILog logger = LogManager.GetLogger(typeof(Service1));
 4         
 5 
 6         public Service1()
 7         {
 8             log4net.Config.XmlConfigurator.Configure();
 9             InitializeComponent();
10             SchedulerManager.GetSchedulerFactory().GetScheduler().ListenerManager.AddJobListener(SchedulerManager.GetJobListener());
11         }
12 
13         protected override void OnStart(string[] args)
14         {
15             try
16             {
17                 JobKey jobKey = new JobKey("CreateIndex", "CreateIndexGroup");
18 
19                 IJobDetail jobDetail = JobBuilder.Create().WithIdentity(jobKey).OfType(typeof(Jobs.QuzrtaNet)).Build();
20 
21                 ITrigger trigger = TriggerBuilder.Create().WithCronSchedule("0 0/59 * * * ?").StartNow().Build();
22                 SchedulerManager.GetScheduler().ScheduleJob(jobDetail, trigger);
23                 logger.Info("任务 " + jobDetail.Key.Group + "已经调度陈功");
24                 if (SchedulerManager.GetScheduler().IsStarted == false)
25                 {
26                     SchedulerManager.GetScheduler().Start();
27                     logger.Info("服务已经成功启动");
28                 }
29             }
30             catch (Exception ex)
31             {
32                 logger.Error("服务启动失败", ex);
33                 if (this.CanStop == true)
34                 {
35                     this.Stop();
36                 }
37             }
38         }
39 
40         protected override void OnStop()
41         {
42             try
43             {
44                 if (SchedulerManager.GetScheduler().IsShutdown == false)
45                 {
46                     SchedulerManager.GetScheduler().Shutdown();
47                     logger.Info("Quartz服务成功终止");
48                 }
49             }
50             catch (Exception ex)
51             {
52                 logger.Error("服务停止失败",ex);
53             }
54 
55         }
56         protected override void OnPause()
57         {
58             
59         }
60 
61         protected override void OnContinue()
62         {
63             
64         }
65     }

 

 

在上面额代码中,有几点我要说明一下。

  1. 对于log4net 需要在程序启动的时候进行配置log4net.Config.XmlConfigurator.Configure();这句代码一定不能少,否则log4net就不会根据配置文件进行更改其特性
  2. 对于Quartz.net的操作,我们利用单例模式来获取对象的实例,因为在Quartz.net 3.0中的很多接口方法都已经改变,所以如果你采用的是最新版的版

    本,那么你可能要进行一些修改,你可以看到我现在的写法和园子里其他园友的写法有所不同,这都不是什么问题,因为接口方法更改了。


    在quartz.net 中通过继承IJOb接口来实现一个任务,我们在Windows 服务启动的时候可以定时执行多个任务。这就会遇到一个问题,例如,你规定了

    一个trigger的周期为1分钟,那么触发器没隔一分钟就胡执行一次这个任务,但是你的这个任务的执行需要一个小时的时间?这时你看会出现什么问题?


    触发器没一分钟触发一次,但是任务会执行一个小时。这个问题我曾经想过,但是也没找到很完美的解决方案,最好的办法可能就是把触发器的触发周期

    调长,比如调成每天执行一次,这样就可以避免多次触发的问题。
  3. 我们再来注意一下log4net,这个框架会根据配置文件来动态的输出日志到不同的介质,但是在我们配置log4net的时候,一定要注意精确,因为一点的拼

    写错误都会导致log4net 不能正常工作。

     1  <configSections>
     2     <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
     3   </configSections>
     4   <log4net debug="false">
     5     <appender name="LogFileAppender" type="log4net.Appender.FileAppender">
     6       <param name="File" value="c:\Log\DBLog.txt"/>
     7       <param name="AppendToFile" value="true"/>
     8       <layout type="log4net.Layout.PatternLayout">
     9         <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n"/>
    10       </layout>
    11     </appender>
    12 
    13     <appender name="ADONetAppender" type="log4net.Appender.ADONetAppender">
    14       <bufferSize value="10"/>
    15       <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
    16       <connectionString value="server=.\sqlexpress;database=DbLog;user id=sa;password=yuanjinzhou"/>
    17       <commandText value="INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Message],[Exception]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception)"/>
    18       <parameter>
    19         <parameterName value="@log_date"/>
    20         <dbType value="DateTime"/>
    21         <layout type="log4net.Layout.RawTimeStampLayout"/>
    22       </parameter>
    23       <parameter>
    24         <parameterName value="@thread"/>
    25         <dbType value="String"/>
    26         <size value="255"/>
    27         <layout type="log4net.Layout.PatternLayout">
    28           <conversionPattern value="%thread"/>
    29         </layout>
    30       </parameter>
    31       <parameter>
    32         <parameterName value="@log_level"/>
    33         <dbType value="String"/>
    34         <size value="50"/>
    35         <layout type="log4net.Layout.PatternLayout">
    36           <conversionPattern value="%level"/>
    37         </layout>
    38       </parameter>
    39       <parameter>
    40         <parameterName value="@logger"/>
    41         <dbType value="String"/>
    42         <size value="255"/>
    43         <layout type="log4net.Layout.PatternLayout">
    44           <conversionPattern value="%logger"/>
    45         </layout>
    46       </parameter>
    47       <parameter>
    48         <parameterName value="@message"/>
    49         <dbType value="String"/>
    50         <size value="4000"/>
    51         <layout type="log4net.Layout.PatternLayout">
    52           <conversionPattern value="%message"/>
    53         </layout>
    54       </parameter>
    55       <parameter>
    56         <parameterName value="@exception"/>
    57         <dbType value="String"/>
    58         <size value="2000"/>
    59         <layout type="log4net.Layout.ExceptionLayout"/>
    60       </parameter>
    61     </appender>
    62     <root>
    63       <level value="DEBUG"/>
    64       <appender-ref ref="ADONetAppender"/>
    65       <appender-ref ref="LogFileAppender"/>
    66     </root>
    67   </log4net>

    log4net的输出介质园子里有很多介绍,我这里贴出来配置文件,就是不想有太多人因为拼写错误而导致log4net不能正常工作。其中我要特别强调一下的

    就是log4net输出日志到数据库中,这里面有很多配置参数,其中 

    <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>

    connectionType结点中的System.Data后面的Version以后的内容我们一定要添加上,我在多次试验中发现如果后面的版本号不加上的话,log4net不

    会正确的把日志插入到数据库中,而加上版本号就可以,所以当你不能把日志插入到数据库中时,检查一下版本号是否写上。

  4. 另外有一点我想再说一下,就是我们在服务启动的时候为Scheduler添加任务以及触发器,其中任务和触发器是分开的,我们不要在继承自IJOb的任务类

    中添加任何的触发器,因为我发现园子里有园友这样用过,所以我提醒一下各位,触发器和任务是分开的。
  5. 对于Lucene.net如何创建索引以及查询,以及分词,我这里使用的分词插件是盘古分词,这些内容在前面的博客中我有说明过,不再赘述。

总结一下,log4net日志管理框架,可以输出日志到任何的介质,对log4net的配置文件的配置是重点。

Lucene.net可以用来实现创建索引,通过索引进行查询,实现全文检索的功能。

Quartz.net 实现的是定时器任务,可以按照定时规则按照规定的时间执行任务。

根据我在仿照博客园搜索功能中遇到的问题,其实任何一个框架的单纯使用都很简单,但是在实战中如何更加合理的使用这些框架,更加高效的让这些框架协同工

作使我们做项目的时候需要思考的重点,有时候思考 分析一下,甚至总结一下会对自己的能力提升有很大的好处。虽然我在这篇博客中总结的问题不多,但是这

都是我在做项目的时候遇到的问题,困扰了我好几天的时间才发现的原因。

有总结才会有提高,有总结才会有进步,我不敢说通过完善这个仿照博客园的系统我有学习到很多东西,但是我发现了我自己的很多不足,这是很珍贵的,只有发
现不足才可以弥补。

我经常看见有些园友看了一下某个框架的配置文件,在试验的时候能够让框架运行输出自己想要的结果,然后就说掌握了这个框架,这个框架有多么的简单,等等,我都会笑一笑,继续其他的事情。

posted @ 2012-12-30 14:29  baidixing  阅读(2571)  评论(2编辑  收藏  举报