日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

渣渣菜鸡的 ElasticSearch 源码解析 —— 启动流程(上)

發布時間:2025/7/25 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 渣渣菜鸡的 ElasticSearch 源码解析 —— 启动流程(上) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

關注我

轉載請務必注明原創地址為: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() {@Overridepublic 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,在程序退出時觸發該HookshutdownHookThread = 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 impossiblethrow 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<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);}//6、根據我們ide配置的 vm options 進行設置path.data、path.home、path.logsputSystemPropertyIfSettingIsMissing(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<String, String> 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<String, String> settings) throws UserException {final String esPathConf = System.getProperty("es.path.conf");//8、讀取我們 vm options 中配置的 es.path.confif (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<String, String> properties, Path configPath) {// just create enough settings to build the environment, to get the config dirSettings.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 outputPath 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 loadedinitializeSettings(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 fileoutput.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 immediatelytry {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 installedBootstrapInfo.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 jarscheckLucene(); //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 setDefaultUncaughtExceptionHandlerThread.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 constructionIOUtils.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 otherwiseif (e instanceof CreationException) {// guice: log the shortened exc to the log fileByteArrayOutputStream 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\n{}", e.getMessage());} else {// full exceptionlogger.error("Exception", e);}// re-enable it if appropriate, so they can see any logging during the shutdown processif (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 installedinitializeProbes();if (addShutdownHook) {Runtime.getRuntime().addShutdownHook(new Thread() {@Overridepublic 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 hellfinal Logger logger = ESLoggerFactory.getLogger(JarHell.class);JarHell.checkJarHell(logger::debug);} catch (IOException | URISyntaxException e) {throw new BootstrapException(e);}// Log ifconfig output before SecurityManager is installedIfConfig.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、新建節點@Overrideprotected void validateNodeBeforeAcceptingRequests(final BootstrapContext context,final BoundTransportAddress boundTransportAddress, List<BootstrapCheck> 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

轉載于:https://www.cnblogs.com/zhisheng/p/9478454.html

總結

以上是生活随笔為你收集整理的渣渣菜鸡的 ElasticSearch 源码解析 —— 启动流程(上)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。