ElasticSearch深度解析

[9]elasticsearch源码深入分析——Plugin组件

2018-01-30  本文已影响183人  飞来来

本篇为elasticsearch源码分析系列文章的第九篇,又到了我们深扒ElasticSearch源码的时候了:)

本篇开始将会详细解释Node实例化的过程中PluginsService的相关内容,PluginService算是Node实例化的重要内容,了解PluginService的加载过程有助于我们理解Node实例化和ElasticSearch启动时工作流程,此外PluginsService还涉及到ElasticSearch中线程池的使用,关于ElasticSearch中线程池的封装使用,我们会在下一篇叙述。

插件的安装

ElasticSearch中的插件是一个允许插入自定义功能的扩展。插件大致分为三类:

在ElasticSearch的bin目录下,可以执行命令

bin/plugin list:查看已经安装了的ElasticSearch插件。
bin/plugin install [plugin_name]:安装一个ElasticSearch插件。

插件的安装过程如图所示:

插件安装

在安装好之后,在ElasticSearch的plugins目录下就能看到插件包了:

安装好的icu插件

可以看出icu分词插件是个纯java插件。icu
是Elasticsearch的分析器插件,使用国际化组件Unicode(ICU)提供丰富的处理 Unicode编码的工具。该查件对处理亚洲语言特别有用,还有大量对除英语外其他语言进行正确匹配和排序所必须的分词过滤器。

插件何时进行加载

落地到编码层次的话,plugins文件夹中的插件在Node实例化的时候被加载,构建代码如下:

this.pluginsService = new PluginsService(tmpSettings, environment.configFile(), environment.modulesFile(), environment.pluginsFile(), classpathPlugins);

其中environment.pluginsFile()的路径的内容就是xxx\elasticsearch\elasticsearch-6.0.0-rc2\plugins\analysis-icu

ElasticSearch插件扩展机制

在ElasticSearch的org.elasticsearch.plugins的包中提供了若干种插件的扩展类,完全覆盖了所有插件的扩展需求。除了能实现以下接口:

进一步定制ElasticSearch外,除了这个类扩展点还声明一些@DeprecatedonModule方法。使用这些方法应该特别注意,使用5.x以前风格扩展语法不能成功构建。这也是成功的开源软件在考虑平滑升级时的设计思路时值得学习的地方。定义了插件的实现方法,这些插件从低版本升级到5.x之后的版本就不会太过艰难。

比如上面提到的icu分词插件,就实现了AnalysisPlugin, MapperPlugin,这两个接口。

public class AnalysisICUPlugin extends Plugin implements AnalysisPlugin, MapperPlugin

关于这些插件的详细解析,我们会在以后的文章中讲解。

PluginsService类的构造函数解析

一般查看组件源码都是先从构造函数开始,PluginsService的构造函数为PluginsService(Settings settings, Path configPath, Path modulesDirectory, Path pluginsDirectory, Collection<Class<? extends Plugin>> classpathPlugins)

有如下五个参数,大都是路径设定类的:

加载classpathPlugins中的插件

PluginsService先构造了一个List,List的元素为PluginsInfo类型和Plugin类型的元组(Tuple)。

接着再遍历classpathPlugins的值中的Plugin实现类对象,通过反射Plugin的实现类,调用方法getConstructors()得到Plugin的子类的构造函数,得到构造函数后,对构造函数进行一系列的检查:

由此可以知道,想自己实现ElasticSearch的插件,就必须继承Plugin类,定义一个构造函数,根据要实现插件的类型实现不同的接口,比如SearchPlugin,ScriptPlugin,RepositoryPlugin

构造Plugins之modulesDirectory

接下来遍历modules路径中各个module的plugin-descriptor.properties文件,取出文件中的如下属性:

构造出各个modulePluginInfo对象,然后遍历出各个module下面的jar包的路径,这样每个module的jar包和信息就都有了,我们可以看到ElasticSearch是封装了一个内部类Bundle,如下图:

内部封装类Bundle

至此找到了所有需要加载的module,下面是加载所有的plugins

构造Plugins之pluginsDirectory

首先调用checkForFailedPluginRemovals()方法,遍历pluginsDirectory路径中所有的包含.removing-字符的文件,抛出应该remove该插件的异常IllegalStateException。

检查完成后,遍历pluginsDirectory路径下的文件,遍历过程中,很细心的检查了当前查询路径是否是MacOS或者可能的桌面系统,而不是服务器。如果符合上述条件就跳过该次遍历,不符合的话依然按照加载module的方法那样,取得plugins文件夹下的各个plugin的plugin-descriptor.properties文件,依次加载name,description,version,elasticsearch.version,java.version,has.native.controller,requires.keystore属性,封装成PluginInfo,查找出jar包,最后封装成Bundle对象。

这样就得到了两个Bundle对象,一个modules,一个plugins。构造一个整体集合后,检查其中的jar包,避免jar-hell

至此PluginsService对象就已经构造完毕,通过赋值语句this.pluginsService = new PluginsService(tmpSettings, environment.configFile(), environment.modulesFile(), environment.pluginsFile(), classpathPlugins),Node就对象获得了PluginsService的对象。

PluginsService构造函数总结

我们整理一下PluginsService的构造函数做了哪些工作,

PluginsService类的作用

在通篇过完了PluginsService类的构造参数后,我们继续来看PluginsService对象在Node中起到的作用。

在Node对象构建完了PluginsService对象后,紧接着在Node中,通过PluginsService对象的updatedSettings()方法,将PluginsService类在构造时从modulesplugins路径中加载的plugin对象取出遍历,依次调用各个plugin的additionalSettings()方法。然后put更新Settings对象,达到了更新Settings对象的目的。

additionalSettings()这个方法在不同的plugin实现类中有不同实现,具体作用是构建插件运行过程中需要的Settings对象。如下图是Netty4plugin的实现:

Netty4Plugin的实现

由此可见,PluginsService组件中保存的Plugin元组是ElasticSearch发挥功能的重要内容,其中加载的Modules路径下的Plugin组件一起构成了ElasticSearch的核心主干功能。

ElasticSearch中线程池的加载参数直接来源于PluginsService,下一篇文章我们会讲解在Node实例化过程中线程池的封装过程,希望大家持续关注哦^ _ ^。

上一篇下一篇

猜你喜欢

热点阅读