1.概述
转载并且补充:[9]elasticsearch源码深入分析——Plugin组件加载
2.开篇
本篇开始将会详细解释Node
实例化的过程中PluginsService
的相关内容,PluginService
算是Node
实例化的重要内容,了解PluginService
的加载过程有助于我们理解Node
实例化和ElasticSearch
启动时工作流程,此外PluginsService
还涉及到ElasticSearch
中线程池的使用,关于ElasticSearch
中线程池的封装使用,我们会在下一篇叙述。
在PluginsService
的构造方法中会加载plugins和modules
目录下的jar
包,并创建相应的plugin和module
实例。创建完以后,Node的构造方法中会调用pluginsService的updatedSettings
方法来获取plugin和module中定义的配置项。接下来Node或使用新的settings和nodeId
来创建LocalNodeFactory
,并使用最新的settings重新创建Environment对象。
3.插件的安装
ElasticSearch
中的插件是一个允许插入自定义功能的扩展。插件大致分为三类:
java插件
:这些插件只包含Jar
文件,并且必须安装在集群中的每个节点上
。安装后,每个节点必须重新启动,该插件才变得可见
。比如分词器插件
。站点插件
:这些插件包含了静态的Web内容,如JavaScript,HTML和CSS文件
,可直接从Elasticsearch访问。站点插件可能只需要在一个节点上安装,并且不需要重新启动就能变得可见。站点插件的内容是通过一个类似的网址访问:HTTP://IP:9200/_plugin/[插件名称]
混合插件
:混合插件同时包含jar
文件和静态的Web
内容
在ElasticSearch
的bin
目录下,可以执行命令
bin/plugin list:查看已经安装了的ElasticSearch插件。
bin/plugin install [plugin_name]:安装一个ElasticSearch插件。
插件的安装过程如图所示:
在安装好之后,在ElasticSearch的plugins目录下就能看到插件包了:
可以看出icu分词插件是个纯java插件。icu
是Elasticsearch的分析器插件,使用国际化组件Unicode(ICU)
提供丰富的处理 Unicode
编码的工具。该查件对处理亚洲语言特别有用,还有大量对除英语外其他语言进行正确匹配和排序所必须的分词过滤器。
4.插件何时进行加载
落地到编码层次的话,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。
5.ElasticSearch插件扩展机制
在ElasticSearch
的org.elasticsearch.plugins
的包中提供了若干种插件的扩展类,完全覆盖了所有插件的扩展需求。除了能实现以下接口:
ActionPlugin
AnalysisPlugin
ClusterPlugin
DiscoveryPlugin
IngestPlugin
MapperPlugin
NetworkPlugin
RepositoryPlugin
ScriptPlugin
SearchPlugin
进一步定制ElasticSearch
外,除了这个类扩展点还声明一些@DeprecatedonModule
方法。使用这些方法应该特别注意,使用5.x
以前风格扩展语法不能成功构建。这也是成功的开源软件在考虑平滑升级时的设计思路时值得学习的地方。定义了插件的实现方法,这些插件从低版本升级到5.x
之后的版本就不会太过艰难。
比如上面提到的icu分词插件,就实现了AnalysisPlugin, MapperPlugin,这两个接口。
public class AnalysisICUPlugin extends Plugin implements AnalysisPlugin, MapperPlugin
关于这些插件的详细解析,我们会在以后的文章中讲解。
6.PluginsService类的构造函数解析
一般查看组件源码都是先从构造函数开始,PluginsService的构造函数为
PluginsService(Settings settings, Path configPath, Path modulesDirectory, Path pluginsDirectory, Collection<Class<? extends Plugin>> classpathPlugins)。
有如下五个参数,大都是路径设定类的:
settings:ElasticSearch
启动时的系统设定Path:ElasticSearch
的config
配置文件的目录,类型为PathmodulesDirectory:ElasticSearch
的modules目录,类型为PathpluginsDirectory:ElasticSearch
的plugins目录,类型为PathclasspathPlugins:
在classpath路径中的插件,是Plugin类的子类集合,有这种情况的出现主要是为了在测试和使用transport clients
的情况下加载插件。
6.1 加载classpathPlugins中的插件
PluginsService
先构造了一个List,List
的元素为PluginsInfo
类型和Plugin
类型的元组(Tuple)
。
接着再遍历classpathPlugins
的值中的Plugin
实现类对象,通过反射Plugin
的实现类,调用方法getConstructors()
得到Plugin
的子类的构造函数,得到构造函数后,对构造函数进行一系列的检查:
- 自定义实现的
Plugin
子类必须有public
的构造函数 - 有且只能有一个构造函数
- 接受参数
不能大于两个
,且第一个为Settings
类型,第二个为Path
类型。看过我以前文章的同学对Setting
肯定不会陌生。
由此可以知道,想自己实现ElasticSearch的插件,就必须继承Plugin
类,定义一个构造函数,根据要实现插件的类型实现不同的接口,比如SearchPlugin,ScriptPlugin,RepositoryPlugin
。
6.2 构造Plugins之modulesDirectory
接下来遍历modules
路径中各个module
的plugin-descriptor.properties
文件,取出文件中的如下属性:
name
description
version
elasticsearch.version
java.version
has.native.controller:该插件是否需要本地控制器
requires.keystore:该插件是否需要ElasticSearch创建秘钥库
构造出各个module
的PluginInfo
对象,然后遍历出各个module
下面的jar
包的路径,这样每个module
的jar
包和信息就都有了,我们可以看到ElasticSearch
是封装了一个内部类Bundle
,如下图:
至此找到了所有需要加载的module
,下面是加载所有的plugins
6.3 构造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的对象。
6.4 PluginsService构造函数总结
我们整理一下PluginsService的构造函数做了哪些工作,
this.configPath = configPath:
设置了configPath的路径值this.info = new PluginsAndModules(pluginsList, modulesList):
加载了Plugins和Modules中的基本信息和jar路径- t
his.plugins = Collections.unmodifiableList(pluginsLoaded):
加载了bundle对象,bundle中包含了基础信息和所有jar的URL
6.5 PluginsService类的作用
在通篇过完了PluginsService
类的构造参数后,我们继续来看PluginsService
对象在Node
中起到的作用。
在Node
对象构建完了PluginsService
对象后,紧接着在Node
中,通过PluginsService
对象的updatedSettings
()方法,将PluginsService
类在构造时从modules
和plugins
路径中加载的plugin
对象取出遍历,依次调用各个plugin
的additionalSettings
()方法。然后put
更新Settings
对象,达到了更新Settings
对象的目的。
additionalSettings
()这个方法在不同的plugin
实现类中有不同实现,具体作用是构建插件运行过程中需要的Settings
对象。如下图是Netty4plugin
的实现:
Netty4Plugin的实现
由此可见,PluginsService
组件中保存的Plugin
元组是ElasticSearch
发挥功能的重要内容,其中加载的Modules路径下的Plugin组件一起构成了ElasticSearch的核心主干功能。
ElasticSearch中线程池的加载参数直接来源于PluginsService,下一篇文章我们会讲解在Node实例化过程中线程池的封装过程,希望大家持续关注哦^ _ ^。