LoginSignup
1
0

More than 5 years have passed since last update.

Node.jsからJMXを介してCassandraの情報を取得する

Posted at

CassandraにはnodetoolユーティリティというJMXを介してCassandraクラスタを管理するツールが付属しています。
nodetoolで取得できる情報をNode.js上で利用したいという事情があり、その方法を探していたところnode-jmxというJMXと通信するためのパッケージを発見しました。
node-jmxを利用してCassandraの情報を取得する方法についてわかったことをまとめます。

node-jmx について

インストール

node-jmxの利用にはJDKが必要です。node-jmxは内部でnode-javaを利用しているようですので、install時のトラブルはnode-javaのInstallationを参照したほうが良いでしょう。

node-gypを利用しているので、python2.7とmake, g++も予め用意しておく必要があります。

準備が整っていれば以下のコマンドでnode-jmxが利用可能になります。

npm instal jmx

使い方

基本は公式ドキュメント通りですが、Clientから返ってくる値はJavaオブジェクトになっているようですので実際の値を取得する際には前処理が必要になります。
以下にHeapMemoryUsageを取得するコードを載せました。HeapMemoryUsagejavax.management.openmbean.CompositeDataインタフェースのオブジェクトですので、get(String key)で取得したい項目の数値を取得できます。ただし、node-javaを介した操作ですのでメソッド名はSyncを付けたgetSync(String key)になります。(ちなみにnode-javaは非同期メソッドも提供しているようです。)取得した値はjava.lang.Numberインタフェースのオブジェクトですので、longValue()等で数値が取得できます。(こちらはSyncを付けないようです。)

const jmx = require('jmx');

const client = jmx.createClient({
    host: 'localhost',
    port: 7199,
});

client.on('connect', () => {
    client.getAttribute('java.lang:type=Memory', 'HeapMemoryUsage', data => {
        const used = data.getSync('used');
        console.log(used.longValue);
    });
});
client.connect();

nodetool status をNode.js上で実装する

本題のCassandraクラスタの情報取得の方法についてまとめます。
手始めにnodetool statusが提供している機能をnode-jmxを利用して実装してみたいと思います。

nodetool statusとは

クラスタに参加している各ノードの情報を収集、表示するコマンドです。( 参考
特に、ノードが機能しているかどうかを示すStatus (Up/Down) の項目はnodetool statusを実行する際に最も関心のある情報の一つではないかと思われます。

本家 nodetoolのstatusコマンドの実装

GitHubのものですが、本家Apache Cassandraのnodetool statusに関する主なソースコードを纏めておきます。

(だいたいこの辺りだったと思いますが、過不足間違い等あったらすみません)

大まかな内容としては、nodetool statusで収集している情報はStorageServiceとEndpointSnitchInfoが管理しており、それぞれMBeanとして公開されています。

それぞれ以下のような情報を管理しています(ごく一部だけ載せてあります)

  • StorageService:
    • クラスタに参加しているノードのIPアドレスとHostIDのMap
    • 健全(Up)なノード、疎通不可(Down)になっているノードのリスト
    • クラスタに参加しようとしている or 抜けようとしているノードのリスト
    • トークンのリストと担当ノードのIPアドレスのMap

など

  • EndpointSnitchInfo:
    • ノードが所属しているデータセンタ名
    • ノードが所属しているラック名

nodetool status ではこれらの情報を適当なフォーマットで表示してくれます。

余談ですが本家nodetoolの実装は少々雑と思わざるを得ない部分もあります。
例えばvnodeやトークンレンジの数が増えるほどresolveIpオプション適用時のnodetool statusの応答時間が悪化する問題があり、その原因はDNSによる名前解決の際にトークンレンジ * vnode数だけのループを行う非効率な処理が行われているためのようです。(実際ほんの数ノードのクラスタでもnodetool statusはめちゃくちゃ遅い)
今回のNode.jsでの実装はもうちょっと効率の良い実装にできたら良いなと考えています。

( 参考: ASF JIRA: improvement of nodetool status -r ) ← 忘れ去られているように見えますが果たして反映されるのでしょうか…

Node.jsでnodetool statusを実装してみた

いきなりですが実装したものをnpm packageとして作成し、公開してみました。

npmjs: cassandra-nodetool
GitHub: cassandra-nodetool

エッセンス

node-jmxでStorageServiceやEndpointSnitchInfoを取得する際のMBean名は以下のとおりです。

  • StorageService: 'org.apache.cassandra.db:type=StorageService'
  • EndpointSnitchInfo: 'org.apache.cassandra.db:type=EndpointSnitchInfo'

ですので、例えばクラスタに参加しているノードのリストを取得するには以下のようなコードになります。
クラスタに参加しているノードのリストはStorageServiceLiveNodes属性にありますので、これを取得しています。

// client.on('connect') イベントを受け取ったあとで
client.getAttribute('org.apache.cassandra.db:type=StorageService', 'LiveNodes', javaLiveNodes => {
    const liveNodes = javaLiveNodes.toArraySync();
    console.log(liveNodes);
});

Unreachableノード(疎通不可ノード)のリストを取得するにはUnreachableNodes属性を取得すればOKです。
上でも少し触れましたがStorageServiceには他にも様々な情報を管理しており、属性の一覧を知りたい際にはソースコードを参照するかjconsoleで接続して直接探してみるのが良いかと思われます。

EndpointSnitchInfoからデータセンター名を取得する例も載せておきます。
EndpointSnitchInfoにはRackDatacenterという属性があり、そちらでもデータセンター名やラック名を取得することが可能です。
ただしこちらから取得できるのは、JMXで接続したノードのデータセンター名やラック名のみです。
クラスタに参加している他のノードのラック情報等を取得したい際はgetRack, getDatacenterメソッドを利用します。

const endpoint = '127.0.0.1';
client.invoke('org.apache.cassandra.db:type=EndpointSnitchInfo', 'getDatacenter', [endpoint], javaDCName => {
    console.log(javaDCName.toString());
});

最後に、StorageServiceのEndpointToHostId属性などのようなjava.util.MapオブジェクトをJavaScript Objectとして扱えるようにパースする処理が度々必要になりましたので、そのイディオムのようなものも載せておきたいと思います。(もっと良い方法があったら教えてください!)

const convertedObject = {};
const entrySets = javaMap.entrySetSync().toArraySync();
for(const entry of entrySets) {
    convertedObject[entry.getKeySync()] = entry.getValueSync();
}

所感

node-jmxnode-javaがこの先もメンテナンスされていくのかは若干不安はありますが、これらのパッケージの利用でNode.jsからJMXを介した処理がとても簡単に実装できました。
当初child_processで本家nodetoolをシェルコマンドとして実行して結果をパースする方法を考えておりましたが、JMXを介して直接情報を取得する方法が分かりましたので、Cassandraの運用ツール開発にJava以外の選択肢が作れそうです。
本家nodetoolにはJSON/YAMLフォーマット出力に対応していないコマンドがあったり、上でも触れたとおりパフォーマンスや信頼性に満足できないところがあったりしたので、しばらくはNode.js向けの代替モジュールとして前述のパッケージ開発を続けていきたいと思います。

参考

nodetoolユーティリティ
nodetool status
GitHub: Apache Cassandra
ASF JIRA Cassandra: improvement of nodetool status -r
GitHub: node-jmx
GitHub: node-java
GitHub: cassandra-nodetool

1
0
0

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
  3. You can use dark theme
What you can do with signing up
1
0