成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

渣渣的 ElasticSearch 源碼解析 —— 啟動流程(上)

AZmake / 3151人閱讀

摘要:總結(jié)這篇文章主要先把大概啟動流程串通,因?yàn)槠^多所以拆開成兩篇,先不扣細(xì)節(jié)了,后面流程啟動文章寫完后我們再單一的扣細(xì)節(jié)。

關(guān)注我

轉(zhuǎn)載請務(wù)必注明原創(chuàng)地址為:http://www.54tianzhisheng.cn/2018/08/11/es-code02/

前提

上篇文章寫了 ElasticSearch 源碼解析 —— 環(huán)境搭建 ,其中里面說了啟動 打開 server 模塊下的 Elasticsearch 類:org.elasticsearch.bootstrap.Elasticsearch,運(yùn)行里面的 main 函數(shù)就可以啟動 ElasticSearch 了,這篇文章講講啟動流程,因?yàn)槠鶗芏?,所以分了兩篇來寫?/p> 啟動流程 main 方法入口

可以看到入口其實(shí)是一個 main 方法,方法里面先是檢查權(quán)限,然后是一個錯誤日志監(jiān)聽器(確保在日志配置之前狀態(tài)日志沒有出現(xiàn) error),然后是 Elasticsearch 對象的創(chuàng)建,然后調(diào)用了靜態(tài)方法 main 方法(18 行),并把創(chuàng)建的對象和參數(shù)以及 Terminal 默認(rèn)值傳進(jìn)去。靜態(tài)的 main 方法里面調(diào)用 elasticsearch.main 方法。

public static void main(final String[] args) throws Exception {         //1、入口
    // we want the JVM to think there is a security manager installed so that if internal policy decisions that would be based on the
    // presence of a security manager or lack thereof act as if there is a security manager present (e.g., DNS cache policy)
    System.setSecurityManager(new SecurityManager() {
        @Override
        public void checkPermission(Permission perm) {
            // grant all permissions so that we can later set the security manager to the one that we want
        }
    });
    LogConfigurator.registerErrorListener();                            //
    final Elasticsearch elasticsearch = new Elasticsearch();
    int status = main(args, elasticsearch, Terminal.DEFAULT); //2、調(diào)用Elasticsearch.main方法
    if (status != ExitCodes.OK) {
        exit(status);
    }
}

static int main(final String[] args, final Elasticsearch elasticsearch, final Terminal terminal) throws Exception {
    return elasticsearch.main(args, terminal);  //3、command main
}

因?yàn)?Elasticsearch 類是繼承了 EnvironmentAwareCommand 類,EnvironmentAwareCommand 類繼承了 Command 類,但是 Elasticsearch 類并沒有重寫 main 方法,所以上面調(diào)用的 elasticsearch.main 其實(shí)是調(diào)用了 Command 的 main 方法,代碼如下:

/** Parses options for this command from args and executes it. */
public final int main(String[] args, Terminal terminal) throws Exception {
    if (addShutdownHook()) {                                                //利用Runtime.getRuntime().addShutdownHook方法加入一個Hook,在程序退出時觸發(fā)該Hook
        shutdownHookThread = new Thread(() -> {
            try {
                this.close();
            } catch (final IOException e) {
                try (
                    StringWriter sw = new StringWriter();
                    PrintWriter pw = new PrintWriter(sw)) {
                    e.printStackTrace(pw);
                    terminal.println(sw.toString());
                } catch (final IOException impossible) {
                    // StringWriter#close declares a checked IOException from the Closeable interface but the Javadocs for StringWriter
                    // say that an exception here is impossible
                    throw new AssertionError(impossible);
                }
            }
        });
        Runtime.getRuntime().addShutdownHook(shutdownHookThread);
    }

    beforeMain.run();

    try {
        mainWithoutErrorHandling(args, terminal);//4、mainWithoutErrorHandling
    } catch (OptionException e) {
        printHelp(terminal);
        terminal.println(Terminal.Verbosity.SILENT, "ERROR: " + e.getMessage());
        return ExitCodes.USAGE;
    } catch (UserException e) {
        if (e.exitCode == ExitCodes.USAGE) {
            printHelp(terminal);
        }
        terminal.println(Terminal.Verbosity.SILENT, "ERROR: " + e.getMessage());
        return e.exitCode;
    }
    return ExitCodes.OK;
}

