国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

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

AZmake / 3129人閱讀

摘要:總結這篇文章主要先把大概啟動流程串通,因為篇幅較多所以拆開成兩篇,先不扣細節了,后面流程啟動文章寫完后我們再單一的扣細節。

關注我

轉載請務必注明原創地址為:http://www.54tianzhisheng.cn/2018/08/11/es-code02/

前提

上篇文章寫了 ElasticSearch 源碼解析 —— 環境搭建 ,其中里面說了啟動 打開 server 模塊下的 Elasticsearch 類:org.elasticsearch.bootstrap.Elasticsearch,運行里面的 main 函數就可以啟動 ElasticSearch 了,這篇文章講講啟動流程,因為篇幅會很多,所以分了兩篇來寫。

啟動流程 main 方法入口

可以看到入口其實是一個 main 方法,方法里面先是檢查權限,然后是一個錯誤日志監聽器(確保在日志配置之前狀態日志沒有出現 error),然后是 Elasticsearch 對象的創建,然后調用了靜態方法 main 方法(18 行),并把創建的對象和參數以及 Terminal 默認值傳進去。靜態的 main 方法里面調用 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、調用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
}

因為 Elasticsearch 類是繼承了 EnvironmentAwareCommand 類,EnvironmentAwareCommand 類繼承了 Command 類,但是 Elasticsearch 類并沒有重寫 main 方法,所以上面調用的 elasticsearch.main 其實是調用了 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,在程序退出時觸發該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;
}

上面代碼一開始利用一個勾子函數,在程序退出時觸發該 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、執行 EnvironmentAwareCommand 中的 execute(),(重寫了command里面抽象的execute方法)
}

上面的代碼從 3 ~ 14 行是解析傳進來的參數并配置 terminal,重要的 execute() 方法,執行的是 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、根據我們ide配置的 vm options 進行設置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、先調用 createEnv 創建環境
    //9、執行elasticsearch的execute方法,elasticsearch中重寫了EnvironmentAwareCommand中的抽象execute方法
}

方法前面是根據傳參去判斷配置的,如果配置為空,就會直接跳到執行 putSystemPropertyIfSettingIsMissing 方法,這里會配置三個屬性:path.data、path.home、path.logs 設置 es 的 data、home、logs 目錄,它這里是根據我們 ide 配置的 vm options 進行設置的,這也是為什么我們上篇文章說的配置信息,如果不配置的話就會直接報錯。下面看看 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)找系統設置
    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);
        }
    }
}

執行這三個方法后:

跳出此方法,繼續看會發現 execute 方法調用了方法,

 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、準備環境 prepareEnvironment
}

讀取我們 ide vm options 中配置的 es.path.conf,同上篇文章也講了這個一定要配置的,因為 es 啟動的時候會加載我們的配置和一些插件。這里繼續看下上面代碼第 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);  //加載文件并讀取配置文件內容
        } 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);          //再一次初始化設置
    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);
}

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

跳出 createEnv 方法,我們繼續看 execute 方法吧。

EnvironmentAwareCommand 類的 execute 方法代碼如下:

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

這是個抽象方法,那么它的實現方法在 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、執行 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 中的靜態 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、創建一個 Bootstrap 實例

    final SecureSettings keystore = loadSecureSettings(initialEnv);//如果注冊了安全模塊則將相關配置加載進來
    final Environment environment = createEnvironment(foreground, pidFile, keystore, initialEnv.settings(), initialEnv.configFile());   //干之前干過的事情
    try {
        LogConfigurator.configure(environment);   //13、log 配置環境
    } 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、調用 setup 方法

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

        INSTANCE.start();         //26、調用 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、創建 Bootstrap 實例

2、如果注冊了安全模塊則將相關配置加載進來

3、創建 Elasticsearch 運行的必須環境以及相關配置, 如將 config、scripts、plugins、modules、logs、lib、bin 等配置目錄加載到運行環境中

4、log 配置環境,創建日志上下文

5、檢查是否存在 PID 文件,如果不存在,創建 PID 文件

6、檢查 Lucene 版本

7、調用 setup 方法(用當前環境來創建一個節點)

setup 方法
private void setup(boolean addShutdownHook, Environment environment) throws BootstrapException {
    Settings settings = environment.settings();//根據環境得到配置
    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、新建節點
        @Override
        protected void validateNodeBeforeAcceptingRequests(
            final BootstrapContext context,
            final BoundTransportAddress boundTransportAddress, List checks) throws NodeValidationException {
            BootstrapChecks.check(context, boundTransportAddress, checks);
        }
    };
}

上面代碼最后就是 Node 節點的創建,這篇文章就不講 Node 的創建了,下篇文章會好好講一下 Node 節點的創建和正式啟動 ES 節點的。

總結

這篇文章主要先把大概啟動流程串通,因為篇幅較多所以拆開成兩篇,先不扣細節了,后面流程啟動文章寫完后我們再單一的扣細節。

相關文章

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

2、渣渣菜雞的 ElasticSearch 源碼解析 —— 環境搭建

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

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

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

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

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

8、Elasticsearch 系列文章(四):ElasticSearch 單個節點監控

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

10、教你如何在 IDEA 遠程 Debug ElasticSearch

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/76906.html

相關文章

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

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

    ztyzz 評論0 收藏0
  • 渣的 ElasticSearch 源碼解析 —— 環境搭建

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

    wudengzan 評論0 收藏0

發表評論

0條評論

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