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 插件体系结构图

 

回页首

插件的内部结构

图 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 图(查看大图

类包括:

  1. PluginRepository 是一个通过加载 Iconfiguration 配置信息初始化的插件库,里面维护了系统中所有的扩展点 ExtensionPoint 和所有的插件 Plugin 实例
  2. ExtensionPoint 是一个扩展点,通过扩展点的定义,插件 Plugin 才能定义实际的扩展 Extension,从而实现扩展,每个 ExtensionPoint 类实例都维护了宣布实现了此扩展点的扩展 Extension.
  3. Plugin 是一个虚拟的组织,提供了一个启动 start 和一个 shutdown 方法,从而实现了插件的启动和停止,他还有一个描述对象 PluginDescriptor,负责保存此插件相关的配置信息,另外还有一个 PluginClassLoader 负责此插件相关类和库的加载。
 

回页首

插件加载过程

图 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插件系统的更多相关文章

  1. 浅谈Nutch插件机制(含开发实例)

    plugin(插件)为nutch提供了一些功能强大的部件,举个例子,HtmlParser就是使用比较普遍的用来分析nutch抓取的html文件的插件. 为什么nutch要使用这样的plugin系统? ...

  2. CloudNotes之桌面客户端篇:插件系统的实现

    [CloudNotes版本更新历史与各版本下载地址请点击此处] [CloudNotes中文系列文章汇总列表请点击此处] [查看CloudNotes源代码请点击此处] 有时候,同一个名词,针对不同的人群 ...

  3. 浅谈C#中一种类插件系统编写的简单方法(插件间、插件宿主间本身不需要通信)

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 三年多前还在上研时,用C#+反射机制写过插件系统,后来又用M ...

  4. (3)MEF插件系统中通信机制的设计和实现

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 一般的WinForm中通过C#自带的Event机制便能很好的实 ...

  5. MongoDB管理工具的插件系统

    MongoDB管理工具  MongoCola的开发已经进入第三个年头了. 官方对于C#驱动的投入不够导致了很多东西都必须自己实现,但是不管怎么样,工具现在已经很强大了. 最近准备着手插件系统的开发,简 ...

  6. .NET简谈插件系统开发模式

    摘选自[王清培]博客 http://www.cnblogs.com/wangiqngpei557/archive/2011/06/10/2077413.html 今天跟大家分享一下我们在日常开发中并不 ...

  7. ctkPlugin插件系统实现项目插件式开发

    插件式开发体会: 自开始写[大话QT]系列就开始接触渲染客户端的开发,说是开发不如更多的说是维护以及重构,在接手这块的东西之前自己还有点犹豫,因为之前我一直认为客户端嘛,没什么技术含量,总是想做比较有 ...

  8. Windows平台下C++插件系统实现的几个关键技术问题及其解决思路

    根据我的实践,在Windows平台下设计并实现一个C++插件系统,需要解决几个关键技术问题.下面我谈谈需要解决的几个关键技术问题以及我想到的简单的解决思路.由于我主要专注于Windows平台C++程序 ...

  9. 【夸QT在十五】ctkPluginFrameWork插件系统Windows编译器

    采用ctkPluginFramework作为一个插件系统开发框架确实有很多优点. 有些车站最近收到的一封信,每个人都想用ctkPluginFramework但我不知道如何建立,本教程对谈ctkPlug ...

随机推荐

  1. C++开源代码覆盖率工具OpenCppCoverage介绍(Windows)

    关于代码覆盖率统计工具,Linux平台下,gcc内置支持gcov,通过编译时加参数选项,进行代码插桩,从而实现代码覆盖率.在Windows平台下,早在几年前,我还没找到特别好用又开源的覆盖率工具,所以 ...

  2. 相对路径获取项目文件 及报错 No mapping found for HTTP request with URI XXX in DispatcherServlet with name ‘springmvc’解决方法

    首先一点,WebRoot目录下的文件是都可以通过浏览器输入路径,直接读取到的 例如这样: 而WebRoot下面WEB-INF是无法浏览器输入路径直接读取的. 因为是受保护的. 如果jsp读取一个图片的 ...

  3. 工作圈redis 使用

    redis作为内存数据库,更多的是作为内存cache来使用. 再所负责的工作圈中的使用,主要是分两方面: 1.数据对象: 主题的内容存储 主题回复内容的存储 用户信息存储 圈子信息存储 2.各数据对象 ...

  4. 常用js方法整理common.js

    项目中常用js方法整理成了common.js var h = {}; h.get = function (url, data, ok, error) { $.ajax({ url: url, data ...

  5. paip. 调试技术打印堆栈 uapi print stack java php python 总结.

    paip. 调试技术打印堆栈 uapi print stack java php python 总结. 作者Attilax  艾龙,  EMAIL:1466519819@qq.com 来源:attil ...

  6. iOS-UIViewController视图控制器跳转界面的几种常用方法

    一.最普通的视图控制器UIViewContoller 一个普通的视图控制器一般只有模态跳转的功能(ipad我不了解除外,这里只说iPhone),这个方法是所有视图控制器对象都可以用的,而实现这种功能, ...

  7. 使用tornado的gen模块改善程序性能

    之前在公司的一个模块,需要从另一处url取得数据,我使用了Python的一个很著名的lib,叫做requests.但是这样做极大的降低了程序的性能,因为tornado是单线程的,它使用了所谓的reac ...

  8. python基于LeanCloud的短信验证

    python基于LeanCloud的短信验证 1. 获取LeanCloud的Id.Key 2. 安装Flask框架和Requests库 pip install flask pip install re ...

  9. MySQL完整性语言

    文章为作者原创,未经许可,禁止转载.    -Sun Yat-sen University 冯兴伟 实验3:完整性语言 完整性语言实验包含3个实验项目,其中2个必修项目,1个选修项目.该实验的各个实验 ...

  10. ListView、PullToRefreshListView滑动加载可见item

    最近用的了PullToRefreshListView框架,也在listView中加载图片,对于滑动加载可见item,网上找了一些相关文档,但都不太合适,如:http://blog.csdn.net/z ...