上面代碼一開始利用一個勾子函數(shù),在程序退出時觸發(fā)該 Hook,該方法主要代碼是 mainWithoutErrorHandling() 方法,然后下面的是 catch 住方法拋出的異常,方法代碼如下:

/*** Executes the command, but all errors are thrown. */
void mainWithoutErrorHandling(String[] args, Terminal terminal) throws Exception {
    final OptionSet options = parser.parse(args);
    if (options.has(helpOption)) {
        printHelp(terminal);
        return;
    }
    if (options.has(silentOption)) {
        terminal.setVerbosity(Terminal.Verbosity.SILENT);
    } else if (options.has(verboseOption)) {
        terminal.setVerbosity(Terminal.Verbosity.VERBOSE);
    } else {
        terminal.setVerbosity(Terminal.Verbosity.NORMAL);
    }
    execute(terminal, options);//5、執(zhí)行 EnvironmentAwareCommand 中的 execute(),(重寫了command里面抽象的execute方法)
}

上面的代碼從 3 ~ 14 行是解析傳進(jìn)來的參數(shù)并配置 terminal,重要的 execute() 方法,執(zhí)行的是 EnvironmentAwareCommand 中的 execute() (重寫了 Command 類里面的抽象 execute 方法),從上面那個繼承圖可以看到 EnvironmentAwareCommand 繼承了 Command,重寫的 execute 方法代碼如下:

@Override
protected void execute(Terminal terminal, OptionSet options) throws Exception {
    final Map settings = new HashMap<>();
    for (final KeyValuePair kvp : settingOption.values(options)) {
        if (kvp.value.isEmpty()) {
            throw new UserException(ExitCodes.USAGE, "setting [" + kvp.key + "] must not be empty");
        }
        if (settings.containsKey(kvp.key)) {
            final String message = String.format(
                Locale.ROOT, "setting [%s] already set, saw [%s] and [%s]",
                kvp.key, settings.get(kvp.key), kvp.value);
            throw new UserException(ExitCodes.USAGE, message);
        }
        settings.put(kvp.key, kvp.value);
    }
    //6、根據(jù)我們ide配置的 vm options 進(jìn)行設(shè)置path.data、path.home、path.logs
    putSystemPropertyIfSettingIsMissing(settings, "path.data", "es.path.data");
    putSystemPropertyIfSettingIsMissing(settings, "path.home", "es.path.home");
    putSystemPropertyIfSettingIsMissing(settings, "path.logs", "es.path.logs");

    execute(terminal, options, createEnv(terminal, settings));//7、先調(diào)用 createEnv 創(chuàng)建環(huán)境
    //9、執(zhí)行elasticsearch的execute方法,elasticsearch中重寫了EnvironmentAwareCommand中的抽象execute方法
}

方法前面是根據(jù)傳參去判斷配置的,如果配置為空,就會直接跳到執(zhí)行 putSystemPropertyIfSettingIsMissing 方法,這里會配置三個屬性:path.data、path.home、path.logs 設(shè)置 es 的 data、home、logs 目錄,它這里是根據(jù)我們 ide 配置的 vm options 進(jìn)行設(shè)置的,這也是為什么我們上篇文章說的配置信息,如果不配置的話就會直接報錯。下面看看 putSystemPropertyIfSettingIsMissing 方法代碼里面怎么做到的:

