Observability task: registration center (#11493)

This commit is contained in:
wxbty 2023-02-25 17:22:44 +08:00 committed by GitHub
parent d57ad31299
commit c041fa1f93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 1462 additions and 82 deletions

View File

@ -68,5 +68,11 @@
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-registry</artifactId>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -16,6 +16,31 @@
*/
package org.apache.dubbo.rpc.cluster.directory;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.config.Configuration;
import org.apache.dubbo.common.config.ConfigurationUtils;
import org.apache.dubbo.common.constants.LoggerCodeConstants;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
import org.apache.dubbo.common.utils.ConcurrentHashSet;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
import org.apache.dubbo.metrics.registry.event.RegistryEvent;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.cluster.Directory;
import org.apache.dubbo.rpc.cluster.Router;
import org.apache.dubbo.rpc.cluster.RouterChain;
import org.apache.dubbo.rpc.cluster.SingleRouterChain;
import org.apache.dubbo.rpc.cluster.router.state.BitList;
import org.apache.dubbo.rpc.cluster.support.ClusterUtils;
import org.apache.dubbo.rpc.model.ApplicationModel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -31,29 +56,6 @@ import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.config.Configuration;
import org.apache.dubbo.common.config.ConfigurationUtils;
import org.apache.dubbo.common.constants.LoggerCodeConstants;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
import org.apache.dubbo.common.utils.ConcurrentHashSet;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.cluster.Directory;
import org.apache.dubbo.rpc.cluster.Router;
import org.apache.dubbo.rpc.cluster.RouterChain;
import org.apache.dubbo.rpc.cluster.SingleRouterChain;
import org.apache.dubbo.rpc.cluster.router.state.BitList;
import org.apache.dubbo.rpc.cluster.support.ClusterUtils;
import org.apache.dubbo.rpc.model.ApplicationModel;
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_RECONNECT_TASK_PERIOD;
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_RECONNECT_TASK_TRY_COUNT;
@ -128,6 +130,10 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
*/
private final int reconnectTaskPeriod;
private final GlobalMetricsEventMulticaster eventMulticaster;
private ApplicationModel applicationModel;
public AbstractDirectory(URL url) {
this(url, null, false);
}
@ -153,7 +159,7 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
}
// remove some local only parameters
ApplicationModel applicationModel = url.getOrDefaultApplicationModel();
applicationModel = url.getOrDefaultApplicationModel();
this.queryMap = applicationModel.getBeanFactory().getBean(ClusterUtils.class).mergeLocalParams(queryMap);
if (consumerUrl == null) {
@ -179,12 +185,14 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
this.reconnectTaskTryCount = configuration.getInt(RECONNECT_TASK_TRY_COUNT, DEFAULT_RECONNECT_TASK_TRY_COUNT);
this.reconnectTaskPeriod = configuration.getInt(RECONNECT_TASK_PERIOD, DEFAULT_RECONNECT_TASK_PERIOD);
setRouterChain(routerChain);
eventMulticaster = applicationModel.getBeanFactory().getBean(GlobalMetricsEventMulticaster.class);
}
@Override
public List<Invoker<T>> list(Invocation invocation) throws RpcException {
if (destroyed) {
throw new RpcException("Directory of type " + this.getClass().getSimpleName() + " already destroyed for service " + getConsumerUrl().getServiceKey() + " from registry " + getUrl());
throw new RpcException("Directory of type " + this.getClass().getSimpleName() + " already destroyed for service " + getConsumerUrl().getServiceKey() + " from registry " + getUrl());
}
BitList<Invoker<T>> availableInvokers;
@ -381,6 +389,7 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
@Override
public void addDisabledInvoker(Invoker<T> invoker) {
eventMulticaster.publishEvent(new RegistryEvent.MetricsDirectoryEvent(applicationModel, RegistryEvent.Type.D_DISABLE));
if (invokers.contains(invoker)) {
disabledInvokers.add(invoker);
removeValidInvoker(invoker);
@ -390,6 +399,7 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
@Override
public void recoverDisabledInvoker(Invoker<T> invoker) {
eventMulticaster.publishEvent(new RegistryEvent.MetricsDirectoryEvent(applicationModel, RegistryEvent.Type.D_RECOVER_DISABLE));
if (disabledInvokers.remove(invoker)) {
try {
addValidInvoker(invoker);
@ -453,6 +463,7 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
this.invokers = invokers;
refreshInvokerInternal();
this.invokersInitialized = true;
eventMulticaster.publishEvent(new RegistryEvent.MetricsDirectoryEvent(applicationModel, RegistryEvent.Type.D_CURRENT, invokers.size()));
}
protected void destroyInvokers() {
@ -463,12 +474,14 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
}
private boolean addValidInvoker(Invoker<T> invoker) {
eventMulticaster.publishEvent(new RegistryEvent.MetricsDirectoryEvent(applicationModel, RegistryEvent.Type.D_VALID));
synchronized (this.validInvokers) {
return this.validInvokers.add(invoker);
}
}
private boolean removeValidInvoker(Invoker<T> invoker) {
eventMulticaster.publishEvent(new RegistryEvent.MetricsDirectoryEvent(applicationModel, RegistryEvent.Type.D_UN_VALID));
synchronized (this.validInvokers) {
return this.validInvokers.remove(invoker);
}

View File

@ -19,6 +19,7 @@ package org.apache.dubbo.rpc.cluster.directory;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.Holder;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.cluster.router.MockInvoker;
@ -26,6 +27,7 @@ import org.apache.dubbo.rpc.cluster.router.condition.ConditionStateRouterFactory
import org.apache.dubbo.rpc.cluster.router.state.BitList;
import org.apache.dubbo.rpc.cluster.router.state.StateRouter;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@ -60,6 +62,7 @@ class StaticDirectoryTest {
List<Invoker<String>> filteredInvokers = router.route(invokers.clone(), URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>());
ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class);
StaticDirectory<String> staticDirectory = new StaticDirectory<>(filteredInvokers);
boolean isAvailable = staticDirectory.isAvailable();
Assertions.assertTrue(!isAvailable);

View File

@ -16,18 +16,11 @@
*/
package org.apache.dubbo.rpc.cluster.support;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
@ -41,6 +34,7 @@ import org.apache.dubbo.rpc.cluster.filter.DemoService;
import org.apache.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance;
import org.apache.dubbo.rpc.cluster.loadbalance.RandomLoadBalance;
import org.apache.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
@ -50,6 +44,14 @@ import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
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.CONSUMER;
import static org.apache.dubbo.common.constants.CommonConstants.ENABLE_CONNECTIVITY_VALIDATION;
import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
@ -104,6 +106,7 @@ class AbstractClusterInvokerTest {
@SuppressWarnings({"unchecked"})
@BeforeEach
public void setUp() throws Exception {
ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class);
Map<String, Object> attributes = new HashMap<>();
attributes.put("application", "abstractClusterInvokerTest");
url = url.putAttribute(REFER_KEY, attributes);

View File

@ -19,6 +19,7 @@ package org.apache.dubbo.rpc.cluster.support;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
@ -28,6 +29,7 @@ import org.apache.dubbo.rpc.cluster.Directory;
import org.apache.dubbo.rpc.cluster.LoadBalance;
import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
@ -70,6 +72,7 @@ class ConnectivityValidationTest {
@BeforeEach
public void setup() {
ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class);
invoker1 = Mockito.mock(Invoker.class);
invoker2 = Mockito.mock(Invoker.class);
invoker3 = Mockito.mock(Invoker.class);

View File

@ -16,11 +16,8 @@
*/
package org.apache.dubbo.rpc.cluster.support;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
import org.apache.dubbo.rpc.AppResponse;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Invocation;
@ -33,10 +30,15 @@ import org.apache.dubbo.rpc.cluster.Directory;
import org.apache.dubbo.rpc.cluster.SingleRouterChain;
import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
import org.apache.dubbo.rpc.cluster.router.state.BitList;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.protocol.AbstractInvoker;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
@ -65,7 +67,7 @@ class FailoverClusterInvokerTest {
@BeforeEach
public void setUp() throws Exception {
ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class);
dic = mock(Directory.class);
given(dic.getUrl()).willReturn(url);

View File

@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.cluster.support.wrapper;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Protocol;
@ -28,6 +29,7 @@ import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.cluster.LoadBalance;
import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.support.MockProtocol;
import org.junit.jupiter.api.Assertions;
@ -49,6 +51,7 @@ class MockClusterInvokerTest {
@BeforeEach
public void beforeMethod() {
ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class);
invokers.clear();
}

View File

@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.cluster.support.wrapper;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ProxyFactory;
@ -28,6 +29,7 @@ import org.apache.dubbo.rpc.cluster.LoadBalance;
import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -46,6 +48,7 @@ class MockProviderRpcExceptionTest {
@BeforeEach
public void beforeMethod() {
ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class);
invokers.clear();
}

View File

@ -41,7 +41,12 @@ public class MetricsConfig extends AbstractConfig {
private Boolean enableJvmMetrics;
/**
* Enable jvm metrics when collecting.
* Enable registry metrics.
*/
private Boolean enableRegistryMetrics;
/**
* Enable metadata metrics.
*/
private Boolean enableMetadataMetrics;
@ -103,6 +108,14 @@ public class MetricsConfig extends AbstractConfig {
this.enableJvmMetrics = enableJvmMetrics;
}
public Boolean getEnableRegistryMetrics() {
return enableRegistryMetrics;
}
public void setEnableRegistryMetrics(Boolean enableRegistryMetrics) {
this.enableRegistryMetrics = enableRegistryMetrics;
}
public String getPort() {
return port;
}

View File

@ -42,12 +42,24 @@
<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-registry</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-metadata</artifactId>

View File

@ -51,6 +51,9 @@ 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.DefaultMetricsCollector;
import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
import org.apache.dubbo.metrics.model.TimePair;
import org.apache.dubbo.metrics.registry.event.RegistryEvent;
import org.apache.dubbo.metrics.report.MetricsReporter;
import org.apache.dubbo.metrics.report.MetricsReporterFactory;
import org.apache.dubbo.metrics.service.MetricsServiceExporter;
@ -825,10 +828,15 @@ public class DefaultApplicationDeployer extends AbstractDeployer<ApplicationMode
private final AtomicInteger serviceRefreshState = new AtomicInteger(0);
private void registerServiceInstance() {
TimePair timePair = TimePair.start();
GlobalMetricsEventMulticaster eventMulticaster = applicationModel.getBeanFactory().getBean(GlobalMetricsEventMulticaster.class);
eventMulticaster.publishEvent(new RegistryEvent.MetricsRegisterEvent(applicationModel, timePair));
try {
registered = true;
ServiceInstanceMetadataUtils.registerMetadataAndInstance(applicationModel);
eventMulticaster.publishFinishEvent(new RegistryEvent.MetricsRegisterEvent(applicationModel, timePair));
} catch (Exception e) {
eventMulticaster.publishErrorEvent(new RegistryEvent.MetricsRegisterEvent(applicationModel, timePair));
logger.error(CONFIG_REGISTER_INSTANCE_ERROR, "configuration server disconnected", "", "Register instance error.", e);
}
if (registered) {

View File

@ -190,6 +190,13 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-registry</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-prometheus</artifactId>
@ -502,6 +509,7 @@
<include>org.apache.dubbo:dubbo-metadata-report-zookeeper</include>
<include>org.apache.dubbo:dubbo-metrics-api</include>
<include>org.apache.dubbo:dubbo-metrics-default</include>
<include>org.apache.dubbo:dubbo-metrics-registry</include>
<include>org.apache.dubbo:dubbo-metrics-metadata</include>
<include>org.apache.dubbo:dubbo-metrics-prometheus</include>
<include>org.apache.dubbo:dubbo-monitor-api</include>

View File

@ -235,6 +235,11 @@
<artifactId>dubbo-metrics-default</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-registry</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-prometheus</artifactId>

View File

@ -36,11 +36,6 @@
<artifactId>dubbo-common</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-cluster</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-api</artifactId>

View File

@ -71,7 +71,7 @@ public class SimpleMetricsEventMulticaster implements MetricsEventMulticaster {
publishTimeEvent(event, metricsLifeListener -> metricsLifeListener.onEventError(event));
}
@SuppressWarnings({"rawtypes", "unchecked"})
@SuppressWarnings({"rawtypes"})
private void publishTimeEvent(MetricsEvent event, Consumer<MetricsLifeListener> consumer) {
if (event instanceof EmptyEvent) {
return;

View File

@ -23,7 +23,7 @@ public enum MetricsKey {
// provider metrics key
METRIC_REQUESTS("dubbo.%s.requests.total", "Total Requests"),
METRIC_REQUESTS_SUCCEED("dubbo.%s.requests.succeed.total", "Succeed Requests"),
METRIC_REQUEST_BUSINESS_FAILED("dubbo.%s.requests.business.failed.total","Failed Business Requests"),
METRIC_REQUEST_BUSINESS_FAILED("dubbo.%s.requests.business.failed.total", "Failed Business Requests"),
METRIC_REQUESTS_PROCESSING("dubbo.%s.requests.processing", "Processing Requests"),
METRIC_REQUESTS_TIMEOUT("dubbo.%s.requests.timeout.total", "Total Timeout Failed Requests"),
METRIC_REQUESTS_LIMIT("dubbo.%s.requests.limit.total", "Total Limit Failed Requests"),
@ -59,6 +59,26 @@ public enum MetricsKey {
GENERIC_METRIC_RT_P99("dubbo.%s.rt.seconds.p99", "Response Time P99"),
GENERIC_METRIC_RT_P95("dubbo.%s.rt.seconds.p95", "Response Time P95"),
// register metrics key
REGISTER_METRIC_REQUESTS("dubbo.registry.register.requests.total", "Total Register Requests"),
REGISTER_METRIC_REQUESTS_SUCCEED("dubbo.registry.register.requests.succeed.total", "Succeed Register Requests"),
REGISTER_METRIC_REQUESTS_FAILED("dubbo.registry.register.requests.failed.total", "Failed Register Requests"),
// subscribe metrics key
SUBSCRIBE_METRIC_NUM("dubbo.registry.subscribe.num.total", "Total Subscribe Num"),
SUBSCRIBE_METRIC_NUM_SUCCEED("dubbo.registry.subscribe.num.succeed.total", "Succeed Subscribe Num"),
SUBSCRIBE_METRIC_NUM_FAILED("dubbo.registry.subscribe.num.failed.total", "Failed Subscribe Num"),
// directory metrics key
DIRECTORY_METRIC_NUM_CURRENT("dubbo.registry.directory.num.current", "Current Directory Urls"),
DIRECTORY_METRIC_NUM_VALID("dubbo.registry.directory.num.valid.total", "Valid Directory Urls"),
DIRECTORY_METRIC_NUM_UN_VALID("dubbo.registry.directory.num.un_valid.total", "UnValid Directory Urls"),
DIRECTORY_METRIC_NUM_DISABLE("dubbo.registry.directory.num.disable.total", "Disable Directory Urls"),
DIRECTORY_METRIC_NUM_RECOVER_DISABLE("dubbo.registry.directory.num.recover.disable.total", "Recover Disable Directory Urls"),
NOTIFY_METRIC_REQUESTS("dubbo.registry.notify.requests.total", "Total Notify Requests"),
NOTIFY_METRIC_NUM_LAST("dubbo.registry.notify.num.last", "Last Notify Nums"),
THREAD_POOL_CORE_SIZE("dubbo.thread.pool.core.size", "Thread Pool Core Size"),
THREAD_POOL_LARGEST_SIZE("dubbo.thread.pool.largest.size", "Thread Pool Largest Size"),
THREAD_POOL_MAX_SIZE("dubbo.thread.pool.max.size", "Thread Pool Max Size"),
@ -85,6 +105,7 @@ public enum MetricsKey {
public final String getName() {
return this.name;
}
public final String getNameByType(String type) {
return String.format(name, type);
}

View File

@ -21,6 +21,7 @@ public class TimePair {
private final long begin;
private long end;
private static final TimePair empty = new TimePair(0L);
public TimePair(long currentTimeMillis) {
this.begin = currentTimeMillis;
@ -37,4 +38,8 @@ public class TimePair {
public long calc() {
return end - begin;
}
public static TimePair empty() {
return empty;
}
}

View File

@ -46,5 +46,11 @@
<artifactId>micrometer-tracing-integration-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-cluster</artifactId>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,40 @@
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>dubbo-metrics-registry</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>The metrics module of dubbo project</description>
<properties>
<skip_maven_deploy>false</skip_maven_deploy>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,123 @@
/*
* 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.registry.collector;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.config.context.ConfigManager;
import org.apache.dubbo.metrics.collector.ApplicationMetricsCollector;
import org.apache.dubbo.metrics.collector.MetricsCollector;
import org.apache.dubbo.metrics.event.MetricsEvent;
import org.apache.dubbo.metrics.event.MetricsEventMulticaster;
import org.apache.dubbo.metrics.model.sample.MetricSample;
import org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite;
import org.apache.dubbo.metrics.registry.event.RegistryEvent;
import org.apache.dubbo.metrics.registry.event.RegistryMetricsEventMulticaster;
import org.apache.dubbo.rpc.model.ApplicationModel;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* Registry implementation of {@link MetricsCollector}
*/
@Activate
public class RegistryMetricsCollector implements ApplicationMetricsCollector<RegistryEvent.Type, RegistryEvent> {
private Boolean collectEnabled = null;
private final RegistryStatComposite stats;
private final MetricsEventMulticaster registryMulticaster;
private final ApplicationModel applicationModel;
public RegistryMetricsCollector(ApplicationModel applicationModel) {
this.stats = new RegistryStatComposite();
this.registryMulticaster = new RegistryMetricsEventMulticaster();
this.applicationModel = applicationModel;
}
public void setCollectEnabled(Boolean collectEnabled) {
if (collectEnabled != null) {
this.collectEnabled = collectEnabled;
}
}
@Override
public boolean isCollectEnabled() {
if (collectEnabled == null) {
ConfigManager configManager = applicationModel.getApplicationConfigManager();
configManager.getMetrics().ifPresent(metricsConfig -> setCollectEnabled(metricsConfig.getEnableRegistryMetrics()));
}
return Optional.ofNullable(collectEnabled).orElse(false);
}
public void setNum(RegistryEvent.Type registryType, String applicationName, Map<String, Integer> lastNumMap) {
lastNumMap.forEach((serviceKey, num) ->
this.stats.setServiceKey(registryType, applicationName, serviceKey, num));
}
public void setNum(RegistryEvent.Type registryType, String applicationName, Integer num) {
this.stats.setApplicationKey(registryType, applicationName, num);
}
@Override
public void increment(String applicationName, RegistryEvent.Type registryType) {
this.stats.increment(registryType, applicationName);
}
@Override
public void addRT(String applicationName, String registryOpType, Long responseTime) {
stats.calcRt(applicationName, registryOpType, responseTime);
}
@Override
public List<MetricSample> collect() {
if (!isCollectEnabled()) {
new ArrayList<>();
}
List<MetricSample> list = new ArrayList<>();
list.addAll(stats.exportNumMetrics());
list.addAll(stats.exportRtMetrics());
list.addAll(stats.exportSkMetrics());
return list;
}
@Override
public boolean isSupport(MetricsEvent event) {
return event instanceof RegistryEvent;
}
@Override
public void onEvent(RegistryEvent event) {
registryMulticaster.publishEvent(event);
}
@Override
public void onEventFinish(RegistryEvent event) {
registryMulticaster.publishFinishEvent(event);
}
@Override
public void onEventError(RegistryEvent event) {
registryMulticaster.publishErrorEvent(event);
}
}

View File

@ -0,0 +1,240 @@
/*
* 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.registry.collector.stat;
import org.apache.dubbo.common.utils.ConcurrentHashMapUtils;
import org.apache.dubbo.metrics.collector.MetricsCollector;
import org.apache.dubbo.metrics.model.ApplicationMetric;
import org.apache.dubbo.metrics.model.MetricsCategory;
import org.apache.dubbo.metrics.model.MetricsKey;
import org.apache.dubbo.metrics.model.MetricsKeyWrapper;
import org.apache.dubbo.metrics.model.sample.GaugeMetricSample;
import org.apache.dubbo.metrics.registry.event.RegistryEvent;
import org.apache.dubbo.metrics.report.MetricsExport;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* As a data aggregator, use internal data containers calculates and classifies
* the registry data collected by {@link MetricsCollector MetricsCollector}, and
* provides an {@link MetricsExport MetricsExport} interface for exporting standard output formats.
*/
public class RegistryStatComposite implements MetricsExport {
public Map<RegistryEvent.Type, Map<String, AtomicLong>> numStats = new ConcurrentHashMap<>();
public Map<RegistryEvent.Type, Map<ServiceKeyMetric, AtomicLong>> skStats = new ConcurrentHashMap<>();
public List<LongContainer<? extends Number>> rtStats = new ArrayList<>();
public static String OP_TYPE_REGISTER = "register";
public static String OP_TYPE_SUBSCRIBE = "subscribe";
public static String OP_TYPE_NOTIFY = "notify";
public RegistryStatComposite() {
for (RegistryEvent.Type type : RegistryEvent.Type.values()) {
numStats.put(type, new ConcurrentHashMap<>());
}
rtStats.addAll(initStats(OP_TYPE_REGISTER));
rtStats.addAll(initStats(OP_TYPE_SUBSCRIBE));
rtStats.addAll(initStats(OP_TYPE_NOTIFY));
}
private List<LongContainer<? extends Number>> initStats(String registryOpType) {
List<LongContainer<? extends Number>> singleRtStats = new ArrayList<>();
singleRtStats.add(new AtomicLongContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.GENERIC_METRIC_RT_LAST)));
singleRtStats.add(new LongAccumulatorContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.GENERIC_METRIC_RT_MIN), new LongAccumulator(Long::min, Long.MAX_VALUE)));
singleRtStats.add(new LongAccumulatorContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.GENERIC_METRIC_RT_MAX), new LongAccumulator(Long::max, Long.MIN_VALUE)));
singleRtStats.add(new AtomicLongContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.GENERIC_METRIC_RT_SUM), (responseTime, longAccumulator) -> longAccumulator.addAndGet(responseTime)));
// AvgContainer is a special counter that stores the number of times but outputs function of sum/times
AtomicLongContainer avgContainer = new AtomicLongContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.GENERIC_METRIC_RT_AVG), (k, v) -> v.incrementAndGet());
avgContainer.setValueSupplier(applicationName -> {
LongContainer<? extends Number> totalContainer = rtStats.stream().filter(longContainer -> longContainer.isKeyWrapper(MetricsKey.GENERIC_METRIC_RT_SUM, registryOpType)).findFirst().get();
AtomicLong totalRtTimes = avgContainer.get(applicationName);
AtomicLong totalRtSum = (AtomicLong) totalContainer.get(applicationName);
return totalRtSum.get() / totalRtTimes.get();
});
singleRtStats.add(avgContainer);
return singleRtStats;
}
public void setApplicationKey(RegistryEvent.Type type, String applicationName, int num) {
if (!numStats.containsKey(type)) {
return;
}
numStats.get(type).computeIfAbsent(applicationName, k -> new AtomicLong(0L)).set(num);
}
public void setServiceKey(RegistryEvent.Type type, String applicationName, String serviceKey, int num) {
if (!skStats.containsKey(type)) {
return;
}
skStats.get(type).computeIfAbsent(new ServiceKeyMetric(applicationName, serviceKey), k -> new AtomicLong(0L)).set(num);
}
public void increment(RegistryEvent.Type type, String applicationName) {
if (!numStats.containsKey(type)) {
return;
}
numStats.get(type).computeIfAbsent(applicationName, k -> new AtomicLong(0L)).incrementAndGet();
}
@SuppressWarnings({"rawtypes", "unchecked"})
public void calcRt(String applicationName, String registryOpType, Long responseTime) {
for (LongContainer container : rtStats.stream().filter(longContainer -> longContainer.specifyType(registryOpType)).collect(Collectors.toList())) {
Number current = (Number) ConcurrentHashMapUtils.computeIfAbsent(container, applicationName, container.getInitFunc());
container.getConsumerFunc().accept(responseTime, current);
}
}
@Override
public List<GaugeMetricSample> exportNumMetrics() {
List<GaugeMetricSample> list = new ArrayList<>();
for (RegistryEvent.Type type : numStats.keySet()) {
Map<String, AtomicLong> stringAtomicLongMap = numStats.get(type);
for (String applicationName : stringAtomicLongMap.keySet()) {
list.add(convertToSample(applicationName, type, MetricsCategory.REGISTRY, stringAtomicLongMap.get(applicationName)));
}
}
return list;
}
@Override
public List<GaugeMetricSample> exportRtMetrics() {
List<GaugeMetricSample> list = new ArrayList<>();
for (LongContainer<? extends Number> rtContainer : rtStats) {
MetricsKeyWrapper metricsKeyWrapper = rtContainer.getMetricsKeyWrapper();
for (Map.Entry<String, ? extends Number> entry : rtContainer.entrySet()) {
list.add(new GaugeMetricSample(metricsKeyWrapper.targetKey(), metricsKeyWrapper.targetDesc(), ApplicationMetric.getTagsByName(entry.getKey()), MetricsCategory.RT, () -> rtContainer.getValueSupplier().apply(entry.getKey())));
}
}
return list;
}
public List<GaugeMetricSample> exportSkMetrics() {
List<GaugeMetricSample> list = new ArrayList<>();
for (RegistryEvent.Type type : skStats.keySet()) {
Map<ServiceKeyMetric, AtomicLong> stringAtomicLongMap = skStats.get(type);
for (ServiceKeyMetric serviceKeyMetric : stringAtomicLongMap.keySet()) {
list.add(new GaugeMetricSample(type.getMetricsKey(), serviceKeyMetric.getTags(), MetricsCategory.REGISTRY, stringAtomicLongMap.get(serviceKeyMetric)::get));
}
}
return list;
}
public GaugeMetricSample convertToSample(String applicationName, RegistryEvent.Type type, MetricsCategory category, AtomicLong targetNumber) {
return new GaugeMetricSample(type.getMetricsKey(), ApplicationMetric.getTagsByName(applicationName), category, targetNumber::get);
}
/**
* Collect Number type data
*
* @param <NUMBER>
*/
public static class LongContainer<NUMBER extends Number> extends ConcurrentHashMap<String, NUMBER> {
/**
* Provide the metric type name
*/
private final MetricsKeyWrapper metricsKeyWrapper;
/**
* The initial value corresponding to the key is generally 0 of different data types
*/
private final Function<String, NUMBER> initFunc;
/**
* Statistical data calculation function, which can be self-increment, self-decrement, or more complex avg function
*/
private final BiConsumer<Long, NUMBER> consumerFunc;
/**
* Data output function required by {@link GaugeMetricSample GaugeMetricSample}
*/
private Function<String, Long> valueSupplier;
public LongContainer(MetricsKeyWrapper metricsKeyWrapper, Supplier<NUMBER> initFunc, BiConsumer<Long, NUMBER> consumerFunc) {
this.metricsKeyWrapper = metricsKeyWrapper;
this.initFunc = s -> initFunc.get();
this.consumerFunc = consumerFunc;
this.valueSupplier = k -> this.get(k).longValue();
}
public boolean specifyType(String type) {
return type.equals(getMetricsKeyWrapper().getType());
}
public MetricsKeyWrapper getMetricsKeyWrapper() {
return metricsKeyWrapper;
}
public boolean isKeyWrapper(MetricsKey metricsKey, String registryOpType) {
return metricsKeyWrapper.isKey(metricsKey,registryOpType);
}
public Function<String, NUMBER> getInitFunc() {
return initFunc;
}
public BiConsumer<Long, NUMBER> getConsumerFunc() {
return consumerFunc;
}
public Function<String, Long> getValueSupplier() {
return valueSupplier;
}
public void setValueSupplier(Function<String, Long> valueSupplier) {
this.valueSupplier = valueSupplier;
}
@Override
public String toString() {
return "LongContainer{" +
"metricsKeyWrapper=" + metricsKeyWrapper +
'}';
}
}
public static class AtomicLongContainer extends LongContainer<AtomicLong> {
public AtomicLongContainer(MetricsKeyWrapper metricsKeyWrapper) {
super(metricsKeyWrapper, AtomicLong::new, (responseTime, longAccumulator) -> longAccumulator.set(responseTime));
}
public AtomicLongContainer(MetricsKeyWrapper metricsKeyWrapper, BiConsumer<Long, AtomicLong> consumerFunc) {
super(metricsKeyWrapper, AtomicLong::new, consumerFunc);
}
}
public static class LongAccumulatorContainer extends LongContainer<LongAccumulator> {
public LongAccumulatorContainer(MetricsKeyWrapper metricsKeyWrapper, LongAccumulator accumulator) {
super(metricsKeyWrapper, () -> accumulator, (responseTime, longAccumulator) -> longAccumulator.accumulate(responseTime));
}
}
}

View File

@ -0,0 +1,83 @@
/*
* 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.registry.collector.stat;
import org.apache.dubbo.metrics.model.Metric;
import java.util.HashMap;
import java.util.Map;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_HOSTNAME;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_INTERFACE_KEY;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_IP;
import static org.apache.dubbo.common.utils.NetUtils.getLocalHost;
import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName;
/**
* Metric class for service.
*/
public class ServiceKeyMetric implements Metric {
private final String applicationName;
private final String serviceKey;
public ServiceKeyMetric(String applicationName, String serviceKey) {
this.applicationName = applicationName;
this.serviceKey = serviceKey;
}
public String getServiceKey() {
return serviceKey;
}
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_INTERFACE_KEY, serviceKey);
return tags;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ServiceKeyMetric that = (ServiceKeyMetric) o;
if (!applicationName.equals(that.applicationName)) return false;
return serviceKey.equals(that.serviceKey);
}
@Override
public int hashCode() {
int result = applicationName.hashCode();
result = 31 * result + serviceKey.hashCode();
return result;
}
@Override
public String toString() {
return "ServiceKeyMetric{" +
"applicationName='" + applicationName + '\'' +
", serviceKey='" + serviceKey + '\'' +
'}';
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.registry.event;
import org.apache.dubbo.metrics.event.MetricsEvent;
import org.apache.dubbo.metrics.listener.MetricsListener;
public class MetricsDirectoryListener implements MetricsListener<RegistryEvent.MetricsDirectoryEvent> {
@Override
public boolean isSupport(MetricsEvent event) {
return event instanceof RegistryEvent.MetricsDirectoryEvent;
}
@Override
public void onEvent(RegistryEvent.MetricsDirectoryEvent event) {
if (!event.isAvailable()) {
return;
}
if (event.getType().isIncrement()) {
event.getCollector().increment(event.getSource().getApplicationName(), event.getType());
} else {
event.getCollector().setNum(event.getType(), event.getSource().getApplicationName(), event.getSize());
}
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.registry.event;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.metrics.event.MetricsEvent;
import org.apache.dubbo.metrics.listener.MetricsLifeListener;
import static org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite.OP_TYPE_NOTIFY;
/**
* The registration center actively pushes Listener, no failure and rt statistics
*/
public class MetricsNotifyListener implements MetricsLifeListener<RegistryEvent.MetricsNotifyEvent> {
protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
@Override
public boolean isSupport(MetricsEvent event) {
return event instanceof RegistryEvent.MetricsNotifyEvent;
}
@Override
public void onEvent(RegistryEvent.MetricsNotifyEvent event) {
event.getCollector().increment(event.getSource().getApplicationName(), RegistryEvent.Type.N_TOTAL);
}
@Override
public void onEventFinish(RegistryEvent.MetricsNotifyEvent event) {
event.getCollector().setNum(RegistryEvent.Type.N_LAST_NUM, event.getSource().getApplicationName(), event.getLastNotifyNum());
event.getCollector().addRT(event.getSource().getApplicationName(), OP_TYPE_NOTIFY, event.getTimePair().calc());
}
@Override
public void onEventError(RegistryEvent.MetricsNotifyEvent event) {
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.registry.event;
import org.apache.dubbo.metrics.event.MetricsEvent;
import org.apache.dubbo.metrics.listener.MetricsLifeListener;
import static org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite.OP_TYPE_REGISTER;
public class MetricsRegisterListener implements MetricsLifeListener<RegistryEvent.MetricsRegisterEvent> {
@Override
public boolean isSupport(MetricsEvent event) {
return event instanceof RegistryEvent.MetricsRegisterEvent;
}
@Override
public void onEvent(RegistryEvent.MetricsRegisterEvent event) {
if (!event.isAvailable()) {
return;
}
event.getCollector().increment(event.getSource().getApplicationName(), RegistryEvent.Type.R_TOTAL);
}
@Override
public void onEventFinish(RegistryEvent.MetricsRegisterEvent event) {
event.getCollector().increment(event.getSource().getApplicationName(), RegistryEvent.Type.R_SUCCEED);
event.getCollector().addRT(event.getSource().getApplicationName(), OP_TYPE_REGISTER, event.getTimePair().calc());
}
@Override
public void onEventError(RegistryEvent.MetricsRegisterEvent event) {
event.getCollector().increment(event.getSource().getApplicationName(), RegistryEvent.Type.R_FAILED);
event.getCollector().addRT(event.getSource().getApplicationName(), OP_TYPE_REGISTER, event.getTimePair().calc());
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.registry.event;
import org.apache.dubbo.metrics.event.MetricsEvent;
import org.apache.dubbo.metrics.listener.MetricsLifeListener;
import static org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite.OP_TYPE_SUBSCRIBE;
public class MetricsSubscribeListener implements MetricsLifeListener<RegistryEvent.MetricsSubscribeEvent> {
@Override
public boolean isSupport(MetricsEvent event) {
return event instanceof RegistryEvent.MetricsSubscribeEvent;
}
@Override
public void onEvent(RegistryEvent.MetricsSubscribeEvent event) {
if (!event.isAvailable()) {
return;
}
event.getCollector().increment(event.getSource().getApplicationName(), RegistryEvent.Type.S_TOTAL);
}
@Override
public void onEventFinish(RegistryEvent.MetricsSubscribeEvent event) {
event.getCollector().increment(event.getSource().getApplicationName(), RegistryEvent.Type.S_SUCCEED);
event.getCollector().addRT(event.getSource().getApplicationName(), OP_TYPE_SUBSCRIBE, event.getTimePair().calc());
}
@Override
public void onEventError(RegistryEvent.MetricsSubscribeEvent event) {
event.getCollector().increment(event.getSource().getApplicationName(), RegistryEvent.Type.S_FAILED);
event.getCollector().addRT(event.getSource().getApplicationName(), OP_TYPE_SUBSCRIBE, event.getTimePair().calc());
}
}

View File

@ -0,0 +1,156 @@
/*
* 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.registry.event;
import org.apache.dubbo.metrics.event.MetricsEvent;
import org.apache.dubbo.metrics.event.TimeCounter;
import org.apache.dubbo.metrics.model.MetricsKey;
import org.apache.dubbo.metrics.model.TimePair;
import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector;
import org.apache.dubbo.rpc.model.ApplicationModel;
import java.util.Map;
/**
* Registry related events
*/
public class RegistryEvent extends MetricsEvent implements TimeCounter {
private final TimePair timePair;
private final RegistryMetricsCollector collector;
private final boolean available;
public RegistryEvent(ApplicationModel applicationModel, TimePair timePair) {
super(applicationModel);
this.timePair = timePair;
this.collector = applicationModel.getBeanFactory().getBean(RegistryMetricsCollector.class);
this.available = this.collector != null && collector.isCollectEnabled();
}
public ApplicationModel getSource() {
return (ApplicationModel) source;
}
public RegistryMetricsCollector getCollector() {
return collector;
}
public boolean isAvailable() {
return available;
}
@Override
public TimePair getTimePair() {
return timePair;
}
public enum Type {
R_TOTAL(MetricsKey.REGISTER_METRIC_REQUESTS),
R_SUCCEED(MetricsKey.REGISTER_METRIC_REQUESTS_SUCCEED),
R_FAILED(MetricsKey.REGISTER_METRIC_REQUESTS_FAILED),
S_TOTAL(MetricsKey.SUBSCRIBE_METRIC_NUM),
S_SUCCEED(MetricsKey.SUBSCRIBE_METRIC_NUM_SUCCEED),
S_FAILED(MetricsKey.SUBSCRIBE_METRIC_NUM_FAILED),
D_VALID(MetricsKey.DIRECTORY_METRIC_NUM_VALID),
D_UN_VALID(MetricsKey.DIRECTORY_METRIC_NUM_UN_VALID),
D_DISABLE(MetricsKey.DIRECTORY_METRIC_NUM_DISABLE),
D_CURRENT(MetricsKey.DIRECTORY_METRIC_NUM_CURRENT, false),
D_RECOVER_DISABLE(MetricsKey.DIRECTORY_METRIC_NUM_RECOVER_DISABLE),
N_TOTAL(MetricsKey.NOTIFY_METRIC_REQUESTS),
N_LAST_NUM(MetricsKey.NOTIFY_METRIC_NUM_LAST),
;
private final MetricsKey metricsKey;
private final boolean isIncrement;
Type(MetricsKey metricsKey) {
this(metricsKey, true);
}
Type(MetricsKey metricsKey, boolean isIncrement) {
this.metricsKey = metricsKey;
this.isIncrement = isIncrement;
}
public MetricsKey getMetricsKey() {
return metricsKey;
}
public boolean isIncrement() {
return isIncrement;
}
}
public static class MetricsRegisterEvent extends RegistryEvent {
public MetricsRegisterEvent(ApplicationModel applicationModel, TimePair timePair) {
super(applicationModel, timePair);
}
}
public static class MetricsSubscribeEvent extends RegistryEvent {
public MetricsSubscribeEvent(ApplicationModel applicationModel, TimePair timePair) {
super(applicationModel, timePair);
}
}
public static class MetricsNotifyEvent extends RegistryEvent {
private final Map<String, Integer> lastNumMap;
public MetricsNotifyEvent(ApplicationModel applicationModel, TimePair timePair, Map<String, Integer> lastNumMap) {
super(applicationModel, timePair);
this.lastNumMap = lastNumMap;
}
public Map<String, Integer> getLastNotifyNum() {
return lastNumMap;
}
}
public static class MetricsDirectoryEvent extends RegistryEvent {
private final RegistryEvent.Type type;
private final int size;
public MetricsDirectoryEvent(ApplicationModel applicationModel, RegistryEvent.Type type) {
this(applicationModel, type, 1);
}
public MetricsDirectoryEvent(ApplicationModel applicationModel, RegistryEvent.Type type, int size) {
super(applicationModel, TimePair.empty());
this.type = type;
this.size = size;
}
public RegistryEvent.Type getType() {
return type;
}
public int getSize() {
return size;
}
}
}

View File

@ -0,0 +1,33 @@
/*
* 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.registry.event;
import org.apache.dubbo.metrics.event.SimpleMetricsEventMulticaster;
public final class RegistryMetricsEventMulticaster extends SimpleMetricsEventMulticaster {
public RegistryMetricsEventMulticaster() {
super.addListener(new MetricsRegisterListener());
super.addListener(new MetricsSubscribeListener());
super.addListener(new MetricsNotifyListener());
super.addListener(new MetricsDirectoryListener());
setAvailable();
}
}

View File

@ -0,0 +1 @@
registry-collector=org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector

View File

@ -0,0 +1,115 @@
/*
* 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.registry.metrics.collector;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
import org.apache.dubbo.metrics.model.MetricsKey;
import org.apache.dubbo.metrics.model.MetricsKeyWrapper;
import org.apache.dubbo.metrics.model.TimePair;
import org.apache.dubbo.metrics.model.sample.GaugeMetricSample;
import org.apache.dubbo.metrics.model.sample.MetricSample;
import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector;
import org.apache.dubbo.metrics.registry.event.RegistryEvent;
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.stream.Collectors;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME;
import static org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite.OP_TYPE_REGISTER;
class RegistryMetricsCollectorTest {
private ApplicationModel applicationModel;
@BeforeEach
public void setup() {
FrameworkModel 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 testPushMetrics() throws InterruptedException {
TimePair timePair = TimePair.start();
GlobalMetricsEventMulticaster eventMulticaster = applicationModel.getBeanFactory().getOrRegisterBean(GlobalMetricsEventMulticaster.class);
RegistryMetricsCollector collector = applicationModel.getBeanFactory().getOrRegisterBean(RegistryMetricsCollector.class);
collector.setCollectEnabled(true);
eventMulticaster.publishEvent(new RegistryEvent.MetricsRegisterEvent(applicationModel, timePair));
List<MetricSample> metricSamples = collector.collect();
// push success +1
Assertions.assertEquals(metricSamples.size(), 1);
Assertions.assertTrue(metricSamples.get(0) instanceof GaugeMetricSample);
eventMulticaster.publishFinishEvent(new RegistryEvent.MetricsRegisterEvent(applicationModel, timePair));
// push finish rt +1
metricSamples = collector.collect();
//num(total+success) + rt(5) = 7
Assertions.assertEquals(metricSamples.size(), 7);
long c1 = timePair.calc();
TimePair lastTimePair = TimePair.start();
eventMulticaster.publishEvent(new RegistryEvent.MetricsRegisterEvent(applicationModel, lastTimePair));
Thread.sleep(50);
// push error rt +1
eventMulticaster.publishErrorEvent(new RegistryEvent.MetricsRegisterEvent(applicationModel, lastTimePair));
long c2 = lastTimePair.calc();
metricSamples = collector.collect();
// num(total+success+error) + rt(5)
Assertions.assertEquals(metricSamples.size(), 8);
// calc rt
for (MetricSample sample : metricSamples) {
Map<String, String> tags = sample.getTags();
Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationModel.getApplicationName());
}
Map<String, Long> sampleMap = metricSamples.stream().collect(Collectors.toMap(MetricSample::getName, k -> {
Number number = ((GaugeMetricSample) k).getSupplier().get();
return number.longValue();
}));
Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_REGISTER, MetricsKey.GENERIC_METRIC_RT_LAST).targetKey()), lastTimePair.calc());
Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_REGISTER, MetricsKey.GENERIC_METRIC_RT_MIN).targetKey()), Math.min(c1, c2));
Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_REGISTER, MetricsKey.GENERIC_METRIC_RT_MAX).targetKey()), Math.max(c1, c2));
Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_REGISTER, MetricsKey.GENERIC_METRIC_RT_AVG).targetKey()), (c1 + c2) / 2);
Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_REGISTER, MetricsKey.GENERIC_METRIC_RT_SUM).targetKey()), c1 + c2);
}
}

View File

@ -0,0 +1,97 @@
/*
* 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.registry.metrics.collector;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.metrics.model.MetricsKey;
import org.apache.dubbo.metrics.model.MetricsKeyWrapper;
import org.apache.dubbo.metrics.model.sample.GaugeMetricSample;
import org.apache.dubbo.metrics.model.sample.MetricSample;
import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector;
import org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite;
import org.apache.dubbo.metrics.registry.event.RegistryEvent;
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.stream.Collectors;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME;
class RegistryMetricsSampleTest {
private ApplicationModel applicationModel;
@BeforeEach
public void setup() {
FrameworkModel 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 testRegisterMetrics() {
}
@Test
void testRTMetrics() {
RegistryMetricsCollector collector = new RegistryMetricsCollector(applicationModel);
collector.setCollectEnabled(true);
String applicationName = applicationModel.getApplicationName();
collector.addRT(applicationName, RegistryStatComposite.OP_TYPE_REGISTER, 10L);
collector.addRT(applicationName, RegistryStatComposite.OP_TYPE_REGISTER, 0L);
List<MetricSample> samples = collector.collect();
for (MetricSample sample : samples) {
Map<String, String> tags = sample.getTags();
Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationName);
}
Map<String, Long> sampleMap = samples.stream().collect(Collectors.toMap(MetricSample::getName, k -> {
Number number = ((GaugeMetricSample) k).getSupplier().get();
return number.longValue();
}));
Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(RegistryStatComposite.OP_TYPE_REGISTER, MetricsKey.GENERIC_METRIC_RT_LAST).targetKey()), 0L);
Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(RegistryStatComposite.OP_TYPE_REGISTER, MetricsKey.GENERIC_METRIC_RT_MIN).targetKey()), 0L);
Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(RegistryStatComposite.OP_TYPE_REGISTER, MetricsKey.GENERIC_METRIC_RT_MAX).targetKey()), 10L);
Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(RegistryStatComposite.OP_TYPE_REGISTER, MetricsKey.GENERIC_METRIC_RT_AVG).targetKey()), 5L);
Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(RegistryStatComposite.OP_TYPE_REGISTER, MetricsKey.GENERIC_METRIC_RT_SUM).targetKey()), 10L);
}
@Test
void testListener() {
RegistryMetricsCollector collector = new RegistryMetricsCollector(applicationModel);
collector.setCollectEnabled(true);
String applicationName = applicationModel.getApplicationName();
collector.increment(applicationName,RegistryEvent.Type.R_TOTAL);
}
}

View File

@ -0,0 +1,79 @@
/*
* 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.registry.metrics.model;
import org.apache.dubbo.metrics.model.MethodMetric;
import org.apache.dubbo.rpc.RpcInvocation;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.Map;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
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_HOSTNAME;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_INTERFACE_KEY;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_IP;
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.apache.dubbo.common.utils.NetUtils.getLocalHost;
import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName;
class MethodMetricTest {
private static final String applicationName = null;
private static String interfaceName;
private static String methodName;
private static String group;
private static String version;
private static RpcInvocation invocation;
@BeforeAll
public static void setup() {
interfaceName = "org.apache.dubbo.MockInterface";
methodName = "mockMethod";
group = "mockGroup";
version = "1.0.0";
invocation = new RpcInvocation(methodName, interfaceName, "serviceKey", null, null);
invocation.setTargetServiceUniqueName(group + "/" + interfaceName + ":" + version);
invocation.setAttachment(GROUP_KEY, group);
invocation.setAttachment(VERSION_KEY, version);
}
@Test
void test() {
MethodMetric metric = new MethodMetric(applicationName, invocation);
Assertions.assertEquals(metric.getInterfaceName(), interfaceName);
Assertions.assertEquals(metric.getMethodName(), methodName);
Assertions.assertEquals(metric.getGroup(), group);
Assertions.assertEquals(metric.getVersion(), version);
Map<String, String> tags = metric.getTags();
Assertions.assertEquals(tags.get(TAG_IP), getLocalHost());
Assertions.assertEquals(tags.get(TAG_HOSTNAME), getLocalHostName());
Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationName);
Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), interfaceName);
Assertions.assertEquals(tags.get(TAG_METHOD_KEY), methodName);
Assertions.assertEquals(tags.get(TAG_GROUP_KEY), group);
Assertions.assertEquals(tags.get(TAG_VERSION_KEY), version);
}
}

View File

@ -0,0 +1,63 @@
/*
* 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.registry.metrics.model.sample;
import org.apache.dubbo.metrics.model.MetricsCategory;
import org.apache.dubbo.metrics.model.sample.GaugeMetricSample;
import org.apache.dubbo.metrics.model.sample.MetricSample;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
class GaugeMetricSampleTest {
private static String name;
private static String description;
private static Map<String, String> tags;
private static MetricsCategory category;
private static String baseUnit;
private static Supplier<Number> supplier;
@BeforeAll
public static void setup() {
name = "test";
description = "test";
tags = new HashMap<>();
category = MetricsCategory.REQUESTS;
baseUnit = "byte";
supplier = () -> 1;
}
@Test
void test() {
GaugeMetricSample sample = new GaugeMetricSample(name, description, tags, category, baseUnit, supplier);
Assertions.assertEquals(sample.getName(), name);
Assertions.assertEquals(sample.getDescription(), description);
Assertions.assertEquals(sample.getTags(), tags);
Assertions.assertEquals(sample.getType(), MetricSample.Type.GAUGE);
Assertions.assertEquals(sample.getCategory(), category);
Assertions.assertEquals(sample.getBaseUnit(), baseUnit);
Assertions.assertEquals(sample.getSupplier().get(), 1);
sample.setSupplier(() -> 2);
Assertions.assertEquals(sample.getSupplier().get(), 2);
}
}

View File

@ -20,6 +20,7 @@
<modules>
<module>dubbo-metrics-api</module>
<module>dubbo-metrics-default</module>
<module>dubbo-metrics-registry</module>
<module>dubbo-metrics-metadata</module>
<module>dubbo-metrics-prometheus</module>
</modules>

View File

@ -98,5 +98,12 @@
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-registry</artifactId>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -16,6 +16,32 @@
*/
package org.apache.dubbo.registry.client.event.listener;
import org.apache.dubbo.common.ProtocolServiceKey;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.URLBuilder;
import org.apache.dubbo.common.beans.factory.ScopeBeanFactory;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.ConcurrentHashSet;
import org.apache.dubbo.metadata.MetadataInfo;
import org.apache.dubbo.metadata.MetadataInfo.ServiceInfo;
import org.apache.dubbo.metrics.event.SimpleMetricsEventMulticaster;
import org.apache.dubbo.metrics.model.TimePair;
import org.apache.dubbo.metrics.registry.event.RegistryEvent;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.client.DefaultServiceInstance;
import org.apache.dubbo.registry.client.ServiceDiscovery;
import org.apache.dubbo.registry.client.ServiceInstance;
import org.apache.dubbo.registry.client.event.RetryServiceInstancesChangedEvent;
import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils;
import org.apache.dubbo.registry.client.metadata.ServiceInstanceNotificationCustomizer;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.ScopeModelUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@ -32,28 +58,6 @@ import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.dubbo.common.ProtocolServiceKey;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.URLBuilder;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.ConcurrentHashSet;
import org.apache.dubbo.metadata.MetadataInfo;
import org.apache.dubbo.metadata.MetadataInfo.ServiceInfo;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.client.DefaultServiceInstance;
import org.apache.dubbo.registry.client.ServiceDiscovery;
import org.apache.dubbo.registry.client.ServiceInstance;
import org.apache.dubbo.registry.client.event.RetryServiceInstancesChangedEvent;
import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils;
import org.apache.dubbo.registry.client.metadata.ServiceInstanceNotificationCustomizer;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.ScopeModelUtil;
import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_REFRESH_ADDRESS;
@ -87,6 +91,7 @@ public class ServiceInstancesChangedListener {
private final ScheduledExecutorService scheduler;
private volatile boolean hasEmptyMetadata;
private final Set<ServiceInstanceNotificationCustomizer> serviceInstanceNotificationCustomizers;
private final ApplicationModel applicationModel;
public ServiceInstancesChangedListener(Set<String> serviceNames, ServiceDiscovery serviceDiscovery) {
@ -99,6 +104,7 @@ public class ServiceInstancesChangedListener {
ApplicationModel applicationModel = ScopeModelUtil.getApplicationModel(serviceDiscovery == null || serviceDiscovery.getUrl() == null ? null : serviceDiscovery.getUrl().getScopeModel());
this.scheduler = applicationModel.getBeanFactory().getBean(FrameworkExecutorRepository.class).getMetadataRetryExecutor();
this.serviceInstanceNotificationCustomizers = applicationModel.getExtensionLoader(ServiceInstanceNotificationCustomizer.class).getSupportedExtensionInstances();
this.applicationModel = applicationModel;
}
/**
@ -398,6 +404,13 @@ public class ServiceInstancesChangedListener {
* race condition is protected by onEvent/doOnEvent
*/
protected void notifyAddressChanged() {
ScopeBeanFactory beanFactory = applicationModel.getFrameworkModel().getBeanFactory();
SimpleMetricsEventMulticaster eventMulticaster = beanFactory.getOrRegisterBean(SimpleMetricsEventMulticaster.class);
TimePair timePair = TimePair.start();
eventMulticaster.publishEvent(new RegistryEvent.MetricsNotifyEvent(applicationModel, timePair, null));
Map<String, Integer> lastNumMap = new HashMap<>();
// 1 different services
listeners.forEach((serviceKey, listenerSet) -> {
// 2 multiple subscription listener of the same service
@ -407,8 +420,11 @@ public class ServiceInstancesChangedListener {
List<URL> urls = toUrlsWithEmpty(getAddresses(listenerWithKey.getProtocolServiceKey(), notifyListener.getConsumerUrl()));
logger.info("Notify service " + listenerWithKey.getProtocolServiceKey() + " with urls " + urls.size());
notifyListener.notify(urls);
lastNumMap.put(serviceKey, urls.size());
}
});
eventMulticaster.publishFinishEvent(new RegistryEvent.MetricsNotifyEvent(applicationModel, timePair, lastNumMap));
}
protected List<URL> toUrlsWithEmpty(List<URL> urls) {

View File

@ -16,19 +16,6 @@
*/
package org.apache.dubbo.registry.integration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.URLBuilder;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
@ -43,6 +30,9 @@ import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.common.utils.UrlUtils;
import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
import org.apache.dubbo.metrics.model.TimePair;
import org.apache.dubbo.metrics.registry.event.RegistryEvent;
import org.apache.dubbo.registry.AddressListener;
import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.rpc.Invoker;
@ -53,8 +43,22 @@ import org.apache.dubbo.rpc.cluster.Router;
import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
import org.apache.dubbo.rpc.cluster.router.state.BitList;
import org.apache.dubbo.rpc.cluster.support.ClusterUtils;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.ModuleModel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.DISABLED_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
@ -110,11 +114,18 @@ public class RegistryDirectory<T> extends DynamicDirectory<T> {
super(serviceType, url);
moduleModel = getModuleModel(url.getScopeModel());
consumerConfigurationListener = getConsumerConfigurationListener(moduleModel);
}
@Override
public void subscribe(URL url) {
ApplicationModel applicationModel = url.getApplicationModel();
GlobalMetricsEventMulticaster eventMulticaster = applicationModel.getBeanFactory().getBean(GlobalMetricsEventMulticaster.class);
TimePair timePair = TimePair.start();
eventMulticaster.publishEvent(new RegistryEvent.MetricsSubscribeEvent(applicationModel, timePair));
super.subscribe(url);
eventMulticaster.publishFinishEvent(new RegistryEvent.MetricsSubscribeEvent(applicationModel, timePair));
if (moduleModel.getModelEnvironment().getConfiguration().convert(Boolean.class, org.apache.dubbo.registry.Constants.ENABLE_CONFIGURATION_LISTEN, true)) {
consumerConfigurationListener.addNotifyListener(this);
referenceConfigurationListener = new ReferenceConfigurationListener(moduleModel, this, url);
@ -233,7 +244,7 @@ public class RegistryDirectory<T> extends DynamicDirectory<T> {
// use local reference to avoid NPE as this.cachedInvokerUrls will be set null by destroyAllInvokers().
Set<URL> localCachedInvokerUrls = this.cachedInvokerUrls;
if (invokerUrls.isEmpty()) {
if(CollectionUtils.isNotEmpty(localCachedInvokerUrls)){
if (CollectionUtils.isNotEmpty(localCachedInvokerUrls)) {
// 1-4 Empty address.
logger.warn(REGISTRY_EMPTY_ADDRESS, "configuration ", "",
"Service" + serviceKey + " received empty address list with no EMPTY protocol set, trigger empty protection.");

View File

@ -148,6 +148,11 @@
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-default</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-registry</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-metadata</artifactId>