1. 程式人生 > >【Elasticsearch 5.6.12 原始碼】——【2】啟動過程分析(上)

【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的支援,類圖:
image

啟動流程

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命令,可以檢視該例項支援的所有命令列引數:

image

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

這是啟動流程的最後一步。