/** Ensure the given setting exists, reading it from system properties if not already set. */
private static void putSystemPropertyIfSettingIsMissing(final Map settings, final String setting, final String key) {
    final String value = System.getProperty(key);//獲取key(es.path.data)找系統(tǒng)設(shè)置
    if (value != null) {
        if (settings.containsKey(setting)) {
            final String message =
                String.format(
                Locale.ROOT,
                "duplicate setting [%s] found via command-line [%s] and system property [%s]",
                setting, settings.get(setting), value);
            throw new IllegalArgumentException(message);
        } else {
            settings.put(setting, value);
        }
    }
}

執(zhí)行這三個方法后:

跳出此方法,繼續(xù)看會發(fā)現(xiàn) execute 方法調(diào)用了方法,

 execute(terminal, options, createEnv(terminal, settings));

這里我們先看看 createEnv(terminal, settings) 方法:

protected Environment createEnv(final Terminal terminal, final Map settings) throws UserException {
    final String esPathConf = System.getProperty("es.path.conf");//8、讀取我們 vm options 中配置的 es.path.conf
    if (esPathConf == null) {
        throw new UserException(ExitCodes.CONFIG, "the system property [es.path.conf] must be set");
    }
    return InternalSettingsPreparer.prepareEnvironment(Settings.EMPTY, terminal, settings, getConfigPath(esPathConf));  //8、準(zhǔn)備環(huán)境 prepareEnvironment
}

讀取我們 ide vm options 中配置的 es.path.conf,同上篇文章也講了這個一定要配置的,因?yàn)?es 啟動的時候會加載我們的配置和一些插件。這里繼續(xù)看下上面代碼第 6 行的 prepareEnvironment 方法:

public static Environment prepareEnvironment(Settings input, Terminal terminal, Map properties, Path configPath) {
    // just create enough settings to build the environment, to get the config dir
    Settings.Builder output = Settings.builder();
    initializeSettings(output, input, properties);
    Environment environment = new Environment(output.build(), configPath);

    //查看 es.path.conf 目錄下的配置文件是不是 yml 格式的,如果不是則拋出一個異常
    if (Files.exists(environment.configFile().resolve("elasticsearch.yaml"))) {
        throw new SettingsException("elasticsearch.yaml was deprecated in 5.5.0 and must be renamed to elasticsearch.yml");
    }

    if (Files.exists(environment.configFile().resolve("elasticsearch.json"))) {
        throw new SettingsException("elasticsearch.json was deprecated in 5.5.0 and must be converted to elasticsearch.yml");
    }

    output = Settings.builder(); // start with a fresh output
    Path path = environment.configFile().resolve("elasticsearch.yml");
    if (Files.exists(path)) {
        try {
            output.loadFromPath(path);  //加載文件并讀取配置文件內(nèi)容
        } catch (IOException e) {
            throw new SettingsException("Failed to load settings from " + path.toString(), e);
        }
    }

    // re-initialize settings now that the config file has been loaded
    initializeSettings(output, input, properties);          //再一次初始化設(shè)置
    finalizeSettings(output, terminal);

    environment = new Environment(output.build(), configPath);

    // we put back the path.logs so we can use it in the logging configuration file
    output.put(Environment.PATH_LOGS_SETTING.getKey(), environment.logsFile().toAbsolutePath().normalize().toString());
    return new Environment(output.build(), configPath);
}

準(zhǔn)備的環(huán)境如上圖,通過構(gòu)建的環(huán)境查看配置文件 elasticsearch.yml 是不是以 yml 結(jié)尾,如果是 yaml 或者 json 結(jié)尾的則拋出異常(在 5.5.0 版本其他兩種格式過期了,只能使用 yml 格式),然后加載該配置文件并讀取里面的內(nèi)容(KV結(jié)構(gòu))。

跳出 createEnv 方法,我們繼續(xù)看 execute 方法吧。

EnvironmentAwareCommand 類的 execute 方法代碼如下:

protected abstract void execute(Terminal terminal, OptionSet options, Environment env) throws Exception;

這是個抽象方法,那么它的實(shí)現(xiàn)方法在 Elasticsearch 類中,代碼如下:

