com.alibaba
fastjson
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleManager.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleManager.java
index 10d34c1d..0fb7f4fe 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleManager.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleManager.java
@@ -15,6 +15,16 @@
*/
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.HashMap;
import java.util.List;
@@ -23,16 +33,6 @@ 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;
-
/**
*
* 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 SentinelProperty> currentProperty = new DynamicSentinelProperty>();
+ /** 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));
diff --git a/sentinel-extension/pom.xml b/sentinel-extension/pom.xml
index 8e336793..ad3c8d07 100755
--- a/sentinel-extension/pom.xml
+++ b/sentinel-extension/pom.xml
@@ -24,6 +24,7 @@
sentinel-datasource-etcd
sentinel-datasource-eureka
sentinel-annotation-cdi-interceptor
+ sentinel-metric-exporter
diff --git a/sentinel-extension/sentinel-metric-exporter/pom.xml b/sentinel-extension/sentinel-metric-exporter/pom.xml
new file mode 100644
index 00000000..e733c094
--- /dev/null
+++ b/sentinel-extension/sentinel-metric-exporter/pom.xml
@@ -0,0 +1,27 @@
+
+
+
+ sentinel-extension
+ com.alibaba.csp
+ 1.8.2
+
+ 4.0.0
+
+ sentinel-metric-exporter
+ jar
+
+
+
+ com.alibaba.csp
+ sentinel-core
+
+
+
+ junit
+ junit
+ test
+
+
+
\ No newline at end of file
diff --git a/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/MetricExporterInit.java b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/MetricExporterInit.java
new file mode 100644
index 00000000..a8d30cfa
--- /dev/null
+++ b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/MetricExporterInit.java
@@ -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 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);
+ }
+ })
+ ));
+ }
+}
diff --git a/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/collector/MetricCollector.java b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/collector/MetricCollector.java
new file mode 100644
index 00000000..ee940e60
--- /dev/null
+++ b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/collector/MetricCollector.java
@@ -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 collectMetric() {
+ final long currentTime = TimeUtil.currentTimeMillis();
+ final long maxTime = currentTime - currentTime % 1000;
+ final long minTime = maxTime - 1000;
+ Map metricNodeMap = new HashMap<>();
+ for (Map.Entry e : ClusterBuilderSlot.getClusterNodeMap().entrySet()) {
+ ClusterNode node = e.getValue();
+ List 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 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 metricNodeMap, List 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);
+ }
+ }
+}
diff --git a/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/MetricExporter.java b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/MetricExporter.java
new file mode 100644
index 00000000..f08eb1d5
--- /dev/null
+++ b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/MetricExporter.java
@@ -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;
+}
diff --git a/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/JMXMetricExporter.java b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/JMXMetricExporter.java
new file mode 100644
index 00000000..cab682c5
--- /dev/null
+++ b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/JMXMetricExporter.java
@@ -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);
+ }
+ }
+ }
+}
diff --git a/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/MBeanRegistry.java b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/MBeanRegistry.java
new file mode 100644
index 00000000..a0168d1a
--- /dev/null
+++ b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/MBeanRegistry.java
@@ -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 mapBean2Name= new ConcurrentHashMap<>(8);
+
+ private Map 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 listAllMBeans() {
+ return new ArrayList<>(mapName2Bean.values());
+ }
+}
diff --git a/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/MetricBean.java b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/MetricBean.java
new file mode 100644
index 00000000..2c169c72
--- /dev/null
+++ b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/MetricBean.java
@@ -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();
+ }
+}
diff --git a/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/MetricBeanWriter.java b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/MetricBeanWriter.java
new file mode 100644
index 00000000..974d0692
--- /dev/null
+++ b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/MetricBeanWriter.java
@@ -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 map) throws Exception {
+ if (map == null || map.isEmpty()) {
+ List 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 metricBeans = mBeanRegistry.listAllMBeans();
+ if (metricBeans == null || metricBeans.isEmpty()) {
+ return;
+ }
+ for (MetricBean metricBean : metricBeans) {
+ if (!Objects.equals(metricBean.getVersion(), version)) {
+ metricBean.reset();
+ mBeanRegistry.unRegister(metricBean);
+ }
+ }
+ }
+}
diff --git a/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/MetricMXBean.java b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/MetricMXBean.java
new file mode 100644
index 00000000..dc6697ea
--- /dev/null
+++ b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/MetricMXBean.java
@@ -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();
+}
diff --git a/sentinel-extension/sentinel-metric-exporter/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.init.InitFunc b/sentinel-extension/sentinel-metric-exporter/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.init.InitFunc
new file mode 100644
index 00000000..9774e1f0
--- /dev/null
+++ b/sentinel-extension/sentinel-metric-exporter/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.init.InitFunc
@@ -0,0 +1 @@
+com.alibaba.csp.sentinel.metric.MetricExporterInit
\ No newline at end of file
diff --git a/sentinel-extension/sentinel-metric-exporter/src/test/java/com/alibaba/cps/sentinel/metric/exporter/MBeanRegistryTest.java b/sentinel-extension/sentinel-metric-exporter/src/test/java/com/alibaba/cps/sentinel/metric/exporter/MBeanRegistryTest.java
new file mode 100644
index 00000000..d07e0726
--- /dev/null
+++ b/sentinel-extension/sentinel-metric-exporter/src/test/java/com/alibaba/cps/sentinel/metric/exporter/MBeanRegistryTest.java
@@ -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);
+ }
+}