(1). 概述

在上一节,剖析了:ES的main入口为:Elasticsearch.它最终会把请求委托给:Bootstrap.init方法,在这里主要剖析:Bootstrap.

(2). Bootstrap.init


private static volatile Bootstrap INSTANCE;
private volatile Node node;
private final CountDownLatch keepAliveLatch = new CountDownLatch(1);
private final Thread keepAliveThread;
private final Spawner spawner = new Spawner();

Bootstrap() {
    // 创建keeplive线程
    keepAliveThread = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                // CountDownLatch阻塞
                keepAliveLatch.await();
            } catch (InterruptedException e) {
                // bail out
            }
        }
    }, "elasticsearch[keepAlive/" + Version.CURRENT + "]");
    // daemon线程
    keepAliveThread.setDaemon(false);

    // keep this thread alive (non daemon thread) until we shutdown
    // 当进程关闭时:CountDownLatch唤醒.
    Runtime.getRuntime().addShutdownHook(new Thread() {
        @Override
        public void run() {
            keepAliveLatch.countDown();
        }
    });
} // end Bootstrap


static void init(
            final boolean foreground,
            final Path pidFile,
            final boolean quiet,
            final Environment initialEnv) throws BootstrapException, NodeValidationException, UserException {
    
    BootstrapInfo.init();
    // 创建keeplive线程
    INSTANCE = new Bootstrap();

    // 加载:elasticsearch.keystore
    final SecureSettings keystore = loadSecureSettings(initialEnv);

    // 对配置文件进行解码
    final Environment environment = createEnvironment(pidFile, keystore, initialEnv.settings(), initialEnv.configFile());

    // environment.settings() = "lixin.macbook.local"
    LogConfigurator.setNodeName(Node.NODE_NAME_SETTING.get(environment.settings()));
    try {
        LogConfigurator.configure(environment);
    } catch (IOException e) {
        throw new BootstrapException(e);
    }

    if (environment.pidFile() != null) { // false
        try {
            PidFile.create(environment.pidFile(), true);
        } catch (IOException e) {
            throw new BootstrapException(e);
        }
    }

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

        // 检查lucene版本与ES版本
        // fail if somebody replaced the lucene jars
        checkLucene();

        // 给线程配置异常处理
        Thread.setDefaultUncaughtExceptionHandler(new ElasticsearchUncaughtExceptionHandler());

        // ************************************************************************
        // 3. Bootstrap.setup
        // ************************************************************************
        INSTANCE.setup(true, environment);

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

        // ************************************************************************
        // 5.Bootstrap.start
        // ************************************************************************
        INSTANCE.start();

        if (closeStandardStreams) { // false
            closeSysError();
        }
    } catch (NodeValidationException | RuntimeException e) {
        // ... ...
        throw e;
    }
} // end init

(3). Bootstrap.setup

private void setup(
        // true
        boolean addShutdownHook, 
        Environment environment) throws BootstrapException {
    
    
    Settings settings = environment.settings();

    try {
        // ******************************************************
        // 4. Spawner.spawnNativeControllers
        // 读取所有的modules,如果有native,则创建:Process
        // ******************************************************
        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 = LogManager.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是核心,后续会专门用几章节再讲
    // *******************************************************
    node = new Node(environment) {
        @Override
        protected void validateNodeBeforeAcceptingRequests(
            final BootstrapContext context,
            final BoundTransportAddress boundTransportAddress, List<BootstrapCheck> checks) throws NodeValidationException {
            BootstrapChecks.check(context, boundTransportAddress, checks);
        }
    };
}// end setup

(4). Spawner.spawnNativeControllers

读取:modules目录,如果有native则创建:Process对象,加载native对象.

void spawnNativeControllers(final Environment environment) throws IOException {
    if (!spawned.compareAndSet(false, true)) {
        throw new IllegalStateException("native controllers already spawned");
    }
    if (!Files.exists(environment.modulesFile())) {
        throw new IllegalStateException("modules directory [" + environment.modulesFile() + "] not found");
    }
    
    // environment.modulesFile() = /Users/lixin/Developer/elastic-search/elasticsearch-7.1.0/modules
    List<Path> paths = PluginsService.findPluginDirs(environment.modulesFile());
    // paths = /Users/lixin/Developer/elastic-search/elasticsearch-7.1.0/modules下所有的目录

    for (final Path modules : paths) {
        // 读取每一个模块:
        // 比如模块: percolator
        // 和模块下的配置文件(plugin-descriptor.properties)
        // 并转换成:PluginInfo
        final PluginInfo info = PluginInfo.readFromProperties(modules);

        // spawnPath = /Users/lixin/Developer/elastic-search/elasticsearch-7.1.0/modules/percolator/platform/darwin-x86_64/bin/controller
        final Path spawnPath = Platforms.nativeControllerPath(modules);

        if (!Files.isRegularFile(spawnPath)) { // true
            continue;
        }

        if (!info.hasNativeController()) {
            final String message = String.format(
                Locale.ROOT,
                "module [%s] does not have permission to fork native controller",
                modules.getFileName());
            throw new IllegalArgumentException(message);
        }

        // 通过:ProcessBuilder,加载了一个本地进程(so文件)
        // ProcessBuilder pb = new ProcessBuilder("/Users/lixin/Developer/elastic-search/elasticsearch-7.1.0/modules/x-pack-ml/platform/darwin-x86_64/bin/controller");

        // spawnPath = /Users/lixin/Developer/elastic-search/elasticsearch-7.1.0/modules/x-pack-ml/platform/darwin-x86_64/bin/controller

        // 创建:通过ProcessBuilder构建出:Process
        final Process process = spawnNativeController(spawnPath, environment.tmpFile());
        processes.add(process);
    }
}

(5). Bootstrap.start

private void start() throws NodeValidationException {
    node.start();
    keepAliveThread.start();
} // end start

(6). 总结

Bootstrap会加载一些Native进程,然后,初始化:org.elasticsearch.node.Node对象. org.elasticsearch.node.Node留到后面几节分析,里面有大量的逻辑.