HDFS-2083 Query JMX statistics over http via JMXJsonServlet. (tanping)
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1138645 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
c7c8f2fe48
commit
15e68cb374
|
@ -292,6 +292,8 @@ Trunk (unreleased changes)
|
||||||
|
|
||||||
HDFS-2055. Add hflush support to libhdfs. (Travis Crawford via eli)
|
HDFS-2055. Add hflush support to libhdfs. (Travis Crawford via eli)
|
||||||
|
|
||||||
|
HDFS-2083. Query JMX statistics over http via JMXJsonServlet. (tanping)
|
||||||
|
|
||||||
IMPROVEMENTS
|
IMPROVEMENTS
|
||||||
|
|
||||||
HDFS-1875. MiniDFSCluster hard-codes dfs.datanode.address to localhost
|
HDFS-1875. MiniDFSCluster hard-codes dfs.datanode.address to localhost
|
||||||
|
|
|
@ -17,8 +17,12 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.hdfs.server.namenode;
|
package org.apache.hadoop.hdfs.server.namenode;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -27,13 +31,7 @@ import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.management.JMX;
|
|
||||||
import javax.management.MBeanServerConnection;
|
|
||||||
import javax.management.MalformedObjectNameException;
|
import javax.management.MalformedObjectNameException;
|
||||||
import javax.management.ObjectName;
|
|
||||||
import javax.management.remote.JMXConnector;
|
|
||||||
import javax.management.remote.JMXConnectorFactory;
|
|
||||||
import javax.management.remote.JMXServiceURL;
|
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
@ -43,24 +41,27 @@ import org.apache.hadoop.hdfs.DFSConfigKeys;
|
||||||
import org.apache.hadoop.hdfs.DFSUtil;
|
import org.apache.hadoop.hdfs.DFSUtil;
|
||||||
import org.apache.hadoop.hdfs.protocol.DatanodeInfo.AdminStates;
|
import org.apache.hadoop.hdfs.protocol.DatanodeInfo.AdminStates;
|
||||||
import org.apache.hadoop.util.StringUtils;
|
import org.apache.hadoop.util.StringUtils;
|
||||||
|
import org.codehaus.jackson.JsonNode;
|
||||||
import org.codehaus.jackson.map.ObjectMapper;
|
import org.codehaus.jackson.map.ObjectMapper;
|
||||||
import org.codehaus.jackson.type.TypeReference;
|
import org.codehaus.jackson.type.TypeReference;
|
||||||
import org.znerd.xmlenc.XMLOutputter;
|
import org.znerd.xmlenc.XMLOutputter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class generates the data that is needed to be displayed on cluster web
|
* This class generates the data that is needed to be displayed on cluster web
|
||||||
* console by connecting to each namenode through JMX.
|
* console.
|
||||||
*/
|
*/
|
||||||
@InterfaceAudience.Private
|
@InterfaceAudience.Private
|
||||||
class ClusterJspHelper {
|
class ClusterJspHelper {
|
||||||
private static final Log LOG = LogFactory.getLog(ClusterJspHelper.class);
|
private static final Log LOG = LogFactory.getLog(ClusterJspHelper.class);
|
||||||
public static final String OVERALL_STATUS = "overall-status";
|
public static final String OVERALL_STATUS = "overall-status";
|
||||||
public static final String DEAD = "Dead";
|
public static final String DEAD = "Dead";
|
||||||
|
private static final String JMX_QRY =
|
||||||
|
"/jmx?qry=Hadoop:service=NameNode,name=NameNodeInfo";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSP helper function that generates cluster health report. When
|
* JSP helper function that generates cluster health report. When
|
||||||
* encountering exception while getting Namenode status, the exception will
|
* encountering exception while getting Namenode status, the exception will
|
||||||
* be listed in the page with corresponding stack trace.
|
* be listed on the page with corresponding stack trace.
|
||||||
*/
|
*/
|
||||||
ClusterStatus generateClusterHealthReport() {
|
ClusterStatus generateClusterHealthReport() {
|
||||||
ClusterStatus cs = new ClusterStatus();
|
ClusterStatus cs = new ClusterStatus();
|
||||||
|
@ -79,26 +80,24 @@ class ClusterJspHelper {
|
||||||
NamenodeMXBeanHelper nnHelper = null;
|
NamenodeMXBeanHelper nnHelper = null;
|
||||||
try {
|
try {
|
||||||
nnHelper = new NamenodeMXBeanHelper(isa, conf);
|
nnHelper = new NamenodeMXBeanHelper(isa, conf);
|
||||||
NamenodeStatus nn = nnHelper.getNamenodeStatus();
|
String mbeanProps= queryMbean(nnHelper.httpAddress, conf);
|
||||||
|
NamenodeStatus nn = nnHelper.getNamenodeStatus(mbeanProps);
|
||||||
if (cs.clusterid.isEmpty() || cs.clusterid.equals("")) { // Set clusterid only once
|
if (cs.clusterid.isEmpty() || cs.clusterid.equals("")) { // Set clusterid only once
|
||||||
cs.clusterid = nnHelper.getClusterId();
|
cs.clusterid = nnHelper.getClusterId(mbeanProps);
|
||||||
}
|
}
|
||||||
cs.addNamenodeStatus(nn);
|
cs.addNamenodeStatus(nn);
|
||||||
} catch ( Exception e ) {
|
} catch ( Exception e ) {
|
||||||
// track exceptions encountered when connecting to namenodes
|
// track exceptions encountered when connecting to namenodes
|
||||||
cs.addException(isa.getHostName(), e);
|
cs.addException(isa.getHostName(), e);
|
||||||
continue;
|
continue;
|
||||||
} finally {
|
|
||||||
if (nnHelper != null) {
|
|
||||||
nnHelper.cleanup();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cs;
|
return cs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function that generates the decommissioning report.
|
* Helper function that generates the decommissioning report. Connect to each
|
||||||
|
* Namenode over http via JmxJsonServlet to collect the data nodes status.
|
||||||
*/
|
*/
|
||||||
DecommissionStatus generateDecommissioningReport() {
|
DecommissionStatus generateDecommissioningReport() {
|
||||||
String clusterid = "";
|
String clusterid = "";
|
||||||
|
@ -127,20 +126,17 @@ class ClusterJspHelper {
|
||||||
NamenodeMXBeanHelper nnHelper = null;
|
NamenodeMXBeanHelper nnHelper = null;
|
||||||
try {
|
try {
|
||||||
nnHelper = new NamenodeMXBeanHelper(isa, conf);
|
nnHelper = new NamenodeMXBeanHelper(isa, conf);
|
||||||
|
String mbeanProps= queryMbean(nnHelper.httpAddress, conf);
|
||||||
if (clusterid.equals("")) {
|
if (clusterid.equals("")) {
|
||||||
clusterid = nnHelper.getClusterId();
|
clusterid = nnHelper.getClusterId(mbeanProps);
|
||||||
}
|
}
|
||||||
nnHelper.getDecomNodeInfoForReport(statusMap);
|
nnHelper.getDecomNodeInfoForReport(statusMap, mbeanProps);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// catch exceptions encountered while connecting to namenodes
|
// catch exceptions encountered while connecting to namenodes
|
||||||
String nnHost = isa.getHostName();
|
String nnHost = isa.getHostName();
|
||||||
decommissionExceptions.put(nnHost, e);
|
decommissionExceptions.put(nnHost, e);
|
||||||
unreportedNamenode.add(nnHost);
|
unreportedNamenode.add(nnHost);
|
||||||
continue;
|
continue;
|
||||||
} finally {
|
|
||||||
if (nnHelper != null) {
|
|
||||||
nnHelper.cleanup();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateUnknownStatus(statusMap, unreportedNamenode);
|
updateUnknownStatus(statusMap, unreportedNamenode);
|
||||||
|
@ -262,38 +258,18 @@ class ClusterJspHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class for connecting to Namenode over JMX and get attributes
|
* Class for connecting to Namenode over http via JmxJsonServlet
|
||||||
* exposed by the MXBean.
|
* to get JMX attributes exposed by the MXBean.
|
||||||
*/
|
*/
|
||||||
static class NamenodeMXBeanHelper {
|
static class NamenodeMXBeanHelper {
|
||||||
private static final ObjectMapper mapper = new ObjectMapper();
|
private static final ObjectMapper mapper = new ObjectMapper();
|
||||||
private final InetSocketAddress rpcAddress;
|
|
||||||
private final String host;
|
private final String host;
|
||||||
private final Configuration conf;
|
private final String httpAddress;
|
||||||
private final JMXConnector connector;
|
|
||||||
private final NameNodeMXBean mxbeanProxy;
|
|
||||||
|
|
||||||
NamenodeMXBeanHelper(InetSocketAddress addr, Configuration conf)
|
NamenodeMXBeanHelper(InetSocketAddress addr, Configuration conf)
|
||||||
throws IOException, MalformedObjectNameException {
|
throws IOException, MalformedObjectNameException {
|
||||||
this.rpcAddress = addr;
|
|
||||||
this.host = addr.getHostName();
|
this.host = addr.getHostName();
|
||||||
this.conf = conf;
|
this.httpAddress = DFSUtil.getInfoServer(addr, conf, false);
|
||||||
int port = conf.getInt("dfs.namenode.jmxport", -1);
|
|
||||||
|
|
||||||
JMXServiceURL jmxURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://"
|
|
||||||
+ host + ":" + port + "/jmxrmi");
|
|
||||||
connector = JMXConnectorFactory.connect(jmxURL);
|
|
||||||
mxbeanProxy = getNamenodeMxBean();
|
|
||||||
}
|
|
||||||
|
|
||||||
private NameNodeMXBean getNamenodeMxBean()
|
|
||||||
throws IOException, MalformedObjectNameException {
|
|
||||||
// Get an MBeanServerConnection on the remote VM.
|
|
||||||
MBeanServerConnection remote = connector.getMBeanServerConnection();
|
|
||||||
ObjectName mxbeanName = new ObjectName(
|
|
||||||
"Hadoop:service=NameNode,name=NameNodeInfo");
|
|
||||||
|
|
||||||
return JMX.newMXBeanProxy(remote, mxbeanName, NameNodeMXBean.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the map corresponding to the JSON string */
|
/** Get the map corresponding to the JSON string */
|
||||||
|
@ -305,10 +281,9 @@ class ClusterJspHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process JSON string returned from JMX connection to get the number of
|
* Get the number of live datanodes.
|
||||||
* live datanodes.
|
|
||||||
*
|
*
|
||||||
* @param json JSON output from JMX call that contains live node status.
|
* @param json JSON string that contains live node status.
|
||||||
* @param nn namenode status to return information in
|
* @param nn namenode status to return information in
|
||||||
*/
|
*/
|
||||||
private static void getLiveNodeCount(String json, NamenodeStatus nn)
|
private static void getLiveNodeCount(String json, NamenodeStatus nn)
|
||||||
|
@ -333,11 +308,10 @@ class ClusterJspHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count the number of dead datanode based on the JSON string returned from
|
* Count the number of dead datanode.
|
||||||
* JMX call.
|
|
||||||
*
|
*
|
||||||
* @param nn namenode
|
* @param nn namenode
|
||||||
* @param json JSON string returned from JMX call
|
* @param json JSON string
|
||||||
*/
|
*/
|
||||||
private static void getDeadNodeCount(String json, NamenodeStatus nn)
|
private static void getDeadNodeCount(String json, NamenodeStatus nn)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
@ -358,51 +332,53 @@ class ClusterJspHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getClusterId() {
|
public String getClusterId(String props) throws IOException {
|
||||||
return mxbeanProxy.getClusterId();
|
return getProperty(props, "ClusterId").getTextValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public NamenodeStatus getNamenodeStatus()
|
public NamenodeStatus getNamenodeStatus(String props) throws IOException,
|
||||||
throws IOException, MalformedObjectNameException {
|
MalformedObjectNameException, NumberFormatException {
|
||||||
NamenodeStatus nn = new NamenodeStatus();
|
NamenodeStatus nn = new NamenodeStatus();
|
||||||
nn.host = host;
|
nn.host = host;
|
||||||
nn.filesAndDirectories = mxbeanProxy.getTotalFiles();
|
nn.filesAndDirectories = getProperty(props, "TotalFiles").getLongValue();
|
||||||
nn.capacity = mxbeanProxy.getTotal();
|
nn.capacity = getProperty(props, "Total").getLongValue();
|
||||||
nn.free = mxbeanProxy.getFree();
|
nn.free = getProperty(props, "Free").getLongValue();
|
||||||
nn.bpUsed = mxbeanProxy.getBlockPoolUsedSpace();
|
nn.bpUsed = getProperty(props, "BlockPoolUsedSpace").getLongValue();
|
||||||
nn.nonDfsUsed = mxbeanProxy.getNonDfsUsedSpace();
|
nn.nonDfsUsed = getProperty(props, "NonDfsUsedSpace").getLongValue();
|
||||||
nn.blocksCount = mxbeanProxy.getTotalBlocks();
|
nn.blocksCount = getProperty(props, "TotalBlocks").getLongValue();
|
||||||
nn.missingBlocksCount = mxbeanProxy.getNumberOfMissingBlocks();
|
nn.missingBlocksCount = getProperty(props, "NumberOfMissingBlocks")
|
||||||
nn.free = mxbeanProxy.getFree();
|
.getLongValue();
|
||||||
nn.httpAddress = DFSUtil.getInfoServer(rpcAddress, conf, false);
|
nn.httpAddress = httpAddress;
|
||||||
getLiveNodeCount(mxbeanProxy.getLiveNodes(), nn);
|
getLiveNodeCount(getProperty(props, "LiveNodes").getValueAsText(), nn);
|
||||||
getDeadNodeCount(mxbeanProxy.getDeadNodes(), nn);
|
getDeadNodeCount(getProperty(props, "DeadNodes").getValueAsText(), nn);
|
||||||
return nn;
|
return nn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect to namenode to get decommission node information.
|
* Get the decommission node information.
|
||||||
* @param statusMap data node status map
|
* @param statusMap data node status map
|
||||||
* @param connector JMXConnector
|
* @param props string
|
||||||
*/
|
*/
|
||||||
private void getDecomNodeInfoForReport(
|
private void getDecomNodeInfoForReport(
|
||||||
Map<String, Map<String, String>> statusMap) throws IOException,
|
Map<String, Map<String, String>> statusMap, String props)
|
||||||
MalformedObjectNameException {
|
throws IOException, MalformedObjectNameException {
|
||||||
getLiveNodeStatus(statusMap, host, mxbeanProxy.getLiveNodes());
|
getLiveNodeStatus(statusMap, host, getProperty(props, "LiveNodes")
|
||||||
getDeadNodeStatus(statusMap, host, mxbeanProxy.getDeadNodes());
|
.getValueAsText());
|
||||||
getDecommissionNodeStatus(statusMap, host, mxbeanProxy.getDecomNodes());
|
getDeadNodeStatus(statusMap, host, getProperty(props, "DeadNodes")
|
||||||
|
.getValueAsText());
|
||||||
|
getDecommissionNodeStatus(statusMap, host,
|
||||||
|
getProperty(props, "DecomNodes").getValueAsText());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process the JSON string returned from JMX call to get live datanode
|
* Store the live datanode status information into datanode status map and
|
||||||
* status. Store the information into datanode status map and
|
* DecommissionNode.
|
||||||
* Decommissionnode.
|
|
||||||
*
|
*
|
||||||
* @param statusMap Map of datanode status. Key is datanode, value
|
* @param statusMap Map of datanode status. Key is datanode, value
|
||||||
* is an inner map whose key is namenode, value is datanode status.
|
* is an inner map whose key is namenode, value is datanode status.
|
||||||
* reported by each namenode.
|
* reported by each namenode.
|
||||||
* @param namenodeHost host name of the namenode
|
* @param namenodeHost host name of the namenode
|
||||||
* @param decomnode update Decommissionnode with alive node status
|
* @param decomnode update DecommissionNode with alive node status
|
||||||
* @param json JSON string contains datanode status
|
* @param json JSON string contains datanode status
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
|
@ -434,15 +410,14 @@ class ClusterJspHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process the JSON string returned from JMX connection to get the dead
|
* Store the dead datanode information into datanode status map and
|
||||||
* datanode information. Store the information into datanode status map and
|
* DecommissionNode.
|
||||||
* Decommissionnode.
|
|
||||||
*
|
*
|
||||||
* @param statusMap map with key being datanode, value being an
|
* @param statusMap map with key being datanode, value being an
|
||||||
* inner map (key:namenode, value:decommisionning state).
|
* inner map (key:namenode, value:decommisionning state).
|
||||||
* @param host datanode hostname
|
* @param host datanode hostname
|
||||||
* @param decomnode
|
* @param decomnode DecommissionNode
|
||||||
* @param json
|
* @param json String
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private static void getDeadNodeStatus(
|
private static void getDeadNodeStatus(
|
||||||
|
@ -478,14 +453,13 @@ class ClusterJspHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We process the JSON string returned from JMX connection to get the
|
* Get the decommisioning datanode information.
|
||||||
* decommisioning datanode information.
|
|
||||||
*
|
*
|
||||||
* @param dataNodeStatusMap map with key being datanode, value being an
|
* @param dataNodeStatusMap map with key being datanode, value being an
|
||||||
* inner map (key:namenode, value:decommisionning state).
|
* inner map (key:namenode, value:decommisionning state).
|
||||||
* @param host datanode
|
* @param host datanode
|
||||||
* @param decomnode Decommissionnode
|
* @param decomnode DecommissionNode
|
||||||
* @param json JSON string returned from JMX connection
|
* @param json String
|
||||||
*/
|
*/
|
||||||
private static void getDecommissionNodeStatus(
|
private static void getDecommissionNodeStatus(
|
||||||
Map<String, Map<String, String>> dataNodeStatusMap, String host,
|
Map<String, Map<String, String>> dataNodeStatusMap, String host,
|
||||||
|
@ -508,19 +482,6 @@ class ClusterJspHelper {
|
||||||
dataNodeStatusMap.put(dn, nnStatus);
|
dataNodeStatusMap.put(dn, nnStatus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void cleanup() {
|
|
||||||
if (connector != null) {
|
|
||||||
try {
|
|
||||||
connector.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
// log failure of close jmx connection
|
|
||||||
LOG.warn("Unable to close JMX connection. "
|
|
||||||
+ StringUtils.stringifyException(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -893,4 +854,47 @@ class ClusterJspHelper {
|
||||||
doc.endTag(); // message
|
doc.endTag(); // message
|
||||||
doc.endTag(); // cluster
|
doc.endTag(); // cluster
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read in the content from a URL
|
||||||
|
* @param url URL To read
|
||||||
|
* @return the text from the output
|
||||||
|
* @throws IOException if something went wrong
|
||||||
|
*/
|
||||||
|
private static String readOutput(URL url) throws IOException {
|
||||||
|
StringBuilder out = new StringBuilder();
|
||||||
|
URLConnection connection = url.openConnection();
|
||||||
|
BufferedReader in = new BufferedReader(
|
||||||
|
new InputStreamReader(
|
||||||
|
connection.getInputStream()));
|
||||||
|
String inputLine;
|
||||||
|
while ((inputLine = in.readLine()) != null) {
|
||||||
|
out.append(inputLine);
|
||||||
|
}
|
||||||
|
in.close();
|
||||||
|
return out.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String queryMbean(String httpAddress, Configuration conf)
|
||||||
|
throws IOException {
|
||||||
|
URL url = new URL("http://"+httpAddress+JMX_QRY);
|
||||||
|
return readOutput(url);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* In order to query a namenode mxbean, a http connection in the form of
|
||||||
|
* "http://hostname/jmx?qry=Hadoop:service=NameNode,name=NameNodeInfo"
|
||||||
|
* is sent to namenode. JMX attributes are exposed via JmxJsonServelet on
|
||||||
|
* the namenode side.
|
||||||
|
*/
|
||||||
|
private static JsonNode getProperty(String props, String propertyname)
|
||||||
|
throws IOException {
|
||||||
|
if (props == null || props.equals("") || propertyname == null
|
||||||
|
|| propertyname.equals("")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ObjectMapper m = new ObjectMapper();
|
||||||
|
JsonNode rootNode = m.readValue(props, JsonNode.class);
|
||||||
|
JsonNode jn = rootNode.get("beans").get(0).get(propertyname);
|
||||||
|
return jn;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue