diff --git a/common/src/main/java/com/alibaba/nacos/common/utils/TopnCounterMetricsContainer.java b/common/src/main/java/com/alibaba/nacos/common/utils/TopnCounterMetricsContainer.java
deleted file mode 100644
index 2f0b4761703..00000000000
--- a/common/src/main/java/com/alibaba/nacos/common/utils/TopnCounterMetricsContainer.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright 1999-2022 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.nacos.common.utils;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * container for top n counter metrics, increment and remove cost O(1) time.
- *
- * @author liuyixiao
- */
-public class TopnCounterMetricsContainer {
-
- /**
- * dataId -> count.
- */
- private ConcurrentHashMap dataCount;
-
- /**
- * count -> node.
- */
- private ConcurrentHashMap specifiedCountDataIdSets;
-
- private DoublyLinkedNode dummyHead;
-
- public TopnCounterMetricsContainer() {
- dataCount = new ConcurrentHashMap<>();
- specifiedCountDataIdSets = new ConcurrentHashMap<>();
- dummyHead = new DoublyLinkedNode(null, null, null, -1);
- dummyHead.next = new DoublyLinkedNode(null, dummyHead, new ConcurrentHashSet<>(), 0);
- specifiedCountDataIdSets.put(0, dummyHead.next);
- }
-
- public List> getTopNCounter(int n) {
- List> topnCounter = new LinkedList<>();
- DoublyLinkedNode curr = dummyHead;
- while (curr.next != null && topnCounter.size() < n) {
- for (String dataId : curr.next.dataSet) {
- // use inner AtomicInteger to reflect change to prometheus
- topnCounter.add(new Pair<>(dataId, dataCount.get(dataId)));
- if (topnCounter.size() == n) {
- break;
- }
- }
- curr = curr.next;
- }
- return topnCounter;
- }
-
- /**
- * put(String dataId, 0).
- *
- * @param dataId data name or data key.
- */
- public void put(String dataId) {
- put(dataId, 0);
- }
-
- /**
- * put new data into container, if already exist, update it.
- * this method could be slow (O(N)), most time use increment.
- *
- * @param dataId data name or data key.
- * @param count data count.
- */
- public void put(String dataId, int count) {
- if (dataCount.containsKey(dataId)) {
- removeFromSpecifiedCountDataIdSets(dataId);
- dataCount.get(dataId).set(count);
- } else {
- dataCount.put(dataId, new AtomicInteger(count));
- }
- insertIntoSpecifiedCountDataIdSets(dataId, count);
- }
-
- /**
- * get data count by dataId.
- *
- * @param dataId data name or data key.
- * @return data count or -1 if not exist.
- */
- public int get(String dataId) {
- if (dataCount.containsKey(dataId)) {
- return dataCount.get(dataId).get();
- }
- return -1;
- }
-
- /**
- * increment the count of dataId.
- *
- * @param dataId data name or data key.
- */
- public void increment(String dataId) {
- if (!dataCount.containsKey(dataId)) {
- put(dataId);
- }
- DoublyLinkedNode prev = removeFromSpecifiedCountDataIdSets(dataId);
- int newCount = dataCount.get(dataId).incrementAndGet();
- if (!isDummyHead(prev) && prev.count == newCount) {
- insertIntoSpecifiedCountDataIdSets(dataId, prev);
- } else {
- // prev.count > newCount
- DoublyLinkedNode newNode = new DoublyLinkedNode(prev.next, prev, new ConcurrentHashSet<>(), newCount);
- if (prev.next != null) {
- prev.next.prev = newNode;
- }
- prev.next = newNode;
- newNode.dataSet.add(dataId);
- specifiedCountDataIdSets.put(newCount, newNode);
- }
- }
-
- /**
- * remove data.
- *
- * @param dataId data name or data key.
- * @return data count or null if data is not exist.
- */
- public AtomicInteger remove(String dataId) {
- if (dataCount.containsKey(dataId)) {
- removeFromSpecifiedCountDataIdSets(dataId);
- return dataCount.remove(dataId);
- }
- return null;
- }
-
- /**
- * remove all data.
- */
- public void removeAll() {
- for (String dataId : dataCount.keySet()) {
- removeFromSpecifiedCountDataIdSets(dataId);
- }
- dataCount.clear();
- }
-
- private DoublyLinkedNode removeFromSpecifiedCountDataIdSets(String dataId) {
- int count = dataCount.get(dataId).get();
- DoublyLinkedNode node = specifiedCountDataIdSets.get(count);
- node.dataSet.remove(dataId);
- // keep the 0 count node.
- if (node.dataSet.size() == 0 && node.count != 0) {
- node.prev.next = node.next;
- if (node.next != null) {
- node.next.prev = node.prev;
- }
- specifiedCountDataIdSets.remove(node.count);
- }
- return node.prev;
- }
-
- private void insertIntoSpecifiedCountDataIdSets(String dataId, int count) {
- if (specifiedCountDataIdSets.containsKey(count)) {
- specifiedCountDataIdSets.get(count).dataSet.add(dataId);
- } else {
- DoublyLinkedNode prev = dummyHead;
- while (prev.next != null) {
- if (prev.next.count < count) {
- break;
- } else {
- prev = prev.next;
- }
- }
- DoublyLinkedNode newNode = new DoublyLinkedNode(prev.next, prev, new ConcurrentHashSet<>(), count);
- if (prev.next != null) {
- prev.next.prev = newNode;
- }
- prev.next = newNode;
- newNode.dataSet.add(dataId);
- specifiedCountDataIdSets.put(count, newNode);
- }
- }
-
- private void insertIntoSpecifiedCountDataIdSets(String dataId, DoublyLinkedNode targetSet) {
- targetSet.dataSet.add(dataId);
- }
-
- private boolean isDummyHead(DoublyLinkedNode node) {
- return node.count == -1;
- }
-
- private class DoublyLinkedNode {
-
- public DoublyLinkedNode next;
-
- public DoublyLinkedNode prev;
-
- public ConcurrentHashSet dataSet;
-
- public int count;
-
- public DoublyLinkedNode(DoublyLinkedNode next, DoublyLinkedNode prev, ConcurrentHashSet dataSet, int count) {
- this.next = next;
- this.prev = prev;
- this.dataSet = dataSet;
- this.count = count;
- }
- }
-}
diff --git a/common/src/test/java/com/alibaba/nacos/common/utils/TopnCounterMetricsContainerTest.java b/common/src/test/java/com/alibaba/nacos/common/utils/TopnCounterMetricsContainerTest.java
deleted file mode 100644
index b2d867b4d47..00000000000
--- a/common/src/test/java/com/alibaba/nacos/common/utils/TopnCounterMetricsContainerTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 1999-2022 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.nacos.common.utils;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Random;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * unit test for TopNCounterMetricsContainer.
- *
- * @author liuyixiao
- */
-public class TopnCounterMetricsContainerTest {
-
- private TopnCounterMetricsContainer topnCounterMetricsContainer;
-
- @Before
- public void setUp() {
- topnCounterMetricsContainer = new TopnCounterMetricsContainer();
- }
-
- @Test
- public void testPut() {
- topnCounterMetricsContainer.put("test");
- Assert.assertEquals(0, topnCounterMetricsContainer.get("test"));
- topnCounterMetricsContainer.put("test1", 1);
- Assert.assertEquals(1, topnCounterMetricsContainer.get("test1"));
- }
-
- @Test
- public void testIncrement() {
- topnCounterMetricsContainer.put("test", 0);
- topnCounterMetricsContainer.increment("test");
- Assert.assertEquals(1, topnCounterMetricsContainer.get("test"));
- }
-
- @Test
- public void testRemove() {
- topnCounterMetricsContainer.put("test");
- Assert.assertEquals(0, topnCounterMetricsContainer.get("test"));
- topnCounterMetricsContainer.remove("test");
- Assert.assertEquals(-1, topnCounterMetricsContainer.get("test"));
- }
-
- @Test
- public void testRemoveAll() {
- topnCounterMetricsContainer.put("test");
- topnCounterMetricsContainer.put("test1");
- topnCounterMetricsContainer.put("test2");
- topnCounterMetricsContainer.removeAll();
- Assert.assertEquals(-1, topnCounterMetricsContainer.get("test"));
- Assert.assertEquals(-1, topnCounterMetricsContainer.get("test1"));
- Assert.assertEquals(-1, topnCounterMetricsContainer.get("test2"));
- }
-
- @Test
- public void testGetTopNCounterAndRemoveAll() {
- final int N = 10;
- String dataIds = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- List> dataList = new ArrayList<>();
- for (int i = 0; i < dataIds.length(); i++) {
- topnCounterMetricsContainer.put(dataIds.substring(i, i + 1));
- dataList.add(new Pair<>(dataIds.substring(i, i + 1), new AtomicInteger()));
- }
- Random random = new Random();
- for (int i = 0; i < 10000; i++) {
- int j = random.nextInt(dataIds.length());
- topnCounterMetricsContainer.increment(dataIds.substring(j, j + 1));
- dataList.get(j).getSecond().incrementAndGet();
- }
- boolean right = true;
- Collections.sort(dataList, (a, b) -> b.getSecond().get() - a.getSecond().get());
- List> result = topnCounterMetricsContainer.getTopNCounter(N);
- for (Pair item : result) {
- // ensure every top N count is greater than (N+1)th greatest.
- if (item.getSecond().get() < dataList.get(N).getSecond().get()) {
- right = false;
- break;
- }
- }
- Assert.assertTrue(right);
- topnCounterMetricsContainer.removeAll();
- for (int i = 0; i < dataIds.length(); i++) {
- Assert.assertEquals(-1, topnCounterMetricsContainer.get(dataIds.substring(i, i + 1)));
- }
- Assert.assertEquals(0, topnCounterMetricsContainer.getTopNCounter(N).size());
- }
-}
diff --git a/config/src/main/java/com/alibaba/nacos/config/server/monitor/ConfigDynamicMeterRefreshService.java b/config/src/main/java/com/alibaba/nacos/config/server/monitor/ConfigDynamicMeterRefreshService.java
index fcb0b7e3f18..8276b63f033 100644
--- a/config/src/main/java/com/alibaba/nacos/config/server/monitor/ConfigDynamicMeterRefreshService.java
+++ b/config/src/main/java/com/alibaba/nacos/config/server/monitor/ConfigDynamicMeterRefreshService.java
@@ -59,6 +59,6 @@ public void refreshTopnConfigChangeCount() {
*/
@Scheduled(cron = "0 0 0 ? * 1")
public void resetTopnConfigChangeCount() {
- MetricsMonitor.getConfigChangeCount().removeAll();
+ MetricsMonitor.getConfigChangeCount().reset();
}
}
diff --git a/config/src/main/java/com/alibaba/nacos/config/server/monitor/MetricsMonitor.java b/config/src/main/java/com/alibaba/nacos/config/server/monitor/MetricsMonitor.java
index 2cc9f734e2d..aca6c68e9f1 100644
--- a/config/src/main/java/com/alibaba/nacos/config/server/monitor/MetricsMonitor.java
+++ b/config/src/main/java/com/alibaba/nacos/config/server/monitor/MetricsMonitor.java
@@ -16,8 +16,8 @@
package com.alibaba.nacos.config.server.monitor;
-import com.alibaba.nacos.common.utils.TopnCounterMetricsContainer;
import com.alibaba.nacos.core.monitor.NacosMeterRegistryCenter;
+import com.alibaba.nacos.core.monitor.topn.StringTopNCounter;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.ImmutableTag;
import io.micrometer.core.instrument.Tag;
@@ -73,7 +73,7 @@ public class MetricsMonitor {
/**
* config change count.
*/
- private static TopnCounterMetricsContainer configChangeCount = new TopnCounterMetricsContainer();
+ private static StringTopNCounter configChangeCount = new StringTopNCounter();
static {
ImmutableTag immutableTag = new ImmutableTag("module", "config");
@@ -166,7 +166,7 @@ public static AtomicInteger getConfigSubscriberMonitor(String version) {
return configSubscriber.get(version);
}
- public static TopnCounterMetricsContainer getConfigChangeCount() {
+ public static StringTopNCounter getConfigChangeCount() {
return configChangeCount;
}
diff --git a/core/src/main/java/com/alibaba/nacos/core/monitor/topn/BaseTopNCounter.java b/core/src/main/java/com/alibaba/nacos/core/monitor/topn/BaseTopNCounter.java
new file mode 100644
index 00000000000..dd31f0f88f4
--- /dev/null
+++ b/core/src/main/java/com/alibaba/nacos/core/monitor/topn/BaseTopNCounter.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 1999-2023 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.nacos.core.monitor.topn;
+
+import com.alibaba.nacos.common.utils.Pair;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Nacos base topN counter.
+ *
+ * @author xiweng.yy
+ */
+@SuppressWarnings("PMD.ClassNamingShouldBeCamelRule")
+public abstract class BaseTopNCounter {
+
+ private final Comparator> comparator;
+
+ protected ConcurrentMap dataCount;
+
+ protected BaseTopNCounter() {
+ dataCount = new ConcurrentHashMap<>();
+ this.comparator = Comparator.comparingInt(value -> value.getSecond().get());
+ }
+
+ /**
+ * Get topN counter by PriorityQueue.
+ *
+ * @param topN topN
+ * @return topN counter
+ */
+ public List> getTopNCounter(int topN) {
+ if (!checkEnabled()) {
+ reset();
+ return Collections.emptyList();
+ }
+ ConcurrentMap snapshot = dataCount;
+ dataCount = new ConcurrentHashMap<>(1);
+ FixedSizePriorityQueue> queue = new FixedSizePriorityQueue<>(topN, comparator);
+ for (T t : snapshot.keySet()) {
+ queue.offer(Pair.with(keyToString(t), snapshot.get(t)));
+ }
+ return queue.toList();
+ }
+
+ /**
+ * Transfer key from type T to String.
+ *
+ * @param t key
+ * @return String
+ */
+ protected abstract String keyToString(T t);
+
+ /**
+ * Increment 1 count for target key.
+ *
+ * @param t key
+ */
+ public void increment(T t) {
+ if (checkEnabled()) {
+ increment(t, 1);
+ }
+ }
+
+ /**
+ * Increment specified count for target key.
+ *
+ * @param t key
+ * @param count count
+ */
+ public void increment(T t, int count) {
+ if (checkEnabled()) {
+ dataCount.computeIfAbsent(t, k -> new AtomicInteger(0)).addAndGet(count);
+ }
+ }
+
+ /**
+ * Directly set count for target key.
+ *
+ * @param t key
+ * @param count new count
+ */
+ public void set(T t, int count) {
+ if (checkEnabled()) {
+ dataCount.computeIfAbsent(t, k -> new AtomicInteger(0)).set(count);
+ }
+ }
+
+ public void reset() {
+ dataCount.clear();
+ }
+
+ protected boolean checkEnabled() {
+ return TopNConfig.getInstance().isEnabled();
+ }
+}
diff --git a/core/src/main/java/com/alibaba/nacos/core/monitor/topn/FixedSizePriorityQueue.java b/core/src/main/java/com/alibaba/nacos/core/monitor/topn/FixedSizePriorityQueue.java
new file mode 100644
index 00000000000..2023d4d5344
--- /dev/null
+++ b/core/src/main/java/com/alibaba/nacos/core/monitor/topn/FixedSizePriorityQueue.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 1999-2023 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.nacos.core.monitor.topn;
+
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Fixed size priority queue.
+ *
+ * @author xiweng.yy
+ */
+@SuppressWarnings("PMD.UndefineMagicConstantRule")
+public class FixedSizePriorityQueue {
+
+ private Object[] elements;
+
+ private int size;
+
+ private Comparator comparator;
+
+ public FixedSizePriorityQueue(int capacity, Comparator comparator) {
+ elements = new Object[capacity];
+ size = 0;
+ this.comparator = comparator;
+ }
+
+ /**
+ * Offer queue, if queue is full and offer element is not bigger than the first element in queue, offer element will
+ * be ignored.
+ *
+ * @param element new element.
+ */
+ public void offer(T element) {
+ if (size == elements.length) {
+ if (comparator.compare(element, (T) elements[0]) > 0) {
+ elements[0] = element;
+ siftDown();
+ }
+ } else {
+ elements[size] = element;
+ siftUp(size);
+ size++;
+ }
+ }
+
+ private void siftUp(int index) {
+ while (index > 0) {
+ int parentIndex = (index - 1) / 2;
+ if (comparator.compare((T) elements[index], (T) elements[parentIndex]) > 0) {
+ break;
+ }
+ swap(index, parentIndex);
+ index = parentIndex;
+ }
+ }
+
+ private void siftDown() {
+ int index = 0;
+ while (index * 2 + 1 < size) {
+ int leftChild = index * 2 + 1;
+ int rightChild = index * 2 + 2;
+ int minChildIndex = leftChild;
+ if (rightChild < size && comparator.compare((T) elements[rightChild], (T) elements[leftChild]) < 0) {
+ minChildIndex = rightChild;
+ }
+ if (comparator.compare((T) elements[index], (T) elements[minChildIndex]) < 0) {
+ break;
+ }
+ swap(index, minChildIndex);
+ index = minChildIndex;
+ }
+ }
+
+ private void swap(int i, int j) {
+ Object temp = elements[i];
+ elements[i] = elements[j];
+ elements[j] = temp;
+ }
+
+ /**
+ * Transfer queue to list without order.
+ *
+ * @return list
+ */
+ public List toList() {
+ List result = new LinkedList<>();
+ for (int i = 0; i < size; i++) {
+ result.add((T) elements[i]);
+ }
+ return result;
+ }
+}
diff --git a/core/src/main/java/com/alibaba/nacos/core/monitor/topn/StringTopNCounter.java b/core/src/main/java/com/alibaba/nacos/core/monitor/topn/StringTopNCounter.java
new file mode 100644
index 00000000000..d6ae6823c0a
--- /dev/null
+++ b/core/src/main/java/com/alibaba/nacos/core/monitor/topn/StringTopNCounter.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999-2023 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.nacos.core.monitor.topn;
+
+/**
+ * String key topN counter.
+ *
+ * @author xiweng.yy
+ */
+@SuppressWarnings("PMD.ClassNamingShouldBeCamelRule")
+public class StringTopNCounter extends BaseTopNCounter {
+
+ @Override
+ protected String keyToString(String s) {
+ return s;
+ }
+}
diff --git a/core/src/main/java/com/alibaba/nacos/core/monitor/topn/TopNConfig.java b/core/src/main/java/com/alibaba/nacos/core/monitor/topn/TopNConfig.java
new file mode 100644
index 00000000000..f6ab940accc
--- /dev/null
+++ b/core/src/main/java/com/alibaba/nacos/core/monitor/topn/TopNConfig.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 1999-2023 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.nacos.core.monitor.topn;
+
+import com.alibaba.nacos.core.config.AbstractDynamicConfig;
+import com.alibaba.nacos.sys.env.EnvUtil;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * TopN configurations.
+ *
+ * @author xiweng.yy
+ */
+@SuppressWarnings("PMD.ClassNamingShouldBeCamelRule")
+public class TopNConfig extends AbstractDynamicConfig {
+
+ private static final String TOP_N = "topN";
+
+ private static final TopNConfig INSTANCE = new TopNConfig();
+
+ private static final String TOP_N_PREFIX = "nacos.core.monitor.topn.";
+
+ private static final String ENABLED_KEY = TOP_N_PREFIX + "enabled";
+
+ private static final String COUNT_KEY = TOP_N_PREFIX + "count";
+
+ private static final String INTERNAL_MS_KEY = TOP_N_PREFIX + "internalMs";
+
+ private static final boolean DEFAULT_ENABLED = true;
+
+ private static final int DEFAULT_COUNT = 10;
+
+ private static final long DEFAULT_INTERNAL_MS = TimeUnit.SECONDS.toMillis(30);
+
+ private boolean enabled;
+
+ private int topNCount;
+
+ private long internalMs;
+
+ private TopNConfig() {
+ super(TOP_N);
+ }
+
+ @Override
+ protected void getConfigFromEnv() {
+ enabled = EnvUtil.getProperty(ENABLED_KEY, Boolean.class, DEFAULT_ENABLED);
+ topNCount = EnvUtil.getProperty(COUNT_KEY, Integer.class, DEFAULT_COUNT);
+ internalMs = EnvUtil.getProperty(INTERNAL_MS_KEY, Long.class, DEFAULT_INTERNAL_MS);
+ }
+
+ @Override
+ protected String printConfig() {
+ return toString();
+ }
+
+ @Override
+ public String toString() {
+ return "TopNConfig{" + "enabled=" + enabled + ", topNCount=" + topNCount + ", internalMs=" + internalMs + '}';
+ }
+
+ public static TopNConfig getInstance() {
+ return INSTANCE;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public int getTopNCount() {
+ return topNCount;
+ }
+
+ public long getInternalMs() {
+ return internalMs;
+ }
+}
diff --git a/core/src/test/java/com/alibaba/nacos/core/monitor/topn/FixedSizePriorityQueueTest.java b/core/src/test/java/com/alibaba/nacos/core/monitor/topn/FixedSizePriorityQueueTest.java
new file mode 100644
index 00000000000..c6ef68ee62b
--- /dev/null
+++ b/core/src/test/java/com/alibaba/nacos/core/monitor/topn/FixedSizePriorityQueueTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 1999-2023 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.nacos.core.monitor.topn;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class FixedSizePriorityQueueTest {
+
+ @Test
+ public void testOfferEmpty() {
+ FixedSizePriorityQueue queue = new FixedSizePriorityQueue<>(10, Comparator.naturalOrder());
+ List list = queue.toList();
+ assertTrue(list.isEmpty());
+ }
+
+ @Test
+ public void testOfferLessThanSize() {
+ FixedSizePriorityQueue queue = new FixedSizePriorityQueue<>(10, Comparator.naturalOrder());
+ for (int i = 0; i < 5; i++) {
+ queue.offer(i);
+ }
+ List list = queue.toList();
+ assertEquals(5, list.size());
+ for (int i = 0; i < 5; i++) {
+ assertTrue(list.contains(i));
+ }
+ }
+
+ @Test
+ public void testOfferMoreThanSizeWithIncreasing() {
+ FixedSizePriorityQueue queue = new FixedSizePriorityQueue<>(10, Comparator.naturalOrder());
+ for (int i = 0; i < 15; i++) {
+ queue.offer(i);
+ }
+ List list = queue.toList();
+ assertEquals(10, list.size());
+ for (int i = 14; i > 4; i--) {
+ assertTrue(list.contains(i));
+ }
+ }
+
+ @Test
+ public void testOfferMoreThanSizeWithDecreasing() {
+ FixedSizePriorityQueue queue = new FixedSizePriorityQueue<>(10, Comparator.naturalOrder());
+ for (int i = 14; i > 0; i--) {
+ queue.offer(i);
+ }
+ List list = queue.toList();
+ assertEquals(10, list.size());
+ for (int i = 14; i > 4; i--) {
+ assertTrue(list.contains(i));
+ }
+ }
+
+ @Test
+ public void testOfferMoreThanSizeWithShuffle() {
+ List testCase = new ArrayList<>(50);
+ for (int i = 0; i < 50; i++) {
+ testCase.add(i);
+ }
+ Collections.shuffle(testCase);
+ FixedSizePriorityQueue queue = new FixedSizePriorityQueue<>(10, Comparator.naturalOrder());
+ testCase.forEach(queue::offer);
+ List list = queue.toList();
+ assertEquals(10, list.size());
+ for (int i = 49; i > 39; i--) {
+ assertTrue(list.contains(i));
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/src/test/java/com/alibaba/nacos/core/monitor/topn/StringTopNCounterTest.java b/core/src/test/java/com/alibaba/nacos/core/monitor/topn/StringTopNCounterTest.java
new file mode 100644
index 00000000000..c6c8e38c5f3
--- /dev/null
+++ b/core/src/test/java/com/alibaba/nacos/core/monitor/topn/StringTopNCounterTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 1999-2023 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.nacos.core.monitor.topn;
+
+import com.alibaba.nacos.common.event.ServerConfigChangeEvent;
+import com.alibaba.nacos.common.utils.Pair;
+import com.alibaba.nacos.sys.env.EnvUtil;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.mock.env.MockEnvironment;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * unit test for TopNCounterMetricsContainer.
+ *
+ * @author liuyixiao
+ */
+public class StringTopNCounterTest {
+
+ private StringTopNCounter stringTopNCounter;
+
+ @Before
+ public void setUp() {
+ stringTopNCounter = new StringTopNCounter();
+ }
+
+ @After
+ public void tearDown() {
+ stringTopNCounter.reset();
+ EnvUtil.setEnvironment(new MockEnvironment());
+ TopNConfig.getInstance().onEvent(new ServerConfigChangeEvent());
+ }
+
+ @Test
+ public void testSet() {
+ stringTopNCounter.set("test1", 1);
+ List> actual = stringTopNCounter.getTopNCounter(10);
+ assertTopNCounter(actual, 1, new String[] {"test1"}, new Integer[] {1});
+ }
+
+ @Test
+ public void testIncrement() {
+ stringTopNCounter.set("test", 0);
+ assertTopNCounter(stringTopNCounter.getTopNCounter(10), 1, new String[] {"test"}, new Integer[] {0});
+ stringTopNCounter.increment("test");
+ assertTopNCounter(stringTopNCounter.getTopNCounter(10), 1, new String[] {"test"}, new Integer[] {1});
+ }
+
+ @Test
+ public void testReset() {
+ stringTopNCounter.set("test", 1);
+ stringTopNCounter.set("test1", 2);
+ stringTopNCounter.set("test2", 3);
+ assertTopNCounter(stringTopNCounter.getTopNCounter(10), 3, new String[] {"test2", "test1", "test"},
+ new Integer[] {3, 2, 1});
+ stringTopNCounter.reset();
+ assertTopNCounter(stringTopNCounter.getTopNCounter(10), 0, new String[] {}, new Integer[] {});
+ }
+
+ @Test
+ public void testGetTopNCounter() {
+ for (int i = 0; i < 20; i++) {
+ stringTopNCounter.set("test" + i, i);
+ }
+ assertTopNCounter(stringTopNCounter.getTopNCounter(10), 10, new String[] {"test19", "test18", "test17", "test16",
+ "test15", "test14", "test13", "test12", "test11", "test10"}, new Integer[] {19, 18, 17, 16, 15, 14, 13, 12, 11, 10});
+ }
+
+ @Test
+ public void testForTopnDisabled() {
+ MockEnvironment env = new MockEnvironment();
+ env.setProperty("nacos.core.monitor.topn.enabled", "false");
+ EnvUtil.setEnvironment(env);
+ TopNConfig.getInstance().onEvent(new ServerConfigChangeEvent());
+ stringTopNCounter.set("test", 1);
+ stringTopNCounter.set("test1", 2);
+ stringTopNCounter.set("test2", 3);
+ assertTopNCounter(stringTopNCounter.getTopNCounter(10), 0, new String[] {}, new Integer[] {});
+ }
+
+ private void assertTopNCounter(List> actual, int size, String[] keys, Integer[] value) {
+ Assert.assertEquals(size, actual.size());
+ for (int i = 0; i < size; i++) {
+ Assert.assertTrue(Arrays.asList(keys).contains(actual.get(i).getFirst()));
+ Assert.assertTrue(Arrays.asList(value).contains(actual.get(i).getSecond().get()));
+ }
+ }
+}
diff --git a/naming/src/main/java/com/alibaba/nacos/naming/monitor/MetricsMonitor.java b/naming/src/main/java/com/alibaba/nacos/naming/monitor/MetricsMonitor.java
index 3b836fd5374..4cd2be774b7 100644
--- a/naming/src/main/java/com/alibaba/nacos/naming/monitor/MetricsMonitor.java
+++ b/naming/src/main/java/com/alibaba/nacos/naming/monitor/MetricsMonitor.java
@@ -16,10 +16,10 @@
package com.alibaba.nacos.naming.monitor;
+import com.alibaba.nacos.core.monitor.NacosMeterRegistryCenter;
import com.alibaba.nacos.naming.core.v2.pojo.BatchInstancePublishInfo;
import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo;
-import com.alibaba.nacos.common.utils.TopnCounterMetricsContainer;
-import com.alibaba.nacos.core.monitor.NacosMeterRegistryCenter;
+import com.alibaba.nacos.naming.core.v2.pojo.Service;
import com.alibaba.nacos.naming.misc.Loggers;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.ImmutableTag;
@@ -90,7 +90,7 @@ public class MetricsMonitor {
/**
* topn service change count.
*/
- private final TopnCounterMetricsContainer serviceChangeCount = new TopnCounterMetricsContainer();
+ private final ServiceTopNCounter serviceChangeCount = new ServiceTopNCounter();
private MetricsMonitor() {
for (Field each : MetricsMonitor.class.getDeclaredFields()) {
@@ -110,18 +110,18 @@ private MetricsMonitor() {
List tags = new ArrayList<>();
tags.add(new ImmutableTag("version", "v1"));
NacosMeterRegistryCenter.gauge(METER_REGISTRY, "nacos_naming_subscriber", tags, namingSubscriber.get("v1"));
-
+
tags = new ArrayList<>();
tags.add(new ImmutableTag("version", "v2"));
NacosMeterRegistryCenter.gauge(METER_REGISTRY, "nacos_naming_subscriber", tags, namingSubscriber.get("v2"));
-
+
namingPublisher.put("v1", new AtomicInteger(0));
namingPublisher.put("v2", new AtomicInteger(0));
-
+
tags = new ArrayList<>();
tags.add(new ImmutableTag("version", "v1"));
NacosMeterRegistryCenter.gauge(METER_REGISTRY, "nacos_naming_publisher", tags, namingPublisher.get("v1"));
-
+
tags = new ArrayList<>();
tags.add(new ImmutableTag("version", "v2"));
NacosMeterRegistryCenter.gauge(METER_REGISTRY, "nacos_naming_publisher", tags, namingPublisher.get("v2"));
@@ -210,7 +210,7 @@ public static AtomicInteger getNamingPublisher(String version) {
return INSTANCE.namingPublisher.get(version);
}
- public static TopnCounterMetricsContainer getServiceChangeCount() {
+ public static ServiceTopNCounter getServiceChangeCount() {
return INSTANCE.serviceChangeCount;
}
@@ -251,8 +251,8 @@ public static void decrementSubscribeCount() {
INSTANCE.subscriberCount.decrementAndGet();
}
- public static void incrementServiceChangeCount(String namespace, String group, String name) {
- INSTANCE.serviceChangeCount.increment(namespace + "@" + group + "@" + name);
+ public static void incrementServiceChangeCount(Service service) {
+ INSTANCE.serviceChangeCount.increment(service);
}
public static Counter getDiskException() {
@@ -260,15 +260,18 @@ public static Counter getDiskException() {
}
public static Counter getLeaderSendBeatFailedException() {
- return NacosMeterRegistryCenter.counter(METER_REGISTRY, "nacos_exception", "module", "naming", "name", "leaderSendBeatFailed");
+ return NacosMeterRegistryCenter
+ .counter(METER_REGISTRY, "nacos_exception", "module", "naming", "name", "leaderSendBeatFailed");
}
/**
* increment IpCount when use batchRegister instance.
- * @param old old instancePublishInfo
+ *
+ * @param old old instancePublishInfo
* @param instancePublishInfo must be BatchInstancePublishInfo
*/
- public static void incrementIpCountWithBatchRegister(InstancePublishInfo old, BatchInstancePublishInfo instancePublishInfo) {
+ public static void incrementIpCountWithBatchRegister(InstancePublishInfo old,
+ BatchInstancePublishInfo instancePublishInfo) {
int newSize = instancePublishInfo.getInstancePublishInfos().size();
if (null == old) {
// First time increment batchPublishInfo, add all into metrics.
@@ -285,6 +288,7 @@ public static void incrementIpCountWithBatchRegister(InstancePublishInfo old, Ba
/**
* decrement IpCount when use batchRegister instance.
+ *
* @param instancePublishInfo must be BatchInstancePublishInfo
*/
public static void decrementIpCountWithBatchRegister(InstancePublishInfo instancePublishInfo) {
diff --git a/naming/src/main/java/com/alibaba/nacos/naming/monitor/NamingDynamicMeterRefreshService.java b/naming/src/main/java/com/alibaba/nacos/naming/monitor/NamingDynamicMeterRefreshService.java
index 7c60d04ab4b..19fb0ccb186 100644
--- a/naming/src/main/java/com/alibaba/nacos/naming/monitor/NamingDynamicMeterRefreshService.java
+++ b/naming/src/main/java/com/alibaba/nacos/naming/monitor/NamingDynamicMeterRefreshService.java
@@ -50,9 +50,9 @@ public void refreshTopnServiceChangeCount() {
for (Pair serviceChangeCount : topnServiceChangeCount) {
List tags = new ArrayList<>();
tags.add(new ImmutableTag("service", serviceChangeCount.getFirst()));
- NacosMeterRegistryCenter.gauge(TOPN_SERVICE_CHANGE_REGISTRY, "service_change_count", tags, serviceChangeCount.getSecond());
+ NacosMeterRegistryCenter
+ .gauge(TOPN_SERVICE_CHANGE_REGISTRY, "service_change_count", tags, serviceChangeCount.getSecond());
}
- MetricsMonitor.getServiceChangeCount().removeAll();
}
/**
@@ -60,6 +60,6 @@ public void refreshTopnServiceChangeCount() {
*/
@Scheduled(cron = "0 0 0 ? * 1")
public void resetTopnServiceChangeCount() {
- MetricsMonitor.getServiceChangeCount().removeAll();
+ MetricsMonitor.getServiceChangeCount().reset();
}
}
diff --git a/naming/src/main/java/com/alibaba/nacos/naming/monitor/ServiceTopNCounter.java b/naming/src/main/java/com/alibaba/nacos/naming/monitor/ServiceTopNCounter.java
new file mode 100644
index 00000000000..4f72360e59e
--- /dev/null
+++ b/naming/src/main/java/com/alibaba/nacos/naming/monitor/ServiceTopNCounter.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2023 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.nacos.naming.monitor;
+
+import com.alibaba.nacos.core.monitor.topn.BaseTopNCounter;
+import com.alibaba.nacos.naming.core.v2.pojo.Service;
+import com.alibaba.nacos.naming.misc.UtilsAndCommons;
+
+/**
+ * Service topN counter.
+ *
+ * @author xiweng.yy
+ */
+@SuppressWarnings("PMD.ClassNamingShouldBeCamelRule")
+public class ServiceTopNCounter extends BaseTopNCounter {
+
+ public ServiceTopNCounter() {
+ super();
+ }
+
+ @Override
+ protected String keyToString(Service service) {
+ return service.getNamespace() + UtilsAndCommons.NAMESPACE_SERVICE_CONNECTOR + service.getGroupedServiceName();
+ }
+}
diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java
index 6933779d0a5..9dd25664f54 100644
--- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java
+++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java
@@ -118,7 +118,7 @@ public void onEvent(Event event) {
ServiceEvent.ServiceChangedEvent serviceChangedEvent = (ServiceEvent.ServiceChangedEvent) event;
Service service = serviceChangedEvent.getService();
delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay()));
- MetricsMonitor.incrementServiceChangeCount(service.getNamespace(), service.getGroup(), service.getName());
+ MetricsMonitor.incrementServiceChangeCount(service);
} else if (event instanceof ServiceEvent.ServiceSubscribedEvent) {
// If service is subscribed by one client, only push this client.
ServiceEvent.ServiceSubscribedEvent subscribedEvent = (ServiceEvent.ServiceSubscribedEvent) event;