Grid topology monitoring
From JPPF 6.2 Documentation
|
Main Page > Development guide > Grid topology monitoring |
As of JPPF v5.0, the package org.jppf.client.monitoring.topology provides an API to explore and monitor the topology of a JPPF grid, such as discovered from a JPPF client's perspective. This API is used by the administration and monitoring console, and allows to explore the topology as well as subscribe to notifications of the changes that occur, such as new server being brought online, nodes started or terminated, etc.
1 Building and exploring the topology
The entry point for this API is the TopologyManager class. A TopologyManager uses a JPPFClient to discover its connections to one or more drivers, and queries these drivers via the management API to obtain information about their attached JPPF nodes and other peer drivers they are connected to. All this information is used to build a model of the JPPF grid topology as a tree. For example, with a grid that has two drivers connected to each other, we would have a representation like this:
The dotted lines emphasize the fact that a peer is actually a "virtual" component which references a real driver. Peer objects are here to allow representing the topology as a tree rather than as a graph, which makes their usage a lot easier.
You may notice that this is very similar to the "toplogy tree" view of the administration console:
This is not a coincidence: the adminstration console indeed uses its own instance of TopologyManager and the tree view is a direct mapping of the topology to a tree-like swing component.
The model representing the components of the JPPF toplogy is a class hierarchy whose base super class is the abtract class AbstractTopologyComponent, which provides an API to navigate within the tree strutcture, along with ways to identify each component and methods to retrieve information that are common to drivers and nodes.
The object model is as follows:
AbstractComponent provides the API to navigate a tree of elements uniquely identified with a uuid:
public abstract class AbstractComponent<E extends AbstractComponent> { // Get the parent of this compponent public synchronized E getParent() { // Get the number of children of this component public synchronized int getChildCount() // Get the children of this component public synchronized List<E> getChildren() // Get the child with the specified uuid public synchronized E getChild(final String uuid) // Get the uuid of this ocmponent public synchronized String getUuid() // Get a user-friendly representation of this topology component public String getDisplayName() }
AbstractTopologyComponent is defined as follows:
public abstract class AbstractTopologyComponent extends AbstractComponent<AbstractTopologyComponent> { // Whether this object represents a driver public boolean isDriver() // Whether this object represents a node public boolean isNode() // Whether this object represents a peer driver public boolean isPeer() // Get the object describing the health of a node or driver public HealthSnapshot getHealthSnapshot() // Get the management information for this component public JPPFManagementInfo getManagementInfo() // Get a user-friendly representation of this component public String getDisplayName() }
TopologyDriver provides the following additional methods:
public class TopologyDriver extends AbstractTopologyComponent { // Get the JMX connection wrapper public JMXDriverConnectionWrapper getJmx() // Get the driver connection public JPPFClientConnection getConnection() // Get a proxy to the MBean that forwards node management requests public JPPFNodeForwardingMBean getForwarder() // Get a proxy to the jobs monitoring MBean public DriverJobManagementMBean getJobManager() // Get a proxy the diagnostics MBean for this driver public DiagnosticsMBean getDiagnostics() }
TopologyNode provides two specialized methods to query the node's state:
public class TopologyNode extends AbstractTopologyComponent { // Get the object describing the current state of a node public JPPFNodeState getNodeState() // Get the number of slaves for a master node (node provisioning) public int getNbSlaveNodes() }
Whereas TopologyPeer does not provide any additional method, since its uuid is all that is needed:
public class TopologyPeer extends TopologyNode { }
The root of the tree is our TopologyManager, defined as follows:
public class TopologyManager implements ClientListener { // Create a topology manager with a new JPPFClient public TopologyManager() // Create a toplogy manager with the specified JPPFClient public TopologyManager(JPPFClient client) // Get the JPPF client public JPPFClient getJPPFClient() // Get all drivers public List<TopologyDriver> getDrivers() // Get all nodes public List<TopologyNode> getNodes() // Get all peers public List<TopologyPeer> getPeers() // Get a driver from its uuid public TopologyDriver getDriver(String uuid) // Get a node from its uuid public TopologyNode getNode(String uuid) // Get a peer from its uuid public TopologyPeer getPeer(String uuid) // Get a node or peer from its uuid public TopologyNode getNodeOrPeer(String uuid) // Get the number of drivers public int getDriverCount() // Get the number of nodes public int getNodeCount() // Get the number of peers public int getPeerCount() // Get and set the node filter public NodeSelector getNodeFilter() public void setNodeFilter(NodeSelector selector) // Get the nodes that are slaves of the specified master node, if any public List<TopologyNode> getSlaveNodes(String masterNodeUuid) }
According to this, the following code example can be used to visit the entire tree:
// this creates a new JPPFClient accessible with manager.getJPPFClient() TopologyManager manager = new TopologyManager(); // iterate over the discovered drivers for (TopologyDriver driver: manager.getDrivers()) { // ... do something with the driver ... // iterate of the nodes and peers for this driver for (AbstractTopologyComponent comp: driver.getChildren()) { if (comp.isNode()) { TopologyNode node = (TopologyNode) comp; // ... do something with the node ... } else { // if (comp.isPeer()) TopologyPeer peer = (TopologyPeer) comp; // retrieve the actual driver the peer refers to TopologyDriver actualDriver = manager.getDriver(peer.getUuid()); // ... do something with the peer ... } } }
A TopologyManager instance also automatically refreshes the states of the nodes, along with the JVM health snapshots of the drivers and nodes. The interval between refreshes is determined via the value of the following configuration properties:
# refresh interval in millis for the grid topology. # this is the interval between 2 successive runs of the task that refreshes the # topology via JMX requests; defaults to 1000 jppf.admin.refresh.interval.topology = 1000 # refresh interval for the JVM health snapshots; defaults to 1000 # this is the interval between 2 successive runs of the task that refreshes # the JVM health snapshots via JMX requests jppf.admin.refresh.interval.health = 1000
These values can be set both in a configuration file and programmatically, for instance:
TypedProperties config = JPPFConfiguration.getProperties(); // refresh the nodes states every 3 seconds config.setLong("jppf.admin.refresh.interval.topology", 3000L); // refresh the JMV health snapshots every 5 seconds config.setLong("jppf.admin.refresh.interval.health", 5000L); TopologyManager manager = new TopologyManager();
2 Receiving notifications of changes in the topology
It is possible to subscribe to topology change events emitted by a TopologyManager, using an implementation of the TopologyListener interface as parameter of the related constructors and methods in TopologyManager:
public class TopologyManager implements ClientListener { // Create a topology manager with a new JPPFClient // and add the specified listeners immediately public TopologyManager(TopologyListener...listeners) // Create a topology manager with the specified JPPFClient // and add the specified listeners immediately public TopologyManager(JPPFClient client, TopologyListener...listeners) // Add a topology change listener public void addTopologyListener(TopologyListener listener) // Remove a topology change listener public void removeTopologyListener(TopologyListener listener) }
TopologyListener is defined as follows:
public interface TopologyListener extends EventListener { // Called when a driver is discovered void driverAdded(TopologyEvent event); // Called when a driver is terminated void driverRemoved(TopologyEvent event); // Called when the state of a driver has changed void driverUpdated(TopologyEvent event); // Called when a node is added to a driver void nodeAdded(TopologyEvent event); // Called when a node is removed from a driver void nodeRemoved(TopologyEvent event); // Called when the state of a node has changed void nodeUpdated(TopologyEvent event); }
As we can see, each notification is encapsulated in a TopologyEvent object:
public class TopologyEvent extends EventObject { // Get the driver data public TopologyDriver getDriver() // Get the related node or peer public TopologyNode getNodeOrPeer() // Get the topology manager which emitted this event public TopologyManager getTopologyManager() }
Please note that, for the driverAdded(...), driverRemoved(...) and driverUpdated(...) notifications, the corresponding event's getNodeOrPeer() method will always return null.
Additionally, if you do not wish to override all the methods of the TopologyListener interface, you can instead extend the class TopologyListenerAdapter, which provides an empty implementation of each method.
Here is a listener implementation that prints topology changes to the console:
public class MyListener extends TopologyListenerAdapter { @Override public void driverAdded(TopologyEvent e) { System.out.printf("added driver %s%n", e.getDriver().getDisplayName()); } @Override public void driverRemoved(TopologyEvent e) { System.out.printf("removed driver %s%n", e.getDriver().getDisplayName()); } @Override public void nodeAdded(TopologyEvent e) { TopologyNode node = e.getNodeOrPeer(); System.out.printf("added %s %s to driver %s%n", (node.isNode() ? "node" : "peer"), node.getDisplayName(), e.getDriver().getDisplayName()); } @Override public void nodeRemoved(TopologyEvent e) { TopologyNode node = e.getNodeOrPeer(); System.out.printf("removed %s %s from driver %s%n", (node.isNode() ?"node" :"peer"), node.getDisplayName(), e.getDriver().getDisplayName()); } }
To subscribe this listener to topology changes:
TopologyManager manager = new TopologyManager(); MyListener myListener = new MyListener(); manager.addTopologyListener(myListener);
or, in a single statement:
TopologyManager manager = new TopologyManager(new MyListener());
3 Node filtering
TopologyManager provides an API to filter the nodes in the grid topology, based on node selectors:
public class TopologyManager implements ClientListener { // Get the node filter public NodeSelector getNodeFilter() // Set the node filter public void setNodeFilter(NodeSelector selector) }
To activate the node filtering, you only need to call setNodeFilter() with a non-null NodeSelector. Inversely, calling setNodeFilter() with a null selector will deactivate the node filtering. Here is an example which filters out all slave nodes in the topology:
TopologyManager manager = ...; // a policy that filters out slave nodes ExecutionPolicy noSlavePolicy = new Equal("jppf.node.provisioning.slave", false); NodeSelector selector = new ExecutionPolicySelector(noSlavePolicy); // activate node filtering with our policy manager.setNodeFilter(selector);
When node filtering is active:
- only the nodes that match the selector will be available via the TopologyManager API
- events will be emitted only for the nodes that match the selector
The administration console also provides a UI to enter an execution policy in XML format to use as a filter:
Main Page > Development guide > Grid topology monitoring |