Elasticsearch學習總結五 elasticSearch插件機制源碼解析

一.首先看看Elasticsearch中插件是如何安裝的c++

咱們安裝好es後,若是要安裝插件須要在 /usr/local/elasticsearch/bin的目錄下使用plugin這個shell腳本,仔細看了下這個shell腳本,發現裏面主要是運行了org.elasticsearch.plugins.PluginManager這個類 exec "$JAVA" $JAVA_OPTS $ES_JAVA_OPTS -Xmx64m -Xms16m -Delasticsearch -Des.path.home="$ES_HOME" $properties -cp "$ES_HOME/lib/*" org.elasticsearch.plugins.PluginManager $argsshell

下面咱們進入PluginManager這個類中看看是如何下載安裝插件的,如何執行這個類,確定應該有個main方法吧,果然如此,下面是pluginManager的main方法less

public static void main(String[] args) {
       ..........
        String url = null;
        OutputMode outputMode = OutputMode.DEFAULT;
        String pluginName = null;
        TimeValue timeout = DEFAULT_TIMEOUT;
        int action = ACTION.NONE;
        if (args.length < 1) {
            displayHelp(null);
        }
        try {
            for (int c = 0; c < args.length; c++) {
                String command = args[c];
                switch (command) {
                    case "-u":
                    case "--url":
                    // deprecated versions:
                    case "url":
                    case "-url":
                        url = getCommandValue(args, ++c, "--url");
                        // Until update is supported, then supplying a URL implies installing
                        // By specifying this action, we also avoid silently failing without
                        //  dubious checks.
                        action = ACTION.INSTALL;
                        break;
                    case "-v":
                    case "--verbose":
                    // deprecated versions:
                    case "verbose":
                    case "-verbose":
                        outputMode = OutputMode.VERBOSE;
                        break;
                    case "-s":
                    case "--silent":
                    // deprecated versions:
                    case "silent":
                    case "-silent":
                        outputMode = OutputMode.SILENT;
                        break;
                    case "-i":
                    case "--install":
                    // deprecated versions:
                    case "install":
                    case "-install":
                        pluginName = getCommandValue(args, ++c, "--install");
                        action = ACTION.INSTALL;
                        break;
                    case "-r":
                    case "--remove":
                    // deprecated versions:
                    case "remove":
                    case "-remove":
                        pluginName = getCommandValue(args, ++c, "--remove");
                        action = ACTION.REMOVE;
                        break;
                    case "-t":
                    case "--timeout":
                    // deprecated versions:
                    case "timeout":
                    case "-timeout":
                        String timeoutValue = getCommandValue(args, ++c, "--timeout");
                        timeout = TimeValue.parseTimeValue(timeoutValue, DEFAULT_TIMEOUT);
                        break;
                    case "-l":
                    case "--list":
                        action = ACTION.LIST;
                        break;
                    case "-h":
                    case "--help":
                        displayHelp(null);
                        break;
                    default:
                        displayHelp("Command [" + command + "] unknown.");
                        // Unknown command. We break...
                        System.exit(EXIT_CODE_CMD_USAGE);
                }
            }
        } catch (Throwable e) {
            displayHelp("Error while parsing options: " + e.getClass().getSimpleName() +
                    ": " + e.getMessage());
            System.exit(EXIT_CODE_CMD_USAGE);
        }

上面代碼中,就是當你輸入各類命令的時候,pluginManager都會根據響應的命令觸發不一樣的動做,例如輸入./plugin list 就會顯示當前es安裝了那些插件elasticsearch

輸入圖片說明

當輸入 plugin -install mobz/elasticsearch-head 命令的時候,pluginManager就會去獲取當前插件名稱, pluginName = getCommandValue(args, ++c, "--install"); 而且將 action賦值 action = ACTION.INSTALL; 當完成賦值後會繼續執行以下代碼,這部分代碼主要構造PluginManger對象,而且根據action的動做去執行相應的操做,pluginManager.downloadAndExtract(pluginName);會根據插件名稱去下載相應的插件模塊化

................
 if (action > ACTION.NONE) {
            int exitCode = EXIT_CODE_ERROR; // we fail unless it's reset
            //構造pluginManger
            PluginManager pluginManager = new PluginManager(initialSettings.v2(), url, outputMode, timeout);
            switch (action) {
                case ACTION.INSTALL:
                    try {
                        pluginManager.log("-> Installing " + Strings.nullToEmpty(pluginName) + "...");
                        pluginManager.downloadAndExtract(pluginName);//執行下載解壓任務
                        exitCode = EXIT_CODE_OK;
                    } catch (IOException e) {
                        exitCode = EXIT_CODE_IO_ERROR;
                        pluginManager.log("Failed to install " + pluginName + ", reason: " + e.getMessage());
                    } catch (Throwable e) {
                        exitCode = EXIT_CODE_ERROR;
                        displayHelp("Error while installing plugin, reason: " + e.getClass().getSimpleName() +
                                ": " + e.getMessage());
                    }
                    break;
.........................

PluginManager的構造函數主要是傳入下載地址,延時時長等,下面看下downloadAndExtract下載安裝是如何工做的函數

public void downloadAndExtract(String name) throws IOException {
        .....
        HttpDownloadHelper downloadHelper = new HttpDownloadHelper();
        boolean downloaded = false;
        HttpDownloadHelper.DownloadProgress progress;
        if (outputMode == OutputMode.SILENT) {
            progress = new HttpDownloadHelper.NullProgress();
        } else {
            progress = new HttpDownloadHelper.VerboseProgress(System.out);
        }
         .........
        if (url != null) {
            URL pluginUrl = new URL(url);
            log("Trying " + pluginUrl.toExternalForm() + "...");
            try {
                downloaded = true;
                downloadHelper.download(pluginUrl, pluginFile, progress, this.timeout);
            } catch (ElasticsearchTimeoutException e) {
                throw e;
            } catch (Exception e) {
                // ignore
                log("Failed: " + ExceptionsHelper.detailedMessage(e));
            }
        }

主要是經過構建HttpDownloadHelper 實現http下載,而且實現了一個下載進度器,這就是咱們安裝插件的時候會有一些輸出的緣由,經過downloadHelper.download(pluginUrl, pluginFile, progress, this.timeout);能夠真正的實現下載,這裏就不在贅述了,當下載完畢後,會去執行解壓和安裝的過程,在解壓文件時候越過父文件夾,將文件解壓到指定目錄,好比head文件夾中,至此下載安裝基本完畢ui

ZipFile zipFile = null;
        try {
            zipFile = new ZipFile(pluginFile);
            boolean removeTopLevelDir = topLevelDirInExcess(zipFile);
            Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
            while (zipEntries.hasMoreElements()) {
                ZipEntry zipEntry = zipEntries.nextElement();
                if (zipEntry.isDirectory()) {
                    continue;
                }
                String zipEntryName = zipEntry.getName().replace('\\', '/');
                if (removeTopLevelDir) {
                    zipEntryName = zipEntryName.substring(zipEntryName.indexOf('/'));
                }
                File target = new File(extractLocation, zipEntryName);
                FileSystemUtils.mkdirs(target.getParentFile());
                Streams.copy(zipFile.getInputStream(zipEntry), new FileOutputStream(target));
            }
            log("Installed " + name + " into " + extractLocation.getAbsolutePath());
        } catch (Exception e) {
            log("failed to extract plugin [" + pluginFile + "]: " + ExceptionsHelper.detailedMessage(e));
            return;
        } finally {
            if (zipFile != null) {
                try {
                    zipFile.close();
                } catch (IOException e) {
                    // ignore
                }
            }
            pluginFile.delete();
        }

二. elasticSearch是如何加載插件的this

elasticsearch裏面的組件基本都是用上面的方式進行模塊化管理,elasticsearch對guice進行了簡單的封裝,經過ModulesBuilder類構建es的模塊,一個es節點確定夠包括PluginsModule插件模塊,下面咱們看下插件模塊是如何工做,加載咱們的插件的url

public class PluginsModule extends AbstractModule implements SpawnModules, PreProcessModule {

    private final Settings settings;

    private final PluginsService pluginsService;

    public PluginsModule(Settings settings, PluginsService pluginsService) {
        this.settings = settings;
        this.pluginsService = pluginsService;
    }

    //預處理組件 
    public Iterable<? extends Module> spawnModules() {
        List<Module> modules = Lists.newArrayList();
        Collection<Class<? extends Module>> modulesClasses = pluginsService.modules();
        for (Class<? extends Module> moduleClass : modulesClasses) {
            modules.add(createModule(moduleClass, settings));
        }
        modules.addAll(pluginsService.modules(settings));
        return modules;
    }
..............

es中全部的組件都是繼承自AbstractModule 插件調用層次爲Modules->PluginsModule->PluginsService->XXPlugin; 都是調用void processModule(Module module)方法。 區別在於 1:PluginsModule實現的是PreProcessModule接口。 2:PluginsService是本身原本的方法。 3:XXPlugin實現的是Plugin接口。 雖然他們都有processModule方法,但不是實現的同一個類。最重要是的仍是pluginService類,它真正負責加載的插件類spa

public PluginsService(Settings settings, Environment environment) {
        super(settings);
        this.environment = environment;
        this.checkLucene = componentSettings.getAsBoolean("check_lucene", true);
       .................................忽律部分代碼
        loadPluginsIntoClassLoader();
        if (loadClasspathPlugins) {
            tupleBuilder.addAll(loadPluginsFromClasspath(settings));
        }
        this.plugins = tupleBuilder.build();

      // we load site plugins
        ImmutableList<Tuple<PluginInfo, Plugin>> tuples = loadSitePlugins();
        for (Tuple<PluginInfo, Plugin> tuple : tuples) {
            sitePlugins.add(tuple.v1().getName());
        }
      .................................忽律部分代碼

其中loadPluginsIntoClassLoader();負責classloader加載class,加載不在當前classpath中的class,使用當前setting中的classloader,這個構造函數負責加載plugin下面的全部的插件,而且使用當前的類加載器加載。

至此,插件的下載和加載所有完成。

相關文章
相關標籤/搜索