摘要:總結(jié)這篇文章主要先把大概啟動(dòng)流程串通,因?yàn)槠^多所以拆開(kāi)成兩篇,先不扣細(xì)節(jié)了,后面流程啟動(dòng)文章寫(xiě)完后我們?cè)賳我坏目奂?xì)節(jié)。
關(guān)注我
轉(zhuǎn)載請(qǐng)務(wù)必注明原創(chuàng)地址為:http://www.54tianzhisheng.cn/2018/08/11/es-code02/
前提上篇文章寫(xiě)了 ElasticSearch 源碼解析 —— 環(huán)境搭建 ,其中里面說(shuō)了啟動(dòng) 打開(kāi) server 模塊下的 Elasticsearch 類(lèi):org.elasticsearch.bootstrap.Elasticsearch,運(yùn)行里面的 main 函數(shù)就可以啟動(dòng) ElasticSearch 了,這篇文章講講啟動(dòng)流程,因?yàn)槠鶗?huì)很多,所以分了兩篇來(lái)寫(xiě)。
啟動(dòng)流程 main 方法入口可以看到入口其實(shí)是一個(gè) main 方法,方法里面先是檢查權(quán)限,然后是一個(gè)錯(cuò)誤日志監(jiān)聽(tīng)器(確保在日志配置之前狀態(tài)日志沒(méi)有出現(xiàn) error),然后是 Elasticsearch 對(duì)象的創(chuàng)建,然后調(diào)用了靜態(tài)方法 main 方法(18 行),并把創(chuàng)建的對(duì)象和參數(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 類(lèi)是繼承了 EnvironmentAwareCommand 類(lèi),EnvironmentAwareCommand 類(lèi)繼承了 Command 類(lèi),但是 Elasticsearch 類(lèi)并沒(méi)有重寫(xiě) 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方法加入一個(gè)Hook,在程序退出時(shí)觸發(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; }
上面代碼一開(kāi)始利用一個(gè)勾子函數(shù),在程序退出時(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(),(重寫(xiě)了command里面抽象的execute方法) }
上面的代碼從 3 ~ 14 行是解析傳進(jìn)來(lái)的參數(shù)并配置 terminal,重要的 execute() 方法,執(zhí)行的是 EnvironmentAwareCommand 中的 execute() (重寫(xiě)了 Command 類(lèi)里面的抽象 execute 方法),從上面那個(gè)繼承圖可以看到 EnvironmentAwareCommand 繼承了 Command,重寫(xiě)的 execute 方法代碼如下:
@Override protected void execute(Terminal terminal, OptionSet options) throws Exception { final Mapsettings = 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中重寫(xiě)了EnvironmentAwareCommand中的抽象execute方法 }
方法前面是根據(jù)傳參去判斷配置的,如果配置為空,就會(huì)直接跳到執(zhí)行 putSystemPropertyIfSettingIsMissing 方法,這里會(huì)配置三個(gè)屬性:path.data、path.home、path.logs 設(shè)置 es 的 data、home、logs 目錄,它這里是根據(jù)我們 ide 配置的 vm options 進(jìn)行設(shè)置的,這也是為什么我們上篇文章說(shuō)的配置信息,如果不配置的話就會(huì)直接報(bào)錯(cuò)。下面看看 putSystemPropertyIfSettingIsMissing 方法代碼里面怎么做到的:
/** Ensure the given setting exists, reading it from system properties if not already set. */ private static void putSystemPropertyIfSettingIsMissing(final Mapsettings, 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í)行這三個(gè)方法后:
跳出此方法,繼續(xù)看會(huì)發(fā)現(xiàn) execute 方法調(diào)用了方法,
execute(terminal, options, createEnv(terminal, settings));
這里我們先看看 createEnv(terminal, settings) 方法:
protected Environment createEnv(final Terminal terminal, final Mapsettings) 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,同上篇文章也講了這個(gè)一定要配置的,因?yàn)?es 啟動(dòng)的時(shí)候會(huì)加載我們的配置和一些插件。這里繼續(xù)看下上面代碼第 6 行的 prepareEnvironment 方法:
public static Environment prepareEnvironment(Settings input, Terminal terminal, Mapproperties, 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 格式的,如果不是則拋出一個(gè)異常 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)境如上圖,通過(guò)構(gòu)建的環(huán)境查看配置文件 elasticsearch.yml 是不是以 yml 結(jié)尾,如果是 yaml 或者 json 結(jié)尾的則拋出異常(在 5.5.0 版本其他兩種格式過(guò)期了,只能使用 yml 格式),然后加載該配置文件并讀取里面的內(nèi)容(KV結(jié)構(gòu))。
跳出 createEnv 方法,我們繼續(xù)看 execute 方法吧。
EnvironmentAwareCommand 類(lèi)的 execute 方法代碼如下:
protected abstract void execute(Terminal terminal, OptionSet options, Environment env) throws Exception;
這是個(gè)抽象方法,那么它的實(shí)現(xiàn)方法在 Elasticsearch 類(lèi)中,代碼如下:
@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)建一個(gè) Bootstrap 實(shí)例 final SecureSettings keystore = loadSecureSettings(initialEnv);//如果注冊(cè)了安全模塊則將相關(guān)配置加載進(jìn)來(lái) final Environment environment = createEnvironment(foreground, pidFile, keystore, initialEnv.settings(), initialEnv.configFile()); //干之前干過(guò)的事情 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、檢查L(zhǎng)ucene版本 // 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、如果注冊(cè)了安全模塊則將相關(guān)配置加載進(jìn)來(lái)
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)境來(lái)創(chuàng)建一個(gè)節(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, Listchecks) throws NodeValidationException { BootstrapChecks.check(context, boundTransportAddress, checks); } }; }
上面代碼最后就是 Node 節(jié)點(diǎn)的創(chuàng)建,這篇文章就不講 Node 的創(chuàng)建了,下篇文章會(huì)好好講一下 Node 節(jié)點(diǎn)的創(chuàng)建和正式啟動(dòng) ES 節(jié)點(diǎn)的。
總結(jié)這篇文章主要先把大概啟動(dòng)流程串通,因?yàn)槠^多所以拆開(kāi)成兩篇,先不扣細(xì)節(jié)了,后面流程啟動(dòng)文章寫(xiě)完后我們?cè)賳我坏目奂?xì)節(jié)。
相關(guān)文章1、渣渣菜雞為什么要看 ElasticSearch 源碼?
2、渣渣菜雞的 ElasticSearch 源碼解析 —— 環(huán)境搭建
3、渣渣菜雞的 ElasticSearch 源碼解析 —— 啟動(dòng)流程(上)
4、渣渣菜雞的 ElasticSearch 源碼解析 —— 啟動(dòng)流程(下)
5、Elasticsearch 系列文章(一):Elasticsearch 默認(rèn)分詞器和中分分詞器之間的比較及使用方法
6、Elasticsearch 系列文章(二):全文搜索引擎 Elasticsearch 集群搭建入門(mén)教程
7、Elasticsearch 系列文章(三):ElasticSearch 集群監(jiān)控
8、Elasticsearch 系列文章(四):ElasticSearch 單個(gè)節(jié)點(diǎn)監(jiān)控
9、Elasticsearch 系列文章(五):ELK 實(shí)時(shí)日志分析平臺(tái)環(huán)境搭建
10、教你如何在 IDEA 遠(yuǎn)程 Debug ElasticSearch
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/76906.html
摘要:關(guān)注我轉(zhuǎn)載請(qǐng)務(wù)必注明原創(chuàng)地址為前提上篇文章寫(xiě)完了流程啟動(dòng)的一部分,方法都入口,以及創(chuàng)建運(yùn)行的必須環(huán)境以及相關(guān)配置,接著就是創(chuàng)建該環(huán)境的節(jié)點(diǎn)了。的創(chuàng)建看下新建節(jié)點(diǎn)的代碼代碼比較多,這里是比較關(guān)鍵的地方,我就把注釋直接寫(xiě)在代碼上面了,實(shí)在不好 關(guān)注我 showImg(https://segmentfault.com/img/remote/1460000012730965?w=258&h=2...
摘要:注意這個(gè)版本需要和下面的源碼版本一致下載源碼從上下載相應(yīng)版本的源代碼,這里建議用,這樣的話后面你可以隨意切換到的其他版本去。我們看下有哪些版本的找到了目前源碼版本最新的版本的穩(wěn)定版為切換到該版本于是就可以切換到該穩(wěn)定版本了。 關(guān)注我 showImg(https://segmentfault.com/img/remote/1460000012730965?w=258&h=258); 轉(zhuǎn)載...
閱讀 3267·2021-11-18 10:02
閱讀 3443·2021-10-11 10:58
閱讀 3376·2021-09-24 09:47
閱讀 1120·2021-09-22 15:21
閱讀 3915·2021-09-10 11:10
閱讀 3277·2021-09-03 10:28
閱讀 1748·2019-08-30 15:45
閱讀 2136·2019-08-30 14:22