Nutch 插件系統淺析

Nutch 基本情況

Nutch 是 Apache 基金會的一個開源項目,它原本是開源文件索引框架 Lucene 項目的一個子項目,後來漸漸發展成長爲一個獨立的開源項目。它基於 Java 開發,基於 Lucene 框架,提供 Web 網頁爬蟲功能。另外很吸引人的一點在於,它提供了一種插件框架,使得其對各種網頁內容的解析、各種數據的採集、查詢、集羣、過濾等功能能夠方便的進行擴 展,正是由於有此框架,使得 Nutch 的插件開發非常容易,第三方的插件也層出不窮,極大的增強了 Nutch 的功能和聲譽。本文就是主要描述這個插件框架內部運行的機制和原理。

 




回頁首


Nutch 的插件體系結構

在 Nutch 的插件體系架構下,有些術語需要在這裏解釋:

  1. 擴展點 ExtensionPoint

    擴展點是系統中可以被再次擴展的類或者接口,通過擴展點的定義,可以使得系統的執行過程變得可插入,可任意變化。

  2. 擴展 Extension

    擴展式插件內部的一個屬性,一個擴展是針對某個擴展點的一個實現,每個擴展都可以有自己的額外屬性,用於在同一個擴展點實現之間進行區分。擴展必須在插件內部進行定義。

  3. 插件 Plugin

    插件實際就是一個虛擬的容器,包含了多個擴展 Extension、依賴插件 RequirePlugins 和自身發佈的庫 Runtime,插件可以被啓動或者停止。

Nutch 爲了擴展,預留了很多擴展點 ExtenstionPoint,同時提供了這些擴展點的基本實現 Extension,Plugin 用來組織這些擴展,這些都通過配置文件進行控制,主要的配置文件包括了多個定義擴展點和插件(擴展)的配置文件,一個控制加載哪些插件的配置文件。體系結 構圖如下:


圖 1. Nutch 插件體系結構圖
圖 1. Nutch 插件體系結構圖




回頁首


插件的內部結構


圖 2. 插件的內部結構
圖 2. 插件的內部結構

  1. runtime 屬性描述了其需要的 Jar 包,和發佈的 Jar 包
  2. requires 屬性描述了依賴的插件
  3. extension-point 描述了本插件宣佈可擴展的擴展點
  4. extension 屬性則描述了擴展點的實現

典型的插件定義:

<plugin
id="query-url" 插件的ID
name="URL Query Filter" 插件的名字
version="1.0.0" 插件的版本
provider-name="nutch.org"> 插件的提供者ID

<runtime>
<library name="query-url.Jar"> 依賴的Jar包
<export name="*"/> 發佈的Jar包
</library>
</runtime>

<requires>
<import plugin="nutch-extensionpoints"/> 依賴的插件
</requires>

<extension id="org.apache.nutch.searcher.url.URLQueryFilter" 擴展的ID
name="Nutch URL Query Filter" 擴展的名字
point="org.apache.nutch.searcher.QueryFilter"> 擴展的擴展點ID
<implementation id="URLQueryFilter" 實現的ID
class="org.apache.nutch.searcher.url.URLQueryFilter"> 實現類
<parameter name="fields" value="url"/> 實現的相關屬性
</implementation>
</extension>
</plugin>





回頁首


插件主要配置

  1. plugin.folders:插件所在的目錄,缺省位置在 plugins 目錄下。
    <property>
    <name>plugin.folders</name>
    <value>plugins</value>
    <description>Directories where nutch plugins are located. Each
    element may be a relative or absolute path. If absolute, it is used
    as is. If relative, it is searched for on the classpath.
    </description>
    </property>
  2. plugin.auto-activation:當被配置爲過濾(即不加載),但是又被其他插件依賴的時候,是否自動啓動,缺省爲 true。
    <property>
    <name>plugin.auto-activation</name>
    <value>true</value>
    <description>Defines if some plugins that are not activated regarding
    the plugin.includes and plugin.excludes properties must be automaticaly
    activated if they are needed by some actived plugins.
    </description>
    </property>
  3. plugin.includes:要包含的插件名稱列表,支持正則表達式方式定義。
    <property>
    <name>plugin.includes</name>
    <value>protocol-http|urlfilter-regex|parse-(text|html|js)|index-(basic|anchor)
    |query-(basic|site|url)|response-(json|xml)|summary-basic|scoring-opic|
    urlnormalizer-(pass|regex|basic)
    </value>
    <description>Regular expression naming plugin directory names to
    include. Any plugin not matching this expression is excluded.
    In any case you need at least include the nutch-extensionpoints plugin. By
    default Nutch includes crawling just HTML and plain text via HTTP,
    and basic indexing and search plugins. In order to use HTTPS please enable
    protocol-httpclient, but be aware of possible intermittent problems with the
    underlying commons-httpclient library.
    </description>
    </property>
  4. plugin.excludes:要排除的插件名稱列表,支持正則表達式方式定義。
    <property>
    <name>plugin.excludes</name>
    <value></value>
    <description>Regular expression naming plugin directory names to exclude.
    </description>
    </property>

 




回頁首


插件主要類 UML 圖


