[9]elasticsearch源碼深刻分析——Plugin組件加載

本篇爲elasticsearch源碼分析系列文章的第九篇,又到了咱們深扒ElasticSearch源碼的時候了:)java

本篇開始將會詳細解釋Node實例化的過程當中PluginsService的相關內容,PluginService算是Node實例化的重要內容,瞭解PluginService的加載過程有助於咱們理解Node實例化和ElasticSearch啓動時工做流程,此外PluginsService還涉及到ElasticSearch中線程池的使用,關於ElasticSearch中線程池的封裝使用,咱們會在下一篇敘述。服務器

插件的安裝

ElasticSearch中的插件是一個容許插入自定義功能的擴展。插件大體分爲三類:app

  • java插件:這些插件只包含Jar文件,而且必須安裝在集羣中的每一個節點上。安裝後,每一個節點必須從新啓動,該插件才變得可見。好比分詞器插件。
  • 站點插件:這些插件包含了靜態的Web內容,如JavaScript,HTML和CSS文件,可直接從Elasticsearch訪問。站點插件可能只須要在一個節點上安裝,而且不須要從新啓動就能變得可見。站點插件的內容是經過一個相似的網址訪問:HTTP://IP:9200/_plugin/[插件名稱]
  • 混合插件:混合插件同時包含jar文件和靜態的Web內容

在ElasticSearch的bin目錄下,能夠執行命令elasticsearch

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的包中提供了若干種插件的擴展類,徹底覆蓋了全部插件的擴展需求。除了能實現如下接口:ui

  • 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
複製代碼

關於這些插件的詳細解析,咱們會在之後的文章中講解。

PluginsService類的構造函數解析

通常查看組件源碼都是先從構造函數開始,PluginsService的構造函數爲PluginsService(Settings settings, Path configPath, Path modulesDirectory, Path pluginsDirectory, Collection<Class<? extends Plugin>> classpathPlugins)

有以下五個參數,大都是路徑設定類的:

  • settings:ElasticSearch啓動時的系統設定
  • Path:ElasticSearch的config配置文件的目錄,類型爲Path
  • modulesDirectory:ElasticSearch的modules目錄,類型爲Path
  • pluginsDirectory:ElasticSearch的plugins目錄,類型爲Path
  • classpathPlugins:在classpath路徑中的插件,是Plugin類的子類集合,有這種狀況的出現主要是爲了在測試和使用transport clients的狀況下加載插件。
加載classpathPlugins中的插件

PluginsService先構造了一個List,List的元素爲PluginsInfo類型和Plugin類型的元組(Tuple)。

接着再遍歷classpathPlugins的值中的Plugin實現類對象,經過反射Plugin的實現類,調用方法getConstructors()獲得Plugin的子類的構造函數,獲得構造函數後,對構造函數進行一系列的檢查:

  • 自定義實現的Plugin子類必須有public的構造函數
  • 有且只能有一個構造函數
  • 接受參數不能大於兩個,且第一個爲Settings類型,第二個爲Path類型。看過我之前文章的同窗對Setting確定不會陌生。

由此能夠知道,想本身實現ElasticSearch的插件,就必須繼承Plugin類,定義一個構造函數,根據要實現插件的類型實現不一樣的接口,好比SearchPlugin,ScriptPlugin,RepositoryPlugin

構造Plugins之modulesDirectory

接下來遍歷modules路徑中各個module的plugin-descriptor.properties文件,取出文件中的以下屬性:

  • name
  • description
  • version
  • elasticsearch.version
  • java.version
  • has.native.controller:該插件是否須要本地控制器
  • requires.keystore:該插件是否須要ElasticSearch建立祕鑰庫

構造出各個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的構造函數作了哪些工做,

  • this.configPath = configPath:設置了configPath的路徑值
  • this.info = new PluginsAndModules(pluginsList, modulesList):加載了Plugins和Modules中的基本信息和jar路徑
  • his.plugins = Collections.unmodifiableList(pluginsLoaded):加載了bundle對象,bundle中包含了基礎信息和全部jar的URL

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實例化過程當中線程池的封裝過程,但願你們持續關注哦^ _ ^。

相關文章
相關標籤/搜索