@Override
protected void execute(Terminal terminal, OptionSet options, Environment env) throws UserException {
    if (options.nonOptionArguments().isEmpty() == false) {
        throw new UserException(ExitCodes.USAGE, "Positional arguments not allowed, found " + options.nonOptionArguments());
    }
    if (options.has(versionOption)) {
        final String versionOutput = String.format(
            Locale.ROOT,
            "Version: %s, Build: %s/%s/%s/%s, JVM: %s",
            Version.displayVersion(Version.CURRENT, Build.CURRENT.isSnapshot()),
            Build.CURRENT.flavor().displayName(),
            Build.CURRENT.type().displayName(),
            Build.CURRENT.shortHash(),
            Build.CURRENT.date(),
            JvmInfo.jvmInfo().version());
        terminal.println(versionOutput);
        return;
    }

    final boolean daemonize = options.has(daemonizeOption);
    final Path pidFile = pidfileOption.value(options);
    final boolean quiet = options.has(quietOption);

    // a misconfigured java.io.tmpdir can cause hard-to-diagnose problems later, so reject it immediately
    try {
        env.validateTmpFile();
    } catch (IOException e) {
        throw new UserException(ExitCodes.CONFIG, e.getMessage());
    }
    try {
        init(daemonize, pidFile, quiet, env);    //10、初始化
    } catch (NodeValidationException e) {
        throw new UserException(ExitCodes.CONFIG, e.getMessage());
    }
}

上面代碼里主要還是看看 init(daemonize, pidFile, quiet, env); 初始化方法吧。

void init(final boolean daemonize, final Path pidFile, final boolean quiet, Environment initialEnv)
    throws NodeValidationException, UserException {
    try {
        Bootstrap.init(!daemonize, pidFile, quiet, initialEnv); //11、執(zhí)行 Bootstrap 中的 init 方法
    } catch (BootstrapException | RuntimeException e) {
        // format exceptions to the console in a special way
        // to avoid 2MB stacktraces from guice, etc.
        throw new StartupException(e);
    }
}
init 方法

Bootstrap 中的靜態(tài) init 方法如下:

static void init(
    final boolean foreground,
    final Path pidFile,
    final boolean quiet,
    final Environment initialEnv) throws BootstrapException, NodeValidationException, UserException {
    // force the class initializer for BootstrapInfo to run before
    // the security manager is installed
    BootstrapInfo.init();

    INSTANCE = new Bootstrap();   //12、創(chuàng)建一個 Bootstrap 實(shí)例

    final SecureSettings keystore = loadSecureSettings(initialEnv);//如果注冊了安全模塊則將相關(guān)配置加載進(jìn)來
    final Environment environment = createEnvironment(foreground, pidFile, keystore, initialEnv.settings(), initialEnv.configFile());   //干之前干過的事情
    try {
        LogConfigurator.configure(environment);   //13、log 配置環(huán)境
    } catch (IOException e) {
        throw new BootstrapException(e);
    }
    if (environment.pidFile() != null) {
        try {
            PidFile.create(environment.pidFile(), true);
        } catch (IOException e) {
            throw new BootstrapException(e);
        }
    }

    final boolean closeStandardStreams = (foreground == false) || quiet;
    try {
        if (closeStandardStreams) {
            final Logger rootLogger = ESLoggerFactory.getRootLogger();
            final Appender maybeConsoleAppender = Loggers.findAppender(rootLogger, ConsoleAppender.class);
            if (maybeConsoleAppender != null) {
                Loggers.removeAppender(rootLogger, maybeConsoleAppender);
            }
            closeSystOut();
        }

        // fail if somebody replaced the lucene jars
        checkLucene();             //14、檢查Lucene版本

// install the default uncaught exception handler; must be done before security is initialized as we do not want to grant the runtime permission setDefaultUncaughtExceptionHandler
        Thread.setDefaultUncaughtExceptionHandler(
            new ElasticsearchUncaughtExceptionHandler(() -> Node.NODE_NAME_SETTING.get(environment.settings())));

        INSTANCE.setup(true, environment);      //15、調(diào)用 setup 方法

        try {
            // any secure settings must be read during node construction
            IOUtils.close(keystore);
        } catch (IOException e) {
            throw new BootstrapException(e);
        }

        INSTANCE.start();         //26、調(diào)用 start 方法

        if (closeStandardStreams) {
            closeSysError();
        }
    } catch (NodeValidationException | RuntimeException e) {
        // disable console logging, so user does not see the exception twice (jvm will show it already)
        final Logger rootLogger = ESLoggerFactory.getRootLogger();
        final Appender maybeConsoleAppender = Loggers.findAppender(rootLogger, ConsoleAppender.class);
        if (foreground && maybeConsoleAppender != null) {
            Loggers.removeAppender(rootLogger, maybeConsoleAppender);
        }
        Logger logger = Loggers.getLogger(Bootstrap.class);
        if (INSTANCE.node != null) {
            logger = Loggers.getLogger(Bootstrap.class, Node.NODE_NAME_SETTING.get(INSTANCE.node.settings()));
        }
        // HACK, it sucks to do this, but we will run users out of disk space otherwise
        if (e instanceof CreationException) {
            // guice: log the shortened exc to the log file
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            PrintStream ps = null;
            try {
                ps = new PrintStream(os, false, "UTF-8");
            } catch (UnsupportedEncodingException uee) {
                assert false;
                e.addSuppressed(uee);
            }
            new StartupException(e).printStackTrace(ps);
            ps.flush();
            try {
                logger.error("Guice Exception: {}", os.toString("UTF-8"));
            } catch (UnsupportedEncodingException uee) {
                assert false;
                e.addSuppressed(uee);
            }
        } else if (e instanceof NodeValidationException) {
            logger.error("node validation exception
{}", e.getMessage());
        } else {
            // full exception
            logger.error("Exception", e);
        }
        // re-enable it if appropriate, so they can see any logging during the shutdown process
        if (foreground && maybeConsoleAppender != null) {
            Loggers.addAppender(rootLogger, maybeConsoleAppender);
        }
        throw e;
    }
}

該方法主要有:

1、創(chuàng)建 Bootstrap 實(shí)例

2、如果注冊了安全模塊則將相關(guān)配置加載進(jìn)來

3、創(chuàng)建 Elasticsearch 運(yùn)行的必須環(huán)境以及相關(guān)配置, 如將 config、scripts、plugins、modules、logs、lib、bin 等配置目錄加載到運(yùn)行環(huán)境中

4、log 配置環(huán)境,創(chuàng)建日志上下文

5、檢查是否存在 PID 文件,如果不存在,創(chuàng)建 PID 文件

6、檢查 Lucene 版本

7、調(diào)用 setup 方法(用當(dāng)前環(huán)境來創(chuàng)建一個節(jié)點(diǎn))

setup 方法
private void setup(boolean addShutdownHook, Environment environment) throws BootstrapException {
    Settings settings = environment.settings();//根據(jù)環(huán)境得到配置
    try {
        spawner.spawnNativeControllers(environment);
    } catch (IOException e) {
        throw new BootstrapException(e);
    }
    initializeNatives(
        environment.tmpFile(),
        BootstrapSettings.MEMORY_LOCK_SETTING.get(settings),
        BootstrapSettings.SYSTEM_CALL_FILTER_SETTING.get(settings),
        BootstrapSettings.CTRLHANDLER_SETTING.get(settings));
    // initialize probes before the security manager is installed
    initializeProbes();
    if (addShutdownHook) {
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                try {
                    IOUtils.close(node, spawner);
                    LoggerContext context = (LoggerContext) LogManager.getContext(false);
                    Configurator.shutdown(context);
                } catch (IOException ex) {
                    throw new ElasticsearchException("failed to stop node", ex);
                }
            }
        });
    }
    try {
        // look for jar hell
        final Logger logger = ESLoggerFactory.getLogger(JarHell.class);
        JarHell.checkJarHell(logger::debug);
    } catch (IOException | URISyntaxException e) {
        throw new BootstrapException(e);
    }
    // Log ifconfig output before SecurityManager is installed
    IfConfig.logIfNecessary();
    // install SM after natives, shutdown hooks, etc.
    try {
        Security.configure(environment, BootstrapSettings.SECURITY_FILTER_BAD_DEFAULTS_SETTING.get(settings));
    } catch (IOException | NoSuchAlgorithmException e) {
        throw new BootstrapException(e);
    }
    node = new Node(environment) {              //16、新建節(jié)點(diǎn)
        @Override
        protected void validateNodeBeforeAcceptingRequests(
            final BootstrapContext context,
            final BoundTransportAddress boundTransportAddress, List checks) throws NodeValidationException {
            BootstrapChecks.check(context, boundTransportAddress, checks);
        }
    };
}

