Add metrics for configcenter (#11602)

* Add ConfigCenter metrics.

* Modifying the obtaining mode for ConfigCenterMetricsCollector.

* Modify method name.

* Add metrics when configCenter initialized.

* Add UnitTest

* Add License.

* bugfix: config test not pass.

* 1.delete author
2.unuse imported

* fix configcenter metrics zk factory

* Get 'dubbo.metrics.configcenter.enable' config from property, get config before startup.

* resolve conflict

---------

Co-authored-by: Albumen Kevin <jhq0812@gmail.com>
Co-authored-by: songxiaosheng <songxiaosheng@elastic.link>
This commit is contained in:
fomeiherz 2023-03-27 09:48:38 +08:00 committed by GitHub
parent 4a3172b80a
commit c91affe875
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 457 additions and 37 deletions

View File

@ -626,4 +626,6 @@ public interface CommonConstants {
String BYTE_ACCESSOR_KEY = "byte.accessor";
String PAYLOAD = "payload";
String DUBBO_METRICS_CONFIGCENTER_ENABLE = "dubbo.metrics.configcenter.enable";
}

View File

@ -37,6 +37,12 @@ public interface MetricsConstants {
String TAG_VERSION_KEY = "version";
String TAG_APPLICATION_VERSION_KEY = "application.version";
String TAG_KEY_KEY = "key";
String TAG_CONFIG_CENTER = "config.center";
String TAG_CHANGE_TYPE = "change.type";
String ENABLE_JVM_METRICS_KEY = "enable.jvm.metrics";

View File

@ -50,6 +50,7 @@ import org.apache.dubbo.config.utils.CompositeReferenceCache;
import org.apache.dubbo.config.utils.ConfigValidationUtils;
import org.apache.dubbo.metadata.report.MetadataReportFactory;
import org.apache.dubbo.metadata.report.MetadataReportInstance;
import org.apache.dubbo.metrics.collector.ConfigCenterMetricsCollector;
import org.apache.dubbo.metrics.collector.DefaultMetricsCollector;
import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
import org.apache.dubbo.metrics.model.TimePair;
@ -782,20 +783,33 @@ public class DefaultApplicationDeployer extends AbstractDeployer<ApplicationMode
throw new IllegalStateException(e);
}
}
ApplicationModel applicationModel = getApplicationModel();
ConfigCenterMetricsCollector collector =
applicationModel.getBeanFactory().getOrRegisterBean(ConfigCenterMetricsCollector.class);
if (StringUtils.isNotEmpty(configCenter.getConfigFile())) {
String configContent = dynamicConfiguration.getProperties(configCenter.getConfigFile(), configCenter.getGroup());
String appGroup = getApplication().getName();
String appConfigContent = null;
String appConfigFile = null;
if (isNotEmpty(appGroup)) {
appConfigContent = dynamicConfiguration.getProperties
(isNotEmpty(configCenter.getAppConfigFile()) ? configCenter.getAppConfigFile() : configCenter.getConfigFile(),
appGroup
);
appConfigFile = isNotEmpty(configCenter.getAppConfigFile()) ? configCenter.getAppConfigFile() : configCenter.getConfigFile();
appConfigContent = dynamicConfiguration.getProperties(appConfigFile, appGroup);
}
try {
environment.updateExternalConfigMap(parseProperties(configContent));
environment.updateAppExternalConfigMap(parseProperties(appConfigContent));
Map<String, String> configMap = parseProperties(configContent);
Map<String, String> appConfigMap = parseProperties(appConfigContent);
environment.updateExternalConfigMap(configMap);
environment.updateAppExternalConfigMap(appConfigMap);
// Add metrics
collector.increase4Initialized(configCenter.getConfigFile(), configCenter.getGroup(),
configCenter.getProtocol(), applicationModel.getApplicationName(), configMap.size());
if (isNotEmpty(appGroup)) {
collector.increase4Initialized(appConfigFile, appGroup,
configCenter.getProtocol(), applicationModel.getApplicationName(), appConfigMap.size());
}
} catch (IOException e) {
throw new IllegalStateException("Failed to parse configurations from Config Center.", e);
}

View File

@ -38,6 +38,21 @@
<artifactId>dubbo-common</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-default</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-prometheus</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>

View File

@ -33,6 +33,8 @@ import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
import com.ctrip.framework.apollo.enums.ConfigSourceType;
import com.ctrip.framework.apollo.enums.PropertyChangeType;
import com.ctrip.framework.apollo.model.ConfigChange;
import org.apache.dubbo.metrics.collector.ConfigCenterMetricsCollector;
import org.apache.dubbo.rpc.model.ApplicationModel;
import java.util.Arrays;
import java.util.Collections;
@ -76,9 +78,11 @@ public class ApolloDynamicConfiguration implements DynamicConfiguration {
private final Config dubboConfig;
private final ConfigFile dubboConfigFile;
private final ConcurrentMap<String, ApolloListener> listeners = new ConcurrentHashMap<>();
private final ApplicationModel applicationModel;
ApolloDynamicConfiguration(URL url) {
ApolloDynamicConfiguration(URL url, ApplicationModel applicationModel) {
this.url = url;
this.applicationModel = applicationModel;
// Instead of using Dubbo's configuration, I would suggest use the original configuration method Apollo provides.
String configEnv = url.getParameter(APOLLO_ENV_KEY);
String configAddr = getAddressWithProtocolPrefix(url);
@ -245,6 +249,10 @@ public class ApolloDynamicConfiguration implements DynamicConfiguration {
ConfigChangedEvent event = new ConfigChangedEvent(key, change.getNamespace(), change.getNewValue(), getChangeType(change));
listeners.forEach(listener -> listener.process(event));
ConfigCenterMetricsCollector collector =
applicationModel.getBeanFactory().getBean(ConfigCenterMetricsCollector.class);
collector.increaseUpdated("apollo", applicationModel.getApplicationName(), event);
}
}

View File

@ -19,13 +19,21 @@ package org.apache.dubbo.configcenter.support.apollo;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.configcenter.AbstractDynamicConfigurationFactory;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
import org.apache.dubbo.rpc.model.ApplicationModel;
/**
*
*/
public class ApolloDynamicConfigurationFactory extends AbstractDynamicConfigurationFactory {
private ApplicationModel applicationModel;
public ApolloDynamicConfigurationFactory(ApplicationModel applicationModel) {
this.applicationModel = applicationModel;
}
@Override
protected DynamicConfiguration createDynamicConfiguration(URL url) {
return new ApolloDynamicConfiguration(url);
return new ApolloDynamicConfiguration(url, applicationModel);
}
}

View File

@ -21,6 +21,7 @@ import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
import com.google.common.util.concurrent.SettableFuture;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -46,6 +47,7 @@ class ApolloDynamicConfigurationTest {
private static final String DEFAULT_NAMESPACE = "dubbo";
private static ApolloDynamicConfiguration apolloDynamicConfiguration;
private static URL url;
private static ApplicationModel applicationModel;
/**
* The constant embeddedApollo.
@ -61,6 +63,7 @@ class ApolloDynamicConfigurationTest {
String apolloUrl = System.getProperty("apollo.configService");
String urlForDubbo = "apollo://" + apolloUrl.substring(apolloUrl.lastIndexOf("/") + 1) + "/org.apache.dubbo.apollo.testService?namespace=dubbo&check=true";
url = URL.valueOf(urlForDubbo).addParameter(SESSION_TIMEOUT_KEY, 15000);
applicationModel = ApplicationModel.defaultModel();
}
// /**
@ -88,7 +91,7 @@ class ApolloDynamicConfigurationTest {
String mockKey = "mockKey1";
String mockValue = String.valueOf(new Random().nextInt());
putMockRuleData(mockKey, mockValue, DEFAULT_NAMESPACE);
apolloDynamicConfiguration = new ApolloDynamicConfiguration(url);
apolloDynamicConfiguration = new ApolloDynamicConfiguration(url, applicationModel);
assertEquals(mockValue, apolloDynamicConfiguration.getConfig(mockKey, DEFAULT_NAMESPACE, 3000L));
mockKey = "notExistKey";
@ -106,7 +109,7 @@ class ApolloDynamicConfigurationTest {
String mockValue = String.valueOf(new Random().nextInt());
putMockRuleData(mockKey, mockValue, DEFAULT_NAMESPACE);
TimeUnit.MILLISECONDS.sleep(1000);
apolloDynamicConfiguration = new ApolloDynamicConfiguration(url);
apolloDynamicConfiguration = new ApolloDynamicConfiguration(url, applicationModel);
assertEquals(mockValue, apolloDynamicConfiguration.getInternalProperty(mockKey));
mockValue = "mockValue2";
@ -129,7 +132,7 @@ class ApolloDynamicConfigurationTest {
final SettableFuture<org.apache.dubbo.common.config.configcenter.ConfigChangedEvent> future = SettableFuture.create();
apolloDynamicConfiguration = new ApolloDynamicConfiguration(url);
apolloDynamicConfiguration = new ApolloDynamicConfiguration(url, applicationModel);
apolloDynamicConfiguration.addListener(mockKey, DEFAULT_NAMESPACE, new ConfigurationListener() {
@Override
@ -187,4 +190,4 @@ class ApolloDynamicConfigurationTest {
}
}
}

View File

@ -41,5 +41,20 @@
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-default</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-prometheus</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -43,6 +43,8 @@ import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.AbstractSharedListener;
import com.alibaba.nacos.api.exception.NacosException;
import org.apache.dubbo.metrics.collector.ConfigCenterMetricsCollector;
import org.apache.dubbo.rpc.model.ApplicationModel;
import static com.alibaba.nacos.api.PropertyKeyConst.PASSWORD;
import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR;
@ -80,6 +82,8 @@ public class NacosDynamicConfiguration implements DynamicConfiguration {
*/
private final NacosConfigServiceWrapper configService;
private ApplicationModel applicationModel;
/**
* The map store the key to {@link NacosConfigListener} mapping
*/
@ -87,10 +91,11 @@ public class NacosDynamicConfiguration implements DynamicConfiguration {
private final MD5Utils md5Utils = new MD5Utils();
NacosDynamicConfiguration(URL url) {
NacosDynamicConfiguration(URL url, ApplicationModel applicationModel) {
this.nacosProperties = buildNacosProperties(url);
this.configService = buildConfigService(url);
watchListenerMap = new ConcurrentHashMap<>();
this.watchListenerMap = new ConcurrentHashMap<>();
this.applicationModel = applicationModel;
}
private NacosConfigServiceWrapper buildConfigService(URL url) {
@ -339,6 +344,10 @@ public class NacosDynamicConfiguration implements DynamicConfiguration {
cacheData.put(dataId, configInfo);
}
listeners.forEach(listener -> listener.process(event));
ConfigCenterMetricsCollector collector =
applicationModel.getBeanFactory().getOrRegisterBean(ConfigCenterMetricsCollector.class);
collector.increaseUpdated("nacos", applicationModel.getApplicationName(), event);
}
void addListener(ConfigurationListener configurationListener) {

View File

@ -23,12 +23,19 @@ import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
import org.apache.dubbo.common.constants.CommonConstants;
import com.alibaba.nacos.api.PropertyKeyConst;
import org.apache.dubbo.rpc.model.ApplicationModel;
/**
* The nacos implementation of {@link AbstractDynamicConfigurationFactory}
*/
public class NacosDynamicConfigurationFactory extends AbstractDynamicConfigurationFactory {
private ApplicationModel applicationModel;
public NacosDynamicConfigurationFactory(ApplicationModel applicationModel) {
this.applicationModel = applicationModel;
}
@Override
protected DynamicConfiguration createDynamicConfiguration(URL url) {
URL nacosURL = url;
@ -36,6 +43,6 @@ public class NacosDynamicConfigurationFactory extends AbstractDynamicConfigurati
// Nacos use empty string as default name space, replace default namespace "dubbo" to ""
nacosURL = url.removeParameter(PropertyKeyConst.NAMESPACE);
}
return new NacosDynamicConfiguration(nacosURL);
return new NacosDynamicConfiguration(nacosURL, applicationModel);
}
}

View File

@ -17,14 +17,14 @@
package org.apache.dubbo.configcenter.support.nacos;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
@ -132,7 +132,7 @@ class NacosDynamicConfigurationTest {
// timeout in 15 seconds.
URL url = URL.valueOf(urlForDubbo)
.addParameter(SESSION_TIMEOUT_KEY, 15000);
config = new NacosDynamicConfiguration(url);
config = new NacosDynamicConfiguration(url, ApplicationModel.defaultModel());
try {
@ -184,4 +184,4 @@ class NacosDynamicConfigurationTest {
}
}
}
}

View File

@ -20,6 +20,7 @@ import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
@ -34,6 +35,8 @@ import static com.alibaba.nacos.client.constant.Constants.HealthCheck.UP;
import static org.mockito.ArgumentMatchers.any;
class RetryTest {
private static ApplicationModel applicationModel = ApplicationModel.defaultModel();
@Test
void testRetryCreate() {
@ -51,10 +54,10 @@ class RetryTest {
URL url = URL.valueOf("nacos://127.0.0.1:8848")
.addParameter("nacos.retry", 5)
.addParameter("nacos.retry-wait", 10);
Assertions.assertThrows(IllegalStateException.class, () -> new NacosDynamicConfiguration(url));
Assertions.assertThrows(IllegalStateException.class, () -> new NacosDynamicConfiguration(url, applicationModel));
try {
new NacosDynamicConfiguration(url);
new NacosDynamicConfiguration(url, applicationModel);
} catch (Throwable t) {
Assertions.fail(t);
}
@ -78,7 +81,7 @@ class RetryTest {
.addParameter("nacos.retry-wait", 10)
.addParameter("nacos.check", "false");
try {
new NacosDynamicConfiguration(url);
new NacosDynamicConfiguration(url, applicationModel);
} catch (Throwable t) {
Assertions.fail(t);
}
@ -110,10 +113,10 @@ class RetryTest {
URL url = URL.valueOf("nacos://127.0.0.1:8848")
.addParameter("nacos.retry", 5)
.addParameter("nacos.retry-wait", 10);
Assertions.assertThrows(IllegalStateException.class, () -> new NacosDynamicConfiguration(url));
Assertions.assertThrows(IllegalStateException.class, () -> new NacosDynamicConfiguration(url, applicationModel));
try {
new NacosDynamicConfiguration(url);
new NacosDynamicConfiguration(url, applicationModel);
} catch (Throwable t) {
Assertions.fail(t);
}

View File

@ -60,6 +60,21 @@
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-default</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-prometheus</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
<profiles>

View File

@ -19,6 +19,7 @@ package org.apache.dubbo.configcenter.support.zookeeper;
import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.ConcurrentHashMapUtils;
import org.apache.dubbo.rpc.model.ApplicationModel;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ -34,9 +35,9 @@ public class CacheListener {
public CacheListener() {
}
public ZookeeperDataListener addListener(String pathKey, ConfigurationListener configurationListener, String key, String group) {
public ZookeeperDataListener addListener(String pathKey, ConfigurationListener configurationListener, String key, String group, ApplicationModel applicationModel) {
ZookeeperDataListener zookeeperDataListener = ConcurrentHashMapUtils.computeIfAbsent(pathKeyListeners, pathKey,
_pathKey -> new ZookeeperDataListener(_pathKey, key, group));
_pathKey -> new ZookeeperDataListener(_pathKey, key, group, applicationModel));
zookeeperDataListener.addListener(configurationListener);
return zookeeperDataListener;
}

View File

@ -20,8 +20,10 @@ import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.metrics.collector.ConfigCenterMetricsCollector;
import org.apache.dubbo.remoting.zookeeper.DataListener;
import org.apache.dubbo.remoting.zookeeper.EventType;
import org.apache.dubbo.rpc.model.ApplicationModel;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
@ -30,16 +32,19 @@ import java.util.concurrent.CopyOnWriteArraySet;
* one path has multi configurationListeners
*/
public class ZookeeperDataListener implements DataListener {
private final String path;
private final String key;
private final String group;
private final Set<ConfigurationListener> listeners;
public ZookeeperDataListener(String path, String key, String group) {
private String path;
private String key;
private String group;
private Set<ConfigurationListener> listeners;
private ApplicationModel applicationModel;
public ZookeeperDataListener(String path, String key, String group, ApplicationModel applicationModel) {
this.path = path;
this.key = key;
this.group = group;
this.listeners = new CopyOnWriteArraySet<>();
this.applicationModel = applicationModel;
}
public void addListener(ConfigurationListener configurationListener) {
@ -71,6 +76,10 @@ public class ZookeeperDataListener implements DataListener {
if (CollectionUtils.isNotEmpty(listeners)) {
listeners.forEach(listener -> listener.process(configChangeEvent));
}
ConfigCenterMetricsCollector collector =
applicationModel.getBeanFactory().getBean(ConfigCenterMetricsCollector.class);
collector.increaseUpdated("zookeeper", applicationModel.getApplicationName(), configChangeEvent);
}
}

View File

@ -26,6 +26,7 @@ import org.apache.dubbo.common.utils.NamedThreadFactory;
import org.apache.dubbo.remoting.zookeeper.ZookeeperClient;
import org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.zookeeper.data.Stat;
import java.util.Collection;
@ -47,11 +48,13 @@ public class ZookeeperDynamicConfiguration extends TreePathDynamicConfiguration
private static final int DEFAULT_ZK_EXECUTOR_THREADS_NUM = 1;
private static final int DEFAULT_QUEUE = 10000;
private static final Long THREAD_KEEP_ALIVE_TIME = 0L;
private final ApplicationModel applicationModel;
ZookeeperDynamicConfiguration(URL url, ZookeeperTransporter zookeeperTransporter) {
ZookeeperDynamicConfiguration(URL url, ZookeeperTransporter zookeeperTransporter, ApplicationModel applicationModel) {
super(url);
this.cacheListener = new CacheListener();
this.applicationModel = applicationModel;
final String threadName = this.getClass().getSimpleName();
this.executor = new ThreadPoolExecutor(DEFAULT_ZK_EXECUTOR_THREADS_NUM, DEFAULT_ZK_EXECUTOR_THREADS_NUM,
@ -150,7 +153,7 @@ public class ZookeeperDynamicConfiguration extends TreePathDynamicConfiguration
if (cachedListener != null) {
cachedListener.addListener(listener);
} else {
ZookeeperDataListener addedListener = cacheListener.addListener(pathKey, listener, key, group);
ZookeeperDataListener addedListener = cacheListener.addListener(pathKey, listener, key, group, applicationModel);
zkClient.addDataListener(pathKey, addedListener, executor);
}
}

View File

@ -26,12 +26,15 @@ public class ZookeeperDynamicConfigurationFactory extends AbstractDynamicConfigu
private final ZookeeperTransporter zookeeperTransporter;
private final ApplicationModel applicationModel;
public ZookeeperDynamicConfigurationFactory(ApplicationModel applicationModel) {
this.applicationModel = applicationModel;
this.zookeeperTransporter = ZookeeperTransporter.getExtension(applicationModel);
}
@Override
protected DynamicConfiguration createDynamicConfiguration(URL url) {
return new ZookeeperDynamicConfiguration(url, zookeeperTransporter);
return new ZookeeperDynamicConfiguration(url, zookeeperTransporter, applicationModel);
}
}

View File

@ -49,5 +49,10 @@
<groupId>com.tdunning</groupId>
<artifactId>t-digest</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-integration-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,88 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.metrics.model;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_CHANGE_TYPE;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_CONFIG_CENTER;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_GROUP_KEY;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_HOSTNAME;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_IP;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_KEY_KEY;
import static org.apache.dubbo.common.utils.NetUtils.getLocalHost;
import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName;
public class ConfigCenterMetric implements Metric {
private String applicationName;
private String key;
private String group;
private String configCenter;
private String changeType;
public ConfigCenterMetric() {
}
public ConfigCenterMetric(String applicationName, String key, String group, String configCenter, String changeType) {
this.applicationName = applicationName;
this.key = key;
this.group = group;
this.configCenter = configCenter;
this.changeType = changeType;
}
@Override
public Map<String, String> getTags() {
Map<String, String> tags = new HashMap<>();
tags.put(TAG_IP, getLocalHost());
tags.put(TAG_HOSTNAME, getLocalHostName());
tags.put(TAG_APPLICATION_NAME, applicationName);
tags.put(TAG_KEY_KEY, key);
tags.put(TAG_GROUP_KEY, group);
tags.put(TAG_CONFIG_CENTER, configCenter);
tags.put(TAG_CHANGE_TYPE, changeType.toLowerCase());
return tags;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ConfigCenterMetric that = (ConfigCenterMetric) o;
if (!Objects.equals(applicationName, that.applicationName))
return false;
if (!Objects.equals(key, that.key)) return false;
if (!Objects.equals(group, that.group)) return false;
if (!Objects.equals(configCenter, that.configCenter)) return false;
return Objects.equals(changeType, that.changeType);
}
@Override
public int hashCode() {
return Objects.hash(applicationName, key, group, configCenter, changeType);
}
}

View File

@ -24,8 +24,9 @@ public enum MetricsCategory {
RT,
QPS,
REQUESTS,
APPLICATION,
CONFIGCENTER,
REGISTRY,
METADATA,
THREAD_POOL,
APPLICATION
}

View File

@ -20,6 +20,8 @@ package org.apache.dubbo.metrics.model;
public enum MetricsKey {
APPLICATION_METRIC_INFO("dubbo.application.info.total", "Total Application Info"),
CONFIGCENTER_METRIC_TOTAL("dubbo.configcenter.total", "Config Changed Total"),
// provider metrics key
METRIC_REQUESTS("dubbo.%s.requests.total", "Total Requests"),
METRIC_REQUESTS_SUCCEED("dubbo.%s.requests.succeed.total", "Total Succeed Requests"),

View File

@ -0,0 +1,99 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.metrics.collector;
import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
import org.apache.dubbo.metrics.model.ConfigCenterMetric;
import org.apache.dubbo.metrics.model.MetricsKey;
import org.apache.dubbo.metrics.model.sample.GaugeMetricSample;
import org.apache.dubbo.metrics.model.sample.MetricSample;
import org.apache.dubbo.rpc.model.ApplicationModel;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_METRICS_CONFIGCENTER_ENABLE;
import static org.apache.dubbo.metrics.model.MetricsCategory.CONFIGCENTER;
public class ConfigCenterMetricsCollector implements MetricsCollector {
private boolean collectEnabled = true;
private final ApplicationModel applicationModel;
private final Map<ConfigCenterMetric, AtomicLong> updatedMetrics = new ConcurrentHashMap<>();
public ConfigCenterMetricsCollector(ApplicationModel applicationModel) {
this.applicationModel = applicationModel;
// default is true, disable when config false
if ("false".equals(System.getProperty(DUBBO_METRICS_CONFIGCENTER_ENABLE))) {
collectEnabled = false;
}
}
public void setCollectEnabled(Boolean collectEnabled) {
if (collectEnabled != null) {
this.collectEnabled = collectEnabled;
}
}
@Override
public boolean isCollectEnabled() {
return collectEnabled;
}
public void increase4Initialized(String key, String group, String protocol, String applicationName, int count) {
if (!isCollectEnabled()) {
return;
}
if (count <= 0) {
return;
}
ConfigCenterMetric metric = new ConfigCenterMetric(applicationName, key, group, protocol, ConfigChangeType.ADDED.name());
AtomicLong aLong = updatedMetrics.computeIfAbsent(metric, k -> new AtomicLong(0L));
aLong.addAndGet(count);
}
public void increaseUpdated(String protocol, String applicationName, ConfigChangedEvent event) {
if (!isCollectEnabled()) {
return;
}
ConfigCenterMetric metric = new ConfigCenterMetric(applicationName, event.getKey(), event.getGroup(), protocol, event.getChangeType().name());
AtomicLong count = updatedMetrics.computeIfAbsent(metric, k -> new AtomicLong(0L));
count.incrementAndGet();
}
@Override
public List<MetricSample> collect() {
// Add metrics to reporter
List<MetricSample> list = new ArrayList<>();
if (!isCollectEnabled()) {
return list;
}
collect(list);
return list;
}
private void collect(List<MetricSample> list) {
updatedMetrics.forEach((k, v) -> list.add(new GaugeMetricSample<>(MetricsKey.CONFIGCENTER_METRIC_TOTAL, k.getTags(), CONFIGCENTER, v, AtomicLong::get)));
}
}

View File

@ -0,0 +1,104 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.metrics.collector;
import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.metrics.model.ConfigCenterMetric;
import org.apache.dubbo.metrics.model.sample.GaugeMetricSample;
import org.apache.dubbo.metrics.model.sample.MetricSample;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_GROUP_KEY;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_INTERFACE_KEY;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_METHOD_KEY;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_VERSION_KEY;
import static org.junit.jupiter.api.Assertions.*;
class ConfigCenterMetricsCollectorTest {
private FrameworkModel frameworkModel;
private ApplicationModel applicationModel;
@BeforeEach
public void setup() {
frameworkModel = FrameworkModel.defaultModel();
applicationModel = frameworkModel.newApplication();
ApplicationConfig config = new ApplicationConfig();
config.setName("MockMetrics");
applicationModel.getApplicationConfigManager().setApplication(config);
}
@AfterEach
public void teardown() {
applicationModel.destroy();
}
@Test
void increase4Initialized() {
ConfigCenterMetricsCollector collector = new ConfigCenterMetricsCollector(applicationModel);
collector.setCollectEnabled(true);
String applicationName = applicationModel.getApplicationName();
collector.increase4Initialized("key", "group", "nacos", applicationName, 1);
collector.increase4Initialized("key", "group", "nacos", applicationName, 1);
List<MetricSample> samples = collector.collect();
for (MetricSample sample : samples) {
Assertions.assertTrue(sample instanceof GaugeMetricSample);
GaugeMetricSample<Long> gaugeSample = (GaugeMetricSample) sample;
Map<String, String> tags = gaugeSample.getTags();
Assertions.assertEquals(gaugeSample.applyAsLong(), 2);
Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationName);
}
}
@Test
void increaseUpdated() {
ConfigCenterMetricsCollector collector = new ConfigCenterMetricsCollector(applicationModel);
collector.setCollectEnabled(true);
String applicationName = applicationModel.getApplicationName();
ConfigChangedEvent event = new ConfigChangedEvent("key", "group", null, ConfigChangeType.ADDED);
collector.increaseUpdated("nacos", applicationName, event);
collector.increaseUpdated("nacos", applicationName, event);
List<MetricSample> samples = collector.collect();
for (MetricSample sample : samples) {
Assertions.assertTrue(sample instanceof GaugeMetricSample);
GaugeMetricSample<Long> gaugeSample = (GaugeMetricSample) sample;
Map<String, String> tags = gaugeSample.getTags();
Assertions.assertEquals(gaugeSample.applyAsLong(), 2);
Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationName);
}
}
}