Using JMX and its use in Apache Cassandra and Apache Artemis
JMX (Java Management Extension)
JMX is used to manage Java applications remotely by using the JMX API, with remote management agent like JConsole.
JMX provides a standard way of dynamically monitoring and managing application resources.
Using JMX MBean server we can also update the values of the variable during runtime.
The Java code at the bottom will demonstrate how to update the variable during runtime, using JConsole with help of JMX MBean server.
Use Case of JMX
Applications like Apache Cassandra, Apache Artemis provides JMX service for management and monitoring the process.
Refer my Stackoverflow link for a implementing monitor logic for queue count from Apache Artemis using JMX and Elastic Search.
The Java code below is an example of how to configure an application with JMX MBean server.
- In the example the Monitor java bean, will return the duration from when the JVM started in seconds.
- We can update the Counter variable using operations of JConsole like in below snapshot. By providing a value in the text box and clicking the method name will update the variable in runtime.
- Once updated the counter variable will be updated refer Attribute section. In this case 12 was provided as input and updated as 12.
Upon invoking the externalStopFlag
with value of true, the while loop will be stop so as the program will stop.
Note: In the below code, only when using system.out.println to print that externalFlag from the monitor class I observed consistent shutdowns using JConsole.
- In order to enable the JMX, we have to start the Java application with below JVM arguments
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=9999 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false
Note:
- When using
-Dcom.sun.management.jmxremote.port=0
, per openjdk documentation it is an undocumented feature which will cause the jvm to ask the OS to assign an ephemeral port. This will guarantee an unused port.
- Open Jconsole using
jconsole
command, if the Java process is running in the local VM, then the process id will be listed when selecting the connection. - If we use the above JVM argument to start the process, we can use
localhost:9090
to connect.
Note: Using jconsole to connect services remotely we can use any of the one options and URL looks like,
service:jmx:rmi:///jndi/rmi://<host-name>:<port>/server
- for Apache Artemisservice:jmx:rmi:///jndi/rmi://<host-name>:<port>/jmxrmi
- for Cassandra (if the node has a username password use that to connect using JConsole)
Additional tip:
- In case of Cassandra, under Mbeans we can expand
org.apache.cassandra.net
- FailureDetector
- Attributes
- SimpleStates
<to display the current status (UP/DOWN) of the node connected>
If we want to connect using JMX client code below is how the sample code for client looks, but for this demonstration I only used JConsole.
MBeanServer mbs = MBeanServerFactory.createMBeanServer();
JMXServiceURL url =
new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/server");
JMXConnectorServer cs =
JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
cs.start();
//wait for some duration
cs.stop();
Below is the Java code example for using JMX
- Defining MBean bean, based on the design the Java class name should be end with MBean.
package jmxdemo.jmxdemo;
public interface MonitorMBean {
//Attribute will display the time in second from when the process started
String getProcessDuration();
//Flag used to stop the process when updated to true form Jconsole
void exteralStopFlag(boolean stopFlag);
// stored the counter value when input provided from Jconsole
void externalCounter(int i);
int getCounter();
boolean getStopFlag();
}
- Implement the MBean which will can be updated remotely.
package jmxdemo.jmxdemo;
public class Monitor implements MonitorMBean{
static long startUpTime;
static {
startUpTime = System.currentTimeMillis();
}
boolean stopFlag =false;
int externalInput=0;
@Override
public String getProcessDuration() {
long currentTime = System.currentTimeMillis();
return (currentTime-startUpTime)/(60*60) +" seconds";
}
@Override
public boolean getStopFlag() {
return stopFlag;
}
@Override
public void exteralStopFlag(boolean stopFlag) {
this.stopFlag = stopFlag;
}
@Override
public void externalCounter(int i) {
externalInput +=i;
}
@Override
public int getCounter() {
return this.externalInput;
}
public boolean checkStopFlag() {
return this.stopFlag;
}
}
- Helper class to register the bean to MBean server
package jmxdemo.jmxdemo;
import java.lang.management.ManagementFactory;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
public class Helper {
void createAndRegisterInJMX(Monitor monitor){
try {
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName objectName = new ObjectName("jmxdemo.jmxdemo:type=monitor");
// another alternate param for ObjectName jmxdemo.jmxdemo:type=basic,mame=monitor
mBeanServer.registerMBean(monitor, objectName);
}
catch (MalformedObjectNameException | InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException e) {
System.err.println("Error occurred during registring MBean");
e.printStackTrace();
}
}
}
- Main class
package jmxdemo.jmxdemo;
public class App {
public static void main(String[] args) {
//MBean instantiation
Monitor monitor = new Monitor();
//Helper class to register the MBean with JMX agent
Helper helper = new Helper();
helper.createAndRegisterInJMX(monitor);
boolean stopProcess = false;
System.out.println("Process started...");
// while loop to demonstrate the flow
while(!stopProcess) {
status = monitor.checkStopFlag();
//Note:
// For some reason, when we don't use the system.out within this block, the loop is not breaking.
// when updating the variable to true from jconsole, by printing the return value of monitor class
// .checkStopFlag() the values are reflected and while loop breaks to stop the process
System.out.println(status);
}
System.out.println("shutting down..");
}
}