上面代碼最后就是 Node 節(jié)點(diǎn)的創(chuàng)建,這篇文章就不講 Node 的創(chuàng)建了,下篇文章會好好講一下 Node 節(jié)點(diǎn)的創(chuàng)建和正式啟動 ES 節(jié)點(diǎn)的。

總結(jié)

這篇文章主要先把大概啟動流程串通,因?yàn)槠^多所以拆開成兩篇,先不扣細(xì)節(jié)了,后面流程啟動文章寫完后我們再單一的扣細(xì)節(jié)。

相關(guān)文章

1、渣渣菜雞為什么要看 ElasticSearch 源碼?

2、渣渣菜雞的 ElasticSearch 源碼解析 —— 環(huán)境搭建

3、渣渣菜雞的 ElasticSearch 源碼解析 —— 啟動流程(上)

4、渣渣菜雞的 ElasticSearch 源碼解析 —— 啟動流程(下)

5、Elasticsearch 系列文章(一):Elasticsearch 默認(rèn)分詞器和中分分詞器之間的比較及使用方法

6、Elasticsearch 系列文章(二):全文搜索引擎 Elasticsearch 集群搭建入門教程

7、Elasticsearch 系列文章(三):ElasticSearch 集群監(jiān)控

8、Elasticsearch 系列文章(四):ElasticSearch 單個節(jié)點(diǎn)監(jiān)控

9、Elasticsearch 系列文章(五):ELK 實(shí)時日志分析平臺環(huán)境搭建

10、教你如何在 IDEA 遠(yuǎn)程 Debug ElasticSearch

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/76906.html

相關(guān)文章

  • 渣的 ElasticSearch 源碼解析 —— 啟動流程(下)

    摘要:關(guān)注我轉(zhuǎn)載請務(wù)必注明原創(chuàng)地址為前提上篇文章寫完了流程啟動的一部分,方法都入口,以及創(chuàng)建運(yùn)行的必須環(huán)境以及相關(guān)配置,接著就是創(chuàng)建該環(huán)境的節(jié)點(diǎn)了。的創(chuàng)建看下新建節(jié)點(diǎn)的代碼代碼比較多,這里是比較關(guān)鍵的地方,我就把注釋直接寫在代碼上面了,實(shí)在不好 關(guān)注我 showImg(https://segmentfault.com/img/remote/1460000012730965?w=258&h=2...

    ztyzz 評論0 收藏0
  • 渣的 ElasticSearch 源碼解析 —— 環(huán)境搭建

    摘要:注意這個版本需要和下面的源碼版本一致下載源碼從上下載相應(yīng)版本的源代碼,這里建議用,這樣的話后面你可以隨意切換到的其他版本去。我們看下有哪些版本的找到了目前源碼版本最新的版本的穩(wěn)定版為切換到該版本于是就可以切換到該穩(wěn)定版本了。 關(guān)注我 showImg(https://segmentfault.com/img/remote/1460000012730965?w=258&h=258); 轉(zhuǎn)載...

    wudengzan 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<