This commit is contained in:
shuhongfan
2023-09-04 16:40:17 +08:00
commit cf5ac25c14
8267 changed files with 1305066 additions and 0 deletions

View File

@@ -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.3</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>

View File

@@ -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);
}
})
));
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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();
}

View File

@@ -0,0 +1 @@
com.alibaba.csp.sentinel.metric.MetricExporterInit

View File

@@ -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);
}
}