【Elasticsearch 5.6.12 源码】——【2】启动过程分析(上)

版权声明:本文为博主原创,转载请注明出处!


简介

本文主要解决以下问题:

1、启动ES的入口类及入口方法是哪个?
2、分析梳理ES服务启动的主要流程?

入口类

ES的入口类为org.elasticsearch.bootstrap.Elasticsearch,启动方法为:

public static void main(final String[] args) throws Exception 

该类通过继承EnvironmentAwareCommand类增加了CLI的支持,类图:
【Elasticsearch 5.6.12 源码】——【2】启动过程分析(上)

启动流程

Step 1、给JVM安装(临时的)安全管理器,并注册错误日志监听器。

ES在程序启动的第一步先给JVM安装了一个安全管理器,并授予了程序所有的权限(临时的,后续会更换新的安全管理器),以方便以后的操作。接下来会注册一个错误日志的监听器,来监听是否有错误发生,随后使用配置文件配置日志组件是会检查监听器是否检测到错误,如果有将通过抛异常的方式中止ES的启动过程。执行该动作的方法:

    // org.elasticsearch.bootstrap.Elasticsearch
    public static void main(final String[] args) throws Exception 
Step 2、创建一个Elasticsearch类的实例(一个CLI的Command实例),并执行实例的main方法。

Elasticsearch类直接继承了EnvironmentAwareCommand,可以认为这一步创建了一个CLI的command对象。在该类的构造方法以及父类中定义了该command对象可以接受的cli的参数。执行该动作的方法:

// org.elasticsearch.bootstrap.Elasticsearch
public static void main(final String[] args) throws Exception 

bin目录下运行Elasticsearch -h命令,可以查看该实例支持的所有命令行参数:

【Elasticsearch 5.6.12 源码】——【2】启动过程分析(上)

Step 3、添加JVM关闭的构造,使用默认配置配置日志组件。

为JVM添加了一个shutdown的钩子函数,在钩子函数中调用了close()方法。使用系统属性es.logger.level的值来配置日志组件,如果没有该配置则使用默认的INFO级别来配置logger.level。执行该动作的方法:

// org.elasticsearch.cli.Command
public final int main(String[] args, Terminal terminal) throws Exception
Step 4、解析helpsilentverbosenormal的参数配置,并输出或设置到Terminal上。

首先检查控制台是否传入了-h--help参数,如果有的话打印帮助信息,并结束ES的启动过程。否则,依次检查-s-v等参数,并根据相应的参数配置terminal的信息输出方式。执行该动作的方法:

// org.elasticsearch.cli.Command
void mainWithoutErrorHandling(String[] args, Terminal terminal) throws Exception
Step 5、解析通过控制台传入的ES的配置参数,并根据配置参数创建Environment对象。

首先,解析通过控制台-E参数传入的ES配置。其次,通过System Property补齐某些确实的配置。这两个过程都会对配置的key进行查重。解析配置的代码:

// org.elasticsearch.cli.EnvironmentAwareCommand
protected void execute(Terminal terminal, OptionSet options) throws Exception {
        final Map<String, String> 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);
        }

        putSystemPropertyIfSettingIsMissing(settings, "default.path.conf", "es.default.path.conf");
        putSystemPropertyIfSettingIsMissing(settings, "default.path.data", "es.default.path.data");
        putSystemPropertyIfSettingIsMissing(settings, "default.path.logs", "es.default.path.logs");
        putSystemPropertyIfSettingIsMissing(settings, "path.conf", "es.path.conf");
        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));
    }

接下来,根据解析到的配置项来创建Environment对象。

// org.elasticsearch.cli.EnvironmentAwareCommand
protected Environment createEnv(Terminal terminal, Map<String, String> settings) {
    return InternalSettingsPreparer.prepareEnvironment(Settings.EMPTY, terminal, settings);
}

最终,通过InternalSettingsPreparer对象的prepareEnironment方法来创建Enironment对象。

// org.elasticsearch.node.InternalSettingsPreparer
public static Environment prepareEnvironment(Settings input, Terminal terminal, Map<String, String> properties)

该方法会合并控制台传入的配置项、部分通过系统环境变量设置的配置项及elasticsearch.yml设置的配置项。最终创建一个Environment对象,该对象中保存了ES程序的配置目录、数据目录、插件目录及日志目录的地址。

Step 6、解析控制台传入的-v-d-p-q等参数,并依据相应的参数初始化Bootstrap

首先,检查是否传入了-v参数,如果有该参数则打印ES的版本信息并退出。接下来解析-d-p-q参数,并依据这些参数调用Bootstrap.init。执行该动作的方法:

// org.elasticsearch.bootstrap.Elasticsearch
protected void execute(Terminal terminal, OptionSet options, Environment env) throws UserException
void init(final boolean daemonize, final Path pidFile, final boolean quiet, Environment initialEnv)
Step 7、通过调用BootstrapInfo.init()方法的方式来初始化BootstrapInfo对象。

通过这个对象可以获取JNA是否可用、系统Memory Lock是否可用等一些系统信息。执行该动作的方法:

// org.elasticsearch.bootstrap.Bootstrap
static void init(final boolean foreground, final Path pidFile,
            final boolean quiet, final Environment initialEnv) throws BootstrapException, NodeValidationException, UserException
Step 8、创建一个Bootstrap实例,加载Secure Settings,并结合原有的配置重新创建Environment对象。

Bootstrap实例中首先启动了一个keep alive线程。结合SecureSettings重新创建Environment对象。依据env中的配置文件配置日志组件,这时会检查是否有错误日志产生。接下来输出一些配置相关的日志,并按需创建pidFile。执行该动作的方法:

// org.elasticsearch.bootstrap.Bootstrap
static void init(final boolean foreground, final Path pidFile,
            final boolean quiet, final Environment initialEnv) throws BootstrapException, NodeValidationException, UserException
Step 9、检查Lucene的版本,设置 默认的未捕获异常的处理器 ,并开始配置新建的Bootstrap实例。

首先,根据是否守护经常运行的方式按需关闭SysOut。接下来检查Lucene库的版本是否匹配。最后,设置 DefaultUncaughtExceptionHandler。执行该动作的方法:

// org.elasticsearch.bootstrap.Bootstrap
static void init(final boolean foreground, final Path pidFile,
            final boolean quiet, final Environment initialEnv) throws BootstrapException, NodeValidationException, UserException
Step 10、设置NativePluginControllers,初始化native资源,初始化资源“探针”,checkJarHell,再次设置系统安全管理器并新建Node对象。

在这些过程中间还会检查老配置项bootstrap.seccomp和新配置项bootstrap.system_call_filter都配置了的情况下是否产生了冲突。执行该动作的方法:

// org.elasticsearch.bootstrap.Bootstrap
private void setup(boolean addShutdownHook, Environment environment) throws BootstrapException
Step 11、关闭SecureSettings的存储文件,并调用Bootstrap对象的start方法,按需closeSysError

这是启动流程的最后一步。

上一篇:Maven使用笔记(七)Maven使用问题记录


下一篇:LAMP+haproxy+varnish实现网站访问的动静分离及静态资源缓存