package org.springside.modules.metrics.exporter; import java.lang.management.ManagementFactory; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import javax.management.InstanceAlreadyExistsException; import javax.management.InstanceNotFoundException; import javax.management.JMException; import javax.management.MBeanRegistrationException; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.ObjectInstance; import javax.management.ObjectName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springside.modules.metrics.Exporter; import org.springside.modules.metrics.MetricRegistry; import org.springside.modules.metrics.MetricRegistryListener; import org.springside.modules.metrics.metric.Counter; import org.springside.modules.metrics.metric.Gauge; import org.springside.modules.metrics.metric.Histogram; import org.springside.modules.metrics.metric.Timer; /** * 以JMX形式,暴露所有Metrics. * * 为每一个Metrics注册一个MBean, 实现MetricRegistryListener接口感知Metrics的变化. * * MBean名字为 ${dommianName}:name=${metriceName} * 属性名见JmxGauge, JmxCounter, JmxHistogram,JmxTimer四个类的Getter函数名. * * TODO: 另一个将所有Metrics暴露成一个JSON字符串的实现. */ public class JmxExporter implements Exporter, MetricRegistryListener { private static Logger logger = LoggerFactory.getLogger(JmxExporter.class); private MBeanServer mBeanServer; private MetricRegistry registry; private String domain; private final Map<ObjectName, ObjectName> registered; public JmxExporter(String domain, MetricRegistry registry) { this.mBeanServer = ManagementFactory.getPlatformMBeanServer(); this.registry = registry; this.domain = domain; this.registered = new ConcurrentHashMap<ObjectName, ObjectName>(); registry.addListener(this); } /** * 将Registry中所有的Metrics注册为独立的MBean */ public void initMBeans() { Map<String, Gauge> gauges = registry.getGauges(); for (Entry<String, Gauge> entry : gauges.entrySet()) { onGaugeAdded(entry.getKey(), entry.getValue()); } Map<String, Counter> counters = registry.getCounters(); for (Entry<String, Counter> entry : counters.entrySet()) { onCounterAdded(entry.getKey(), entry.getValue()); } Map<String, Histogram> histograms = registry.getHistograms(); for (Entry<String, Histogram> entry : histograms.entrySet()) { onHistogramAdded(entry.getKey(), entry.getValue()); } Map<String, Timer> timers = registry.getTimers(); for (Entry<String, Timer> entry : timers.entrySet()) { onTimerAdded(entry.getKey(), entry.getValue()); } } /** * 将Registry中所有的Metrics取消MBean注册 */ public void destroyMBeans() { Map<String, Gauge> gauges = registry.getGauges(); for (String key : gauges.keySet()) { onGaugeRemoved(key); } Map<String, Counter> counters = registry.getCounters(); for (String key : counters.keySet()) { onCounterRemoved(key); } Map<String, Histogram> histograms = registry.getHistograms(); for (String key : histograms.keySet()) { onHistogramRemoved(key); } Map<String, Timer> timers = registry.getTimers(); for (String key : timers.keySet()) { onTimerRemoved(key); } } private void registerMBean(ObjectName objectName, Object mBean) { try { ObjectInstance objectInstance = mBeanServer.registerMBean(mBean, objectName); if (objectInstance != null) { // the websphere mbeanserver rewrites the objectname to include cell, node & server info // make sure we capture the new objectName for unregistration registered.put(objectName, objectInstance.getObjectName()); } else { registered.put(objectName, objectName); } } catch (InstanceAlreadyExistsException e) { logger.debug("Unable to register already exist mbean:" + objectName, e); } catch (JMException e) { logger.warn("Unable to register:" + objectName, e); } } private void unregisterMBean(ObjectName originalObjectName) { ObjectName storedObjectName = registered.remove(originalObjectName); try { if (storedObjectName != null) { mBeanServer.unregisterMBean(storedObjectName); } else { mBeanServer.unregisterMBean(originalObjectName); } } catch (InstanceNotFoundException e) { logger.debug("Unable to unregister:" + originalObjectName, e); } catch (MBeanRegistrationException e) { logger.warn("Unable to unregister:" + originalObjectName, e); } } private ObjectName createObjectName(String name) { try { return new ObjectName(this.domain, "name", name); } catch (MalformedObjectNameException e) { try { return new ObjectName(this.domain, "name", ObjectName.quote(name)); } catch (MalformedObjectNameException e1) { logger.warn("Unable to register {}", name, e1); throw new RuntimeException(e1); } } } @Override public void onGaugeAdded(String name, Gauge gauge) { ObjectName objectName = createObjectName(name); registerMBean(objectName, new JmxGauge(gauge, objectName)); } @Override public void onCounterAdded(String name, Counter counter) { ObjectName objectName = createObjectName(name); registerMBean(objectName, new JmxCounter(counter, objectName)); } @Override public void onHistogramAdded(String name, Histogram histogram) { ObjectName objectName = createObjectName(name); registerMBean(objectName, new JmxHistogram(histogram, objectName)); } @Override public void onTimerAdded(String name, Timer timer) { ObjectName objectName = createObjectName(name); registerMBean(objectName, new JmxTimer(timer, objectName)); } @Override public void onGaugeRemoved(String name) { ObjectName objectName = createObjectName(name); unregisterMBean(objectName); } @Override public void onCounterRemoved(String name) { ObjectName objectName = createObjectName(name); unregisterMBean(objectName); } @Override public void onHistogramRemoved(String name) { ObjectName objectName = createObjectName(name); unregisterMBean(objectName); } @Override public void onTimerRemoved(String name) { ObjectName objectName = createObjectName(name); unregisterMBean(objectName); } ///////// MBean定义/////// public interface MetricMBean { ObjectName objectName(); } public interface JmxGaugeMBean extends MetricMBean { Number getValue(); } public interface JmxCounterMBean extends MetricMBean { long getLatestCount(); long getLatestRate(); long getTotalCount(); long getAvgRate(); } public interface JmxHistogramMBean extends MetricMBean { long getMin(); long getMax(); double getAvg(); Map<Double,Long> getPcts(); } public interface JmxTimerMBean extends MetricMBean { long getLatestCount(); long getLatestRate(); long getTotalCount(); long getAvgRate(); long getMinLatency(); long getMaxLatency(); double getAvgLatency(); Map<Double,Long> getPcts(); } private abstract static class AbstractBean implements MetricMBean { private final ObjectName objectName; AbstractBean(ObjectName objectName) { this.objectName = objectName; } @Override public ObjectName objectName() { return objectName; } } /** * Gauge类型的JmxMbean, JMX属性名见getter函数名. */ private static class JmxGauge extends AbstractBean implements JmxGaugeMBean { private final Gauge metric; public JmxGauge(Gauge gauge, ObjectName objectName) { super(objectName); this.metric = gauge; } @Override public Number getValue() { return metric.latestMetric; } } /** * Counter类型的JmxMbean, JMX属性名见getter函数名. */ private static class JmxCounter extends AbstractBean implements JmxCounterMBean { private final Counter metric; private JmxCounter(Counter metric, ObjectName objectName) { super(objectName); this.metric = metric; } @Override public long getLatestCount() { return metric.latestMetric.latestCount; } @Override public long getLatestRate() { return metric.latestMetric.latestRate; } @Override public long getTotalCount() { return metric.latestMetric.totalCount; } @Override public long getAvgRate() { return metric.latestMetric.avgRate; } } /** * Histogram类型的JmxMbean, JMX属性名见getter函数名. */ private static class JmxHistogram extends AbstractBean implements JmxHistogramMBean { private final Histogram metric; private JmxHistogram(Histogram metric, ObjectName objectName) { super(objectName); this.metric = metric; } @Override public long getMin() { return metric.latestMetric.min; } @Override public long getMax() { return metric.latestMetric.max; } @Override public double getAvg() { return metric.latestMetric.avg; } @Override public Map<Double, Long> getPcts() { return metric.latestMetric.pcts; } } /** * Timer类型的JmxMbean, JMX属性名见getter函数名. */ private static class JmxTimer extends AbstractBean implements JmxTimerMBean { private final Timer metric; private JmxTimer(Timer metric, ObjectName objectName) { super(objectName); this.metric = metric; } @Override public long getLatestCount() { return metric.latestMetric.counterMetric.latestCount; } @Override public long getLatestRate() { return metric.latestMetric.counterMetric.latestRate; } @Override public long getTotalCount() { return metric.latestMetric.counterMetric.totalCount; } @Override public long getAvgRate() { return metric.latestMetric.counterMetric.avgRate; } @Override public long getMinLatency() { return metric.latestMetric.histogramMetric.min; } @Override public long getMaxLatency() { return metric.latestMetric.histogramMetric.max; } @Override public double getAvgLatency() { return metric.latestMetric.histogramMetric.avg; } @Override public Map<Double, Long> getPcts() { return metric.latestMetric.histogramMetric.pcts; } } }