仿照博客園搜索功能 找找看的實現 發現問題 雜談


我記得在園子里有園友提出博客園的搜索功能是采用的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 實現的是定時器任務,可以按照定時規則按照規定的時間執行任務。

根據我在仿照博客園搜索功能中遇到的問題,其實任何一個框架的單純使用都很簡單,但是在實戰中如何更加合理的使用這些框架,更加高效的讓這些框架協同工

作使我們做項目的時候需要思考的重點,有時候思考 分析一下,甚至總結一下會對自己的能力提升有很大的好處。雖然我在這篇博客中總結的問題不多,但是這

都是我在做項目的時候遇到的問題,困擾了我好幾天的時間才發現的原因。

有總結才會有提高,有總結才會有進步,我不敢說通過完善這個仿照博客園的系統我有學習到很多東西,但是我發現了我自己的很多不足,這是很珍貴的,只有發
現不足才可以彌補。

我經常看見有些園友看了一下某個框架的配置文件,在試驗的時候能夠讓框架運行輸出自己想要的結果,然后就說掌握了這個框架,這個框架有多么的簡單,等等,我都會笑一笑,繼續其他的事情。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM