読者です 読者をやめる 読者になる 読者になる

Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

Elasticsearchソースコードリーディング~内部構造把握の第一歩~

Elasticsearch Java

こんにちは! ツカノ(@)です。

ElasticsearchKibanaを使うとログなどを簡単に可視化できるため、利用者が急速に増えてきています(私もそのひとりです)。様々なブログで紹介されているKibana画面はとても素敵で、積極的に使いたくなります。
ただ、それなりの規模でElasticsearchを利用するには、中身を知ったり、データ設計や運用面なども考える必要がありますが、そのあたりの情報はまだ十分ではないように思います。そこで、Elasticsearchの中身をもっと知るために、ひとりでひっそりソースコードリーディングをしてみました。
f:id:acro-engineer:20140131080117p:plain

今回利用したElasticsearchのバージョンは1.0.0.RC2です。以下の場所から実行媒体とソースコードをダウンロードして確認しています。また、確認はWindowsで行っています。

1.ElasticsearchはどんなJVM引数で起動するのか?

まずは、実行媒体(zip)を展開します。ひとまずデフォルト設定のままで、elasticsearch.batを実行して起動します。

以下のようなログがコンソールに表示されて、起動完了です。

[2014-02-07 06:35:10,225][INFO ][node                     ] [Blind Faith] version[1.0.0.RC2], pid[9052], build[a9d736e/2014-02-03T15:02:11Z]
[2014-02-07 06:35:10,225][INFO ][node                     ] [Blind Faith] initializing ...
[2014-02-07 06:35:10,229][INFO ][plugins                  ] [Blind Faith] loaded [], sites []
[2014-02-07 06:35:13,525][INFO ][node                     ] [Blind Faith] initialized
[2014-02-07 06:35:13,526][INFO ][node                     ] [Blind Faith] starting ...
[2014-02-07 06:35:13,743][INFO ][transport                ] [Blind Faith] bound_address {inet[/0:0:0:0:0:0:0:0:9300]}, publish_address {inet[/172.20.10.4:9300]}
[2014-02-07 06:35:16,971][INFO ][cluster.service          ] [Blind Faith] new_master [Blind Faith][NYDlh_0TRJeafDu2KZxB1A][Poincare][inet[/172.20.10.4:9300]], reason: zen-disco-join (elected_as_master)
[2014-02-07 06:35:17,007][INFO ][discovery                ] [Blind Faith] elasticsearch/NYDlh_0TRJeafDu2KZxB1A
[2014-02-07 06:35:17,039][INFO ][gateway                  ] [Blind Faith] recovered [0] indices into cluster_state
[2014-02-07 06:35:17,140][INFO ][http                     ] [Blind Faith] bound_address {inet[/0:0:0:0:0:0:0:0:9200]}, publish_address {inet[/172.20.10.4:9200]}
[2014-02-07 06:35:17,141][INFO ][node                     ] [Blind Faith] started


さて、VisualVMを起動して、Elasticsearchに接続してみましょう。以下の図の赤枠で囲った部分にElasticsearchが見えていますね。
f:id:acro-engineer:20140207084727p:plain


「概要」タブをクリックして、JVM引数を確認してみると、以下のようになっています。
(es.path.homeは実行媒体を展開したディレクトリによって異なります)

-Xms256m
-Xmx1g
-Xss256k
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly
-XX:+HeapDumpOnOutOfMemoryError
-Delasticsearch
-Des-foreground=yes
-Des.path.home=C:\elasticsearch-1.0.0.RC2

これら設定値はelasticsearch.batの中に記載されているため、JVM引数を変更する場合はこのファイルを修正してください。Linux版の場合はelasticsearchelasticsearch.in.shにまたがってJVM引数の設定が記載されています。

2.Elasticsearchのスレッド構成は?

引き続き、VisualVMを操作します。「スレッド」タブから「スレッドダンプ」ボタンを押して、スレッドダンプを取得してみましょう。ちょっと長いですが、取得したスレッド一覧は以下の通りになりました。(見やすいように並び替えています)

Attach Listener
C2 CompilerThread0
C2 CompilerThread1
Concurrent Mark-Sweep GC Thread
DestroyJavaVM
elasticsearch[Blind Faith][[timer]]
elasticsearch[Blind Faith][[ttl_expire]]
elasticsearch[Blind Faith][clusterService#updateTask][T#1]
elasticsearch[Blind Faith][discovery#multicast#receiver][T#1]
elasticsearch[Blind Faith][generic][T#1]
elasticsearch[Blind Faith][http_server_boss][T#1]{New I/O server boss #51}
elasticsearch[Blind Faith][http_server_worker][T#1]{New I/O worker #35}
elasticsearch[Blind Faith][http_server_worker][T#2]{New I/O worker #36}
elasticsearch[Blind Faith][http_server_worker][T#3]{New I/O worker #37}
elasticsearch[Blind Faith][http_server_worker][T#4]{New I/O worker #38}
elasticsearch[Blind Faith][http_server_worker][T#5]{New I/O worker #39}
elasticsearch[Blind Faith][http_server_worker][T#6]{New I/O worker #40}
elasticsearch[Blind Faith][http_server_worker][T#7]{New I/O worker #41}
elasticsearch[Blind Faith][http_server_worker][T#8]{New I/O worker #42}
elasticsearch[Blind Faith][http_server_worker][T#9]{New I/O worker #43}
elasticsearch[Blind Faith][http_server_worker][T#10]{New I/O worker #44}
elasticsearch[Blind Faith][http_server_worker][T#11]{New I/O worker #45}
elasticsearch[Blind Faith][http_server_worker][T#12]{New I/O worker #46}
elasticsearch[Blind Faith][http_server_worker][T#13]{New I/O worker #47}
elasticsearch[Blind Faith][http_server_worker][T#14]{New I/O worker #48}
elasticsearch[Blind Faith][http_server_worker][T#15]{New I/O worker #49}
elasticsearch[Blind Faith][http_server_worker][T#16]{New I/O worker #50}
elasticsearch[Blind Faith][management][T#1]
elasticsearch[Blind Faith][management][T#2]
elasticsearch[Blind Faith][riverClusterService#updateTask][T#1]
elasticsearch[Blind Faith][scheduler][T#1]
elasticsearch[Blind Faith][transport_client_boss][T#1]{New I/O boss #17}
elasticsearch[Blind Faith][transport_client_timer][T#1]{Hashed wheel timer #1}
elasticsearch[Blind Faith][transport_client_worker][T#1]{New I/O worker #1}
elasticsearch[Blind Faith][transport_client_worker][T#2]{New I/O worker #2}
elasticsearch[Blind Faith][transport_client_worker][T#3]{New I/O worker #3}
elasticsearch[Blind Faith][transport_client_worker][T#4]{New I/O worker #4}
elasticsearch[Blind Faith][transport_client_worker][T#5]{New I/O worker #5}
elasticsearch[Blind Faith][transport_client_worker][T#6]{New I/O worker #6}
elasticsearch[Blind Faith][transport_client_worker][T#7]{New I/O worker #7}
elasticsearch[Blind Faith][transport_client_worker][T#8]{New I/O worker #8}
elasticsearch[Blind Faith][transport_client_worker][T#9]{New I/O worker #9}
elasticsearch[Blind Faith][transport_client_worker][T#10]{New I/O worker #10}
elasticsearch[Blind Faith][transport_client_worker][T#11]{New I/O worker #11}
elasticsearch[Blind Faith][transport_client_worker][T#12]{New I/O worker #12}
elasticsearch[Blind Faith][transport_client_worker][T#13]{New I/O worker #13}
elasticsearch[Blind Faith][transport_client_worker][T#14]{New I/O worker #14}
elasticsearch[Blind Faith][transport_client_worker][T#15]{New I/O worker #15}
elasticsearch[Blind Faith][transport_client_worker][T#16]{New I/O worker #16}
elasticsearch[Blind Faith][transport_server_boss][T#1]{New I/O server boss #34}
elasticsearch[Blind Faith][transport_server_worker][T#1]{New I/O worker #18}
elasticsearch[Blind Faith][transport_server_worker][T#2]{New I/O worker #19}
elasticsearch[Blind Faith][transport_server_worker][T#3]{New I/O worker #20}
elasticsearch[Blind Faith][transport_server_worker][T#4]{New I/O worker #21}
elasticsearch[Blind Faith][transport_server_worker][T#5]{New I/O worker #22}
elasticsearch[Blind Faith][transport_server_worker][T#6]{New I/O worker #23}
elasticsearch[Blind Faith][transport_server_worker][T#7]{New I/O worker #24}
elasticsearch[Blind Faith][transport_server_worker][T#8]{New I/O worker #25}
elasticsearch[Blind Faith][transport_server_worker][T#9]{New I/O worker #26}
elasticsearch[Blind Faith][transport_server_worker][T#10]{New I/O worker #27}
elasticsearch[Blind Faith][transport_server_worker][T#11]{New I/O worker #28}
elasticsearch[Blind Faith][transport_server_worker][T#12]{New I/O worker #29}
elasticsearch[Blind Faith][transport_server_worker][T#13]{New I/O worker #30}
elasticsearch[Blind Faith][transport_server_worker][T#14]{New I/O worker #31}
elasticsearch[Blind Faith][transport_server_worker][T#15]{New I/O worker #32}
elasticsearch[Blind Faith][transport_server_worker][T#16]{New I/O worker #33}
elasticsearch[keepAlive/1.0.0.RC2]
Finalizer
Gang worker#0 (Parallel CMS Threads)
Gang worker#1 (Parallel CMS Threads)
Gang worker#0 (Parallel GC Threads)
Gang worker#1 (Parallel GC Threads)
Gang worker#2 (Parallel GC Threads)
Gang worker#3 (Parallel GC Threads)
Gang worker#4 (Parallel GC Threads)
Gang worker#5 (Parallel GC Threads)
Gang worker#6 (Parallel GC Threads)
Gang worker#7 (Parallel GC Threads)
JMX server connection timeout 80
Reference Handler
RMI Scheduler(0)
RMI TCP Accept-0
RMI TCP Connection(12)-172.20.10.4
Service Thread
Signal Dispatcher
Surrogate Locker Thread (Concurrent GC)
VM Periodic Task Thread
VM Thread

スレッド名が「elasticsearch」で始まるスレッドは62個あり、これがElasticsearchのアプリケーションに関連するスレッドになっています。これは、分かりやすいですね。
この一覧を眺めてみると、「http_server」「transport_client」「transport_server」という名称を含むスレッドが多いのが目立ちます。これは何でしょうか? Elasticsearchは通信周りのライブラリに、以前このブログでも紹介したNetty(ver.3)を利用しています。これらはNetty関連の通信スレッドになります。

「http_server」を含むスレッド

Elasticsearchに対するオペレーションは主にhttpで行います。Elasticsearchにアクセスするのにcurlを使った例をよく見ますが、その処理を行う箇所です。http通信周りのスレッドになります。

「transport_client」を含むスレッド

Elasticsearchがクラスタ間通信を行う際のクライアント側スレッドです。サーバ側と違い、こちらにはtimerスレッドがあります。

「transport_server」を含むスレッド

Elasticsearchがクラスタ間通信を行う際のサーバ側スレッドです。

3.Elasticsearchの入口

何か問題があったときには、ソースを読んだり、リモートデバッグすることもあると思います。そのための、入口になる箇所を探してみましよう。他のプロセスと通信処理を行っている箇所が分かれば、そこが入口になります。
Elasticsearchは通信処理にNettyを利用しています。Nettyの初期化は、org.jboss.netty.channel.ChannelPipelineFactoryを継承したクラスのgetPipelineメソッドで行います。このメソッドを実装している箇所を探してみましょう。
すると、以下のクラスが見つかりました。通信周りの設定を知りたい場合は、これらのソースコードを読むのが良いと思います。

org.elasticsearch.benchmark.transport.netty.NettyEchoBenchmark

これはElasticsearchのテストコードに入っているベンチマーク用クラスです。Elasticsearch本体とは関係ないですね。

org.elasticsearch.bulk.udp.BulkUdpService

bulk udp apiで利用される通信処理を初期化しているクラスです。この機能はデフォルトではdisabledとなっているため、先ほどのスレッド一覧にも登場していません。
リモートデバッグしたい場合は、BulkUdpServiceクラスの内部クラスHandlerのmessageReceivedメソッドにブレークポイントを設定してください。

org.elasticsearch.http.netty.NettyHttpServerTransport

Elasticsearchのhttp通信を初期化しているクラスです。内部クラスMyChannelPipelineFactoryで初期化しています。
リモートデバッグしたい場合は、org.elasticsearch.http.netty.HttpRequestHandlerのmessageReceivedメソッドにブレークポイントを設定してください。

org.elasticsearch.transport.netty.NettyTransport

Elasticsearchのクラスタ間通信を初期化しているクラスです。doStartメソッドの中でクライアント側、サーバ側の順に初期化しています。
リモートデバッグしたい場合は、org.elasticsearch.transport.netty.MessageChannelHandlerのmessageReceivedメソッドにブレークポイントを設定してください。クライアント側、サーバ側共に同じクラスを利用しますが、スレッド名が違うので区別できます。

4.Elasticsearchにリモートデバッグする方法

Windows版だとelasticsearch.batLinux版だとelasticsearch.in.shに、JVM引数を指定する変数JAVA_OPTSがあるので、ここにリモートデバッグに必要なオプションを追加しましょう。
例えばWindows版は、デフォルトで以下の設定になっています。

set JAVA_OPTS=%JAVA_OPTS% -XX:+HeapDumpOnOutOfMemoryError

次のようにデバッグオプションを追加しましょう。

set JAVA_OPTS=%JAVA_OPTS% -XX:+HeapDumpOnOutOfMemoryError -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8765

その後、以下の手順を実行します。

  • 「3.Elasticsearchの入口」に記載したブレークポイントを設定します。
  • Elasticsearchを起動します。
  • リモートデバッグで8765番ポートに接続します。

これで準備ができました。
例えば、curlでhttpリクエストを送信すると、HttpRequestHandlerのmessageReceivedメソッドに設定したブレークポイントで停止します。
 

さて、いかがだったでしょうか?
これをベースに、Elasticsearchのアクションやプラグインあたりの処理を追うと面白そうですね。
今回はElasticsearchの入口部分だけですが、理解やデバッグの参考になればと思います。

Acroquest Technologyでは、キャリア採用を行っています。


  • 日頃勉強している成果を、Hadoop、Storm、NoSQL、HTML5/CSS3/JavaScriptといった最新の技術を使ったプロジェクトで発揮したい。
  • 社会貢献性の高いプロジェクトに提案からリリースまで携わりたい。
  • 書籍・雑誌等の執筆や対外的な勉強会の開催を通した技術の発信や、社内勉強会での技術情報共有により、技術的に成長したい。
  • OSSの開発に携わりたい。

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
 キャリア採用ページ