search
LoginSignup
2

More than 1 year has passed since last update.

posted at

updated at

Organization

Elasticsearchのソースコードを読む - Elasticsearchの起動

この記事は スタンバイ Advent Calendar 2021の18日目の記事です。

はじめに

最近、Elasticsearchのカスタムプラグインを開発する機会があったのと、Elasticsearchへの理解がまだ足りないことを感じ、今期からチームメンバーと社内Elasticsearchのソースコードリーディング勉強会を行っています。

今回の記事では、これまでの勉強会の内容を大枠にまとめて、Elasticsearchの起動プロセスを整理しました。

今回利用したElasticsearchのバージョンは7.14で、起動前の準備(環境変数の設定など)もあるけど、ここで割愛します。知識不足に関してあらかじめご了承ください。記事の内容についてこんなところをこう理解すればいいよなどアドバイスをいただけると非常に嬉しいです!

Elasticsearchが起動されるまでの概要

Elasticsearchの起動プロセスは大体以下のステップ

  1. org.elasticsearch.bootstrap.Elasticsearch#main()
  2. org.elasticsearch.bootstrap.Bootstrap
  3. org.elasticsearch.node.Node

まずElasticsearchのエントリーポイントを説明します。elasticsearchの起動スクリプトから(bin/elasticsearch)確認して、エントリーポイントはorg.elasticsearch.bootstrap.Elasticsearchです

  exec \
    "$JAVA" \
    "$XSHARE" \
    $ES_JAVA_OPTS \
    -Des.path.home="$ES_HOME" \
    -Des.path.conf="$ES_PATH_CONF" \
    -Des.distribution.flavor="$ES_DISTRIBUTION_FLAVOR" \
    -Des.distribution.type="$ES_DISTRIBUTION_TYPE" \
    -Des.bundled_jdk="$ES_BUNDLED_JDK" \
    -cp "$ES_CLASSPATH" \
    org.elasticsearch.bootstrap.Elasticsearch \
    "$@" <<<"$KEYSTORE_PASSWORD"

org.elasticsearch.bootstrap.Elasticsearch#main()

entry pointのコードは以下になります

スクリーンショット 2021-11-14 7.48.53.png

やることは以下になります

  • 起動コマンドからDNS Cache Policyを設定するなら、DNS Cache Policyをオーバーライドする
  • SecurityManagerを設定、checkPermissionで全部の権限を許可

JavaのSecurityManagerはセキュリティーを損なう恐れのある操作(File、Socketをいじるなど)の実行権限をチェック、権限あれば実行、なければ例外を出す

  • エラーログリスナーを登録
    • 早めにログリスナーを使って、ログが記録できないことを防ぐ
  • Elasticsearchインスタンスを作って、staticのelasticsearch.main(args, terminal)を呼び出し、作成されたElasticsearchインスタンス、パラメータとTerminalデフォルト値を渡す

    • ElasticsearchクラスはEnvironmentAwareCommandクラスを継承され、EnvironmentAwareCommandクラスはCommandクラスを継承され、Elasticsearchクラスはmainメソッドをoverrideしないのでelasticsearch.mainはCommandのmainメソッドを使う

    以下はCommandのmainメソッドコード

    スクリーンショット 2021-11-14 16.25.10.png

    継承があるので以下の順番で実行していく

  • Command#main

    • RuntimeのaddShutdownHookメソッドでshutdownHookを登録
      • スレッド終了時、Elasticsearch#closeを呼び出す
      • 異常でshutdownされる場合にスタックトレースをプリントアウト
    • mainWithoutErrorHandlingを呼び出す

JavaのShutdownHookはJVMから終了連絡を受けて、ShutdownHook内のメソッドを呼び出し、掃除処理を行い、スムーズにJVM終了できる

  • Command#mainWithoutErrorHandling
    • パラメータをパースしてterminalを設置して、EnvironmentAwareCommandのexecute()を実行(Commandクラスの抽象executeメソッドをオーバーライト)
  • EnvironmentAwareCommand#execute
    • パラメータからSettingを判別、Settingがなければvm optionsからpath.data、path.home、path.logsをSettingに配置
    • execute(terminal, options, createEnv(settings))を呼び出し
  • EnvironmentAwareCommand#createEnv
    • prepareEnvironmentを経由elasticsearch.ymlをロードして、Environmentを作成
  • Elasticsearch#execute
    • daemonizer/pidFile/quietの値を取得、Bootstrapのinitを呼び出し

org.elasticsearch.bootstrap.Bootstrap

initのコードが多くて、リンクだけ残します
https://github.com/elastic/elasticsearch/blob/fc519e380029c6daecc950802435f7266f08708e/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java#L334

最初はBootstrapインスタンスを作って、KeepAliveスレッドを起動

private final CountDownLatch keepAliveLatch = new CountDownLatch(1);
    private final Thread keepAliveThread;
    private final Spawner spawner = new Spawner();

    /** creates a new instance */
    Bootstrap() {
        keepAliveThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //count
                    keepAliveLatch.await();
                } catch (InterruptedException e) {
                    // bail out
                }
            }
        }, "elasticsearch[keepAlive/" + Version.CURRENT + "]");
        keepAliveThread.setDaemon(false);
        // keep this thread alive (non daemon thread) until we shutdown
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                //thread終了時、KeepAliveLatchが0になって、keepAliveThread終了
                keepAliveLatch.countDown();
            }
        });
    }

CountDownLatchは他のスレッドの処理を待機する仕組みです。他のスレッドを処理完了後処理することができます。

Javaはユーザースレッドとデーモンスレッドがあります。 JVMの終了フロー 1. ユーザースレッドがない場合にJVM終了を始める 2. JVMが全デーモンスレッドを終了 3. JVM終了

keepAliveThreadはCountDownLatchで設定されてかつユーザースレッドとして設定されます。
なので、ここの流れは

1. JVMが終了請求を受ける 2. shutdownHook実行 3. keepAliveLatch.countDown()実行 4. KeepAliveLatchが0になって、keepAliveThread終了 5. JVM終了

KeepAliveスレッドを起動後、Bootstrapのinit()について、大枠のプロセスは以下のように

  • SettingとconfigによってES実行時Environmentを作る
  • セキュリティとログ周りの設定
    • keystoreのセキュリティ設定をロードする。keystoreファイルない時に作って保存する。keystoreファイルある時に復号化して、keystoreを更新する
    • setNodeName ログをプリント時Node名を設定、log4j2.propertiesの設定ファイルからログの設定をロード
  • PIDファイルがあるかどうかを確認、なければ作る、現在のpidを書き込む
  • Luceneのバージョンを確認
  • setDefaultUncaughtExceptionHandlerでCatchされない例外の処理方法を設置

マルチスレッドで他のスレッドから出す例外をcatchできないケースもあります。例外をcatchと処理することが必要で、UncaughtExceptionHandlerはこんな処理です

  • INSTANCE.setup(true, environment);INSTANCE.start();
    • Nodeインスタンスを作るため準備して、Nodeインスタンスを作って、Nodeインスタンスを起動

感想など

  • Elasticsearchの起動だけでこんなプロセスがあることを理解できて、細かい粒度でElasticsearchの仕組みを把握できるようになりました。
  • CountDownLatchなどのプロセス調査により、Javaのマルチスレッドプログラミングに詳しくなりました。

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2