Skip to content

Commit

Permalink
Add metric exporter extension for exporting Sentinel metrics via JMX (a…
Browse files Browse the repository at this point in the history
…libaba#2275)

* Expose the MetricItem AS MBean
  • Loading branch information
brotherlu-xcq authored Sep 15, 2021
1 parent 6684739 commit 25651de
Show file tree
Hide file tree
Showing 14 changed files with 810 additions and 11 deletions.
8 changes: 7 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,13 @@
<artifactId>sentinel-adapter</artifactId>
<version>${project.version}</version>
</dependency>


<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-metric-exporter</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,23 @@
*/
package com.alibaba.csp.sentinel.slots.block.flow;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
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;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.csp.sentinel.util.StringUtil;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
* <p>
Expand All @@ -53,6 +53,7 @@ public class FlowRuleManager {
private static final FlowPropertyListener LISTENER = new FlowPropertyListener();
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")
private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1,
new NamedThreadFactory("sentinel-metrics-record-task", true));
Expand Down
1 change: 1 addition & 0 deletions sentinel-extension/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<module>sentinel-datasource-etcd</module>
<module>sentinel-datasource-eureka</module>
<module>sentinel-annotation-cdi-interceptor</module>
<module>sentinel-metric-exporter</module>
</modules>

</project>
27 changes: 27 additions & 0 deletions sentinel-extension/sentinel-metric-exporter/pom.xml
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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);
}
})
));
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
Loading

0 comments on commit 25651de

Please sign in to comment.