[9]elasticsearch原始碼深入分析——Plugin元件載入

飛來來發表於2018-01-30

本篇為elasticsearch原始碼分析系列文章的第九篇,又到了我們深扒ElasticSearch原始碼的時候了:)

本篇開始將會詳細解釋Node例項化的過程中PluginsService的相關內容,PluginService算是Node例項化的重要內容,瞭解PluginService的載入過程有助於我們理解Node例項化和ElasticSearch啟動時工作流程,此外PluginsService還涉及到ElasticSearch中執行緒池的使用,關於ElasticSearch中執行緒池的封裝使用,我們會在下一篇敘述。

外掛的安裝

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外掛

可以看出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的包中提供了若干種外掛的擴充套件類,完全覆蓋了所有外掛的擴充套件需求。除了能實現以下介面:

  • 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例項化過程中執行緒池的封裝過程,希望大家持續關注哦^ _ ^。

相關文章