Add metric exporter extension for exporting Sentinel metrics via JMX (#2275)
* Expose the MetricItem AS MBean
This commit is contained in:
parent
6684739346
commit
25651de09a
6
pom.xml
6
pom.xml
|
|
@ -165,6 +165,12 @@
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-metric-exporter</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
<artifactId>fastjson</artifactId>
|
<artifactId>fastjson</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,16 @@
|
||||||
*/
|
*/
|
||||||
package com.alibaba.csp.sentinel.slots.block.flow;
|
package com.alibaba.csp.sentinel.slots.block.flow;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory;
|
||||||
|
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||||
|
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||||
|
import com.alibaba.csp.sentinel.node.metric.MetricTimerListener;
|
||||||
|
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
||||||
|
import com.alibaba.csp.sentinel.property.PropertyListener;
|
||||||
|
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
||||||
|
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||||
|
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -23,16 +33,6 @@ import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory;
|
|
||||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
|
||||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
|
||||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
|
||||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
|
||||||
import com.alibaba.csp.sentinel.node.metric.MetricTimerListener;
|
|
||||||
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
|
||||||
import com.alibaba.csp.sentinel.property.PropertyListener;
|
|
||||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* One resources can have multiple rules. And these rules take effects in the following order:
|
* One resources can have multiple rules. And these rules take effects in the following order:
|
||||||
|
|
@ -53,6 +53,7 @@ public class FlowRuleManager {
|
||||||
private static final FlowPropertyListener LISTENER = new FlowPropertyListener();
|
private static final FlowPropertyListener LISTENER = new FlowPropertyListener();
|
||||||
private static SentinelProperty<List<FlowRule>> currentProperty = new DynamicSentinelProperty<List<FlowRule>>();
|
private static SentinelProperty<List<FlowRule>> currentProperty = new DynamicSentinelProperty<List<FlowRule>>();
|
||||||
|
|
||||||
|
/** the corePool size of SCHEDULER must be set at 1, so the two task ({@link #startMetricTimerListener()} can run orderly by the SCHEDULER **/
|
||||||
@SuppressWarnings("PMD.ThreadPoolCreationRule")
|
@SuppressWarnings("PMD.ThreadPoolCreationRule")
|
||||||
private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1,
|
private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1,
|
||||||
new NamedThreadFactory("sentinel-metrics-record-task", true));
|
new NamedThreadFactory("sentinel-metrics-record-task", true));
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
<module>sentinel-datasource-etcd</module>
|
<module>sentinel-datasource-etcd</module>
|
||||||
<module>sentinel-datasource-eureka</module>
|
<module>sentinel-datasource-eureka</module>
|
||||||
<module>sentinel-annotation-cdi-interceptor</module>
|
<module>sentinel-annotation-cdi-interceptor</module>
|
||||||
|
<module>sentinel-metric-exporter</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>sentinel-extension</artifactId>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<version>1.8.2</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>sentinel-metric-exporter</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2021 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.alibaba.csp.sentinel.metric;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.init.InitFunc;
|
||||||
|
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||||
|
import com.alibaba.csp.sentinel.metric.collector.MetricCollector;
|
||||||
|
import com.alibaba.csp.sentinel.metric.exporter.MetricExporter;
|
||||||
|
import com.alibaba.csp.sentinel.metric.exporter.jmx.JMXMetricExporter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The{@link MetricExporterInit} work on load Metric exporters.
|
||||||
|
*
|
||||||
|
* @author chenglu
|
||||||
|
* @date 2021-07-01 19:58
|
||||||
|
* @since 1.8.3
|
||||||
|
*/
|
||||||
|
public class MetricExporterInit implements InitFunc {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the list of metric exporters.
|
||||||
|
*/
|
||||||
|
private static List<MetricExporter> metricExporters = new ArrayList<>();
|
||||||
|
|
||||||
|
/*
|
||||||
|
load metric exporters.
|
||||||
|
*/
|
||||||
|
static {
|
||||||
|
// now we use this simple way to load MetricExporter.
|
||||||
|
metricExporters.add(new JMXMetricExporter());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() throws Exception {
|
||||||
|
RecordLog.info("[MetricExporterInit] MetricExporter start init.");
|
||||||
|
// start the metric exporters.
|
||||||
|
for (MetricExporter metricExporter : metricExporters) {
|
||||||
|
try {
|
||||||
|
metricExporter.start();
|
||||||
|
} catch (Exception e) {
|
||||||
|
RecordLog.warn("[MetricExporterInit] MetricExporterInit start the metricExport[{}] failed, will ignore it.",
|
||||||
|
metricExporter.getClass().getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add shutdown hook.
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(
|
||||||
|
() -> metricExporters.forEach(metricExporter -> {
|
||||||
|
try {
|
||||||
|
metricExporter.shutdown();
|
||||||
|
} catch (Exception e) {
|
||||||
|
RecordLog.warn("[MetricExporterInit] MetricExporterInit shutdown the metricExport[{}] failed, will ignore it.",
|
||||||
|
metricExporter.getClass().getName(), e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2021 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.alibaba.csp.sentinel.metric.collector;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.Constants;
|
||||||
|
import com.alibaba.csp.sentinel.node.ClusterNode;
|
||||||
|
import com.alibaba.csp.sentinel.node.metric.MetricNode;
|
||||||
|
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||||
|
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
|
||||||
|
import com.alibaba.csp.sentinel.util.TimeUtil;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link MetricCollector} work on collecting metrics in {@link MetricNode}.
|
||||||
|
*
|
||||||
|
* @author chenglu
|
||||||
|
* @date 2021-07-01 20:01
|
||||||
|
* @since 1.8.3
|
||||||
|
*/
|
||||||
|
public class MetricCollector {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* collect the metrics in {@link MetricNode}.
|
||||||
|
*
|
||||||
|
* @return the metric grouped by resource name.
|
||||||
|
*/
|
||||||
|
public Map<String, MetricNode> collectMetric() {
|
||||||
|
final long currentTime = TimeUtil.currentTimeMillis();
|
||||||
|
final long maxTime = currentTime - currentTime % 1000;
|
||||||
|
final long minTime = maxTime - 1000;
|
||||||
|
Map<String, MetricNode> metricNodeMap = new HashMap<>();
|
||||||
|
for (Map.Entry<ResourceWrapper, ClusterNode> e : ClusterBuilderSlot.getClusterNodeMap().entrySet()) {
|
||||||
|
ClusterNode node = e.getValue();
|
||||||
|
List<MetricNode> metrics = getLastMetrics(node, minTime, maxTime);
|
||||||
|
aggregate(metricNodeMap, metrics, node);
|
||||||
|
}
|
||||||
|
aggregate(metricNodeMap, getLastMetrics(Constants.ENTRY_NODE, minTime, maxTime), Constants.ENTRY_NODE);
|
||||||
|
return metricNodeMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the last second {@link MetricNode} of {@link ClusterNode}
|
||||||
|
* @param node {@link ClusterNode}
|
||||||
|
* @param minTime the min time.
|
||||||
|
* @param maxTime the max time.
|
||||||
|
* @return the list of {@link MetricNode}
|
||||||
|
*/
|
||||||
|
private List<MetricNode> getLastMetrics(ClusterNode node, long minTime, long maxTime) {
|
||||||
|
return node.rawMetricsInMin(time -> time >= minTime && time < maxTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* aggregate the metrics, the metrics under the same resource will left the lasted value
|
||||||
|
* @param metricNodeMap metrics map
|
||||||
|
* @param metrics metrics info group by timestamp
|
||||||
|
* @param node the node
|
||||||
|
*/
|
||||||
|
private void aggregate(Map<String, MetricNode> metricNodeMap, List<MetricNode> metrics, ClusterNode node) {
|
||||||
|
if (metrics == null || metrics.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (MetricNode metricNode : metrics) {
|
||||||
|
String resource = node.getName();
|
||||||
|
metricNode.setResource(resource);
|
||||||
|
metricNode.setClassification(node.getResourceType());
|
||||||
|
MetricNode existMetricNode = metricNodeMap.get(resource);
|
||||||
|
// always keep the MetricNode is the last
|
||||||
|
if (existMetricNode != null && existMetricNode.getTimestamp() > metricNode.getTimestamp()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
metricNodeMap.put(resource, metricNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2021 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.alibaba.csp.sentinel.metric.exporter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link MetricExporter} work on export metric to target monitor.
|
||||||
|
* you can implement your export ways by this class.
|
||||||
|
*
|
||||||
|
* @author chenglu
|
||||||
|
* @date 2021-07-01 21:16
|
||||||
|
*/
|
||||||
|
public interface MetricExporter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* start the {@link MetricExporter}.
|
||||||
|
*
|
||||||
|
* @throws Exception start exception.
|
||||||
|
*/
|
||||||
|
void start() throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* export the data to target monitor by the implement.
|
||||||
|
*
|
||||||
|
* @throws Exception export exception.
|
||||||
|
*/
|
||||||
|
void export() throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shutdown the {@link MetricExporter}.
|
||||||
|
*
|
||||||
|
* @throws Exception shutdown exception.
|
||||||
|
*/
|
||||||
|
void shutdown() throws Exception;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2021 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.alibaba.csp.sentinel.metric.exporter.jmx;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory;
|
||||||
|
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||||
|
import com.alibaba.csp.sentinel.metric.collector.MetricCollector;
|
||||||
|
import com.alibaba.csp.sentinel.metric.exporter.MetricExporter;
|
||||||
|
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The JMX metric exporter, mainly for write metric datas to JMX bean. It implement {@link MetricExporter}, provide method
|
||||||
|
* start, export and shutdown. The mainly design for the jmx is refresh the JMX bean data scheduled.
|
||||||
|
* {@link JMXExportTask} work on export data to {@link MetricBean}.
|
||||||
|
*
|
||||||
|
* @author chenglu
|
||||||
|
* @date 2021-07-01 20:02
|
||||||
|
* @since 1.8.3
|
||||||
|
*/
|
||||||
|
public class JMXMetricExporter implements MetricExporter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* schedule executor.
|
||||||
|
*/
|
||||||
|
private final ScheduledExecutorService jmxExporterSchedule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JMX metric writer, write metric datas to {@link MetricBean}.
|
||||||
|
*/
|
||||||
|
private final MetricBeanWriter metricBeanWriter = new MetricBeanWriter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* global metrics collector.
|
||||||
|
*/
|
||||||
|
private final MetricCollector metricCollector = new MetricCollector();
|
||||||
|
|
||||||
|
public JMXMetricExporter() {
|
||||||
|
jmxExporterSchedule = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("sentinel-metrics-jmx-exporter-task", true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() throws Exception {
|
||||||
|
jmxExporterSchedule.scheduleAtFixedRate(new JMXExportTask(), 1, 1, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void export() throws Exception {
|
||||||
|
metricBeanWriter.write(metricCollector.collectMetric());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() throws Exception {
|
||||||
|
jmxExporterSchedule.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JMXExportTask mainly work on execute the JMX metric export.
|
||||||
|
*/
|
||||||
|
class JMXExportTask implements Runnable {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
export();
|
||||||
|
} catch (Exception e) {
|
||||||
|
RecordLog.warn("[JMX Metric Exporter] export to JMX MetricBean failed.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2021 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.alibaba.csp.sentinel.metric.exporter.jmx;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||||
|
|
||||||
|
import javax.management.JMException;
|
||||||
|
import javax.management.MBeanServer;
|
||||||
|
import javax.management.MBeanServerFactory;
|
||||||
|
import javax.management.ObjectName;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides a unified interface for registering/unregistering of Metric MBean.
|
||||||
|
*
|
||||||
|
* @author chenglu
|
||||||
|
* @date 2021-07-01 20:02
|
||||||
|
* @since 1.8.3
|
||||||
|
*/
|
||||||
|
public class MBeanRegistry {
|
||||||
|
|
||||||
|
private static volatile MBeanRegistry instance = new MBeanRegistry();
|
||||||
|
|
||||||
|
private Map<MetricBean, String> mapBean2Name= new ConcurrentHashMap<>(8);
|
||||||
|
|
||||||
|
private Map<String, MetricBean> mapName2Bean = new ConcurrentHashMap<>(8);
|
||||||
|
|
||||||
|
private MBeanServer mBeanServer;
|
||||||
|
|
||||||
|
public static MBeanRegistry getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MBeanRegistry() {
|
||||||
|
try {
|
||||||
|
mBeanServer = ManagementFactory.getPlatformMBeanServer();
|
||||||
|
} catch (Error e) {
|
||||||
|
// Account for running within IKVM and create a new MBeanServer
|
||||||
|
// if the PlatformMBeanServer does not exist.
|
||||||
|
mBeanServer = MBeanServerFactory.createMBeanServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a new MBean with the platform MBean server.
|
||||||
|
* @param bean the bean being registered
|
||||||
|
* @param mBeanName the mBeanName
|
||||||
|
* @throws JMException MBean can not register exception
|
||||||
|
*/
|
||||||
|
public void register(MetricBean bean, String mBeanName) throws JMException {
|
||||||
|
assert bean != null;
|
||||||
|
try {
|
||||||
|
ObjectName oname = new ObjectName(mBeanName);
|
||||||
|
mBeanServer.registerMBean(bean, oname);
|
||||||
|
mapBean2Name.put(bean, mBeanName);
|
||||||
|
mapName2Bean.put(mBeanName, bean);
|
||||||
|
} catch (JMException e) {
|
||||||
|
RecordLog.warn("[MBeanRegistry] Failed to register MBean " + mBeanName, e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unregister the MetricBean
|
||||||
|
* @param bean MetricBean
|
||||||
|
*/
|
||||||
|
public void unRegister(MetricBean bean) {
|
||||||
|
assert bean != null;
|
||||||
|
String beanName = mapBean2Name.get(bean);
|
||||||
|
if (beanName == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ObjectName objectName = new ObjectName(beanName);
|
||||||
|
mBeanServer.unregisterMBean(objectName);
|
||||||
|
mapBean2Name.remove(bean);
|
||||||
|
mapName2Bean.remove(beanName);
|
||||||
|
} catch (JMException e) {
|
||||||
|
RecordLog.warn("[MBeanRegistry] UnRegister the MetricBean fail", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* find the MBean by BeanName
|
||||||
|
* @param mBeanName mBeanName
|
||||||
|
* @return MetricMBean
|
||||||
|
*/
|
||||||
|
public MetricBean findMBean(String mBeanName) {
|
||||||
|
if (mBeanName == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return mapName2Bean.get(mBeanName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list all MBeans which is registered into MBeanRegistry
|
||||||
|
* @return MetricBeans
|
||||||
|
*/
|
||||||
|
public List<MetricBean> listAllMBeans() {
|
||||||
|
return new ArrayList<>(mapName2Bean.values());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2021 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.alibaba.csp.sentinel.metric.exporter.jmx;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.node.metric.MetricNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the MetricBean for JMX expose.
|
||||||
|
*
|
||||||
|
* @author chenglu
|
||||||
|
* @date 2021-07-01 20:02
|
||||||
|
* @since 1.8.3
|
||||||
|
*/
|
||||||
|
public class MetricBean implements MetricMXBean {
|
||||||
|
|
||||||
|
private String resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource classification (e.g. SQL or RPC)
|
||||||
|
*/
|
||||||
|
private int classification;
|
||||||
|
|
||||||
|
private long timestamp;
|
||||||
|
|
||||||
|
private long passQps;
|
||||||
|
|
||||||
|
private long blockQps;
|
||||||
|
|
||||||
|
private long successQps;
|
||||||
|
|
||||||
|
private long exceptionQps;
|
||||||
|
|
||||||
|
private long rt;
|
||||||
|
|
||||||
|
private long occupiedPassQps;
|
||||||
|
|
||||||
|
private int concurrency;
|
||||||
|
|
||||||
|
private long version;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getResource() {
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getClassification() {
|
||||||
|
return classification;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getPassQps() {
|
||||||
|
return passQps;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getBlockQps() {
|
||||||
|
return blockQps;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getSuccessQps() {
|
||||||
|
return successQps;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getExceptionQps() {
|
||||||
|
return exceptionQps;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getRt() {
|
||||||
|
return rt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getOccupiedPassQps() {
|
||||||
|
return occupiedPassQps;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getConcurrency() {
|
||||||
|
return concurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set the version to current Mbean.
|
||||||
|
*
|
||||||
|
* @param version current version.
|
||||||
|
*/
|
||||||
|
public void setVersion(long version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reset the MBean value to the initialized value.
|
||||||
|
*/
|
||||||
|
public void reset() {
|
||||||
|
this.blockQps = 0;
|
||||||
|
this.passQps = 0;
|
||||||
|
this.timestamp = System.currentTimeMillis();
|
||||||
|
this.exceptionQps = 0;
|
||||||
|
this.occupiedPassQps = 0;
|
||||||
|
this.successQps = 0;
|
||||||
|
this.rt = 0;
|
||||||
|
this.concurrency = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set the MetricBean's value which from MetricNode.
|
||||||
|
*
|
||||||
|
* @param metricNode metric Node for write file
|
||||||
|
*/
|
||||||
|
public void setValueFromNode(MetricNode metricNode) {
|
||||||
|
if (metricNode == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.successQps = metricNode.getSuccessQps();
|
||||||
|
this.blockQps = metricNode.getBlockQps();
|
||||||
|
this.passQps = metricNode.getPassQps();
|
||||||
|
this.occupiedPassQps = metricNode.getOccupiedPassQps();
|
||||||
|
this.exceptionQps = metricNode.getExceptionQps();
|
||||||
|
this.timestamp = metricNode.getTimestamp();
|
||||||
|
this.classification = metricNode.getClassification();
|
||||||
|
this.concurrency = metricNode.getConcurrency();
|
||||||
|
this.resource = metricNode.getResource();
|
||||||
|
this.rt = metricNode.getRt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2021 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.alibaba.csp.sentinel.metric.exporter.jmx;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||||
|
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||||
|
import com.alibaba.csp.sentinel.node.metric.MetricNode;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the metric bean writer, it provides {@link MetricBeanWriter#write} method for register the
|
||||||
|
* MetricBean in {@link MBeanRegistry} or update the value of MetricBean
|
||||||
|
*
|
||||||
|
* @author chenglu
|
||||||
|
* @date 2021-07-01 20:02
|
||||||
|
* @since 1.8.3
|
||||||
|
*/
|
||||||
|
public class MetricBeanWriter {
|
||||||
|
|
||||||
|
private final MBeanRegistry mBeanRegistry = MBeanRegistry.getInstance();
|
||||||
|
|
||||||
|
private static final String DEFAULT_APP_NAME = "sentinel-application";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write the MetricNode value to MetricBean
|
||||||
|
* if the MetricBean is not registered into {@link MBeanRegistry},
|
||||||
|
* it will be created and registered into {@link MBeanRegistry}.
|
||||||
|
* else it will update the value of MetricBean.
|
||||||
|
* Notes. if the MetricNode is null, then {@link MetricBean} will be reset.
|
||||||
|
* @param map metricNode value group by resource
|
||||||
|
* @throws Exception write failed exception
|
||||||
|
*/
|
||||||
|
public synchronized void write(Map<String, MetricNode> map) throws Exception {
|
||||||
|
if (map == null || map.isEmpty()) {
|
||||||
|
List<MetricBean> metricNodes = mBeanRegistry.listAllMBeans();
|
||||||
|
if (metricNodes == null || metricNodes.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (MetricBean metricNode : metricNodes) {
|
||||||
|
metricNode.reset();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String appName = SentinelConfig.getAppName();
|
||||||
|
if (appName == null) {
|
||||||
|
appName = DEFAULT_APP_NAME;
|
||||||
|
}
|
||||||
|
long version = System.currentTimeMillis();
|
||||||
|
// set or update the new value
|
||||||
|
for (MetricNode metricNode : map.values()) {
|
||||||
|
final String mBeanName = "Sentinel:type=" + appName + ",name=\"" + metricNode.getResource()
|
||||||
|
+"\",classification=\"" + metricNode.getClassification() +"\"";
|
||||||
|
MetricBean metricBean = mBeanRegistry.findMBean(mBeanName);
|
||||||
|
if (metricBean != null) {
|
||||||
|
metricBean.setValueFromNode(metricNode);
|
||||||
|
metricBean.setVersion(version);
|
||||||
|
} else {
|
||||||
|
metricBean = new MetricBean();
|
||||||
|
metricBean.setValueFromNode(metricNode);
|
||||||
|
metricBean.setVersion(version);
|
||||||
|
mBeanRegistry.register(metricBean, mBeanName);
|
||||||
|
RecordLog.info("[MetricBeanWriter] Registering with JMX as Metric MBean [{}]", mBeanName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// reset the old value
|
||||||
|
List<MetricBean> metricBeans = mBeanRegistry.listAllMBeans();
|
||||||
|
if (metricBeans == null || metricBeans.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (MetricBean metricBean : metricBeans) {
|
||||||
|
if (!Objects.equals(metricBean.getVersion(), version)) {
|
||||||
|
metricBean.reset();
|
||||||
|
mBeanRegistry.unRegister(metricBean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2021 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.alibaba.csp.sentinel.metric.exporter.jmx;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the Metric JMX Bean interface.
|
||||||
|
*
|
||||||
|
* @author chenglu
|
||||||
|
* @date 2021-07-01 20:02
|
||||||
|
* @since 1.8.3
|
||||||
|
*/
|
||||||
|
public interface MetricMXBean {
|
||||||
|
|
||||||
|
long getTimestamp();
|
||||||
|
|
||||||
|
long getOccupiedPassQps();
|
||||||
|
|
||||||
|
long getSuccessQps();
|
||||||
|
|
||||||
|
long getPassQps();
|
||||||
|
|
||||||
|
long getExceptionQps();
|
||||||
|
|
||||||
|
long getBlockQps();
|
||||||
|
|
||||||
|
long getRt();
|
||||||
|
|
||||||
|
String getResource();
|
||||||
|
|
||||||
|
int getClassification();
|
||||||
|
|
||||||
|
int getConcurrency();
|
||||||
|
|
||||||
|
long getVersion();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
com.alibaba.csp.sentinel.metric.MetricExporterInit
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.alibaba.cps.sentinel.metric.exporter;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.metric.exporter.jmx.MBeanRegistry;
|
||||||
|
import com.alibaba.csp.sentinel.metric.exporter.jmx.MetricBean;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.management.JMException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link com.alibaba.csp.sentinel.metric.exporter.jmx.MBeanRegistry} unit test.
|
||||||
|
*
|
||||||
|
* @author chenglu
|
||||||
|
* @date 2021-07-01 23:07
|
||||||
|
*/
|
||||||
|
public class MBeanRegistryTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMBeanRegistry() throws JMException {
|
||||||
|
MBeanRegistry mBeanRegistry = MBeanRegistry.getInstance();
|
||||||
|
|
||||||
|
MetricBean metricBean = new MetricBean();
|
||||||
|
String mBeanName = "Sentinel:type=test,name=test";
|
||||||
|
mBeanRegistry.register(metricBean, mBeanName);
|
||||||
|
|
||||||
|
MetricBean mb1 = mBeanRegistry.findMBean(mBeanName);
|
||||||
|
Assert.assertEquals(mb1, metricBean);
|
||||||
|
|
||||||
|
mBeanRegistry.unRegister(metricBean);
|
||||||
|
|
||||||
|
MetricBean mb2 = mBeanRegistry.findMBean(mBeanName);
|
||||||
|
Assert.assertNull(mb2);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue