通过Node.js通过JMX获取Cassandra的信息

Cassandra自带了一个名为nodetool的工具,通过JMX来管理Cassandra集群。
有一种情况是想要在Node.js上利用nodetool获取的信息,于是在寻找方法时发现了一个名为node-jmx的用于与JMX通信的软件包。
通过使用node-jmx,学到了获取Cassandra信息的方法,并将其总结如下。

关于node-jmx

安装

使用node-jmx需要JDK。似乎node-jmx在内部使用了node-java,所以在安装时遇到问题时,最好参考node-java的安装指南。

由于使用了node-gyp,所以需要预先安装python2.7、make和g++。

如果准备工作完成,使用以下命令将可以使用node-jmx。

npm instal jmx

用法 fǎ)

基本上按照官方文件操作,但是从客户端返回的值似乎是一个Java对象,所以在获取实际值时需要进行前处理。
以下是获取HeapMemoryUsage的代码。HeapMemoryUsage是javax.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();

在Node.js上实现nodetool status

我打算先尝试使用node-jmx来实现通过nodetool status获取Cassandra集群信息的方法,并对其进行总结。

nodetool status是什么?

这个命令可以收集并显示参与集群的每个节点的信息。( 参考 )
特别是节点的状态(上/下)这个项目,在执行nodetool status命令时应该是最重要的信息之一。

实现了本家 nodetool 的状态命令。

这是GitHub上的一个项目,主要总结了与主要的Apache Cassandra工具nodetool status相关的源代码。

    • org/apache/cassandra/tools/nodetool/Status.java

 

    • org/apache/cassandra/tools/NodeProbe.java

 

    • org/apache/cassandra/service/StorageService.java

 

    org/apache/cassandra/locator/EndpointSnitchInfo.java

如果我没记错的话,大概是在这附近,如果有任何错误或者遗漏,请谅解。

大致的内容是,通过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:nodetool status -r的改进)← 看起来好像被遗忘了,但它是否会被反映呢…

我在Node.js中尝试实现nodetool status。

突然但我将实施的内容制作成了npm包并进行了公开试用。

npmjs: cassandra-nodetool 的包在 GitHub 上也有相应的代码。

精华

在使用node-jmx获取StorageService和EndpointSnitchInfo时,MBean的名称如下所示。

    • StorageService: ‘org.apache.cassandra.db:type=StorageService’

EndpointSnitchInfo: ‘org.apache.cassandra.db:type=EndpointSnitchInfo’

因此,要获取参与集群的节点列表,可以使用以下代码。
集群中参与的节点列表存储在StorageService的LiveNodes属性中,通过获取此属性来实现。

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

要获取Unreachable节点(无法连接的节点)的列表,只需获取UnreachableNodes属性即可。
如前所述,StorageService还管理着各种不同的信息,如果想要了解属性的列表,建议参考源代码或者通过jconsole连接并直接搜索。

可以在示例中使用 EndpointSnitchInfo 来获取数据中心的名称。

在 EndpointSnitchInfo 中有属性 Rack 和 Datacenter,您也可以使用它们来获取数据中心的名称和机架名称。

但是,您只能从已连接到 JMX 的节点获取到节点的数据中心名称和机架名称。

如果您想获取集群中其他参与的节点的机架信息等,请使用 getRack 和 getDatacenter 方法。

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

最后,我经常需要将java.util.Map对象,如StorageService的EndpointToHostId属性,解析为JavaScript对象。所以,我想分享一下关于这个操作的示例代码(如果有更好的方法,请告诉我!)。

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

所感 – “感受”

我稍微有些担心node-jmx和node-java以后是否会继续维护,但通过使用这些包,我很容易地实现了通过Node.js与JMX进行交互的处理。
最初我考虑使用child_process来执行原始的nodetool命令并解析结果,但现在我知道了可以直接通过JMX获取信息的方法,所以在开发Cassandra运维工具时可以考虑Java以外的选择。
原始的nodetool可能不支持以JSON/YAML格式输出的命令,而且正如之前提到的,它的性能和可靠性也无法令人满意,所以在一段时间内,我希望继续开发前述的这些包作为针对Node.js的替代模块。

请参考

nodetool实用工具
nodetool状态
GitHub:Apache Cassandra
ASF JIRA Cassandra:改进nodetool状态 -r
GitHub:node-jmx
GitHub:node-java
GitHub:cassandra-nodetool