圖 3. 插件主要類 UML 圖( 查看大圖
圖 3. 插件主要類 UML 圖

類包括:

  1. PluginRepository 是一個通過加載 Iconfiguration 配置信息初始化的插件庫,裏面維護了系統中所有的擴展點 ExtensionPoint 和所有的插件 Plugin 實例
  2. ExtensionPoint 是一個擴展點,通過擴展點的定義,插件 Plugin 才能定義實際的擴展 Extension,從而實現擴展,每個 ExtensionPoint 類實例都維護了宣佈實現了此擴展點的擴展 Extension.
  3. Plugin 是一個虛擬的組織,提供了一個啓動 start 和一個 shutdown 方法,從而實現了插件的啓動和停止,他還有一個描述對象 PluginDescriptor,負責保存此插件相關的配置信息,另外還有一個 PluginClassLoader 負責此插件相關類和庫的加載。

 




回頁首


插件加載過程


圖 4 . 插件加載過程時序圖( 查看大圖
圖 4 . 插件加載過程時序圖

通過序列圖可以發現,Nutch 加載插件的過程需要 actor 全程直接調用每個關聯對象,最終得到的是插件的實現對象。詳細過程如下:

  1. 首 先通過 PluginRepository.getConf() 方法加載配置信息,配置的內容包括插件的目錄,插件的配置文件信息 plugin.properties 等,此時 pluginrepository 將根據配置信息加載各個插件的 plugin.xml,同時根據 Plugin.xml 加載插件的依賴類。
  2. 當 actor 需要加載某個擴展點的插件的時候,他可以:
    1. 首先根據擴展點的名稱,通過 PluginRepository 得到擴展點的實例,即 ExtensionPoint 類的實例;
    2. 然後調用 ExtensionPoint 對象的 getExtensions 方法,返回的是實現此擴展點的實例列表(Extension[]);
    3. 對每個實現的擴展實例 Extension,調用它的 getExtensionInstance() 方法,以得到實際的實現類實例,此處爲 Object;
    4. 根據實際情況,將 Object 轉型爲實際的類對象類型,然後調用它們的實現方法,例如 helloworld 方法。

 




回頁首


插件的典型調用方式

得到某個語言例如“GBK”擴展點的實例:

this.extensionPoint.getExtensions();// 得到擴展點的所有擴展
for (int i=0; i<extensions.length; i++) {// 遍歷每個擴展
if (“GBK”.equals(extensions[i].getAttribute("lang"))) {// 找到某個屬性的擴展
return extensions[i];// 返回
}
}
}
extension.getExtensionInstance()// 得到此擴展實現的實例對象





回頁首


插件類加載機制

實際整個系統如果使用了插件架構,則插件類的加載是由 PluginClassLoader 類完成的,每個 Plugin 都有自己的 classLoader,此 classloader 繼承自 URLClassLoader,並沒有做任何事情:

public class PluginClassLoader extends URLClassLoader { 
/**
* Construtor
*
* @param urls
* Array of urls with own libraries and all exported libraries of
* plugins that are required to this plugin
* @param parent
*/
public PluginClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
}

 

這個 classloader 是屬於這個插件的,它只負責加載本插件相關的類、本地庫和依賴插件的發佈 (exported) 庫,也包括一些基本的配置文件例如 .properties 文件。

此類的實例化過程:

if (fClassLoader != null) 
return fClassLoader;
ArrayList<URL> arrayList = new ArrayList<URL>();
arrayList.addAll(fExportedLibs);
arrayList.addAll(fNotExportedLibs);
arrayList.addAll(getDependencyLibs());
File file = new File(getPluginPath());
try {
for (File file2 : file.listFiles()) {
if (file2.getAbsolutePath().endsWith("properties"))
arrayList.add(file2.getParentFile().toURL());
}
} catch (MalformedURLException e) {
LOG.debug(getPluginId() + " " + e.toString());
}
URL[] urls = arrayList.toArray(new URL[arrayList.size()]);
fClassLoader = new PluginClassLoader(urls, PluginDescriptor.class
.getClassLoader());
return fClassLoader;

 

  • 首先判斷緩存是否存在
  • 加載需要的 Jar 包、自身需要的 Jar 包,依賴插件發佈的 Jar 包
  • 加載本地的 properties 文件
  • 構造此 classloader,父 classloader 爲 PluginDescriptor 的加載者,通常是 contextClassLoader

 




回頁首


總結

Nutch 是一個非常出色的開源搜索框架,它的插件架構更加是它的一個技術亮點,通過此架構,可以保證 Nutch 方便的被靈活的擴展而不用修改原來的代碼,通過配置文件可以簡單方便的控制加載或者不加載哪些插件,而且這些都不需要額外的容器支持。這些都是我們在系統 架構設計的時候可以學習和參考的有益經驗。


參考資料

學習

  • 查看網站“Nutch 官方網站 ”,瞭解 Nutch 的基本情況。
  • Nutch 實戰 ”(developerWorks,2008 年 11 月):本文介紹了開源搜索引擎 Nutch 的基本信息,並詳細說明了在 Eclispe 下運行 Nutch 的步驟和需要注意的問題。
  • 開發基於 Nutch 的集羣式搜索引擎 ”(developerWorks,2008 年 10 月):本文首先介紹 Nutch 的背景知識,包括 Nutch 架構,爬蟲和搜索器。然後以開發一個基於 Nutch 的實際應用爲例向讀者展示如何使用 Nutch 開發自己的搜索引擎。
  • developerWorks Java 技術專區 :查找關於 Java 編程各方面的數百篇文章。


獲得產品和技術

  • 下載“nutch ”,瞭解更多 Nutch 的細節。


討論

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章