Merge branch 'apache-3.2' into apache-3.3
# Conflicts: # dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter # dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java # dubbo-config/dubbo-config-api/pom.xml # dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java # dubbo-dependencies-bom/pom.xml # dubbo-dependencies/dubbo-dependencies-zookeeper-curator5/pom.xml # dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml # dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter # dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Constants.java # dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/NettyHttpRestServer.java # dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/deploy/ServiceDeployer.java # dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/handler/NettyHttpHandler.java # dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/JsonCodec.java # pom.xml
This commit is contained in:
commit
093fb7c208
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.rpc.cluster.filter.support;
|
||||
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.metrics.filter.MetricsFilter;
|
||||
import org.apache.dubbo.rpc.BaseFilter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.filter.ClusterFilter;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
|
||||
|
||||
@Activate(group = {CONSUMER}, order = Integer.MIN_VALUE + 100)
|
||||
public class MetricsConsumerFilter extends MetricsFilter implements ClusterFilter, BaseFilter.Listener {
|
||||
public MetricsConsumerFilter() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
return super.invoke(invoker, invocation, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
|
||||
super.onResponse(appResponse, invoker, invocation, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
|
||||
super.onError(t, invoker, invocation, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1,2 @@
|
|||
router-snapshot=org.apache.dubbo.rpc.cluster.router.RouterSnapshotFilter
|
||||
metricsConsumerFilter=org.apache.dubbo.rpc.cluster.filter.support.MetricsConsumerFilter
|
||||
|
|
|
|||
|
|
@ -634,7 +634,10 @@ public interface CommonConstants {
|
|||
String DUBBO_PACKABLE_METHOD_FACTORY = "dubbo.application.parameters." + PACKABLE_METHOD_FACTORY_KEY;
|
||||
|
||||
String DUBBO_TAG_HEADER = "dubbo-tag";
|
||||
|
||||
|
||||
String REST_SERVICE_DEPLOYER_URL_ATTRIBUTE_KEY = "restServiceDeployerAttributeKey";
|
||||
|
||||
String SERVICE_DEPLOYER_ATTRIBUTE_KEY = "serviceDeployer";
|
||||
String RESTEASY_NETTY_HTTP_REQUEST_ATTRIBUTE_KEY = "resteasyNettyHttpRequest";
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.apache.dubbo.common.resource;
|
||||
|
||||
import org.apache.dubbo.common.config.ConfigurationUtils;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.NamedThreadFactory;
|
||||
|
|
@ -25,7 +26,9 @@ import java.util.List;
|
|||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_SERVER_SHUTDOWN_TIMEOUT;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION;
|
||||
|
||||
/**
|
||||
|
|
@ -105,6 +108,16 @@ public class GlobalResourcesRepository {
|
|||
}
|
||||
if (executorService != null) {
|
||||
executorService.shutdownNow();
|
||||
try {
|
||||
if (!executorService.awaitTermination(
|
||||
ConfigurationUtils.reCalShutdownTime(DEFAULT_SERVER_SHUTDOWN_TIMEOUT),
|
||||
TimeUnit.MILLISECONDS)) {
|
||||
logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "",
|
||||
"Wait global executor service terminated timeout.");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", "destroy resources failed: " + e.getMessage(), e);
|
||||
}
|
||||
executorService = null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.apache.dubbo.common.threadpool.manager;
|
||||
|
||||
import org.apache.dubbo.common.config.ConfigurationUtils;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.resource.Disposable;
|
||||
|
|
@ -31,6 +32,7 @@ import java.util.concurrent.SynchronousQueue;
|
|||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_SERVER_SHUTDOWN_TIMEOUT;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXECUTORS_SHUTDOWN;
|
||||
|
||||
public class FrameworkExecutorRepository implements Disposable {
|
||||
|
|
@ -225,6 +227,12 @@ public class FrameworkExecutorRepository implements Disposable {
|
|||
private void shutdownExecutorService(ExecutorService executorService, String name) {
|
||||
try {
|
||||
executorService.shutdownNow();
|
||||
if (!executorService.awaitTermination(
|
||||
ConfigurationUtils.reCalShutdownTime(DEFAULT_SERVER_SHUTDOWN_TIMEOUT),
|
||||
TimeUnit.MILLISECONDS)) {
|
||||
logger.warn(COMMON_UNEXPECTED_EXECUTORS_SHUTDOWN, "", "",
|
||||
"Wait global executor service terminated timeout.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
String msg = "shutdown executor service [" + name + "] failed: ";
|
||||
logger.warn(COMMON_UNEXPECTED_EXECUTORS_SHUTDOWN, "", "", msg + e.getMessage(), e);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,14 @@ public class AggregationConfig implements Serializable {
|
|||
*/
|
||||
private Boolean enabled;
|
||||
|
||||
private Boolean enableQps;
|
||||
|
||||
private Boolean enableRtPxx;
|
||||
|
||||
private Boolean enableRt;
|
||||
|
||||
private Boolean enableRequest;
|
||||
|
||||
/**
|
||||
* Bucket num for time window quantile
|
||||
*/
|
||||
|
|
@ -35,6 +43,11 @@ public class AggregationConfig implements Serializable {
|
|||
*/
|
||||
private Integer timeWindowSeconds;
|
||||
|
||||
/**
|
||||
* Time window mill seconds for qps
|
||||
*/
|
||||
private Integer qpsTimeWindowMillSeconds;
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
|
@ -58,4 +71,44 @@ public class AggregationConfig implements Serializable {
|
|||
public void setTimeWindowSeconds(Integer timeWindowSeconds) {
|
||||
this.timeWindowSeconds = timeWindowSeconds;
|
||||
}
|
||||
|
||||
public Boolean getEnableQps() {
|
||||
return enableQps;
|
||||
}
|
||||
|
||||
public void setEnableQps(Boolean enableQps) {
|
||||
this.enableQps = enableQps;
|
||||
}
|
||||
|
||||
public Boolean getEnableRtPxx() {
|
||||
return enableRtPxx;
|
||||
}
|
||||
|
||||
public void setEnableRtPxx(Boolean enableRtPxx) {
|
||||
this.enableRtPxx = enableRtPxx;
|
||||
}
|
||||
|
||||
public Boolean getEnableRt() {
|
||||
return enableRt;
|
||||
}
|
||||
|
||||
public void setEnableRt(Boolean enableRt) {
|
||||
this.enableRt = enableRt;
|
||||
}
|
||||
|
||||
public Boolean getEnableRequest() {
|
||||
return enableRequest;
|
||||
}
|
||||
|
||||
public void setEnableRequest(Boolean enableRequest) {
|
||||
this.enableRequest = enableRequest;
|
||||
}
|
||||
|
||||
public Integer getQpsTimeWindowMillSeconds() {
|
||||
return qpsTimeWindowMillSeconds;
|
||||
}
|
||||
|
||||
public void setQpsTimeWindowMillSeconds(Integer qpsTimeWindowMillSeconds) {
|
||||
this.qpsTimeWindowMillSeconds = qpsTimeWindowMillSeconds;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -252,5 +252,13 @@
|
|||
<version>1.18.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jackson-provider</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -57,12 +57,12 @@ import org.apache.dubbo.rpc.support.ProtocolUtils;
|
|||
import java.beans.Transient;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.TreeSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_KEY;
|
||||
|
|
@ -795,6 +795,17 @@ public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
|
|||
configPostProcessors.forEach(component -> component.postProcessReferConfig(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if ReferenceConfig has been initialized
|
||||
* Note: Cannot use `isInitilized` as it may be treated as a Java Bean property
|
||||
*
|
||||
* @return initialized
|
||||
*/
|
||||
@Transient
|
||||
public boolean configInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* just for test
|
||||
*
|
||||
|
|
|
|||
|
|
@ -514,8 +514,7 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
|
|||
List<URL> registryURLs = !Boolean.FALSE.equals(isRegister()) ?
|
||||
ConfigValidationUtils.loadRegistries(this, true) : Collections.emptyList();
|
||||
|
||||
MetricsEventBus.post(RegistryEvent.toRsEvent(module.getApplicationModel(), getUniqueServiceName(), protocols.size() * registryURLs.size()),
|
||||
() -> {
|
||||
MetricsEventBus.post(RegistryEvent.toRsEvent(getApplicationModel(), getUniqueServiceName(), protocols.size() * registryURLs.size()), () -> {
|
||||
for (ProtocolConfig protocolConfig : protocols) {
|
||||
String pathKey = URL.buildKey(getContextPath(protocolConfig)
|
||||
.map(p -> p + "/" + path)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import org.apache.dubbo.config.spring.reference.ReferenceBeanSupport;
|
|||
import org.apache.dubbo.config.spring.schema.DubboBeanDefinitionParser;
|
||||
import org.apache.dubbo.config.spring.util.LazyTargetInvocationHandler;
|
||||
import org.apache.dubbo.config.spring.util.LazyTargetSource;
|
||||
import org.apache.dubbo.config.spring.util.LockUtils;
|
||||
import org.apache.dubbo.config.support.Parameter;
|
||||
import org.apache.dubbo.rpc.proxy.AbstractProxyFactory;
|
||||
|
||||
|
|
@ -44,7 +45,6 @@ import org.springframework.beans.factory.InitializingBean;
|
|||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
||||
|
|
@ -399,7 +399,10 @@ public class ReferenceBean<T> implements FactoryBean<T>,
|
|||
//Subclasses should synchronize on the given Object if they perform any sort of extended singleton creation phase.
|
||||
// In particular, subclasses should not have their own mutexes involved in singleton creation, to avoid the potential for deadlocks in lazy-init situations.
|
||||
//The redundant type cast is to be compatible with earlier than spring-4.2
|
||||
synchronized (((DefaultSingletonBeanRegistry) getBeanFactory()).getSingletonMutex()) {
|
||||
if (referenceConfig.configInitialized()) {
|
||||
return referenceConfig.get();
|
||||
}
|
||||
synchronized (LockUtils.getSingletonMutex(applicationContext)) {
|
||||
return referenceConfig.get();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import org.apache.dubbo.common.utils.Assert;
|
|||
import org.apache.dubbo.config.spring.context.event.DubboApplicationStateEvent;
|
||||
import org.apache.dubbo.config.spring.context.event.DubboModuleStateEvent;
|
||||
import org.apache.dubbo.config.spring.util.DubboBeanUtils;
|
||||
import org.apache.dubbo.config.spring.util.LockUtils;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.rpc.model.ModelConstants;
|
||||
import org.apache.dubbo.rpc.model.ModuleModel;
|
||||
|
|
@ -146,8 +147,12 @@ public class DubboDeployApplicationListener implements ApplicationListener<Appli
|
|||
private void onContextRefreshedEvent(ContextRefreshedEvent event) {
|
||||
ModuleDeployer deployer = moduleModel.getDeployer();
|
||||
Assert.notNull(deployer, "Module deployer is null");
|
||||
Object singletonMutex = LockUtils.getSingletonMutex(applicationContext);
|
||||
// start module
|
||||
Future future = deployer.start();
|
||||
Future future = null;
|
||||
synchronized (singletonMutex) {
|
||||
future = deployer.start();
|
||||
}
|
||||
|
||||
// if the module does not start in background, await finish
|
||||
if (!deployer.isBackground()) {
|
||||
|
|
@ -177,7 +182,7 @@ public class DubboDeployApplicationListener implements ApplicationListener<Appli
|
|||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return LOWEST_PRECEDENCE;
|
||||
return HIGHEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,6 +189,7 @@ public class ReferenceBeanManager implements ApplicationContextAware {
|
|||
|
||||
// register ReferenceConfig
|
||||
moduleModel.getConfigManager().addReference(referenceConfig);
|
||||
moduleModel.getDeployer().setPending();
|
||||
}
|
||||
|
||||
// associate referenceConfig to referenceBean
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.config.spring.util;
|
||||
|
||||
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class LockUtils {
|
||||
|
||||
private static final String DUBBO_SINGLETON_MUTEX_KEY = "DUBBO_SINGLETON_MUTEX";
|
||||
|
||||
/**
|
||||
* Get the mutex to lock the singleton in the specified {@link ApplicationContext}
|
||||
*/
|
||||
public static synchronized Object getSingletonMutex(ApplicationContext applicationContext) {
|
||||
DefaultSingletonBeanRegistry autowireCapableBeanFactory = (DefaultSingletonBeanRegistry) applicationContext.getAutowireCapableBeanFactory();
|
||||
try {
|
||||
return autowireCapableBeanFactory.getSingletonMutex();
|
||||
} catch (Throwable t1) {
|
||||
try {
|
||||
// try protected
|
||||
Method method = DefaultSingletonBeanRegistry.class.getDeclaredMethod("getSingletonMutex");
|
||||
method.setAccessible(true);
|
||||
return method.invoke(autowireCapableBeanFactory);
|
||||
} catch (Throwable t2) {
|
||||
// Before Spring 4.2, there is no getSingletonMutex method
|
||||
if (!autowireCapableBeanFactory.containsSingleton(DUBBO_SINGLETON_MUTEX_KEY)) {
|
||||
autowireCapableBeanFactory.registerSingleton(DUBBO_SINGLETON_MUTEX_KEY, new Object());
|
||||
}
|
||||
return autowireCapableBeanFactory.getSingleton(DUBBO_SINGLETON_MUTEX_KEY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -96,13 +96,13 @@
|
|||
<javassist_version>3.29.2-GA</javassist_version>
|
||||
<bytebuddy.version>1.14.5</bytebuddy.version>
|
||||
<netty_version>3.2.10.Final</netty_version>
|
||||
<netty4_version>4.1.92.Final</netty4_version>
|
||||
<netty4_version>4.1.94.Final</netty4_version>
|
||||
<mina_version>2.2.1</mina_version>
|
||||
<grizzly_version>2.4.4</grizzly_version>
|
||||
<httpclient_version>4.5.14</httpclient_version>
|
||||
<httpcore_version>4.4.16</httpcore_version>
|
||||
<fastjson_version>1.2.83</fastjson_version>
|
||||
<fastjson2_version>2.0.34</fastjson2_version>
|
||||
<fastjson2_version>2.0.35</fastjson2_version>
|
||||
<zookeeper_version>3.4.14</zookeeper_version>
|
||||
<curator_version>4.3.0</curator_version>
|
||||
<curator_test_version>2.12.0</curator_test_version>
|
||||
|
|
@ -114,7 +114,7 @@
|
|||
<cxf_version>3.5.5</cxf_version>
|
||||
<thrift_version>0.18.1</thrift_version>
|
||||
<hessian_version>4.0.66</hessian_version>
|
||||
<protobuf-java_version>3.23.3</protobuf-java_version>
|
||||
<protobuf-java_version>3.23.4</protobuf-java_version>
|
||||
<javax_annotation-api_version>1.3.2</javax_annotation-api_version>
|
||||
<servlet_version>3.1.0</servlet_version>
|
||||
<jetty_version>9.4.51.v20230217</jetty_version>
|
||||
|
|
@ -133,14 +133,14 @@
|
|||
<commons_lang3_version>3.12.0</commons_lang3_version>
|
||||
<protostuff_version>1.8.0</protostuff_version>
|
||||
<envoy_api_version>0.1.35</envoy_api_version>
|
||||
<micrometer.version>1.11.1</micrometer.version>
|
||||
<micrometer.version>1.11.2</micrometer.version>
|
||||
<opentelemetry.version>1.26.0</opentelemetry.version>
|
||||
<zipkin-reporter.version>2.16.4</zipkin-reporter.version>
|
||||
<micrometer-tracing.version>1.1.2</micrometer-tracing.version>
|
||||
<t_digest.version>3.3</t_digest.version>
|
||||
<prometheus_client.version>0.16.0</prometheus_client.version>
|
||||
<reactive.version>1.0.4</reactive.version>
|
||||
<reactor.version>3.5.7</reactor.version>
|
||||
<reactor.version>3.5.8</reactor.version>
|
||||
<rxjava.version>2.2.21</rxjava.version>
|
||||
<okhttp_version>3.14.9</okhttp_version>
|
||||
|
||||
|
|
@ -148,11 +148,11 @@
|
|||
<resteasy_version>3.15.6.Final</resteasy_version>
|
||||
<codehaus-jackson_version>1.9.13</codehaus-jackson_version>
|
||||
<tomcat_embed_version>8.5.87</tomcat_embed_version>
|
||||
<jetcd_version>0.7.5</jetcd_version>
|
||||
<jetcd_version>0.7.6</jetcd_version>
|
||||
<nacos_version>2.2.4</nacos_version>
|
||||
<sentinel.version>1.8.6</sentinel.version>
|
||||
<seata.version>1.6.1</seata.version>
|
||||
<grpc.version>1.56.0</grpc.version>
|
||||
<grpc.version>1.56.1</grpc.version>
|
||||
<grpc_contrib_verdion>0.8.1</grpc_contrib_verdion>
|
||||
<jprotoc_version>1.2.2</jprotoc_version>
|
||||
<!-- Log libs -->
|
||||
|
|
@ -178,11 +178,11 @@
|
|||
<jaxb_version>2.2.7</jaxb_version>
|
||||
<activation_version>1.2.0</activation_version>
|
||||
<test_container_version>1.18.3</test_container_version>
|
||||
<etcd_launcher_version>0.7.5</etcd_launcher_version>
|
||||
<etcd_launcher_version>0.7.6</etcd_launcher_version>
|
||||
<hessian_lite_version>3.2.13</hessian_lite_version>
|
||||
<swagger_version>1.6.11</swagger_version>
|
||||
|
||||
<snappy_java_version>1.1.10.1</snappy_java_version>
|
||||
<snappy_java_version>1.1.10.2</snappy_java_version>
|
||||
<bouncycastle-bcprov_version>1.70</bouncycastle-bcprov_version>
|
||||
<metrics_version>2.0.6</metrics_version>
|
||||
<sofa_registry_version>5.4.3</sofa_registry_version>
|
||||
|
|
|
|||
|
|
@ -1125,6 +1125,24 @@
|
|||
META-INF/dubbo/internal/org.apache.dubbo.registry.xds.XdsCertificateSigner
|
||||
</resource>
|
||||
</transformer>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>
|
||||
META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.filter.RestResponseInterceptor
|
||||
</resource>
|
||||
</transformer>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>
|
||||
META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.filter.RestRequestFilter
|
||||
</resource>
|
||||
</transformer>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>
|
||||
META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.filter.RestResponseFilter
|
||||
</resource>
|
||||
</transformer>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package org.apache.dubbo.metadata;
|
|||
|
||||
import org.apache.dubbo.common.cache.FileCacheStore;
|
||||
import org.apache.dubbo.common.cache.FileCacheStoreFactory;
|
||||
import org.apache.dubbo.common.config.ConfigurationUtils;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.resource.Disposable;
|
||||
|
|
@ -32,7 +33,9 @@ import java.util.concurrent.Executors;
|
|||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_SERVER_SHUTDOWN_TIMEOUT;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_FAILED_LOAD_MAPPING_CACHE;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION;
|
||||
|
||||
public abstract class AbstractCacheManager<V> implements Disposable {
|
||||
protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
|
||||
|
|
@ -109,6 +112,16 @@ public abstract class AbstractCacheManager<V> implements Disposable {
|
|||
public void destroy() {
|
||||
if (executorService != null) {
|
||||
executorService.shutdownNow();
|
||||
try {
|
||||
if (!executorService.awaitTermination(
|
||||
ConfigurationUtils.reCalShutdownTime(DEFAULT_SERVER_SHUTDOWN_TIMEOUT),
|
||||
TimeUnit.MILLISECONDS)) {
|
||||
logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "",
|
||||
"Wait global executor service terminated timeout.");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", "destroy resources failed: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
if (cacheStore != null) {
|
||||
cacheStore.destroy();
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ public class ProtobufTypeBuilder implements TypeBuilder, Prioritized {
|
|||
*/
|
||||
private static Type STRING_LIST_TYPE;
|
||||
|
||||
private final boolean protobufExist;
|
||||
|
||||
static {
|
||||
try {
|
||||
STRING_LIST_TYPE = ProtobufTypeBuilder.class.getDeclaredField("LIST").getGenericType();
|
||||
|
|
@ -56,6 +58,19 @@ public class ProtobufTypeBuilder implements TypeBuilder, Prioritized {
|
|||
}
|
||||
}
|
||||
|
||||
public ProtobufTypeBuilder() {
|
||||
protobufExist = checkProtobufExist();
|
||||
}
|
||||
|
||||
private boolean checkProtobufExist() {
|
||||
try {
|
||||
Class.forName("com.google.protobuf.GeneratedMessageV3");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return -1;
|
||||
|
|
@ -67,6 +82,10 @@ public class ProtobufTypeBuilder implements TypeBuilder, Prioritized {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!protobufExist) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return GeneratedMessageV3.class.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ package org.apache.dubbo.metrics;
|
|||
public interface MetricsConstants {
|
||||
|
||||
String INVOCATION = "metric_filter_invocation";
|
||||
String METHOD_METRICS = "metric_filter_method_metrics";
|
||||
String INVOCATION_METRICS_COUNTER = "metric_filter_invocation_counter";
|
||||
|
||||
String INVOCATION_SIDE = "metric_filter_side";
|
||||
|
|
|
|||
|
|
@ -119,8 +119,6 @@ public class DubboMergingDigest extends DubboAbstractTDigest {
|
|||
// weight limits.
|
||||
public static boolean useWeightLimit = true;
|
||||
|
||||
private volatile boolean merging = false;
|
||||
|
||||
/**
|
||||
* Allocates a buffer merging t-digest. This is the normally used constructor that
|
||||
* allocates default sized internal arrays. Other versions are available, but should
|
||||
|
|
@ -286,16 +284,13 @@ public class DubboMergingDigest extends DubboAbstractTDigest {
|
|||
if (Double.isNaN(x)) {
|
||||
throw new IllegalArgumentException("Cannot add NaN to t-digest");
|
||||
}
|
||||
while (merging) {
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
|
||||
synchronized (this) {
|
||||
// There is a small probability of entering here
|
||||
if (tempUsed.get() >= tempWeight.length - lastUsedCell.get() - 1) {
|
||||
mergeNewValues();
|
||||
}
|
||||
}
|
||||
if (tempUsed.get() >= tempWeight.length - lastUsedCell.get() - 1) {
|
||||
mergeNewValues();
|
||||
}
|
||||
int where = tempUsed.getAndIncrement();
|
||||
tempWeight[where] = w;
|
||||
tempMean[where] = x;
|
||||
|
|
@ -326,13 +321,8 @@ public class DubboMergingDigest extends DubboAbstractTDigest {
|
|||
throw new MetricsNeverHappenException("Method not used");
|
||||
}
|
||||
|
||||
private synchronized void mergeNewValues() {
|
||||
merging = true;
|
||||
try {
|
||||
mergeNewValues(false, compression);
|
||||
} finally {
|
||||
merging = false;
|
||||
}
|
||||
private void mergeNewValues() {
|
||||
mergeNewValues(false, compression);
|
||||
}
|
||||
|
||||
private void mergeNewValues(boolean force, double compression) {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,10 @@ public class TimeWindowCounter {
|
|||
return TimeUnit.MILLISECONDS.toSeconds(this.slidingWindow.values().size() * this.slidingWindow.getPaneIntervalInMs());
|
||||
}
|
||||
|
||||
public long bucketLivedMillSeconds() {
|
||||
return this.slidingWindow.getIntervalInMs() - (System.currentTimeMillis() - this.slidingWindow.currentPane().getEndInMs());
|
||||
}
|
||||
|
||||
public void increment() {
|
||||
this.increment(1L);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ public interface ApplicationMetricsCollector<E extends TimeCounterEvent> extends
|
|||
|
||||
void increment(MetricsKey metricsKey);
|
||||
|
||||
void addRt(String registryOpType, Long responseTime);
|
||||
void addApplicationRt(String registryOpType, Long responseTime);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import org.apache.dubbo.metrics.data.BaseStatComposite;
|
|||
import org.apache.dubbo.metrics.event.MetricsEventMulticaster;
|
||||
import org.apache.dubbo.metrics.event.TimeCounterEvent;
|
||||
import org.apache.dubbo.metrics.listener.AbstractMetricsListener;
|
||||
import org.apache.dubbo.metrics.model.MethodMetric;
|
||||
import org.apache.dubbo.metrics.model.MetricsCategory;
|
||||
import org.apache.dubbo.metrics.model.key.MetricsKey;
|
||||
import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper;
|
||||
|
|
@ -60,24 +61,30 @@ public abstract class CombMetricsCollector<E extends TimeCounterEvent> extends A
|
|||
}
|
||||
|
||||
@Override
|
||||
public void addRt(String registryOpType, Long responseTime) {
|
||||
public void addApplicationRt(String registryOpType, Long responseTime) {
|
||||
stats.calcApplicationRt(registryOpType, responseTime);
|
||||
}
|
||||
|
||||
public void addRt(String serviceKey, String registryOpType, Long responseTime) {
|
||||
@Override
|
||||
public void addServiceRt(String serviceKey, String registryOpType, Long responseTime) {
|
||||
stats.calcServiceKeyRt(serviceKey, registryOpType, responseTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void increment(Invocation invocation, MetricsKeyWrapper wrapper, int size) {
|
||||
this.stats.incrementMethodKey(wrapper, invocation, size);
|
||||
public void addServiceRt(Invocation invocation, String registryOpType, Long responseTime) {
|
||||
stats.calcServiceKeyRt(invocation, registryOpType, responseTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRt(Invocation invocation, String registryOpType, Long responseTime) {
|
||||
public void addMethodRt(Invocation invocation, String registryOpType, Long responseTime) {
|
||||
stats.calcMethodKeyRt(invocation, registryOpType, responseTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void increment(MethodMetric methodMetric, MetricsKeyWrapper wrapper, int size) {
|
||||
this.stats.incrementMethodKey(wrapper, methodMetric, size);
|
||||
}
|
||||
|
||||
protected List<MetricSample> export(MetricsCategory category) {
|
||||
return stats.export(category);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package org.apache.dubbo.metrics.collector;
|
||||
|
||||
import org.apache.dubbo.metrics.event.TimeCounterEvent;
|
||||
import org.apache.dubbo.metrics.model.MethodMetric;
|
||||
import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
|
||||
|
|
@ -26,8 +27,8 @@ import org.apache.dubbo.rpc.Invocation;
|
|||
*/
|
||||
public interface MethodMetricsCollector<E extends TimeCounterEvent> extends MetricsCollector<E> {
|
||||
|
||||
void increment(Invocation invocation, MetricsKeyWrapper wrapper, int size);
|
||||
void increment(MethodMetric methodMetric, MetricsKeyWrapper wrapper, int size);
|
||||
|
||||
void addRt(Invocation invocation, String registryOpType, Long responseTime);
|
||||
void addMethodRt(Invocation invocation, String registryOpType, Long responseTime);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package org.apache.dubbo.metrics.collector;
|
|||
|
||||
import org.apache.dubbo.metrics.event.TimeCounterEvent;
|
||||
import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
|
||||
/**
|
||||
* Service-level collector.
|
||||
|
|
@ -30,6 +31,8 @@ public interface ServiceMetricsCollector<E extends TimeCounterEvent> extends Met
|
|||
|
||||
void setNum(MetricsKeyWrapper metricsKey, String serviceKey, int num);
|
||||
|
||||
void addRt(String serviceKey, String registryOpType, Long responseTime);
|
||||
void addServiceRt(String serviceKey, String registryOpType, Long responseTime);
|
||||
|
||||
void addServiceRt(Invocation invocation, String registryOpType, Long responseTime);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package org.apache.dubbo.metrics.data;
|
||||
|
||||
import org.apache.dubbo.metrics.collector.MetricsCollector;
|
||||
import org.apache.dubbo.metrics.model.MethodMetric;
|
||||
import org.apache.dubbo.metrics.model.MetricsCategory;
|
||||
import org.apache.dubbo.metrics.model.key.MetricsKey;
|
||||
import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper;
|
||||
|
|
@ -76,6 +77,10 @@ public abstract class BaseStatComposite implements MetricsExport {
|
|||
rtStatComposite.calcServiceKeyRt(serviceKey, registryOpType, responseTime);
|
||||
}
|
||||
|
||||
public void calcServiceKeyRt(Invocation invocation, String registryOpType, Long responseTime) {
|
||||
rtStatComposite.calcServiceKeyRt(invocation, registryOpType, responseTime);
|
||||
}
|
||||
|
||||
public void calcMethodKeyRt(Invocation invocation, String registryOpType, Long responseTime) {
|
||||
rtStatComposite.calcMethodKeyRt(invocation, registryOpType, responseTime);
|
||||
}
|
||||
|
|
@ -92,8 +97,8 @@ public abstract class BaseStatComposite implements MetricsExport {
|
|||
serviceStatComposite.incrementServiceKey(metricsKeyWrapper, attServiceKey, size);
|
||||
}
|
||||
|
||||
public void incrementMethodKey(MetricsKeyWrapper metricsKeyWrapper, Invocation invocation, int size) {
|
||||
methodStatComposite.incrementMethodKey(metricsKeyWrapper, invocation, size);
|
||||
public void incrementMethodKey(MetricsKeyWrapper metricsKeyWrapper, MethodMetric methodMetric, int size) {
|
||||
methodStatComposite.incrementMethodKey(metricsKeyWrapper, methodMetric, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@
|
|||
|
||||
package org.apache.dubbo.metrics.data;
|
||||
|
||||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.metrics.exception.MetricsNeverHappenException;
|
||||
import org.apache.dubbo.metrics.model.MethodMetric;
|
||||
|
|
@ -28,7 +26,6 @@ import org.apache.dubbo.metrics.model.sample.CounterMetricSample;
|
|||
import org.apache.dubbo.metrics.model.sample.GaugeMetricSample;
|
||||
import org.apache.dubbo.metrics.model.sample.MetricSample;
|
||||
import org.apache.dubbo.metrics.report.AbstractMetricsExport;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -44,8 +41,6 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||
*/
|
||||
public class MethodStatComposite extends AbstractMetricsExport {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MethodStatComposite.class);
|
||||
|
||||
public MethodStatComposite(ApplicationModel applicationModel) {
|
||||
super(applicationModel);
|
||||
}
|
||||
|
|
@ -59,11 +54,16 @@ public class MethodStatComposite extends AbstractMetricsExport {
|
|||
metricsKeyWrappers.forEach(appKey -> methodNumStats.put(appKey, new ConcurrentHashMap<>()));
|
||||
}
|
||||
|
||||
public void incrementMethodKey(MetricsKeyWrapper wrapper, Invocation invocation, int size) {
|
||||
public void incrementMethodKey(MetricsKeyWrapper wrapper, MethodMetric methodMetric, int size) {
|
||||
if (!methodNumStats.containsKey(wrapper)) {
|
||||
return;
|
||||
}
|
||||
methodNumStats.get(wrapper).computeIfAbsent(new MethodMetric(getApplicationModel(), invocation), k -> new AtomicLong(0L)).getAndAdd(size);
|
||||
AtomicLong stat = methodNumStats.get(wrapper).get(methodMetric);
|
||||
if (stat == null) {
|
||||
methodNumStats.get(wrapper).putIfAbsent(methodMetric, new AtomicLong(0L));
|
||||
stat = methodNumStats.get(wrapper).get(methodMetric);
|
||||
}
|
||||
stat.getAndAdd(size);
|
||||
// MetricsSupport.fillZero(methodNumStats);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,11 @@
|
|||
|
||||
package org.apache.dubbo.metrics.data;
|
||||
|
||||
import org.apache.dubbo.common.utils.ConcurrentHashMapUtils;
|
||||
import org.apache.dubbo.metrics.model.ApplicationMetric;
|
||||
import org.apache.dubbo.metrics.model.MethodMetric;
|
||||
import org.apache.dubbo.metrics.model.Metric;
|
||||
import org.apache.dubbo.metrics.model.MetricsCategory;
|
||||
import org.apache.dubbo.metrics.model.ServiceKeyMetric;
|
||||
import org.apache.dubbo.metrics.model.container.AtomicLongContainer;
|
||||
import org.apache.dubbo.metrics.model.container.LongAccumulatorContainer;
|
||||
import org.apache.dubbo.metrics.model.container.LongContainer;
|
||||
|
|
@ -30,14 +33,15 @@ import org.apache.dubbo.metrics.model.sample.MetricSample;
|
|||
import org.apache.dubbo.metrics.report.AbstractMetricsExport;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.rpc.support.RpcUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
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.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
|
@ -52,13 +56,19 @@ public class RtStatComposite extends AbstractMetricsExport {
|
|||
super(applicationModel);
|
||||
}
|
||||
|
||||
private final List<LongContainer<? extends Number>> rtStats = new ArrayList<>();
|
||||
private final Map<String, List<LongContainer<? extends Number>>> rtStats = new ConcurrentHashMap<>();
|
||||
|
||||
public void init(MetricsPlaceValue... placeValues) {
|
||||
if (placeValues == null) {
|
||||
return;
|
||||
}
|
||||
Arrays.stream(placeValues).forEach(metricsPlaceType -> rtStats.addAll(initStats(metricsPlaceType)));
|
||||
for (MetricsPlaceValue placeValue : placeValues) {
|
||||
List<LongContainer<? extends Number>> containers = initStats(placeValue);
|
||||
for (LongContainer<? extends Number> container : containers) {
|
||||
rtStats.computeIfAbsent(container.getMetricsKeyWrapper().getType(), k -> new ArrayList<>())
|
||||
.add(container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<LongContainer<? extends Number>> initStats(MetricsPlaceValue placeValue) {
|
||||
|
|
@ -70,7 +80,7 @@ public class RtStatComposite extends AbstractMetricsExport {
|
|||
// AvgContainer is a special counter that stores the number of times but outputs function of sum/times
|
||||
AtomicLongContainer avgContainer = new AtomicLongContainer(new MetricsKeyWrapper(MetricsKey.METRIC_RT_AVG, placeValue), (k, v) -> v.incrementAndGet());
|
||||
avgContainer.setValueSupplier(applicationName -> {
|
||||
LongContainer<? extends Number> totalContainer = rtStats.stream().filter(longContainer -> longContainer.isKeyWrapper(MetricsKey.METRIC_RT_SUM, placeValue.getType())).findFirst().get();
|
||||
LongContainer<? extends Number> totalContainer = rtStats.values().stream().flatMap(List::stream).filter(longContainer -> longContainer.isKeyWrapper(MetricsKey.METRIC_RT_SUM, placeValue.getType())).findFirst().get();
|
||||
AtomicLong totalRtTimes = avgContainer.get(applicationName);
|
||||
AtomicLong totalRtSum = (AtomicLong) totalContainer.get(applicationName);
|
||||
return totalRtSum.get() / totalRtTimes.get();
|
||||
|
|
@ -80,39 +90,144 @@ public class RtStatComposite extends AbstractMetricsExport {
|
|||
}
|
||||
|
||||
public void calcApplicationRt(String registryOpType, Long responseTime) {
|
||||
for (LongContainer container : rtStats.stream().filter(longContainer -> longContainer.specifyType(registryOpType)).collect(Collectors.toList())) {
|
||||
Number current = (Number) ConcurrentHashMapUtils.computeIfAbsent(container, getAppName(), container.getInitFunc());
|
||||
ApplicationMetric key = new ApplicationMetric(getApplicationModel());
|
||||
for (LongContainer container : rtStats.get(registryOpType)) {
|
||||
Number current = (Number) container.get(key);
|
||||
if (current == null) {
|
||||
container.putIfAbsent(key, container.getInitFunc().apply(key));
|
||||
current = (Number) container.get(key);
|
||||
}
|
||||
container.getConsumerFunc().accept(responseTime, current);
|
||||
}
|
||||
}
|
||||
|
||||
public void calcServiceKeyRt(String serviceKey, String registryOpType, Long responseTime) {
|
||||
for (LongContainer container : rtStats.stream().filter(longContainer -> longContainer.specifyType(registryOpType)).collect(Collectors.toList())) {
|
||||
Number current = (Number) ConcurrentHashMapUtils.computeIfAbsent(container, serviceKey, container.getInitFunc());
|
||||
ServiceKeyMetric key = new ServiceKeyMetric(getApplicationModel(), serviceKey);
|
||||
for (LongContainer container : rtStats.get(registryOpType)) {
|
||||
Number current = (Number) container.get(key);
|
||||
if (current == null) {
|
||||
container.putIfAbsent(key, container.getInitFunc().apply(key));
|
||||
current = (Number) container.get(key);
|
||||
}
|
||||
container.getConsumerFunc().accept(responseTime, current);
|
||||
}
|
||||
}
|
||||
|
||||
public void calcMethodKeyRt(Invocation invocation, String registryOpType, Long responseTime) {
|
||||
for (LongContainer container : rtStats.stream().filter(longContainer -> longContainer.specifyType(registryOpType)).collect(Collectors.toList())) {
|
||||
Number current = (Number) ConcurrentHashMapUtils.computeIfAbsent(container, invocation.getTargetServiceUniqueName() + "_" + RpcUtils.getMethodName(invocation), container.getInitFunc());
|
||||
container.getConsumerFunc().accept(responseTime, current);
|
||||
public void calcServiceKeyRt(Invocation invocation, String registryOpType, Long responseTime) {
|
||||
List<Action> actions;
|
||||
if (invocation.getServiceModel() != null && invocation.getServiceModel().getServiceKey() != null) {
|
||||
Map<String, Object> attributeMap = invocation.getServiceModel().getServiceMetadata().getAttributeMap();
|
||||
Map<String, List<Action>> cache = (Map<String, List<Action>>) attributeMap.get("ServiceKeyRt");
|
||||
if (cache == null) {
|
||||
attributeMap.putIfAbsent("ServiceKeyRt", new ConcurrentHashMap<>(32));
|
||||
cache = (Map<String, List<Action>>) attributeMap.get("ServiceKeyRt");
|
||||
}
|
||||
actions = cache.get(registryOpType);
|
||||
if (actions == null) {
|
||||
actions = calServiceRtActions(invocation, registryOpType);
|
||||
cache.putIfAbsent(registryOpType, actions);
|
||||
actions = cache.get(registryOpType);
|
||||
}
|
||||
} else {
|
||||
actions = calServiceRtActions(invocation, registryOpType);
|
||||
}
|
||||
|
||||
for (Action action : actions) {
|
||||
action.run(responseTime);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Action> calServiceRtActions(Invocation invocation, String registryOpType) {
|
||||
List<Action> actions;
|
||||
actions = new LinkedList<>();
|
||||
|
||||
ServiceKeyMetric key = new ServiceKeyMetric(getApplicationModel(), invocation.getTargetServiceUniqueName());
|
||||
for (LongContainer container : rtStats.get(registryOpType)) {
|
||||
Number current = (Number) container.get(key);
|
||||
if (current == null) {
|
||||
container.putIfAbsent(key, container.getInitFunc().apply(key));
|
||||
current = (Number) container.get(key);
|
||||
}
|
||||
actions.add(new Action(container.getConsumerFunc(), current));
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
||||
public void calcMethodKeyRt(Invocation invocation, String registryOpType, Long responseTime) {
|
||||
List<Action> actions;
|
||||
|
||||
if (invocation.getServiceModel() != null && invocation.getServiceModel().getServiceMetadata() != null) {
|
||||
Map<String, Object> attributeMap = invocation.getServiceModel().getServiceMetadata().getAttributeMap();
|
||||
Map<String, List<Action>> cache = (Map<String, List<Action>>) attributeMap.get("MethodKeyRt");
|
||||
if (cache == null) {
|
||||
attributeMap.putIfAbsent("MethodKeyRt", new ConcurrentHashMap<>(32));
|
||||
cache = (Map<String, List<Action>>) attributeMap.get("MethodKeyRt");
|
||||
}
|
||||
actions = cache.get(registryOpType);
|
||||
if (actions == null) {
|
||||
actions = calMethodRtActions(invocation, registryOpType);
|
||||
cache.putIfAbsent(registryOpType, actions);
|
||||
actions = cache.get(registryOpType);
|
||||
}
|
||||
} else {
|
||||
actions = calMethodRtActions(invocation, registryOpType);
|
||||
}
|
||||
|
||||
for (Action action : actions) {
|
||||
action.run(responseTime);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Action> calMethodRtActions(Invocation invocation, String registryOpType) {
|
||||
List<Action> actions;
|
||||
actions = new LinkedList<>();
|
||||
for (LongContainer container : rtStats.get(registryOpType)) {
|
||||
MethodMetric key = new MethodMetric(getApplicationModel(), invocation);
|
||||
Number current = (Number) container.get(key);
|
||||
if (current == null) {
|
||||
container.putIfAbsent(key, container.getInitFunc().apply(key));
|
||||
current = (Number) container.get(key);
|
||||
}
|
||||
actions.add(new Action(container.getConsumerFunc(), current));
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
||||
public List<MetricSample> export(MetricsCategory category) {
|
||||
List<MetricSample> 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(), metricsKeyWrapper.tagName(getApplicationModel(), entry.getKey()), category, entry.getKey().intern(), value -> rtContainer.getValueSupplier().apply(value.intern())));
|
||||
for (List<LongContainer<? extends Number>> containers : rtStats.values()) {
|
||||
for (LongContainer<? extends Number> container : containers) {
|
||||
MetricsKeyWrapper metricsKeyWrapper = container.getMetricsKeyWrapper();
|
||||
for (Metric key : container.keySet()) {
|
||||
// Use keySet to obtain the original key instance reference of ConcurrentHashMap to avoid early recycling of the micrometer
|
||||
list.add(new GaugeMetricSample<>(metricsKeyWrapper.targetKey(),
|
||||
metricsKeyWrapper.targetDesc(),
|
||||
key.getTags(),
|
||||
category,
|
||||
key,
|
||||
value -> container.getValueSupplier().apply(value)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<LongContainer<? extends Number>> getRtStats() {
|
||||
return rtStats;
|
||||
return rtStats.values().stream().flatMap(List::stream).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
private static class Action {
|
||||
private final BiConsumer<Long, Number> consumerFunc;
|
||||
private final Number initValue;
|
||||
|
||||
public Action(BiConsumer<Long, Number> consumerFunc, Number initValue) {
|
||||
this.consumerFunc = consumerFunc;
|
||||
this.initValue = initValue;
|
||||
}
|
||||
|
||||
public void run(Long responseTime) {
|
||||
consumerFunc.accept(responseTime, initValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,13 @@
|
|||
|
||||
package org.apache.dubbo.metrics.event;
|
||||
|
||||
import org.apache.dubbo.common.beans.factory.ScopeBeanFactory;
|
||||
import org.apache.dubbo.metrics.exception.MetricsNeverHappenException;
|
||||
import org.apache.dubbo.metrics.model.MethodMetric;
|
||||
import org.apache.dubbo.metrics.model.key.TypeWrapper;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
|
@ -33,13 +34,19 @@ public abstract class MetricsEvent {
|
|||
/**
|
||||
* Metric object. (eg. {@link MethodMetric})
|
||||
*/
|
||||
protected transient ApplicationModel source;
|
||||
protected transient final ApplicationModel source;
|
||||
private boolean available = true;
|
||||
private final TypeWrapper typeWrapper;
|
||||
private final String appName;
|
||||
private final MetricsDispatcher metricsDispatcher;
|
||||
|
||||
private final Map<String, Object> attachment = new HashMap<>(8);
|
||||
private final Map<String, Object> attachment = new IdentityHashMap<>(8);
|
||||
|
||||
public MetricsEvent(ApplicationModel source, TypeWrapper typeWrapper) {
|
||||
this(source, null, null, typeWrapper);
|
||||
}
|
||||
|
||||
public MetricsEvent(ApplicationModel source, String appName, MetricsDispatcher metricsDispatcher, TypeWrapper typeWrapper) {
|
||||
this.typeWrapper = typeWrapper;
|
||||
if (source == null) {
|
||||
this.source = ApplicationModel.defaultModel();
|
||||
|
|
@ -48,6 +55,26 @@ public abstract class MetricsEvent {
|
|||
} else {
|
||||
this.source = source;
|
||||
}
|
||||
if (metricsDispatcher == null) {
|
||||
if (this.source.isDestroyed()) {
|
||||
this.metricsDispatcher = null;
|
||||
} else {
|
||||
ScopeBeanFactory beanFactory = this.source.getBeanFactory();
|
||||
if (beanFactory.isDestroyed()) {
|
||||
this.metricsDispatcher = null;
|
||||
} else {
|
||||
MetricsDispatcher dispatcher = beanFactory.getBean(MetricsDispatcher.class);
|
||||
this.metricsDispatcher = dispatcher;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.metricsDispatcher = metricsDispatcher;
|
||||
}
|
||||
if (appName == null) {
|
||||
this.appName = this.source.tryGetApplicationName();
|
||||
} else {
|
||||
this.appName = appName;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
@ -79,8 +106,12 @@ public abstract class MetricsEvent {
|
|||
return source;
|
||||
}
|
||||
|
||||
public MetricsDispatcher getMetricsDispatcher() {
|
||||
return metricsDispatcher;
|
||||
}
|
||||
|
||||
public String appName() {
|
||||
return getSource().getApplicationName();
|
||||
return appName;
|
||||
}
|
||||
|
||||
public TypeWrapper getTypeWrapper() {
|
||||
|
|
|
|||
|
|
@ -17,9 +17,6 @@
|
|||
|
||||
package org.apache.dubbo.metrics.event;
|
||||
|
||||
import org.apache.dubbo.common.beans.factory.ScopeBeanFactory;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
|
@ -38,15 +35,7 @@ public class MetricsEventBus {
|
|||
if (event.getSource() == null) {
|
||||
return;
|
||||
}
|
||||
ApplicationModel applicationModel = event.getSource();
|
||||
if (applicationModel.isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
ScopeBeanFactory beanFactory = applicationModel.getBeanFactory();
|
||||
if (beanFactory.isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
MetricsDispatcher dispatcher = beanFactory.getBean(MetricsDispatcher.class);
|
||||
MetricsDispatcher dispatcher = event.getMetricsDispatcher();
|
||||
Optional.ofNullable(dispatcher).ifPresent(d -> d.publishEvent(event));
|
||||
}
|
||||
|
||||
|
|
@ -95,21 +84,14 @@ public class MetricsEventBus {
|
|||
return result;
|
||||
}
|
||||
|
||||
public static void before(MetricsEvent event) {
|
||||
before(event, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applicable to the scene where execution and return are separated,
|
||||
* eventSaveRunner saves the event, so that the calculation rt is introverted
|
||||
*/
|
||||
public static void before(MetricsEvent event, Runnable eventSaveRunner) {
|
||||
public static void before(MetricsEvent event) {
|
||||
MetricsDispatcher dispatcher = validate(event);
|
||||
if (dispatcher == null) return;
|
||||
dispatcher.publishEvent(event);
|
||||
if (eventSaveRunner != null) {
|
||||
eventSaveRunner.run();
|
||||
}
|
||||
}
|
||||
|
||||
public static void after(MetricsEvent event, Object result) {
|
||||
|
|
@ -126,18 +108,7 @@ public class MetricsEventBus {
|
|||
}
|
||||
|
||||
private static MetricsDispatcher validate(MetricsEvent event) {
|
||||
if (event.getSource() == null) {
|
||||
return null;
|
||||
}
|
||||
ApplicationModel applicationModel = event.getSource();
|
||||
if (applicationModel.isDestroyed()) {
|
||||
return null;
|
||||
}
|
||||
ScopeBeanFactory beanFactory = applicationModel.getBeanFactory();
|
||||
if (beanFactory.isDestroyed()) {
|
||||
return null;
|
||||
}
|
||||
MetricsDispatcher dispatcher = beanFactory.getBean(MetricsDispatcher.class);
|
||||
MetricsDispatcher dispatcher = event.getMetricsDispatcher();
|
||||
if (dispatcher == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.apache.dubbo.metrics.event;
|
||||
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.metrics.listener.MetricsLifeListener;
|
||||
import org.apache.dubbo.metrics.listener.MetricsListener;
|
||||
|
||||
|
|
@ -53,7 +54,7 @@ public class SimpleMetricsEventMulticaster implements MetricsEventMulticaster {
|
|||
private boolean validateIfApplicationConfigExist(MetricsEvent event) {
|
||||
if (event.getSource() != null) {
|
||||
// Check if exist application config
|
||||
return event.getSource().NotExistApplicationConfig();
|
||||
return StringUtils.isEmpty(event.appName());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ public abstract class TimeCounterEvent extends MetricsEvent {
|
|||
this.timePair = TimePair.start();
|
||||
}
|
||||
|
||||
public TimeCounterEvent(ApplicationModel source, String appName, MetricsDispatcher metricsDispatcher, TypeWrapper typeWrapper) {
|
||||
super(source, appName, metricsDispatcher, typeWrapper);
|
||||
this.timePair = TimePair.start();
|
||||
}
|
||||
|
||||
public TimePair getTimePair() {
|
||||
return timePair;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,13 +25,17 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
|
||||
public abstract class AbstractMetricsListener<E extends MetricsEvent> implements MetricsListener<E> {
|
||||
|
||||
private final Map<Class<?>, Boolean> eventMatchCache = new ConcurrentHashMap<>();
|
||||
private final Map<Integer, Boolean> eventMatchCache = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Whether to support the general determination of event points depends on the event type
|
||||
*/
|
||||
public boolean isSupport(MetricsEvent event) {
|
||||
Boolean eventMatch = eventMatchCache.computeIfAbsent(event.getClass(), clazz -> ReflectionUtils.match(getClass(), AbstractMetricsListener.class, event));
|
||||
Boolean eventMatch = eventMatchCache.get(System.identityHashCode(event.getClass()));
|
||||
if (eventMatch == null) {
|
||||
eventMatch = ReflectionUtils.match(getClass(), AbstractMetricsListener.class, event);
|
||||
eventMatchCache.put(System.identityHashCode(event.getClass()), eventMatch);
|
||||
}
|
||||
return event.isAvailable() && eventMatch;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public class MetricsApplicationListener extends AbstractMetricsKeyListener {
|
|||
return AbstractMetricsKeyListener.onFinish(metricsKey,
|
||||
event -> {
|
||||
collector.increment(metricsKey);
|
||||
collector.addRt(placeType.getType(), event.getTimePair().calc());
|
||||
collector.addApplicationRt(placeType.getType(), event.getTimePair().calc());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -46,7 +46,7 @@ public class MetricsApplicationListener extends AbstractMetricsKeyListener {
|
|||
return AbstractMetricsKeyListener.onError(metricsKey,
|
||||
event -> {
|
||||
collector.increment(metricsKey);
|
||||
collector.addRt(placeType.getType(), event.getTimePair().calc());
|
||||
collector.addApplicationRt(placeType.getType(), event.getTimePair().calc());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import org.apache.dubbo.rpc.model.ApplicationModel;
|
|||
|
||||
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_APPLICATION_VERSION_KEY;
|
||||
|
|
@ -62,4 +63,22 @@ public class ApplicationMetric implements Metric {
|
|||
tags.put(MetricsKey.METADATA_GIT_COMMITID_METRIC.getName(), commitId);
|
||||
return tags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof ApplicationMetric)) return false;
|
||||
ApplicationMetric that = (ApplicationMetric) o;
|
||||
return Objects.equals(getApplicationName(), that.applicationModel.getApplicationName());
|
||||
}
|
||||
|
||||
private volatile int hashCode;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (hashCode == 0) {
|
||||
hashCode = Objects.hash(getApplicationName());
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
package org.apache.dubbo.metrics.model;
|
||||
|
||||
import org.apache.dubbo.metrics.model.sample.MetricSample;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.rpc.support.RpcUtils;
|
||||
|
|
@ -27,7 +26,6 @@ import java.util.Objects;
|
|||
|
||||
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_GROUP_KEY;
|
||||
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_VERSION_KEY;
|
||||
import static org.apache.dubbo.metrics.MetricsConstants.INVOCATION_METRICS_COUNTER;
|
||||
|
||||
/**
|
||||
* Metric class for method.
|
||||
|
|
@ -37,7 +35,6 @@ public class MethodMetric extends ServiceKeyMetric {
|
|||
private final String methodName;
|
||||
private String group;
|
||||
private String version;
|
||||
private final MetricSample.Type sampleType;
|
||||
|
||||
public MethodMetric(ApplicationModel applicationModel, Invocation invocation) {
|
||||
super(applicationModel, MetricsSupport.getInterfaceName(invocation));
|
||||
|
|
@ -45,11 +42,6 @@ public class MethodMetric extends ServiceKeyMetric {
|
|||
this.side = MetricsSupport.getSide(invocation);
|
||||
this.group = MetricsSupport.getGroup(invocation);
|
||||
this.version = MetricsSupport.getVersion(invocation);
|
||||
this.sampleType = (MetricSample.Type) invocation.get(INVOCATION_METRICS_COUNTER);
|
||||
}
|
||||
|
||||
public MetricSample.Type getSampleType() {
|
||||
return sampleType;
|
||||
}
|
||||
|
||||
public String getGroup() {
|
||||
|
|
@ -69,7 +61,7 @@ public class MethodMetric extends ServiceKeyMetric {
|
|||
}
|
||||
|
||||
public Map<String, String> getTags() {
|
||||
Map<String, String> tags = MetricsSupport.methodTags(getApplicationModel(), getInterfaceName(), methodName);
|
||||
Map<String, String> tags = MetricsSupport.methodTags(getApplicationModel(), getServiceKey(), methodName);
|
||||
tags.put(TAG_GROUP_KEY, group);
|
||||
tags.put(TAG_VERSION_KEY, version);
|
||||
return tags;
|
||||
|
|
@ -92,7 +84,7 @@ public class MethodMetric extends ServiceKeyMetric {
|
|||
return "MethodMetric{" +
|
||||
"applicationName='" + getApplicationName() + '\'' +
|
||||
", side='" + side + '\'' +
|
||||
", interfaceName='" + getInterfaceName() + '\'' +
|
||||
", interfaceName='" + getServiceKey() + '\'' +
|
||||
", methodName='" + methodName + '\'' +
|
||||
", group='" + group + '\'' +
|
||||
", version='" + version + '\'' +
|
||||
|
|
@ -104,11 +96,15 @@ public class MethodMetric extends ServiceKeyMetric {
|
|||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
MethodMetric that = (MethodMetric) o;
|
||||
return Objects.equals(getApplicationName(), that.getApplicationName()) && Objects.equals(side, that.side) && Objects.equals(getInterfaceName(), that.getInterfaceName()) && Objects.equals(methodName, that.methodName) && Objects.equals(group, that.group) && Objects.equals(version, that.version);
|
||||
return Objects.equals(getApplicationModel(), that.getApplicationModel()) && Objects.equals(side, that.side) && Objects.equals(getServiceKey(), that.getServiceKey()) && Objects.equals(methodName, that.methodName) && Objects.equals(group, that.group) && Objects.equals(version, that.version);
|
||||
}
|
||||
|
||||
private volatile int hashCode = 0;
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getApplicationName(), side, getInterfaceName(), methodName, group, version);
|
||||
if (hashCode == 0) {
|
||||
hashCode = Objects.hash(getApplicationModel(), side, getServiceKey(), methodName, group, version);
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ import static org.apache.dubbo.common.utils.NetUtils.getLocalHost;
|
|||
import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName;
|
||||
import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SERVICE;
|
||||
import static org.apache.dubbo.metrics.MetricsConstants.INVOCATION;
|
||||
import static org.apache.dubbo.metrics.MetricsConstants.METHOD_METRICS;
|
||||
import static org.apache.dubbo.metrics.MetricsConstants.SELF_INCREMENT_SIZE;
|
||||
|
||||
public class MetricsSupport {
|
||||
|
|
@ -111,6 +112,8 @@ public class MetricsSupport {
|
|||
if (e.isNetwork()) {
|
||||
targetKey = MetricsKey.METRIC_REQUESTS_NETWORK_FAILED;
|
||||
}
|
||||
} else {
|
||||
targetKey = MetricsKey.METRIC_REQUEST_BUSINESS_FAILED;
|
||||
}
|
||||
return targetKey;
|
||||
}
|
||||
|
|
@ -134,6 +137,8 @@ public class MetricsSupport {
|
|||
if (e.isNetwork()) {
|
||||
targetKey = MetricsKey.METRIC_REQUESTS_NETWORK_FAILED_AGG;
|
||||
}
|
||||
} else {
|
||||
targetKey = MetricsKey.METRIC_REQUEST_BUSINESS_FAILED_AGG;
|
||||
}
|
||||
return targetKey;
|
||||
}
|
||||
|
|
@ -145,38 +150,50 @@ public class MetricsSupport {
|
|||
|
||||
|
||||
public static String getInterfaceName(Invocation invocation) {
|
||||
String serviceUniqueName = invocation.getTargetServiceUniqueName();
|
||||
String interfaceAndVersion;
|
||||
String[] arr = serviceUniqueName.split(PATH_SEPARATOR);
|
||||
if (arr.length == 2) {
|
||||
interfaceAndVersion = arr[1];
|
||||
if (invocation.getServiceModel() != null && invocation.getServiceModel().getServiceMetadata() != null) {
|
||||
return invocation.getServiceModel().getServiceMetadata().getServiceInterfaceName();
|
||||
} else {
|
||||
interfaceAndVersion = arr[0];
|
||||
String serviceUniqueName = invocation.getTargetServiceUniqueName();
|
||||
String interfaceAndVersion;
|
||||
String[] arr = serviceUniqueName.split(PATH_SEPARATOR);
|
||||
if (arr.length == 2) {
|
||||
interfaceAndVersion = arr[1];
|
||||
} else {
|
||||
interfaceAndVersion = arr[0];
|
||||
}
|
||||
String[] ivArr = interfaceAndVersion.split(GROUP_CHAR_SEPARATOR);
|
||||
return ivArr[0];
|
||||
}
|
||||
String[] ivArr = interfaceAndVersion.split(GROUP_CHAR_SEPARATOR);
|
||||
return ivArr[0];
|
||||
}
|
||||
|
||||
public static String getGroup(Invocation invocation) {
|
||||
String serviceUniqueName = invocation.getTargetServiceUniqueName();
|
||||
String group = null;
|
||||
String[] arr = serviceUniqueName.split(PATH_SEPARATOR);
|
||||
if (arr.length == 2) {
|
||||
group = arr[0];
|
||||
if (invocation.getServiceModel() != null && invocation.getServiceModel().getServiceMetadata() != null) {
|
||||
return invocation.getServiceModel().getServiceMetadata().getGroup();
|
||||
} else {
|
||||
String serviceUniqueName = invocation.getTargetServiceUniqueName();
|
||||
String group = null;
|
||||
String[] arr = serviceUniqueName.split(PATH_SEPARATOR);
|
||||
if (arr.length == 2) {
|
||||
group = arr[0];
|
||||
}
|
||||
return group;
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
public static String getVersion(Invocation invocation) {
|
||||
String interfaceAndVersion;
|
||||
String[] arr = invocation.getTargetServiceUniqueName().split(PATH_SEPARATOR);
|
||||
if (arr.length == 2) {
|
||||
interfaceAndVersion = arr[1];
|
||||
if (invocation.getServiceModel() != null && invocation.getServiceModel().getServiceMetadata() != null) {
|
||||
return invocation.getServiceModel().getServiceMetadata().getVersion();
|
||||
} else {
|
||||
interfaceAndVersion = arr[0];
|
||||
String interfaceAndVersion;
|
||||
String[] arr = invocation.getTargetServiceUniqueName().split(PATH_SEPARATOR);
|
||||
if (arr.length == 2) {
|
||||
interfaceAndVersion = arr[1];
|
||||
} else {
|
||||
interfaceAndVersion = arr[0];
|
||||
}
|
||||
String[] ivArr = interfaceAndVersion.split(GROUP_CHAR_SEPARATOR);
|
||||
return ivArr.length == 2 ? ivArr[1] : null;
|
||||
}
|
||||
String[] ivArr = interfaceAndVersion.split(GROUP_CHAR_SEPARATOR);
|
||||
return ivArr.length == 2 ? ivArr[1] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -191,29 +208,35 @@ public class MetricsSupport {
|
|||
*/
|
||||
public static void incrAndAddRt(MetricsKey metricsKey, MetricsPlaceValue placeType, ServiceMetricsCollector<TimeCounterEvent> collector, TimeCounterEvent event) {
|
||||
collector.increment(event.getAttachmentValue(ATTACHMENT_KEY_SERVICE), new MetricsKeyWrapper(metricsKey, placeType), SELF_INCREMENT_SIZE);
|
||||
collector.addRt(event.getAttachmentValue(ATTACHMENT_KEY_SERVICE), placeType.getType(), event.getTimePair().calc());
|
||||
Invocation invocation = event.getAttachmentValue(INVOCATION);
|
||||
if (invocation != null) {
|
||||
collector.addServiceRt(invocation, placeType.getType(), event.getTimePair().calc());
|
||||
return;
|
||||
} else {
|
||||
collector.addServiceRt((String) event.getAttachmentValue(ATTACHMENT_KEY_SERVICE), placeType.getType(), event.getTimePair().calc());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Incr method num
|
||||
*/
|
||||
public static void increment(MetricsKey metricsKey, MetricsPlaceValue placeType, MethodMetricsCollector<TimeCounterEvent> collector, MetricsEvent event) {
|
||||
collector.increment(event.getAttachmentValue(INVOCATION), new MetricsKeyWrapper(metricsKey, placeType), SELF_INCREMENT_SIZE);
|
||||
collector.increment(event.getAttachmentValue(METHOD_METRICS), new MetricsKeyWrapper(metricsKey, placeType), SELF_INCREMENT_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dec method num
|
||||
*/
|
||||
public static void dec(MetricsKey metricsKey, MetricsPlaceValue placeType, MethodMetricsCollector<TimeCounterEvent> collector, MetricsEvent event) {
|
||||
collector.increment(event.getAttachmentValue(INVOCATION), new MetricsKeyWrapper(metricsKey, placeType), -SELF_INCREMENT_SIZE);
|
||||
collector.increment(event.getAttachmentValue(METHOD_METRICS), new MetricsKeyWrapper(metricsKey, placeType), -SELF_INCREMENT_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Incr method num&&rt
|
||||
*/
|
||||
public static void incrAndAddRt(MetricsKey metricsKey, MetricsPlaceValue placeType, MethodMetricsCollector<TimeCounterEvent> collector, TimeCounterEvent event) {
|
||||
collector.increment(event.getAttachmentValue(INVOCATION), new MetricsKeyWrapper(metricsKey, placeType), SELF_INCREMENT_SIZE);
|
||||
collector.addRt(event.getAttachmentValue(INVOCATION), placeType.getType(), event.getTimePair().calc());
|
||||
collector.increment(event.getAttachmentValue(METHOD_METRICS), new MetricsKeyWrapper(metricsKey, placeType), SELF_INCREMENT_SIZE);
|
||||
collector.addMethodRt(event.getAttachmentValue(INVOCATION), placeType.getType(), event.getTimePair().calc());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -20,25 +20,26 @@ package org.apache.dubbo.metrics.model;
|
|||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Metric class for service.
|
||||
*/
|
||||
public class ServiceKeyMetric extends ApplicationMetric {
|
||||
private final String interfaceName;
|
||||
private final String serviceKey;
|
||||
|
||||
public ServiceKeyMetric(ApplicationModel applicationModel, String serviceKey) {
|
||||
super(applicationModel);
|
||||
this.interfaceName = serviceKey;
|
||||
this.serviceKey = serviceKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getTags() {
|
||||
return MetricsSupport.serviceTags(getApplicationModel(), interfaceName);
|
||||
return MetricsSupport.serviceTags(getApplicationModel(), serviceKey);
|
||||
}
|
||||
|
||||
public String getInterfaceName() {
|
||||
return interfaceName;
|
||||
public String getServiceKey() {
|
||||
return serviceKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -55,21 +56,25 @@ public class ServiceKeyMetric extends ApplicationMetric {
|
|||
if (!getApplicationName().equals(that.getApplicationName())) {
|
||||
return false;
|
||||
}
|
||||
return interfaceName.equals(that.interfaceName);
|
||||
return serviceKey.equals(that.serviceKey);
|
||||
}
|
||||
|
||||
|
||||
private volatile int hashCode = 0;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = getApplicationName().hashCode();
|
||||
result = 31 * result + interfaceName.hashCode();
|
||||
return result;
|
||||
if (hashCode == 0) {
|
||||
hashCode = Objects.hash(getApplicationName(), serviceKey);
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ServiceKeyMetric{" +
|
||||
"applicationName='" + getApplicationName() + '\'' +
|
||||
", serviceKey='" + interfaceName + '\'' +
|
||||
", serviceKey='" + serviceKey + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,9 @@
|
|||
|
||||
package org.apache.dubbo.metrics.model.container;
|
||||
|
||||
import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper;
|
||||
import org.apache.dubbo.metrics.model.Metric;
|
||||
import org.apache.dubbo.metrics.model.key.MetricsKey;
|
||||
import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper;
|
||||
import org.apache.dubbo.metrics.model.sample.GaugeMetricSample;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
|
@ -30,7 +31,7 @@ import java.util.function.Supplier;
|
|||
* Long type data container
|
||||
* @param <N>
|
||||
*/
|
||||
public class LongContainer<N extends Number> extends ConcurrentHashMap<String, N> {
|
||||
public class LongContainer<N extends Number> extends ConcurrentHashMap<Metric, N> {
|
||||
|
||||
/**
|
||||
* Provide the metric type name
|
||||
|
|
@ -39,7 +40,7 @@ public class LongContainer<N extends Number> extends ConcurrentHashMap<String, N
|
|||
/**
|
||||
* The initial value corresponding to the key is generally 0 of different data types
|
||||
*/
|
||||
private final transient Function<String, N> initFunc;
|
||||
private final transient Function<Metric, N> initFunc;
|
||||
/**
|
||||
* Statistical data calculation function, which can be self-increment, self-decrement, or more complex avg function
|
||||
*/
|
||||
|
|
@ -47,10 +48,11 @@ public class LongContainer<N extends Number> extends ConcurrentHashMap<String, N
|
|||
/**
|
||||
* Data output function required by {@link GaugeMetricSample GaugeMetricSample}
|
||||
*/
|
||||
private transient Function<String, Long> valueSupplier;
|
||||
private transient Function<Metric, Long> valueSupplier;
|
||||
|
||||
|
||||
public LongContainer(MetricsKeyWrapper metricsKeyWrapper, Supplier<N> initFunc, BiConsumer<Long, N> consumerFunc) {
|
||||
super(128, 0.5f);
|
||||
this.metricsKeyWrapper = metricsKeyWrapper;
|
||||
this.initFunc = s -> initFunc.get();
|
||||
this.consumerFunc = consumerFunc;
|
||||
|
|
@ -69,7 +71,7 @@ public class LongContainer<N extends Number> extends ConcurrentHashMap<String, N
|
|||
return metricsKeyWrapper.isKey(metricsKey, registryOpType);
|
||||
}
|
||||
|
||||
public Function<String, N> getInitFunc() {
|
||||
public Function<Metric, N> getInitFunc() {
|
||||
return initFunc;
|
||||
}
|
||||
|
||||
|
|
@ -77,11 +79,11 @@ public class LongContainer<N extends Number> extends ConcurrentHashMap<String, N
|
|||
return consumerFunc;
|
||||
}
|
||||
|
||||
public Function<String, Long> getValueSupplier() {
|
||||
public Function<Metric, Long> getValueSupplier() {
|
||||
return valueSupplier;
|
||||
}
|
||||
|
||||
public void setValueSupplier(Function<String, Long> valueSupplier) {
|
||||
public void setValueSupplier(Function<Metric, Long> valueSupplier) {
|
||||
this.valueSupplier = valueSupplier;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,8 +46,9 @@ class TimeWindowQuantileTest {
|
|||
while (index < 100) {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
int finalI = i;
|
||||
Assertions.assertDoesNotThrow(() -> quantile.add(finalI));
|
||||
executorService.execute(() ->
|
||||
Assertions.assertDoesNotThrow(() -> quantile.add(finalI)));
|
||||
quantile.add(finalI));
|
||||
}
|
||||
index++;
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import org.apache.dubbo.config.context.ConfigManager;
|
|||
import org.apache.dubbo.metrics.listener.AbstractMetricsListener;
|
||||
import org.apache.dubbo.metrics.listener.MetricsLifeListener;
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public class ConfigCenterEvent extends TimeCounterEvent {
|
|||
|
||||
|
||||
public ConfigCenterEvent(ApplicationModel applicationModel, TypeWrapper typeWrapper) {
|
||||
super(applicationModel,typeWrapper);
|
||||
super(applicationModel, typeWrapper);
|
||||
ScopeBeanFactory beanFactory = applicationModel.getBeanFactory();
|
||||
ConfigCenterMetricsCollector collector;
|
||||
if (!beanFactory.isDestroyed()) {
|
||||
|
|
|
|||
|
|
@ -56,8 +56,9 @@ import static org.apache.dubbo.metrics.model.MetricsCategory.RT;
|
|||
* This collector only enabled when metrics aggregation config is enabled.
|
||||
*/
|
||||
public class AggregateMetricsCollector implements MetricsCollector<RequestEvent> {
|
||||
private int bucketNum;
|
||||
private int timeWindowSeconds;
|
||||
private int bucketNum = DEFAULT_BUCKET_NUM;
|
||||
private int timeWindowSeconds = DEFAULT_TIME_WINDOW_SECONDS;
|
||||
private int qpsTimeWindowMillSeconds = DEFAULT_QPS_TIME_WINDOW_MILL_SECONDS;
|
||||
private final Map<MetricsKeyWrapper, ConcurrentHashMap<MethodMetric, TimeWindowCounter>> methodTypeCounter = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<MethodMetric, TimeWindowQuantile> rt = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<MethodMetric, TimeWindowCounter> qps = new ConcurrentHashMap<>();
|
||||
|
|
@ -65,7 +66,13 @@ public class AggregateMetricsCollector implements MetricsCollector<RequestEvent>
|
|||
private static final Integer DEFAULT_COMPRESSION = 100;
|
||||
private static final Integer DEFAULT_BUCKET_NUM = 10;
|
||||
private static final Integer DEFAULT_TIME_WINDOW_SECONDS = 120;
|
||||
private static final Integer DEFAULT_QPS_TIME_WINDOW_MILL_SECONDS = 3000;
|
||||
private Boolean collectEnabled = null;
|
||||
private boolean enableQps;
|
||||
private boolean enableRtPxx;
|
||||
private boolean enableRt;
|
||||
private boolean enableRequest;
|
||||
|
||||
private final ConcurrentMap<MethodMetric, TimeWindowAggregator> rtAgr = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
|
|
@ -78,8 +85,15 @@ public class AggregateMetricsCollector implements MetricsCollector<RequestEvent>
|
|||
if (optional.isPresent()) {
|
||||
registerListener();
|
||||
AggregationConfig aggregation = optional.get().getAggregation();
|
||||
this.bucketNum = aggregation.getBucketNum() == null ? DEFAULT_BUCKET_NUM : aggregation.getBucketNum();
|
||||
this.timeWindowSeconds = aggregation.getTimeWindowSeconds() == null ? DEFAULT_TIME_WINDOW_SECONDS : aggregation.getTimeWindowSeconds();
|
||||
this.bucketNum = Optional.ofNullable(aggregation.getBucketNum()).orElse(DEFAULT_BUCKET_NUM);
|
||||
this.timeWindowSeconds = Optional.ofNullable(aggregation.getTimeWindowSeconds())
|
||||
.orElse(DEFAULT_TIME_WINDOW_SECONDS);
|
||||
this.qpsTimeWindowMillSeconds = Optional.ofNullable(aggregation.getQpsTimeWindowMillSeconds())
|
||||
.orElse(DEFAULT_QPS_TIME_WINDOW_MILL_SECONDS);
|
||||
this.enableQps = Optional.ofNullable(aggregation.getEnableQps()).orElse(true);
|
||||
this.enableRtPxx = Optional.ofNullable(aggregation.getEnableRtPxx()).orElse(true);
|
||||
this.enableRt = Optional.ofNullable(aggregation.getEnableRt()).orElse(true);
|
||||
this.enableRequest = Optional.ofNullable(aggregation.getEnableRequest()).orElse(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -89,7 +103,7 @@ public class AggregateMetricsCollector implements MetricsCollector<RequestEvent>
|
|||
this.collectEnabled = collectEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isCollectEnabled() {
|
||||
|
|
@ -107,9 +121,12 @@ public class AggregateMetricsCollector implements MetricsCollector<RequestEvent>
|
|||
|
||||
@Override
|
||||
public void onEvent(RequestEvent event) {
|
||||
MethodMetric metric = calcWindowCounter(event, MetricsKey.METRIC_REQUESTS);
|
||||
TimeWindowCounter qpsCounter = ConcurrentHashMapUtils.computeIfAbsent(qps, metric, methodMetric -> new TimeWindowCounter(bucketNum, timeWindowSeconds));
|
||||
qpsCounter.increment();
|
||||
if (enableQps) {
|
||||
MethodMetric metric = calcWindowCounter(event, MetricsKey.METRIC_REQUESTS);
|
||||
TimeWindowCounter qpsCounter = ConcurrentHashMapUtils.computeIfAbsent(qps, metric,
|
||||
methodMetric -> new TimeWindowCounter(bucketNum, qpsTimeWindowMillSeconds));
|
||||
qpsCounter.increment();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -125,23 +142,33 @@ public class AggregateMetricsCollector implements MetricsCollector<RequestEvent>
|
|||
|
||||
@Override
|
||||
public void onEventError(RequestEvent event) {
|
||||
MetricsKey targetKey = MetricsKey.METRIC_REQUESTS_FAILED;
|
||||
Object throwableObj = event.getAttachmentValue(METRIC_THROWABLE);
|
||||
if (throwableObj != null) {
|
||||
targetKey = MetricsSupport.getAggMetricsKey((Throwable) throwableObj);
|
||||
if (enableRequest) {
|
||||
MetricsKey targetKey = MetricsKey.METRIC_REQUESTS_FAILED;
|
||||
Object throwableObj = event.getAttachmentValue(METRIC_THROWABLE);
|
||||
if (throwableObj != null) {
|
||||
targetKey = MetricsSupport.getAggMetricsKey((Throwable) throwableObj);
|
||||
}
|
||||
calcWindowCounter(event, targetKey);
|
||||
}
|
||||
if (enableRt || enableRtPxx) {
|
||||
onRTEvent(event);
|
||||
}
|
||||
calcWindowCounter(event, targetKey);
|
||||
onRTEvent(event);
|
||||
}
|
||||
|
||||
private void onRTEvent(RequestEvent event) {
|
||||
MethodMetric metric = new MethodMetric(applicationModel, event.getAttachmentValue(MetricsConstants.INVOCATION));
|
||||
long responseTime = event.getTimePair().calc();
|
||||
TimeWindowQuantile quantile = ConcurrentHashMapUtils.computeIfAbsent(rt, metric, k -> new TimeWindowQuantile(DEFAULT_COMPRESSION, bucketNum, timeWindowSeconds));
|
||||
quantile.add(responseTime);
|
||||
if (enableRt) {
|
||||
TimeWindowQuantile quantile = ConcurrentHashMapUtils.computeIfAbsent(rt, metric,
|
||||
k -> new TimeWindowQuantile(DEFAULT_COMPRESSION, bucketNum, timeWindowSeconds));
|
||||
quantile.add(responseTime);
|
||||
}
|
||||
|
||||
TimeWindowAggregator timeWindowAggregator = ConcurrentHashMapUtils.computeIfAbsent(rtAgr, metric, methodMetric -> new TimeWindowAggregator(bucketNum, timeWindowSeconds));
|
||||
timeWindowAggregator.add(responseTime);
|
||||
if (enableRtPxx) {
|
||||
TimeWindowAggregator timeWindowAggregator = ConcurrentHashMapUtils.computeIfAbsent(rtAgr, metric,
|
||||
methodMetric -> new TimeWindowAggregator(bucketNum, timeWindowSeconds));
|
||||
timeWindowAggregator.add(responseTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -152,7 +179,8 @@ public class AggregateMetricsCollector implements MetricsCollector<RequestEvent>
|
|||
|
||||
ConcurrentMap<MethodMetric, TimeWindowCounter> counter = methodTypeCounter.computeIfAbsent(metricsKeyWrapper, k -> new ConcurrentHashMap<>());
|
||||
|
||||
TimeWindowCounter windowCounter = ConcurrentHashMapUtils.computeIfAbsent(counter, metric, methodMetric -> new TimeWindowCounter(bucketNum, timeWindowSeconds));
|
||||
TimeWindowCounter windowCounter = ConcurrentHashMapUtils.computeIfAbsent(counter, metric,
|
||||
methodMetric -> new TimeWindowCounter(bucketNum, timeWindowSeconds));
|
||||
windowCounter.increment();
|
||||
return metric;
|
||||
}
|
||||
|
|
@ -160,6 +188,9 @@ public class AggregateMetricsCollector implements MetricsCollector<RequestEvent>
|
|||
@Override
|
||||
public List<MetricSample> collect() {
|
||||
List<MetricSample> list = new ArrayList<>();
|
||||
if (!isCollectEnabled()){
|
||||
return list;
|
||||
}
|
||||
collectRequests(list);
|
||||
collectQPS(list);
|
||||
collectRT(list);
|
||||
|
|
@ -196,7 +227,11 @@ public class AggregateMetricsCollector implements MetricsCollector<RequestEvent>
|
|||
|
||||
private void collectQPS(List<MetricSample> list) {
|
||||
qps.forEach((k, v) -> list.add(new GaugeMetricSample<>(MetricsKey.METRIC_QPS.getNameByType(k.getSide()),
|
||||
MetricsKey.METRIC_QPS.getDescription(), k.getTags(), QPS, v, value -> value.get() / value.bucketLivedSeconds())));
|
||||
MetricsKey.METRIC_QPS.getDescription(), k.getTags(), QPS, v, value -> {
|
||||
double total = value.get();
|
||||
long millSeconds = value.bucketLivedMillSeconds();
|
||||
return total / millSeconds * 1000;
|
||||
})));
|
||||
}
|
||||
|
||||
private void collectRT(List<MetricSample> list) {
|
||||
|
|
@ -211,7 +246,7 @@ public class AggregateMetricsCollector implements MetricsCollector<RequestEvent>
|
|||
MetricsKey.METRIC_RT_P50.getDescription(), k.getTags(), RT, v, value -> value.quantile(0.50)));
|
||||
});
|
||||
|
||||
rtAgr.forEach((k,v)->{
|
||||
rtAgr.forEach((k, v) -> {
|
||||
list.add(new GaugeMetricSample<>(MetricsKey.METRIC_RT_MIN_AGG.getNameByType(k.getSide()),
|
||||
MetricsKey.METRIC_RT_MIN_AGG.getDescription(), k.getTags(), RT, v, value -> v.get().getMin()));
|
||||
|
||||
|
|
|
|||
|
|
@ -50,9 +50,10 @@ public final class DefaultSubDispatcher extends SimpleMetricsEventMulticaster {
|
|||
return event instanceof RequestBeforeEvent;
|
||||
}
|
||||
|
||||
|
||||
private final MetricsPlaceValue dynamicPlaceType = MetricsPlaceValue.of(CommonConstants.CONSUMER, MetricsLevel.METHOD);
|
||||
@Override
|
||||
public void onEvent(RequestBeforeEvent event) {
|
||||
MetricsPlaceValue dynamicPlaceType = MetricsPlaceValue.of(CommonConstants.CONSUMER, MetricsLevel.METHOD);
|
||||
MetricsSupport.increment(METRIC_REQUESTS_SERVICE_UNAVAILABLE_FAILED, dynamicPlaceType, (MethodMetricsCollector) collector, event);
|
||||
}
|
||||
});
|
||||
|
|
@ -81,6 +82,7 @@ public final class DefaultSubDispatcher extends SimpleMetricsEventMulticaster {
|
|||
targetKey = key;
|
||||
} else {
|
||||
targetKey = MetricsSupport.getMetricsKey((Throwable) throwableObj);
|
||||
MetricsSupport.increment(MetricsKey.METRIC_REQUESTS_TOTAL_FAILED, dynamicPlaceType, (MethodMetricsCollector) collector, event);
|
||||
}
|
||||
MetricsSupport.incrAndAddRt(targetKey, dynamicPlaceType, (MethodMetricsCollector) collector, event);
|
||||
})),
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package org.apache.dubbo.metrics.event;
|
||||
|
||||
import org.apache.dubbo.metrics.MetricsConstants;
|
||||
import org.apache.dubbo.metrics.model.MethodMetric;
|
||||
import org.apache.dubbo.metrics.model.MetricsSupport;
|
||||
import org.apache.dubbo.metrics.model.key.MetricsKey;
|
||||
import org.apache.dubbo.metrics.model.key.MetricsLevel;
|
||||
|
|
@ -32,16 +33,18 @@ import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SERVICE;
|
|||
*/
|
||||
public class RequestBeforeEvent extends TimeCounterEvent {
|
||||
|
||||
public RequestBeforeEvent(ApplicationModel source, TypeWrapper typeWrapper) {
|
||||
super(source, typeWrapper);
|
||||
public RequestBeforeEvent(ApplicationModel source, String appName, MetricsDispatcher metricsDispatcher, TypeWrapper typeWrapper) {
|
||||
super(source, appName, metricsDispatcher, typeWrapper);
|
||||
|
||||
}
|
||||
|
||||
public static RequestBeforeEvent toEvent(ApplicationModel applicationModel, Invocation invocation) {
|
||||
RequestBeforeEvent event = new RequestBeforeEvent(applicationModel, new TypeWrapper(MetricsLevel.METHOD, MetricsKey.METRIC_REQUESTS));
|
||||
private static final TypeWrapper REQUEST_BEFORE_EVENT = new TypeWrapper(MetricsLevel.METHOD, MetricsKey.METRIC_REQUESTS);
|
||||
public static RequestBeforeEvent toEvent(ApplicationModel applicationModel, String appName, MetricsDispatcher metricsDispatcher, Invocation invocation, String side) {
|
||||
RequestBeforeEvent event = new RequestBeforeEvent(applicationModel, appName, metricsDispatcher, REQUEST_BEFORE_EVENT);
|
||||
event.putAttachment(ATTACHMENT_KEY_SERVICE, MetricsSupport.getInterfaceName(invocation));
|
||||
event.putAttachment(MetricsConstants.INVOCATION_SIDE, MetricsSupport.getSide(invocation));
|
||||
event.putAttachment(MetricsConstants.INVOCATION_SIDE, side);
|
||||
event.putAttachment(MetricsConstants.INVOCATION, invocation);
|
||||
event.putAttachment(MetricsConstants.METHOD_METRICS, new MethodMetric(applicationModel, invocation));
|
||||
return event;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import org.apache.dubbo.common.beans.factory.ScopeBeanFactory;
|
|||
import org.apache.dubbo.metrics.MetricsConstants;
|
||||
import org.apache.dubbo.metrics.collector.DefaultMetricsCollector;
|
||||
import org.apache.dubbo.metrics.exception.MetricsNeverHappenException;
|
||||
import org.apache.dubbo.metrics.model.MethodMetric;
|
||||
import org.apache.dubbo.metrics.model.MetricsSupport;
|
||||
import org.apache.dubbo.metrics.model.key.MetricsLevel;
|
||||
import org.apache.dubbo.metrics.model.key.TypeWrapper;
|
||||
|
|
@ -38,34 +39,39 @@ import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUEST_BUSIN
|
|||
* Request related events
|
||||
*/
|
||||
public class RequestEvent extends TimeCounterEvent {
|
||||
public RequestEvent(ApplicationModel applicationModel, TypeWrapper typeWrapper) {
|
||||
super(applicationModel,typeWrapper);
|
||||
ScopeBeanFactory beanFactory = applicationModel.getBeanFactory();
|
||||
DefaultMetricsCollector collector;
|
||||
if (!beanFactory.isDestroyed()) {
|
||||
collector = beanFactory.getBean(DefaultMetricsCollector.class);
|
||||
super.setAvailable(collector != null && collector.isCollectEnabled());
|
||||
private static final TypeWrapper TYPE_WRAPPER = new TypeWrapper(MetricsLevel.SERVICE, METRIC_REQUESTS, METRIC_REQUESTS_SUCCEED, METRIC_REQUEST_BUSINESS_FAILED);
|
||||
|
||||
public RequestEvent(ApplicationModel applicationModel, String appName, MetricsDispatcher metricsDispatcher, DefaultMetricsCollector collector, TypeWrapper TYPE_WRAPPER) {
|
||||
super(applicationModel, appName, metricsDispatcher, TYPE_WRAPPER);
|
||||
if (collector == null) {
|
||||
ScopeBeanFactory beanFactory = applicationModel.getBeanFactory();
|
||||
if (!beanFactory.isDestroyed()) {
|
||||
collector = beanFactory.getBean(DefaultMetricsCollector.class);
|
||||
}
|
||||
}
|
||||
super.setAvailable(collector != null && collector.isCollectEnabled());
|
||||
}
|
||||
|
||||
public static RequestEvent toRequestEvent(ApplicationModel applicationModel, Invocation invocation) {
|
||||
RequestEvent requestEvent = new RequestEvent(applicationModel, new TypeWrapper(MetricsLevel.SERVICE, METRIC_REQUESTS, METRIC_REQUESTS_SUCCEED, METRIC_REQUEST_BUSINESS_FAILED)) {
|
||||
@Override
|
||||
public void customAfterPost(Object postResult) {
|
||||
if (postResult == null) {
|
||||
return;
|
||||
}
|
||||
if (!(postResult instanceof Result)) {
|
||||
throw new MetricsNeverHappenException("Result type error, postResult:" + postResult.getClass().getName());
|
||||
}
|
||||
super.putAttachment(METRIC_THROWABLE, ((Result) postResult).getException());
|
||||
}
|
||||
};
|
||||
public static RequestEvent toRequestEvent(ApplicationModel applicationModel, String appName,
|
||||
MetricsDispatcher metricsDispatcher, DefaultMetricsCollector collector,
|
||||
Invocation invocation, String side) {
|
||||
MethodMetric methodMetric = new MethodMetric(applicationModel, invocation);
|
||||
RequestEvent requestEvent = new RequestEvent(applicationModel, appName, metricsDispatcher, collector, TYPE_WRAPPER);
|
||||
requestEvent.putAttachment(MetricsConstants.INVOCATION, invocation);
|
||||
requestEvent.putAttachment(MetricsConstants.METHOD_METRICS, methodMetric);
|
||||
requestEvent.putAttachment(ATTACHMENT_KEY_SERVICE, MetricsSupport.getInterfaceName(invocation));
|
||||
requestEvent.putAttachment(MetricsConstants.INVOCATION_SIDE, MetricsSupport.getSide(invocation));
|
||||
requestEvent.putAttachment(MetricsConstants.INVOCATION_SIDE, side);
|
||||
return requestEvent;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void customAfterPost(Object postResult) {
|
||||
if (postResult == null) {
|
||||
return;
|
||||
}
|
||||
if (!(postResult instanceof Result)) {
|
||||
throw new MetricsNeverHappenException("Result type error, postResult:" + postResult.getClass().getName());
|
||||
}
|
||||
super.putAttachment(METRIC_THROWABLE, ((Result) postResult).getException());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,14 +16,14 @@
|
|||
*/
|
||||
package org.apache.dubbo.metrics.filter;
|
||||
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.config.MetricsConfig;
|
||||
import org.apache.dubbo.metrics.collector.DefaultMetricsCollector;
|
||||
import org.apache.dubbo.metrics.event.MetricsDispatcher;
|
||||
import org.apache.dubbo.metrics.event.MetricsEventBus;
|
||||
import org.apache.dubbo.metrics.event.RequestEvent;
|
||||
import org.apache.dubbo.rpc.BaseFilter;
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.metrics.model.MetricsSupport;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
|
|
@ -37,25 +37,35 @@ import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERR
|
|||
import static org.apache.dubbo.metrics.DefaultConstants.METRIC_FILTER_EVENT;
|
||||
import static org.apache.dubbo.metrics.DefaultConstants.METRIC_THROWABLE;
|
||||
|
||||
@Activate(group = {CONSUMER, PROVIDER}, order = Integer.MIN_VALUE + 100)
|
||||
public class MetricsFilter implements Filter, BaseFilter.Listener, ScopeModelAware {
|
||||
public class MetricsFilter implements ScopeModelAware {
|
||||
|
||||
private ApplicationModel applicationModel;
|
||||
private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(MetricsFilter.class);
|
||||
private boolean rpcMetricsEnable;
|
||||
private String appName;
|
||||
private MetricsDispatcher metricsDispatcher;
|
||||
private DefaultMetricsCollector defaultMetricsCollector;
|
||||
|
||||
@Override
|
||||
public void setApplicationModel(ApplicationModel applicationModel) {
|
||||
this.applicationModel = applicationModel;
|
||||
this.rpcMetricsEnable = applicationModel.getApplicationConfigManager().getMetrics().map(MetricsConfig::getEnableRpc).orElse(true);
|
||||
this.appName = applicationModel.tryGetApplicationName();
|
||||
this.metricsDispatcher = applicationModel.getBeanFactory().getBean(MetricsDispatcher.class);
|
||||
this.defaultMetricsCollector = applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
return invoke(invoker, invocation, PROVIDER.equals(MetricsSupport.getSide(invocation)));
|
||||
}
|
||||
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation, boolean isProvider) throws RpcException {
|
||||
if (rpcMetricsEnable) {
|
||||
try {
|
||||
RequestEvent requestEvent = RequestEvent.toRequestEvent(applicationModel, invocation);
|
||||
MetricsEventBus.before(requestEvent, () -> invocation.put(METRIC_FILTER_EVENT, requestEvent));
|
||||
RequestEvent requestEvent = RequestEvent.toRequestEvent(applicationModel, appName, metricsDispatcher,
|
||||
defaultMetricsCollector, invocation, isProvider ? PROVIDER : CONSUMER);
|
||||
MetricsEventBus.before(requestEvent);
|
||||
invocation.put(METRIC_FILTER_EVENT, requestEvent);
|
||||
} catch (Throwable t) {
|
||||
LOGGER.warn(INTERNAL_ERROR, "", "", "Error occurred when invoke.", t);
|
||||
}
|
||||
|
|
@ -63,8 +73,11 @@ public class MetricsFilter implements Filter, BaseFilter.Listener, ScopeModelAwa
|
|||
return invoker.invoke(invocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
|
||||
onResponse(result, invoker, invocation, PROVIDER.equals(MetricsSupport.getSide(invocation)));
|
||||
}
|
||||
|
||||
public void onResponse(Result result, Invoker<?> invoker, Invocation invocation, boolean isProvider) {
|
||||
Object eventObj = invocation.get(METRIC_FILTER_EVENT);
|
||||
if (eventObj != null) {
|
||||
try {
|
||||
|
|
@ -75,8 +88,11 @@ public class MetricsFilter implements Filter, BaseFilter.Listener, ScopeModelAwa
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
|
||||
onError(t, invoker, invocation, PROVIDER.equals(MetricsSupport.getSide(invocation)));
|
||||
}
|
||||
|
||||
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation, boolean isProvider) {
|
||||
Object eventObj = invocation.get(METRIC_FILTER_EVENT);
|
||||
if (eventObj != null) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.filter;
|
||||
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.rpc.BaseFilter;
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
|
||||
|
||||
@Activate(group = {PROVIDER}, order = Integer.MIN_VALUE + 100)
|
||||
public class MetricsProviderFilter extends MetricsFilter implements Filter, BaseFilter.Listener {
|
||||
public MetricsProviderFilter() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
return super.invoke(invoker, invocation, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
|
||||
super.onResponse(appResponse, invoker, invocation, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
|
||||
super.onError(t, invoker, invocation, true);
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ package org.apache.dubbo.rpc.cluster.filter.support;
|
|||
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.metrics.collector.DefaultMetricsCollector;
|
||||
import org.apache.dubbo.metrics.event.MetricsDispatcher;
|
||||
import org.apache.dubbo.metrics.event.MetricsEventBus;
|
||||
import org.apache.dubbo.metrics.event.RequestBeforeEvent;
|
||||
import org.apache.dubbo.rpc.BaseFilter;
|
||||
|
|
@ -32,17 +33,22 @@ import org.apache.dubbo.rpc.model.ApplicationModel;
|
|||
import org.apache.dubbo.rpc.model.ScopeModelAware;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
|
||||
|
||||
@Activate(group = CONSUMER, onClass = "org.apache.dubbo.metrics.collector.DefaultMetricsCollector")
|
||||
public class MetricsClusterFilter implements ClusterFilter, BaseFilter.Listener, ScopeModelAware {
|
||||
|
||||
private ApplicationModel applicationModel;
|
||||
private DefaultMetricsCollector collector;
|
||||
private String appName;
|
||||
private MetricsDispatcher metricsDispatcher;
|
||||
|
||||
@Override
|
||||
public void setApplicationModel(ApplicationModel applicationModel) {
|
||||
this.applicationModel = applicationModel;
|
||||
this.collector = applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class);
|
||||
this.appName = applicationModel.tryGetApplicationName();
|
||||
this.metricsDispatcher = applicationModel.getBeanFactory().getBean(MetricsDispatcher.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -67,7 +73,7 @@ public class MetricsClusterFilter implements ClusterFilter, BaseFilter.Listener,
|
|||
if (t instanceof RpcException) {
|
||||
RpcException e = (RpcException) t;
|
||||
if (e.isForbidden()) {
|
||||
MetricsEventBus.publish(RequestBeforeEvent.toEvent(applicationModel, invocation));
|
||||
MetricsEventBus.publish(RequestBeforeEvent.toEvent(applicationModel, appName, metricsDispatcher, invocation, CONSUMER_SIDE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
metrics-beta=org.apache.dubbo.metrics.filter.MetricsFilter
|
||||
metrics-provider=org.apache.dubbo.metrics.filter.MetricsProviderFilter
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ import org.apache.dubbo.rpc.RpcContext;
|
|||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
|
@ -141,8 +142,8 @@ class AggregateMetricsCollectorTest {
|
|||
@Test
|
||||
void testListener() {
|
||||
AggregateMetricsCollector metricsCollector = new AggregateMetricsCollector(applicationModel);
|
||||
RequestEvent event = RequestEvent.toRequestEvent(applicationModel, invocation);
|
||||
RequestBeforeEvent beforeEvent = new RequestBeforeEvent(applicationModel, new TypeWrapper(MetricsLevel.METHOD, MetricsKey.METRIC_REQUESTS));
|
||||
RequestEvent event = RequestEvent.toRequestEvent(applicationModel, null, null, null, invocation, MetricsSupport.getSide(invocation));
|
||||
RequestBeforeEvent beforeEvent = new RequestBeforeEvent(applicationModel, null, null, new TypeWrapper(MetricsLevel.METHOD, MetricsKey.METRIC_REQUESTS));
|
||||
Assertions.assertTrue(metricsCollector.isSupport(event));
|
||||
Assertions.assertFalse(metricsCollector.isSupport(beforeEvent));
|
||||
}
|
||||
|
|
@ -248,7 +249,7 @@ class AggregateMetricsCollectorTest {
|
|||
rtList.add(30L);
|
||||
|
||||
for (Long requestTime: rtList) {
|
||||
RequestEvent requestEvent = RequestEvent.toRequestEvent(applicationModel, invocation);
|
||||
RequestEvent requestEvent = RequestEvent.toRequestEvent(applicationModel, null, null, null, invocation, MetricsSupport.getSide(invocation));
|
||||
TestRequestEvent testRequestEvent = new TestRequestEvent(requestEvent.getSource(), requestEvent.getTypeWrapper());
|
||||
testRequestEvent.putAttachment(MetricsConstants.INVOCATION, invocation);
|
||||
testRequestEvent.putAttachment(ATTACHMENT_KEY_SERVICE, MetricsSupport.getInterfaceName(invocation));
|
||||
|
|
@ -298,7 +299,7 @@ class AggregateMetricsCollectorTest {
|
|||
double manualP99 = requestTimes.get((int) Math.round(p99Index));
|
||||
|
||||
for (Long requestTime : requestTimes) {
|
||||
RequestEvent requestEvent = RequestEvent.toRequestEvent(applicationModel, invocation);
|
||||
RequestEvent requestEvent = RequestEvent.toRequestEvent(applicationModel, null, null, null, invocation, MetricsSupport.getSide(invocation));
|
||||
TestRequestEvent testRequestEvent = new TestRequestEvent(requestEvent.getSource(), requestEvent.getTypeWrapper());
|
||||
testRequestEvent.putAttachment(MetricsConstants.INVOCATION, invocation);
|
||||
testRequestEvent.putAttachment(ATTACHMENT_KEY_SERVICE, MetricsSupport.getInterfaceName(invocation));
|
||||
|
|
@ -345,7 +346,7 @@ class AggregateMetricsCollectorTest {
|
|||
private long rt;
|
||||
|
||||
public TestRequestEvent(ApplicationModel applicationModel, TypeWrapper typeWrapper) {
|
||||
super(applicationModel, typeWrapper);
|
||||
super(applicationModel, null, null, null, typeWrapper);
|
||||
}
|
||||
|
||||
public void setRt(long rt) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import org.apache.dubbo.metrics.event.MetricsDispatcher;
|
|||
import org.apache.dubbo.metrics.event.RequestBeforeEvent;
|
||||
import org.apache.dubbo.metrics.event.RequestEvent;
|
||||
import org.apache.dubbo.metrics.filter.MetricsFilter;
|
||||
import org.apache.dubbo.metrics.model.MetricsSupport;
|
||||
import org.apache.dubbo.metrics.model.ServiceKeyMetric;
|
||||
import org.apache.dubbo.metrics.model.key.MetricsKey;
|
||||
import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper;
|
||||
|
|
@ -112,8 +113,8 @@ class DefaultCollectorTest {
|
|||
@Test
|
||||
void testListener() {
|
||||
DefaultMetricsCollector metricsCollector = new DefaultMetricsCollector(applicationModel);
|
||||
RequestEvent event = RequestEvent.toRequestEvent(applicationModel, invocation);
|
||||
RequestBeforeEvent beforeEvent = new RequestBeforeEvent(applicationModel, new TypeWrapper(MetricsLevel.METHOD, MetricsKey.METRIC_REQUESTS));
|
||||
RequestEvent event = RequestEvent.toRequestEvent(applicationModel, null, null, null, invocation, MetricsSupport.getSide(invocation));
|
||||
RequestBeforeEvent beforeEvent = new RequestBeforeEvent(applicationModel, null, null, new TypeWrapper(MetricsLevel.METHOD, MetricsKey.METRIC_REQUESTS));
|
||||
Assertions.assertTrue(metricsCollector.isSupport(event));
|
||||
Assertions.assertTrue(metricsCollector.isSupport(beforeEvent));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ class MetricsFilterTest {
|
|||
private static final String INTERFACE_NAME = "org.apache.dubbo.MockInterface";
|
||||
private static final String METHOD_NAME = "mockMethod";
|
||||
private static final String GROUP = "mockGroup";
|
||||
private static final String VERSION = "1.0.0";
|
||||
private static final String VERSION = "1.0.0_BETA";
|
||||
private String side;
|
||||
|
||||
private AtomicBoolean initApplication = new AtomicBoolean(false);
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ class MethodMetricTest {
|
|||
@Test
|
||||
void test() {
|
||||
MethodMetric metric = new MethodMetric(applicationModel, invocation);
|
||||
Assertions.assertEquals(metric.getInterfaceName(), interfaceName);
|
||||
Assertions.assertEquals(metric.getServiceKey(), interfaceName);
|
||||
Assertions.assertEquals(metric.getMethodName(), methodName);
|
||||
Assertions.assertEquals(metric.getGroup(), group);
|
||||
Assertions.assertEquals(metric.getVersion(), version);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ import static org.apache.dubbo.metrics.model.key.MetricsKey.STORE_PROVIDER_METAD
|
|||
*/
|
||||
public class MetadataEvent extends TimeCounterEvent {
|
||||
public MetadataEvent(ApplicationModel applicationModel, TypeWrapper typeWrapper) {
|
||||
super(applicationModel,typeWrapper);
|
||||
super(applicationModel, typeWrapper);
|
||||
ScopeBeanFactory beanFactory = applicationModel.getBeanFactory();
|
||||
MetadataMetricsCollector collector;
|
||||
if (!beanFactory.isDestroyed()) {
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ class MetadataMetricsCollectorTest {
|
|||
@Test
|
||||
void testListener() {
|
||||
MetadataEvent event = MetadataEvent.toPushEvent(applicationModel);
|
||||
MetricsEvent otherEvent = new MetricsEvent(applicationModel,null){
|
||||
MetricsEvent otherEvent = new MetricsEvent(applicationModel,null, null, null){
|
||||
};
|
||||
Assertions.assertTrue(collector.isSupport(event));
|
||||
Assertions.assertFalse(collector.isSupport(otherEvent));
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import org.apache.dubbo.metrics.data.ApplicationStatComposite;
|
|||
import org.apache.dubbo.metrics.data.BaseStatComposite;
|
||||
import org.apache.dubbo.metrics.data.RtStatComposite;
|
||||
import org.apache.dubbo.metrics.data.ServiceStatComposite;
|
||||
import org.apache.dubbo.metrics.model.ApplicationMetric;
|
||||
import org.apache.dubbo.metrics.model.Metric;
|
||||
import org.apache.dubbo.metrics.model.container.LongContainer;
|
||||
import org.apache.dubbo.metrics.model.key.MetricsKey;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
|
|
@ -80,7 +82,7 @@ public class MetadataStatCompositeTest {
|
|||
Assertions.assertEquals(v.get(), new AtomicLong(0L).get())));
|
||||
statComposite.getRtStatComposite().getRtStats().forEach(rtContainer ->
|
||||
{
|
||||
for (Map.Entry<String, ? extends Number> entry : rtContainer.entrySet()) {
|
||||
for (Map.Entry<Metric, ? extends Number> entry : rtContainer.entrySet()) {
|
||||
Assertions.assertEquals(0L, rtContainer.getValueSupplier().apply(entry.getKey()));
|
||||
}
|
||||
});
|
||||
|
|
@ -95,9 +97,9 @@ public class MetadataStatCompositeTest {
|
|||
|
||||
@Test
|
||||
void testCalcRt() {
|
||||
statComposite.calcApplicationRt( OP_TYPE_SUBSCRIBE.getType(), 10L);
|
||||
statComposite.calcApplicationRt(OP_TYPE_SUBSCRIBE.getType(), 10L);
|
||||
Assertions.assertTrue(statComposite.getRtStatComposite().getRtStats().stream().anyMatch(longContainer -> longContainer.specifyType(OP_TYPE_SUBSCRIBE.getType())));
|
||||
Optional<LongContainer<? extends Number>> subContainer = statComposite.getRtStatComposite().getRtStats().stream().filter(longContainer -> longContainer.specifyType(OP_TYPE_SUBSCRIBE.getType())).findFirst();
|
||||
subContainer.ifPresent(v -> Assertions.assertEquals(10L, v.get(applicationModel.getApplicationName()).longValue()));
|
||||
subContainer.ifPresent(v -> Assertions.assertEquals(10L, v.get(new ApplicationMetric(applicationModel)).longValue()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SIZE;
|
|||
*/
|
||||
public class RegistryEvent extends TimeCounterEvent {
|
||||
public RegistryEvent(ApplicationModel applicationModel, TypeWrapper typeWrapper) {
|
||||
super(applicationModel,typeWrapper);
|
||||
super(applicationModel, typeWrapper);
|
||||
ScopeBeanFactory beanFactory = getSource().getBeanFactory();
|
||||
RegistryMetricsCollector collector;
|
||||
if (!beanFactory.isDestroyed()) {
|
||||
|
|
@ -47,18 +47,21 @@ public class RegistryEvent extends TimeCounterEvent {
|
|||
}
|
||||
}
|
||||
|
||||
private static final TypeWrapper REGISTER_EVENT = new TypeWrapper(MetricsLevel.APP, MetricsKey.REGISTER_METRIC_REQUESTS, MetricsKey.REGISTER_METRIC_REQUESTS_SUCCEED, MetricsKey.REGISTER_METRIC_REQUESTS_FAILED);
|
||||
public static RegistryEvent toRegisterEvent(ApplicationModel applicationModel) {
|
||||
return new RegistryEvent(applicationModel, new TypeWrapper(MetricsLevel.APP, MetricsKey.REGISTER_METRIC_REQUESTS, MetricsKey.REGISTER_METRIC_REQUESTS_SUCCEED, MetricsKey.REGISTER_METRIC_REQUESTS_FAILED));
|
||||
return new RegistryEvent(applicationModel, REGISTER_EVENT);
|
||||
}
|
||||
|
||||
|
||||
private static final TypeWrapper SUBSCRIBE_EVENT = new TypeWrapper(MetricsLevel.APP, MetricsKey.SUBSCRIBE_METRIC_NUM, MetricsKey.SUBSCRIBE_METRIC_NUM_SUCCEED, MetricsKey.SUBSCRIBE_METRIC_NUM_FAILED);
|
||||
public static RegistryEvent toSubscribeEvent(ApplicationModel applicationModel) {
|
||||
return new RegistryEvent(applicationModel, new TypeWrapper(MetricsLevel.APP, MetricsKey.SUBSCRIBE_METRIC_NUM, MetricsKey.SUBSCRIBE_METRIC_NUM_SUCCEED, MetricsKey.SUBSCRIBE_METRIC_NUM_FAILED));
|
||||
return new RegistryEvent(applicationModel, SUBSCRIBE_EVENT);
|
||||
}
|
||||
|
||||
|
||||
private static final TypeWrapper NOTIFY_EVENT = new TypeWrapper(MetricsLevel.APP, MetricsKey.NOTIFY_METRIC_REQUESTS, MetricsKey.NOTIFY_METRIC_NUM_LAST, (MetricsKey) null);
|
||||
public static RegistryEvent toNotifyEvent(ApplicationModel applicationModel) {
|
||||
return new RegistryEvent(applicationModel, new TypeWrapper(MetricsLevel.APP, MetricsKey.NOTIFY_METRIC_REQUESTS, MetricsKey.NOTIFY_METRIC_NUM_LAST, (MetricsKey) null)) {
|
||||
return new RegistryEvent(applicationModel, NOTIFY_EVENT) {
|
||||
@Override
|
||||
public void customAfterPost(Object postResult) {
|
||||
super.putAttachment(ATTACHMENT_KEY_LAST_NUM_MAP, postResult);
|
||||
|
|
@ -66,21 +69,24 @@ public class RegistryEvent extends TimeCounterEvent {
|
|||
};
|
||||
}
|
||||
|
||||
private static final TypeWrapper RS_EVENT = new TypeWrapper(MetricsLevel.SERVICE, MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS, MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_SUCCEED, MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_FAILED);
|
||||
public static RegistryEvent toRsEvent(ApplicationModel applicationModel, String serviceKey, int size) {
|
||||
RegistryEvent ddEvent = new RegistryEvent(applicationModel, new TypeWrapper(MetricsLevel.SERVICE, MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS, MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_SUCCEED, MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_FAILED));
|
||||
RegistryEvent ddEvent = new RegistryEvent(applicationModel, RS_EVENT);
|
||||
ddEvent.putAttachment(ATTACHMENT_KEY_SERVICE, serviceKey);
|
||||
ddEvent.putAttachment(ATTACHMENT_KEY_SIZE, size);
|
||||
return ddEvent;
|
||||
}
|
||||
|
||||
private static final TypeWrapper SS_EVENT = new TypeWrapper(MetricsLevel.SERVICE, MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM, MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_SUCCEED, MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_FAILED);
|
||||
public static RegistryEvent toSsEvent(ApplicationModel applicationModel, String serviceKey) {
|
||||
RegistryEvent ddEvent = new RegistryEvent(applicationModel, new TypeWrapper(MetricsLevel.SERVICE, MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM, MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_SUCCEED, MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_FAILED));
|
||||
RegistryEvent ddEvent = new RegistryEvent(applicationModel, SS_EVENT);
|
||||
ddEvent.putAttachment(ATTACHMENT_KEY_SERVICE, serviceKey);
|
||||
return ddEvent;
|
||||
}
|
||||
|
||||
private static final TypeWrapper DIRECTORY_EVENT = new TypeWrapper(MetricsLevel.APP, MetricsKey.DIRECTORY_METRIC_NUM_VALID, null, null);
|
||||
public static RegistryEvent refreshDirectoryEvent(ApplicationModel applicationModel, Map<MetricsKey, Map<String, Integer>> summaryMap) {
|
||||
RegistryEvent registryEvent = new RegistryEvent(applicationModel, new TypeWrapper(MetricsLevel.APP, MetricsKey.DIRECTORY_METRIC_NUM_VALID, null, null));
|
||||
RegistryEvent registryEvent = new RegistryEvent(applicationModel, DIRECTORY_EVENT);
|
||||
registryEvent.putAttachment(ATTACHMENT_DIRECTORY_MAP, summaryMap);
|
||||
return registryEvent;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ public final class RegistrySubDispatcher extends SimpleMetricsEventMulticaster {
|
|||
MetricsCat APPLICATION_NOTIFY_FINISH = new MetricsCat(MetricsKey.NOTIFY_METRIC_NUM_LAST,
|
||||
(key, placeType, collector) -> AbstractMetricsKeyListener.onFinish(key,
|
||||
event -> {
|
||||
collector.addRt(event.appName(), placeType.getType(), event.getTimePair().calc());
|
||||
collector.addServiceRt(event.appName(), placeType.getType(), event.getTimePair().calc());
|
||||
Map<String, Integer> lastNumMap = Collections.unmodifiableMap(event.getAttachmentValue(ATTACHMENT_KEY_LAST_NUM_MAP));
|
||||
lastNumMap.forEach(
|
||||
(k, v) -> collector.setNum(new MetricsKeyWrapper(key, OP_TYPE_NOTIFY), k, v));
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import org.apache.dubbo.metrics.model.sample.MetricSample;
|
|||
import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector;
|
||||
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;
|
||||
|
|
@ -64,8 +65,8 @@ class RegistryMetricsSampleTest {
|
|||
RegistryMetricsCollector collector = new RegistryMetricsCollector(applicationModel);
|
||||
collector.setCollectEnabled(true);
|
||||
String applicationName = applicationModel.getApplicationName();
|
||||
collector.addRt(applicationName, OP_TYPE_REGISTER.getType(), 10L);
|
||||
collector.addRt(applicationName, OP_TYPE_REGISTER.getType(), 0L);
|
||||
collector.addServiceRt(applicationName, OP_TYPE_REGISTER.getType(), 10L);
|
||||
collector.addServiceRt(applicationName, OP_TYPE_REGISTER.getType(), 0L);
|
||||
|
||||
List<MetricSample> samples = collector.collect();
|
||||
for (MetricSample sample : samples) {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ 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.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
|
@ -35,7 +36,11 @@ import org.junit.jupiter.api.Test;
|
|||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import org.apache.dubbo.metrics.data.ApplicationStatComposite;
|
|||
import org.apache.dubbo.metrics.data.BaseStatComposite;
|
||||
import org.apache.dubbo.metrics.data.RtStatComposite;
|
||||
import org.apache.dubbo.metrics.data.ServiceStatComposite;
|
||||
import org.apache.dubbo.metrics.model.ApplicationMetric;
|
||||
import org.apache.dubbo.metrics.model.Metric;
|
||||
import org.apache.dubbo.metrics.model.MetricsCategory;
|
||||
import org.apache.dubbo.metrics.model.container.LongContainer;
|
||||
import org.apache.dubbo.metrics.model.sample.GaugeMetricSample;
|
||||
|
|
@ -92,7 +94,7 @@ public class RegistryStatCompositeTest {
|
|||
Assertions.assertEquals(v.get(), new AtomicLong(0L).get())));
|
||||
statComposite.getRtStatComposite().getRtStats().forEach(rtContainer ->
|
||||
{
|
||||
for (Map.Entry<String, ? extends Number> entry : rtContainer.entrySet()) {
|
||||
for (Map.Entry<Metric, ? extends Number> entry : rtContainer.entrySet()) {
|
||||
Assertions.assertEquals(0L, rtContainer.getValueSupplier().apply(entry.getKey()));
|
||||
}
|
||||
});
|
||||
|
|
@ -109,7 +111,7 @@ public class RegistryStatCompositeTest {
|
|||
statComposite.calcApplicationRt(OP_TYPE_NOTIFY.getType(), 10L);
|
||||
Assertions.assertTrue(statComposite.getRtStatComposite().getRtStats().stream().anyMatch(longContainer -> longContainer.specifyType(OP_TYPE_NOTIFY.getType())));
|
||||
Optional<LongContainer<? extends Number>> subContainer = statComposite.getRtStatComposite().getRtStats().stream().filter(longContainer -> longContainer.specifyType(OP_TYPE_NOTIFY.getType())).findFirst();
|
||||
subContainer.ifPresent(v -> Assertions.assertEquals(10L, v.get(applicationName).longValue()));
|
||||
subContainer.ifPresent(v -> Assertions.assertEquals(10L, v.get(new ApplicationMetric(applicationModel)).longValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import org.apache.dubbo.common.json.GsonUtils;
|
|||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.serialize.Serialization;
|
||||
import org.apache.dubbo.common.utils.ClassUtils;
|
||||
import org.apache.dubbo.common.utils.PojoUtils;
|
||||
import org.apache.dubbo.common.utils.ReflectUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
|
|
@ -40,7 +41,9 @@ import org.apache.dubbo.rpc.RpcContext;
|
|||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.rpc.model.MethodDescriptor;
|
||||
import org.apache.dubbo.rpc.model.ScopeModelAware;
|
||||
import org.apache.dubbo.rpc.model.ServiceModel;
|
||||
import org.apache.dubbo.rpc.service.GenericException;
|
||||
import org.apache.dubbo.rpc.service.GenericService;
|
||||
import org.apache.dubbo.rpc.support.ProtocolUtils;
|
||||
|
|
@ -48,6 +51,10 @@ import org.apache.dubbo.rpc.support.ProtocolUtils;
|
|||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.$INVOKE;
|
||||
|
|
@ -67,6 +74,8 @@ public class GenericFilter implements Filter, Filter.Listener, ScopeModelAware {
|
|||
|
||||
private ApplicationModel applicationModel;
|
||||
|
||||
private final Map<ClassLoader, Map<String, Class<?>>> classCache = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void setApplicationModel(ApplicationModel applicationModel) {
|
||||
this.applicationModel = applicationModel;
|
||||
|
|
@ -82,7 +91,7 @@ public class GenericFilter implements Filter, Filter.Listener, ScopeModelAware {
|
|||
String[] types = (String[]) inv.getArguments()[1];
|
||||
Object[] args = (Object[]) inv.getArguments()[2];
|
||||
try {
|
||||
Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), name, types);
|
||||
Method method = findMethodByMethodSignature(invoker.getInterface(), name, types, inv.getServiceModel());
|
||||
Class<?>[] params = method.getParameterTypes();
|
||||
if (args == null) {
|
||||
args = new Object[params.length];
|
||||
|
|
@ -219,6 +228,54 @@ public class GenericFilter implements Filter, Filter.Listener, ScopeModelAware {
|
|||
return generic;
|
||||
}
|
||||
|
||||
public Method findMethodByMethodSignature(Class<?> clazz, String methodName, String[] parameterTypes, ServiceModel serviceModel)
|
||||
throws NoSuchMethodException, ClassNotFoundException {
|
||||
Method method;
|
||||
if (parameterTypes == null) {
|
||||
List<Method> finded = new ArrayList<>();
|
||||
for (Method m : clazz.getMethods()) {
|
||||
if (m.getName().equals(methodName)) {
|
||||
finded.add(m);
|
||||
}
|
||||
}
|
||||
if (finded.isEmpty()) {
|
||||
throw new NoSuchMethodException("No such method " + methodName + " in class " + clazz);
|
||||
}
|
||||
if (finded.size() > 1) {
|
||||
String msg = String.format("Not unique method for method name(%s) in class(%s), find %d methods.",
|
||||
methodName, clazz.getName(), finded.size());
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
method = finded.get(0);
|
||||
} else {
|
||||
Class<?>[] types = new Class<?>[parameterTypes.length];
|
||||
for (int i = 0; i < parameterTypes.length; i++) {
|
||||
ClassLoader classLoader = ClassUtils.getClassLoader();
|
||||
Map<String, Class<?>> cacheMap = classCache.get(classLoader);
|
||||
if (cacheMap == null) {
|
||||
cacheMap = new ConcurrentHashMap<>();
|
||||
classCache.putIfAbsent(classLoader, cacheMap);
|
||||
cacheMap = classCache.get(classLoader);
|
||||
}
|
||||
types[i] = cacheMap.get(parameterTypes[i]);
|
||||
if (types[i] == null) {
|
||||
types[i] = ReflectUtils.name2class(parameterTypes[i]);
|
||||
cacheMap.put(parameterTypes[i], types[i]);
|
||||
}
|
||||
}
|
||||
if (serviceModel != null) {
|
||||
MethodDescriptor methodDescriptor = serviceModel.getServiceModel().getMethod(methodName, types);
|
||||
if (methodDescriptor == null) {
|
||||
throw new NoSuchMethodException("No such method " + methodName + " in class " + clazz);
|
||||
}
|
||||
method = methodDescriptor.getMethod();
|
||||
} else {
|
||||
method = clazz.getMethod(methodName, types);
|
||||
}
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Result appResponse, Invoker<?> invoker, Invocation inv) {
|
||||
if ((inv.getMethodName().equals($INVOKE) || inv.getMethodName().equals($INVOKE_ASYNC))
|
||||
|
|
|
|||
|
|
@ -54,10 +54,13 @@ public class ProviderAppStateRouter<T> extends ListenableStateRouter<T> {
|
|||
String providerApplication = url.getRemoteApplication();
|
||||
|
||||
// provider application is empty or equals with the current application
|
||||
if (isEmpty(providerApplication) || providerApplication.equals(currentApplication)) {
|
||||
if (isEmpty(providerApplication)) {
|
||||
logger.warn(CLUSTER_TAG_ROUTE_EMPTY, "condition router get providerApplication is empty, will not subscribe to provider app rules.", "", "");
|
||||
return;
|
||||
}
|
||||
if (providerApplication.equals(currentApplication)) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (!providerApplication.equals(application)) {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ package org.apache.dubbo.qos.api;
|
|||
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public class CommandContext {
|
||||
|
||||
private String commandName;
|
||||
|
|
@ -98,4 +102,17 @@ public class CommandContext {
|
|||
public boolean isAllowAnonymousAccess(){
|
||||
return this.qosConfiguration.isAllowAnonymousAccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CommandContext{" +
|
||||
"commandName='" + commandName + '\'' +
|
||||
", args=" + Arrays.toString(args) +
|
||||
", remote=" + Optional.ofNullable(remote).map(Channel::remoteAddress).map(Objects::toString).orElse("unknown") +
|
||||
", local=" + Optional.ofNullable(remote).map(Channel::localAddress).map(Objects::toString).orElse("unknown") +
|
||||
", isHttp=" + isHttp +
|
||||
", httpCode=" + httpCode +
|
||||
", qosConfiguration=" + qosConfiguration +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -555,6 +555,16 @@ public class InstanceAddressURL extends URL {
|
|||
*/
|
||||
@Override
|
||||
public String getAnyMethodParameter(String key) {
|
||||
if (consumerParamFirst(key)) {
|
||||
URL consumerUrl = RpcContext.getServiceContext().getConsumerUrl();
|
||||
if (consumerUrl != null) {
|
||||
String v = consumerUrl.getAnyMethodParameter(key);
|
||||
if (StringUtils.isNotEmpty(v)) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String suffix = "." + key;
|
||||
String protocolServiceKey = getProtocolServiceKey();
|
||||
if (StringUtils.isNotEmpty(protocolServiceKey)) {
|
||||
|
|
|
|||
|
|
@ -65,8 +65,6 @@ import static org.apache.dubbo.common.constants.RegistryConstants.ENABLE_EMPTY_P
|
|||
import static org.apache.dubbo.metadata.RevisionResolver.EMPTY_REVISION;
|
||||
import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getExportedServicesRevision;
|
||||
|
||||
;
|
||||
|
||||
/**
|
||||
* TODO, refactor to move revision-metadata mapping to ServiceDiscovery. Instances should have already been mapped with metadata when reached here.
|
||||
* <p>
|
||||
|
|
@ -93,7 +91,6 @@ public class ServiceInstancesChangedListener {
|
|||
private final Set<ServiceInstanceNotificationCustomizer> serviceInstanceNotificationCustomizers;
|
||||
private final ApplicationModel applicationModel;
|
||||
|
||||
|
||||
public ServiceInstancesChangedListener(Set<String> serviceNames, ServiceDiscovery serviceDiscovery) {
|
||||
this.serviceNames = serviceNames;
|
||||
this.serviceDiscovery = serviceDiscovery;
|
||||
|
|
@ -209,11 +206,7 @@ public class ServiceInstancesChangedListener {
|
|||
|
||||
Map<Integer, Map<Set<String>, Object>> portToRevisions = protocolRevisionsToUrls.computeIfAbsent(serviceInfo.getProtocol(), k -> new HashMap<>());
|
||||
Map<Set<String>, Object> revisionsToUrls = portToRevisions.computeIfAbsent(serviceInfo.getPort(), k -> new HashMap<>());
|
||||
Object urls = revisionsToUrls.get(revisions);
|
||||
if (urls == null) {
|
||||
urls = getServiceUrlsCache(revisionToInstances, revisions, serviceInfo.getProtocol(), serviceInfo.getPort());
|
||||
revisionsToUrls.put(revisions, urls);
|
||||
}
|
||||
Object urls = revisionsToUrls.computeIfAbsent(revisions, k -> getServiceUrlsCache(revisionToInstances, revisions, serviceInfo.getProtocol(), serviceInfo.getPort()));
|
||||
|
||||
List<ProtocolServiceKeyWithUrls> list = newServiceUrls.computeIfAbsent(serviceInfo.getPath(), k -> new LinkedList<>());
|
||||
list.add(new ProtocolServiceKeyWithUrls(serviceInfo.getProtocolServiceKey(), (List<URL>) urls));
|
||||
|
|
@ -255,7 +248,7 @@ public class ServiceInstancesChangedListener {
|
|||
notifyListeners.removeIf(listener -> listener.getNotifyListener().equals(notifyListener));
|
||||
|
||||
// ServiceKey has no listener, remove set
|
||||
if (notifyListeners.size() == 0) {
|
||||
if (notifyListeners.isEmpty()) {
|
||||
this.listeners.remove(serviceKey);
|
||||
}
|
||||
}
|
||||
|
|
@ -478,7 +471,7 @@ public class ServiceInstancesChangedListener {
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getClass(), getServiceNames(), listeners);
|
||||
return Objects.hash(getClass(), getServiceNames());
|
||||
}
|
||||
|
||||
protected class AddressRefreshRetryTask implements Runnable {
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
|
|||
import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_INIT_SERIALIZATION_OPTIMIZER;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_REFER_INVOKER;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNSUPPORTED;
|
||||
|
|
@ -114,7 +113,6 @@ public class RegistryDirectory<T> extends DynamicDirectory<T> {
|
|||
super(serviceType, url);
|
||||
moduleModel = getModuleModel(url.getScopeModel());
|
||||
consumerConfigurationListener = getConsumerConfigurationListener(moduleModel);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -132,7 +130,7 @@ public class RegistryDirectory<T> extends DynamicDirectory<T> {
|
|||
}
|
||||
|
||||
ApplicationModel applicationModel = url.getApplicationModel();
|
||||
MetricsEventBus.post(RegistryEvent.toSubscribeEvent(applicationModel),() ->
|
||||
MetricsEventBus.post(RegistryEvent.toSubscribeEvent(applicationModel), () ->
|
||||
{
|
||||
super.subscribe(url);
|
||||
return null;
|
||||
|
|
@ -504,7 +502,7 @@ public class RegistryDirectory<T> extends DynamicDirectory<T> {
|
|||
|
||||
// FIXME, kept for mock
|
||||
if (providerUrl.hasParameter(MOCK_KEY) || providerUrl.getAnyMethodParameter(MOCK_KEY) != null) {
|
||||
providerUrl = providerUrl.removeParameter(TAG_KEY);
|
||||
providerUrl = providerUrl.removeParameter(MOCK_KEY);
|
||||
}
|
||||
|
||||
if ((providerUrl.getPath() == null || providerUrl.getPath()
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ public class ZookeeperRegistry extends CacheableFailbackRegistry {
|
|||
public void doRegister(URL url) {
|
||||
try {
|
||||
checkDestroyed();
|
||||
zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true), false);
|
||||
zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true), true);
|
||||
} catch (Throwable e) {
|
||||
throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,4 +177,6 @@ public interface Constants {
|
|||
|
||||
List<String> REST_SERVER = Arrays.asList("jetty", "tomcat", "netty");
|
||||
String CONTENT_LENGTH_KEY = "content-length";
|
||||
String USE_SECURE_RANDOM_ID = "dubbo.application.use-secure-random-request-id";
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,9 @@ public abstract class AbstractConnectionClient extends AbstractClient {
|
|||
|
||||
protected AbstractConnectionClient(URL url, ChannelHandler handler) throws RemotingException {
|
||||
super(url, handler);
|
||||
}
|
||||
|
||||
public final void increase() {
|
||||
COUNTER_UPDATER.set(this, 1L);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,16 +18,19 @@ package org.apache.dubbo.remoting.exchange;
|
|||
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.HEARTBEAT_EVENT;
|
||||
import static org.apache.dubbo.remoting.Constants.USE_SECURE_RANDOM_ID;
|
||||
|
||||
/**
|
||||
* Request.
|
||||
*/
|
||||
public class Request {
|
||||
|
||||
private static final AtomicLong INVOKE_ID = new AtomicLong(0);
|
||||
private static final AtomicLong INVOKE_ID;
|
||||
|
||||
private final long mId;
|
||||
|
||||
|
|
@ -51,6 +54,18 @@ public class Request {
|
|||
mId = id;
|
||||
}
|
||||
|
||||
static {
|
||||
long startID = ThreadLocalRandom.current().nextLong();
|
||||
if (Boolean.parseBoolean(System.getProperty(USE_SECURE_RANDOM_ID, "false"))) {
|
||||
try {
|
||||
SecureRandom rand = new SecureRandom(SecureRandom.getSeed(20));
|
||||
startID = rand.nextLong();
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
}
|
||||
INVOKE_ID = new AtomicLong(startID);
|
||||
}
|
||||
|
||||
private static long newId() {
|
||||
// getAndIncrement() When it grows to MAX_VALUE, it will grow to MIN_VALUE, and the negative can be used as ID
|
||||
return INVOKE_ID.getAndIncrement();
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ class RequestTest {
|
|||
|
||||
@Test
|
||||
void test() {
|
||||
Request requestStart = new Request();
|
||||
|
||||
Request request = new Request();
|
||||
request.setTwoWay(true);
|
||||
request.setBroken(true);
|
||||
|
|
@ -36,7 +38,7 @@ class RequestTest {
|
|||
Assertions.assertTrue(request.isEvent());
|
||||
Assertions.assertEquals(request.getVersion(), "1.0.0");
|
||||
Assertions.assertEquals(request.getData(), "data");
|
||||
Assertions.assertTrue(request.getId() >= 0);
|
||||
Assertions.assertEquals(requestStart.getId() + 1, request.getId());
|
||||
Assertions.assertEquals(1024, request.getPayload());
|
||||
|
||||
request.setHeartbeat(true);
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ public class NettyConnectionClient extends AbstractConnectionClient {
|
|||
this.channel = new AtomicReference<>();
|
||||
this.closePromise = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
|
||||
this.init = new AtomicBoolean(false);
|
||||
this.increase();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ import java.nio.charset.Charset;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
|
@ -481,13 +482,14 @@ public class CuratorZookeeperClient extends AbstractZookeeperClient<CuratorZooke
|
|||
}
|
||||
|
||||
if (childListener != null) {
|
||||
Runnable task = () -> {
|
||||
try {
|
||||
childListener.childChanged(path, client.getChildren().usingWatcher(CuratorWatcherImpl.this).forPath(path));
|
||||
} catch (Exception e) {
|
||||
logger.warn(REGISTRY_ZOOKEEPER_EXCEPTION, "", "", "client get children error", e);
|
||||
}
|
||||
};
|
||||
Runnable task = () -> Optional.ofNullable(childListener)
|
||||
.ifPresent(c -> {
|
||||
try {
|
||||
c.childChanged(path, client.getChildren().usingWatcher(CuratorWatcherImpl.this).forPath(path));
|
||||
} catch (Exception e) {
|
||||
logger.warn(REGISTRY_ZOOKEEPER_EXCEPTION, "", "", "client get children error", e);
|
||||
}
|
||||
});
|
||||
initExecutorIfNecessary();
|
||||
if (!closed && CURATOR_WATCHER_EXECUTOR_SERVICE != null) {
|
||||
CURATOR_WATCHER_EXECUTOR_SERVICE.execute(task);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import org.apache.dubbo.common.serialize.ObjectInput;
|
|||
import org.apache.dubbo.common.utils.Assert;
|
||||
import org.apache.dubbo.common.utils.CacheableSupplier;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.common.utils.ReflectUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.remoting.Channel;
|
||||
import org.apache.dubbo.remoting.Codec;
|
||||
|
|
@ -158,10 +157,13 @@ public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Dec
|
|||
if (desc.length() > 0) {
|
||||
pts = drawPts(path, version, desc, pts);
|
||||
if (pts == DubboCodec.EMPTY_CLASS_ARRAY) {
|
||||
if (!RpcUtils.isGenericCall(desc, getMethodName()) && !RpcUtils.isEcho(desc, getMethodName())) {
|
||||
if (RpcUtils.isGenericCall(desc, getMethodName())) {
|
||||
pts = DubboCodec.GENERIC_PTS_ARRAY;
|
||||
} else if (RpcUtils.isEcho(desc, getMethodName())) {
|
||||
pts = DubboCodec.ECHO_PTS_ARRAY;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Service not found:" + path + ", " + getMethodName());
|
||||
}
|
||||
pts = ReflectUtils.desc2classArray(desc);
|
||||
}
|
||||
args = drawArgs(in, pts);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ public class DubboCodec extends ExchangeCodec {
|
|||
public static final byte RESPONSE_NULL_VALUE_WITH_ATTACHMENTS = 5;
|
||||
public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
|
||||
public static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
|
||||
public static final Class<?>[] GENERIC_PTS_ARRAY = new Class<?>[]{String.class, String[].class, Object[].class};
|
||||
public static final Class<?>[] ECHO_PTS_ARRAY = new Class<?>[]{Object.class};
|
||||
private static final ErrorTypeAwareLogger log = LoggerFactory.getErrorTypeAwareLogger(DubboCodec.class);
|
||||
|
||||
private static final AtomicBoolean decodeInUserThreadLogged = new AtomicBoolean(false);
|
||||
|
|
|
|||
|
|
@ -515,12 +515,14 @@ public class DubboProtocol extends AbstractProtocol {
|
|||
}
|
||||
|
||||
try {
|
||||
ScopeModel scopeModel = url.getScopeModel();
|
||||
int heartbeat = UrlUtils.getHeartbeat(url);
|
||||
// Replace InstanceAddressURL with ServiceConfigURL.
|
||||
url = new ServiceConfigURL(DubboCodec.NAME, url.getUsername(), url.getPassword(), url.getHost(), url.getPort(), url.getPath(), url.getAllParameters());
|
||||
url = url.addParameter(CODEC_KEY, DubboCodec.NAME);
|
||||
// enable heartbeat by default
|
||||
url = url.addParameterIfAbsent(HEARTBEAT_KEY, Integer.toString(heartbeat));
|
||||
url = url.setScopeModel(scopeModel);
|
||||
|
||||
// connection should be lazy
|
||||
return url.getParameter(LAZY_CONNECT_KEY, false)
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jaxrs</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
@ -72,21 +73,25 @@
|
|||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-netty4</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jdk-http</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jackson-provider</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jaxb-provider</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- swagger -->
|
||||
|
|
|
|||
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* 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.rpc.protocol.rest;
|
||||
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.utils.NetUtils;
|
||||
import org.apache.dubbo.metadata.rest.ServiceRestMetadata;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
|
||||
import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
|
||||
import org.apache.dubbo.rpc.protocol.rest.netty.NettyServer;
|
||||
import org.apache.dubbo.rpc.protocol.rest.netty.RestHttpRequestDecoder;
|
||||
import org.apache.dubbo.rpc.protocol.rest.netty.UnSharedHandlerCreator;
|
||||
import org.apache.dubbo.rpc.protocol.rest.netty.ssl.SslServerTlsHandler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.BACKLOG_KEY;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.IO_THREADS_KEY;
|
||||
import static org.apache.dubbo.remoting.Constants.BIND_IP_KEY;
|
||||
import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY;
|
||||
import static org.apache.dubbo.remoting.Constants.DEFAULT_IO_THREADS;
|
||||
|
||||
/**
|
||||
* netty http server
|
||||
*/
|
||||
public class NettyHttpRestServer implements RestProtocolServer {
|
||||
|
||||
private ServiceDeployer serviceDeployer = new ServiceDeployer();
|
||||
private NettyServer server = getNettyServer();
|
||||
|
||||
/**
|
||||
* for triple override
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected NettyServer getNettyServer() {
|
||||
return new NettyServer();
|
||||
}
|
||||
|
||||
private String address;
|
||||
|
||||
private final Map<String, Object> attributes = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
@Override
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAddress(String address) {
|
||||
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
server.stop();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(URL url) {
|
||||
|
||||
registerExtension(url);
|
||||
|
||||
String bindIp = url.getParameter(BIND_IP_KEY, url.getHost());
|
||||
if (!url.isAnyHost() && NetUtils.isValidLocalHost(bindIp)) {
|
||||
server.setHostname(bindIp);
|
||||
}
|
||||
server.setPort(url.getParameter(BIND_PORT_KEY, url.getPort()));
|
||||
|
||||
// child options
|
||||
server.setChildChannelOptions(getChildChannelOptionMap(url));
|
||||
|
||||
// set options
|
||||
server.setChannelOptions(getChannelOptionMap(url));
|
||||
// set unshared callback
|
||||
server.setUnSharedHandlerCallBack(getUnSharedHttpChannelHandlers());
|
||||
// set channel handler and @Shared
|
||||
server.setChannelHandlers(getChannelHandlers(url));
|
||||
server.setIoWorkerCount(url.getParameter(IO_THREADS_KEY, DEFAULT_IO_THREADS));
|
||||
server.start(url);
|
||||
}
|
||||
|
||||
private UnSharedHandlerCreator getUnSharedHttpChannelHandlers() {
|
||||
return new UnSharedHandlerCreator() {
|
||||
@Override
|
||||
public List<ChannelHandler> getUnSharedHandlers(URL url) {
|
||||
return Arrays.asList(
|
||||
// add SslServerTlsHandler
|
||||
new SslServerTlsHandler(url),
|
||||
new HttpRequestDecoder(
|
||||
url.getParameter(RestConstant.MAX_INITIAL_LINE_LENGTH_PARAM, RestConstant.MAX_INITIAL_LINE_LENGTH),
|
||||
url.getParameter(RestConstant.MAX_HEADER_SIZE_PARAM, RestConstant.MAX_HEADER_SIZE),
|
||||
url.getParameter(RestConstant.MAX_CHUNK_SIZE_PARAM, RestConstant.MAX_CHUNK_SIZE)),
|
||||
new HttpObjectAggregator(url.getParameter(RestConstant.MAX_REQUEST_SIZE_PARAM, RestConstant.MAX_REQUEST_SIZE)),
|
||||
new HttpResponseEncoder(), new RestHttpRequestDecoder(url, serviceDeployer))
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* create child channel options map
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
protected Map<ChannelOption, Object> getChildChannelOptionMap(URL url) {
|
||||
Map<ChannelOption, Object> channelOption = new HashMap<>();
|
||||
channelOption.put(ChannelOption.SO_KEEPALIVE, url.getParameter(Constants.KEEP_ALIVE_KEY, Constants.DEFAULT_KEEP_ALIVE));
|
||||
return channelOption;
|
||||
}
|
||||
|
||||
/**
|
||||
* create channel options map
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
protected Map<ChannelOption, Object> getChannelOptionMap(URL url) {
|
||||
Map<ChannelOption, Object> options = new HashMap<>();
|
||||
|
||||
options.put(ChannelOption.SO_REUSEADDR, Boolean.TRUE);
|
||||
options.put(ChannelOption.TCP_NODELAY, Boolean.TRUE);
|
||||
options.put(ChannelOption.SO_BACKLOG, url.getPositiveParameter(BACKLOG_KEY, org.apache.dubbo.remoting.Constants.DEFAULT_BACKLOG));
|
||||
options.put(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* create channel handler
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
protected List<ChannelHandler> getChannelHandlers(URL url) {
|
||||
List<ChannelHandler> channelHandlers = new ArrayList<>();
|
||||
|
||||
return channelHandlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deploy(ServiceRestMetadata serviceRestMetadata, Invoker invoker) {
|
||||
serviceDeployer.deploy(serviceRestMetadata, invoker);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undeploy(ServiceRestMetadata serviceRestMetadata) {
|
||||
serviceDeployer.undeploy(serviceRestMetadata);
|
||||
}
|
||||
|
||||
private void registerExtension(URL url) {
|
||||
serviceDeployer.registerExtension(url);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -73,7 +73,7 @@ public class RestInvoker<T> extends AbstractInvoker<T> {
|
|||
RequestTemplate requestTemplate = new RequestTemplate(invocation, restMethodMetadata.getRequest().getMethod(), getUrl().getAddress());
|
||||
|
||||
HttpConnectionCreateContext httpConnectionCreateContext =
|
||||
creatHttpConnectionCreateContext(invocation, serviceRestMetadata, restMethodMetadata, requestTemplate);
|
||||
createHttpConnectionCreateContext(invocation, serviceRestMetadata, restMethodMetadata, requestTemplate);
|
||||
|
||||
// fill real data
|
||||
for (HttpConnectionPreBuildIntercept intercept : httpConnectionPreBuildIntercepts) {
|
||||
|
|
@ -130,7 +130,7 @@ public class RestInvoker<T> extends AbstractInvoker<T> {
|
|||
* @param requestTemplate
|
||||
* @return
|
||||
*/
|
||||
private HttpConnectionCreateContext creatHttpConnectionCreateContext(Invocation invocation, ServiceRestMetadata serviceRestMetadata, RestMethodMetadata restMethodMetadata, RequestTemplate requestTemplate) {
|
||||
private HttpConnectionCreateContext createHttpConnectionCreateContext(Invocation invocation, ServiceRestMetadata serviceRestMetadata, RestMethodMetadata restMethodMetadata, RequestTemplate requestTemplate) {
|
||||
HttpConnectionCreateContext httpConnectionCreateContext = new HttpConnectionCreateContext();
|
||||
httpConnectionCreateContext.setRequestTemplate(requestTemplate);
|
||||
httpConnectionCreateContext.setRestMethodMetadata(restMethodMetadata);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ import org.apache.dubbo.rpc.protocol.rest.annotation.metadata.MetadataResolver;
|
|||
import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
|
||||
import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployerManager;
|
||||
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
|
@ -59,7 +61,7 @@ public class RestProtocol extends AbstractProtocol {
|
|||
|
||||
public RestProtocol(FrameworkModel frameworkModel) {
|
||||
this.clientFactory = frameworkModel.getExtensionLoader(RestClientFactory.class).getAdaptiveExtension();
|
||||
this.httpConnectionPreBuildIntercepts = frameworkModel.getExtensionLoader(HttpConnectionPreBuildIntercept.class).getSupportedExtensionInstances();
|
||||
this.httpConnectionPreBuildIntercepts = new LinkedHashSet<>(frameworkModel.getExtensionLoader(HttpConnectionPreBuildIntercept.class).getActivateExtensions());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import org.apache.dubbo.rpc.RpcContext;
|
|||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.protocol.rest.annotation.ParamParserManager;
|
||||
import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.ProviderParseContext;
|
||||
import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
|
||||
import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
|
||||
import org.apache.dubbo.rpc.protocol.rest.exception.ParamParseException;
|
||||
import org.apache.dubbo.rpc.protocol.rest.pair.InvokerAndRestMethodMetadataPair;
|
||||
import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
|
||||
|
|
@ -39,6 +39,8 @@ import java.lang.reflect.Method;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.SERVICE_DEPLOYER_ATTRIBUTE_KEY;
|
||||
|
||||
|
||||
public class RestRPCInvocationUtil {
|
||||
|
||||
|
|
@ -142,12 +144,12 @@ public class RestRPCInvocationUtil {
|
|||
*/
|
||||
public static InvokerAndRestMethodMetadataPair getRestMethodMetadataAndInvokerPair(PathMatcher pathMatcher) {
|
||||
|
||||
PathAndInvokerMapper pathAndInvokerMapper = (PathAndInvokerMapper) RpcContext.getServerAttachment().getObjectAttachment(RestConstant.PATH_AND_INVOKER_MAPPER);
|
||||
ServiceDeployer serviceDeployer = (ServiceDeployer) RpcContext.getServiceContext().getObjectAttachment(SERVICE_DEPLOYER_ATTRIBUTE_KEY);
|
||||
|
||||
if (pathAndInvokerMapper == null) {
|
||||
if (serviceDeployer == null) {
|
||||
return null;
|
||||
}
|
||||
return pathAndInvokerMapper.getRestMethodMetadata(pathMatcher);
|
||||
return serviceDeployer.getPathAndInvokerMapper().getRestMethodMetadata(pathMatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -196,7 +198,9 @@ public class RestRPCInvocationUtil {
|
|||
return null;
|
||||
}
|
||||
|
||||
InvokerAndRestMethodMetadataPair pair = getRestMethodMetadataAndInvokerPair(PathMatcher.getInvokeCreatePathMatcher(serviceMethod));
|
||||
PathMatcher pathMatcher = PathMatcher.getInvokeCreatePathMatcher(serviceMethod);
|
||||
|
||||
InvokerAndRestMethodMetadataPair pair = getRestMethodMetadataAndInvokerPair(pathMatcher);
|
||||
|
||||
if (pair == null) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ import java.util.Collection;
|
|||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* resolve method args from header
|
||||
* resolve method args from header
|
||||
*/
|
||||
@Activate(value = RestConstant.REQUEST_HEADER_INTERCEPT, order = 2)
|
||||
@Activate(value = RestConstant.REQUEST_HEADER_INTERCEPT, order = Integer.MAX_VALUE - 1)
|
||||
public class RequestHeaderIntercept implements HttpConnectionPreBuildIntercept {
|
||||
|
||||
@Override
|
||||
|
|
@ -45,6 +45,7 @@ public class RequestHeaderIntercept implements HttpConnectionPreBuildIntercept {
|
|||
|
||||
requestTemplate.addHeaders(RestHeaderEnum.CONTENT_TYPE.getHeader(), consumes);
|
||||
|
||||
|
||||
Collection<String> produces = restMethodMetadata.getRequest().getProduces();
|
||||
if (produces == null || produces.isEmpty()) {
|
||||
requestTemplate.addHeader(RestHeaderEnum.ACCEPT.getHeader(), RestConstant.DEFAULT_ACCEPT);
|
||||
|
|
|
|||
|
|
@ -60,7 +60,8 @@ public class SerializeBodyIntercept implements HttpConnectionPreBuildIntercept {
|
|||
MediaType mediaType = MediaTypeUtil.convertMediaType(requestTemplate.getBodyType(), headers.toArray(new String[0]));
|
||||
|
||||
// add mediaType by targetClass serialize
|
||||
if (headers.isEmpty() && mediaType != null && !mediaType.equals(MediaType.ALL_VALUE)) {
|
||||
if (mediaType != null && !mediaType.equals(MediaType.ALL_VALUE)) {
|
||||
headers.clear();
|
||||
headers.add(mediaType.value);
|
||||
}
|
||||
HttpMessageCodecManager.httpMessageEncode(outputStream, unSerializedBody, url, mediaType, requestTemplate.getBodyType());
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@
|
|||
package org.apache.dubbo.rpc.protocol.rest.deploy;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.ClassUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.metadata.rest.PathMatcher;
|
||||
import org.apache.dubbo.metadata.rest.RestMethodMetadata;
|
||||
|
|
@ -26,15 +29,26 @@ import org.apache.dubbo.rpc.protocol.rest.Constants;
|
|||
import org.apache.dubbo.rpc.protocol.rest.PathAndInvokerMapper;
|
||||
import org.apache.dubbo.rpc.protocol.rest.RpcExceptionMapper;
|
||||
import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionMapper;
|
||||
import org.apache.dubbo.rpc.protocol.rest.exception.mapper.RestEasyExceptionMapper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
|
||||
|
||||
public class ServiceDeployer {
|
||||
|
||||
|
||||
private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
|
||||
|
||||
|
||||
private final PathAndInvokerMapper pathAndInvokerMapper = new PathAndInvokerMapper();
|
||||
private final ExceptionMapper exceptionMapper = new ExceptionMapper();
|
||||
private final ExceptionMapper exceptionMapper = createExceptionMapper();
|
||||
|
||||
private final Set<Object> extensions = new HashSet<>();
|
||||
|
||||
|
||||
public void deploy(ServiceRestMetadata serviceRestMetadata, Invoker invoker) {
|
||||
|
|
@ -58,12 +72,30 @@ public class ServiceDeployer {
|
|||
|
||||
}
|
||||
|
||||
public void registerExceptionMapper(URL url) {
|
||||
public void registerExtension(URL url) {
|
||||
|
||||
for (String clazz : COMMA_SPLIT_PATTERN.split(url.getParameter(Constants.EXTENSION_KEY, RpcExceptionMapper.class.getName()))) {
|
||||
if (!StringUtils.isEmpty(clazz)) {
|
||||
exceptionMapper.registerMapper(clazz);
|
||||
|
||||
if (StringUtils.isEmpty(clazz)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
Class<?> aClass = ClassUtils.forName(clazz);
|
||||
|
||||
// exception handler
|
||||
if (ExceptionMapper.isSupport(aClass)) {
|
||||
exceptionMapper.registerMapper(clazz);
|
||||
} else {
|
||||
|
||||
|
||||
extensions.add(aClass.newInstance());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.warn("", "", "dubbo rest registerExtension error: ", e.getMessage(), e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -74,4 +106,42 @@ public class ServiceDeployer {
|
|||
public ExceptionMapper getExceptionMapper() {
|
||||
return exceptionMapper;
|
||||
}
|
||||
|
||||
public Set<Object> getExtensions() {
|
||||
return extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* get extensions by type
|
||||
*
|
||||
* @param extensionClass
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
// TODO add javax.annotation.Priority sort
|
||||
public <T> List<T> getExtensions(Class<T> extensionClass) {
|
||||
|
||||
ArrayList<T> exts = new ArrayList<>();
|
||||
if (extensions.isEmpty()) {
|
||||
return exts;
|
||||
}
|
||||
|
||||
|
||||
for (Object extension : extensions) {
|
||||
if (extensionClass.isAssignableFrom(extension.getClass())) {
|
||||
exts.add((T) extension);
|
||||
}
|
||||
}
|
||||
|
||||
return exts;
|
||||
}
|
||||
|
||||
private ExceptionMapper createExceptionMapper() {
|
||||
if (ClassUtils.isPresent("javax.ws.rs.ext.ExceptionMapper", Thread.currentThread().getContextClassLoader())) {
|
||||
return new RestEasyExceptionMapper();
|
||||
}
|
||||
return new ExceptionMapper();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,4 +22,9 @@ public interface ExceptionHandler<E extends Throwable> {
|
|||
|
||||
Object result(E exception);
|
||||
|
||||
default int status() {
|
||||
return 200;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.rpc.protocol.rest.exception.mapper;
|
||||
|
||||
public class ExceptionHandlerResult {
|
||||
private int status;
|
||||
private Object entity;
|
||||
|
||||
public ExceptionHandlerResult() {
|
||||
|
||||
}
|
||||
|
||||
public ExceptionHandlerResult setStatus(int status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExceptionHandlerResult setEntity(Object entity) {
|
||||
this.entity = entity;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static ExceptionHandlerResult build() {
|
||||
return new ExceptionHandlerResult();
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public Object getEntity() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ExceptionHandlerResult{" +
|
||||
"status=" + status +
|
||||
", entity=" + entity +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -34,19 +34,26 @@ public class ExceptionMapper {
|
|||
private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
|
||||
|
||||
|
||||
// TODO static or instance ? think about influence between difference url exception
|
||||
private final Map<Class<?>, ExceptionHandler> exceptionHandlerMap = new ConcurrentHashMap<>();
|
||||
|
||||
public Object exceptionToResult(Object throwable) {
|
||||
ExceptionHandler exceptionHandler = getExceptionHandler(throwable.getClass());
|
||||
if (exceptionHandler == null) {
|
||||
return throwable;
|
||||
}
|
||||
return exceptionHandler.result((Throwable) throwable);
|
||||
private final Map allExceptionHandlers = new ConcurrentHashMap<>();
|
||||
|
||||
public ExceptionHandlerResult exceptionToResult(Object throwable) {
|
||||
ExceptionHandler exceptionHandler = (ExceptionHandler) getExceptionHandler(throwable.getClass());
|
||||
|
||||
Object result = exceptionHandler.result((Throwable) throwable);
|
||||
|
||||
return ExceptionHandlerResult.build().setEntity(result).setStatus(exceptionHandler.status());
|
||||
}
|
||||
|
||||
public ExceptionHandler getExceptionHandler(Class causeClass) {
|
||||
ExceptionHandler exceptionHandler = null;
|
||||
|
||||
public Object getExceptionHandler(Class causeClass) {
|
||||
|
||||
return getExceptionHandler(allExceptionHandlers, causeClass);
|
||||
}
|
||||
|
||||
public Object getExceptionHandler(Map exceptionHandlerMap, Class causeClass) {
|
||||
Object exceptionHandler = null;
|
||||
while (causeClass != null) {
|
||||
exceptionHandler = exceptionHandlerMap.get(causeClass);
|
||||
if (exceptionHandler != null) {
|
||||
|
|
@ -62,7 +69,7 @@ public class ExceptionMapper {
|
|||
if (throwable == null) {
|
||||
return false;
|
||||
}
|
||||
return getExceptionHandler(throwable.getClass()) != null;
|
||||
return allExceptionHandlers.containsKey(throwable.getClass());
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -70,12 +77,11 @@ public class ExceptionMapper {
|
|||
|
||||
|
||||
try {
|
||||
if (!ExceptionHandler.class.isAssignableFrom(exceptionHandler)) {
|
||||
List<Method> methods = getExceptionHandlerMethods(exceptionHandler);
|
||||
|
||||
if (methods == null || methods.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// resolve Java_Zulu_jdk/17.0.6-10/x64 param is not throwable
|
||||
List<Method> methods = ReflectUtils.getMethodByNameList(exceptionHandler, "result");
|
||||
|
||||
|
||||
Set<Class<?>> exceptions = new HashSet<>();
|
||||
|
||||
|
|
@ -107,9 +113,7 @@ public class ExceptionMapper {
|
|||
// if exceptionHandler is inner class , no arg construct don`t appear , so newInstance don`t use noArgConstruct
|
||||
Object handler = constructors.get(0).newInstance(new Object[constructors.get(0).getParameterCount()]);
|
||||
|
||||
for (Class<?> exception : exceptions) {
|
||||
exceptionHandlerMap.put(exception, (ExceptionHandler) handler);
|
||||
}
|
||||
putExtensionToMap(exceptions, handler);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("dubbo rest protocol exception mapper register error ", e);
|
||||
|
|
@ -118,6 +122,31 @@ public class ExceptionMapper {
|
|||
|
||||
}
|
||||
|
||||
protected void putExtensionToMap(Set<Class<?>> exceptions, Object handler) {
|
||||
|
||||
Map exceptionHandlerMaps = getExceptionHandlerMap(handler);
|
||||
|
||||
for (Class<?> exception : exceptions) {
|
||||
// put to instance map
|
||||
exceptionHandlerMaps.put(exception, handler);
|
||||
// put to all map
|
||||
allExceptionHandlers.put(exception, handler);
|
||||
}
|
||||
}
|
||||
|
||||
protected Map getExceptionHandlerMap(Object handler) {
|
||||
return exceptionHandlerMap;
|
||||
}
|
||||
|
||||
protected List<Method> getExceptionHandlerMethods(Class<?> exceptionHandler) {
|
||||
if (!ExceptionHandler.class.isAssignableFrom(exceptionHandler)) {
|
||||
return null;
|
||||
}
|
||||
// resolve Java_Zulu_jdk/17.0.6-10/x64 param is not throwable
|
||||
List<Method> methods = ReflectUtils.getMethodByNameList(exceptionHandler, "result");
|
||||
return methods;
|
||||
}
|
||||
|
||||
public void registerMapper(String exceptionMapper) {
|
||||
try {
|
||||
registerMapper(ReflectUtils.findClass(exceptionMapper));
|
||||
|
|
@ -131,4 +160,12 @@ public class ExceptionMapper {
|
|||
public void unRegisterMapper(Class<?> exception) {
|
||||
exceptionHandlerMap.remove(exception);
|
||||
}
|
||||
|
||||
public static boolean isSupport(Class<?> exceptionHandler) {
|
||||
try {
|
||||
return ExceptionHandler.class.isAssignableFrom(exceptionHandler) || ReflectUtils.findClassTryException("javax.ws.rs.ext.ExceptionMapper").isAssignableFrom(exceptionHandler);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.rpc.protocol.rest.exception.mapper;
|
||||
|
||||
import org.apache.dubbo.rpc.protocol.rest.util.ReflectUtils;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* for rest easy exception mapper extension
|
||||
*/
|
||||
public class RestEasyExceptionMapper extends ExceptionMapper {
|
||||
|
||||
private final Map<Class<?>, javax.ws.rs.ext.ExceptionMapper> exceptionMappers = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
protected List<Method> getExceptionHandlerMethods(Class<?> exceptionHandler) {
|
||||
if (!javax.ws.rs.ext.ExceptionMapper.class.isAssignableFrom(exceptionHandler)) {
|
||||
return super.getExceptionHandlerMethods(exceptionHandler);
|
||||
}
|
||||
// resolve Java_Zulu_jdk/17.0.6-10/x64 param is not throwable
|
||||
List<Method> methods = ReflectUtils.getMethodByNameList(exceptionHandler, "toResponse");
|
||||
return methods;
|
||||
}
|
||||
|
||||
protected Map getExceptionHandlerMap(Object handler) {
|
||||
if (handler instanceof ExceptionHandler) {
|
||||
return super.getExceptionHandlerMap(handler);
|
||||
}
|
||||
return exceptionMappers;
|
||||
}
|
||||
|
||||
public ExceptionHandlerResult exceptionToResult(Object throwable) {
|
||||
Object exceptionMapper = getExceptionHandler(throwable.getClass());
|
||||
if (exceptionMapper == null || exceptionMapper instanceof ExceptionHandler) {
|
||||
return super.exceptionToResult(throwable);
|
||||
}
|
||||
|
||||
Response response = ((javax.ws.rs.ext.ExceptionMapper) exceptionMapper).toResponse((Throwable) throwable);
|
||||
|
||||
return ExceptionHandlerResult.build().setStatus(response.getStatus()).setEntity(response.getEntity());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* 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.rpc.protocol.rest.extension.resteasy;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.http.HttpContent;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.metadata.rest.media.MediaType;
|
||||
import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
|
||||
import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter.DubboContainerResponseContextImpl;
|
||||
import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter.DubboPreMatchContainerRequestContext;
|
||||
import org.apache.dubbo.rpc.protocol.rest.filter.ServiceInvokeRestFilter;
|
||||
import org.apache.dubbo.rpc.protocol.rest.netty.ChunkOutputStream;
|
||||
import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
|
||||
import org.apache.dubbo.rpc.protocol.rest.request.NettyRequestFacade;
|
||||
import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
|
||||
import org.apache.dubbo.rpc.protocol.rest.util.MediaTypeUtil;
|
||||
import org.jboss.resteasy.core.interception.ResponseContainerRequestContext;
|
||||
import org.jboss.resteasy.plugins.server.netty.NettyHttpRequest;
|
||||
import org.jboss.resteasy.plugins.server.netty.NettyUtil;
|
||||
import org.jboss.resteasy.specimpl.BuiltResponse;
|
||||
import org.jboss.resteasy.specimpl.ResteasyHttpHeaders;
|
||||
import org.jboss.resteasy.spi.HttpResponse;
|
||||
import org.jboss.resteasy.spi.ResteasyUriInfo;
|
||||
|
||||
import javax.ws.rs.container.ContainerRequestFilter;
|
||||
import javax.ws.rs.container.ContainerResponseFilter;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface ResteasyContext {
|
||||
String HTTP_PROTOCOL = "http://";
|
||||
String HTTP = "http";
|
||||
String HTTPS_PROTOCOL = "https://";
|
||||
|
||||
|
||||
/**
|
||||
* return extensions that are filtered by extension type
|
||||
*
|
||||
* @param extension
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
default <T> List<T> getExtension(ServiceDeployer serviceDeployer, Class<T> extension) {
|
||||
|
||||
return serviceDeployer.getExtensions(extension);
|
||||
|
||||
}
|
||||
|
||||
default DubboPreMatchContainerRequestContext convertHttpRequestToContainerRequestContext(RequestFacade requestFacade, ContainerRequestFilter[] requestFilters) {
|
||||
|
||||
|
||||
NettyRequestFacade nettyRequestFacade = (NettyRequestFacade) requestFacade;
|
||||
HttpRequest request = (HttpRequest) requestFacade.getRequest();
|
||||
|
||||
NettyHttpRequest nettyRequest = createNettyHttpRequest(nettyRequestFacade, request);
|
||||
|
||||
if (request instanceof HttpContent) {
|
||||
|
||||
try {
|
||||
byte[] inputStream = requestFacade.getInputStream();
|
||||
ByteBuf buffer = nettyRequestFacade.getNettyChannelContext().alloc().buffer();
|
||||
buffer.writeBytes(inputStream);
|
||||
nettyRequest.setContentBuffer(buffer);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
return new DubboPreMatchContainerRequestContext(nettyRequest, requestFilters, null);
|
||||
|
||||
}
|
||||
|
||||
default ResteasyUriInfo extractUriInfo(HttpRequest request) {
|
||||
String host = HttpHeaders.getHost(request, "unknown");
|
||||
if ("".equals(host)) {
|
||||
host = "unknown";
|
||||
}
|
||||
String uri = request.getUri();
|
||||
|
||||
String uriString;
|
||||
|
||||
// If we appear to have an absolute URL, don't try to recreate it from the host and request line.
|
||||
if (uri.startsWith(HTTP_PROTOCOL) || uri.startsWith(HTTPS_PROTOCOL)) {
|
||||
uriString = uri;
|
||||
} else {
|
||||
uriString = HTTP + "://" + host + uri;
|
||||
}
|
||||
|
||||
URI absoluteURI = URI.create(uriString);
|
||||
return new ResteasyUriInfo(uriString, absoluteURI.getRawQuery(), "");
|
||||
}
|
||||
|
||||
default NettyHttpRequest createNettyHttpRequest(NettyRequestFacade nettyRequestFacade, HttpRequest request) {
|
||||
ResteasyHttpHeaders headers = NettyUtil.extractHttpHeaders(request);
|
||||
ResteasyUriInfo uriInfo = extractUriInfo(request);
|
||||
NettyHttpRequest nettyRequest = new NettyHttpRequest(nettyRequestFacade.getNettyChannelContext(), headers, uriInfo, request.getMethod().name(),
|
||||
null, null, HttpHeaders.is100ContinueExpected(request));
|
||||
|
||||
return nettyRequest;
|
||||
}
|
||||
|
||||
|
||||
default NettyHttpRequest createNettyHttpRequest(RequestFacade requestFacade) {
|
||||
NettyRequestFacade nettyRequestFacade = (NettyRequestFacade) requestFacade;
|
||||
HttpRequest request = (HttpRequest) requestFacade.getRequest();
|
||||
|
||||
ResteasyHttpHeaders headers = NettyUtil.extractHttpHeaders(request);
|
||||
ResteasyUriInfo uriInfo = extractUriInfo(request);
|
||||
NettyHttpRequest nettyRequest = new NettyHttpRequest(nettyRequestFacade.getNettyChannelContext(), headers, uriInfo, request.getMethod().name(),
|
||||
null, null, HttpHeaders.is100ContinueExpected(request));
|
||||
|
||||
return nettyRequest;
|
||||
}
|
||||
|
||||
default void writeResteasyResponse(URL url, RequestFacade requestFacade, NettyHttpResponse response, BuiltResponse restResponse) throws Exception {
|
||||
if (restResponse.getMediaType() != null) {
|
||||
MediaType mediaType = MediaTypeUtil.convertMediaType(restResponse.getEntityClass(), restResponse.getMediaType().toString());
|
||||
ServiceInvokeRestFilter.writeResult(response, url, restResponse.getEntity(), restResponse.getEntityClass(), mediaType);
|
||||
} else {
|
||||
ServiceInvokeRestFilter.writeResult(response, requestFacade, url, restResponse.getEntity(), restResponse.getEntityClass());
|
||||
}
|
||||
}
|
||||
|
||||
default MediaType getAcceptMediaType(RequestFacade request, Class<?> returnType) {
|
||||
|
||||
return ServiceInvokeRestFilter.getAcceptMediaType(request, returnType);
|
||||
}
|
||||
|
||||
default void addResponseHeaders(NettyHttpResponse response, MultivaluedMap<String, Object> headers) {
|
||||
if (headers == null || headers.isEmpty()) {
|
||||
|
||||
return;
|
||||
}
|
||||
for (Map.Entry<String, List<Object>> entry : headers.entrySet()) {
|
||||
|
||||
String key = entry.getKey();
|
||||
if (entry.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
response.addOutputHeaders(key, entry.getValue().toString());
|
||||
}
|
||||
}
|
||||
|
||||
default DubboContainerResponseContextImpl createContainerResponseContext(RequestFacade request, HttpResponse httpResponse, BuiltResponse jaxrsResponse, ContainerResponseFilter[] responseFilters) {
|
||||
|
||||
NettyHttpRequest nettyHttpRequest = createNettyHttpRequest(request);
|
||||
|
||||
ResponseContainerRequestContext requestContext = new ResponseContainerRequestContext(nettyHttpRequest);
|
||||
DubboContainerResponseContextImpl responseContext = new DubboContainerResponseContextImpl(nettyHttpRequest, httpResponse, jaxrsResponse,
|
||||
requestContext, responseFilters, null, null);
|
||||
|
||||
return responseContext;
|
||||
}
|
||||
|
||||
default void restOutputStream(NettyHttpResponse response) throws IOException {
|
||||
ChunkOutputStream outputStream = (ChunkOutputStream) response.getOutputStream();
|
||||
outputStream.reset();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,380 @@
|
|||
/*
|
||||
* 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.rpc.protocol.rest.extension.resteasy.filter;
|
||||
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.jboss.resteasy.core.Dispatcher;
|
||||
import org.jboss.resteasy.core.ServerResponseWriter;
|
||||
import org.jboss.resteasy.core.SynchronousDispatcher;
|
||||
import org.jboss.resteasy.core.interception.ResponseContainerRequestContext;
|
||||
import org.jboss.resteasy.core.interception.jaxrs.SuspendableContainerResponseContext;
|
||||
import org.jboss.resteasy.specimpl.BuiltResponse;
|
||||
import org.jboss.resteasy.spi.ApplicationException;
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.jboss.resteasy.spi.HttpResponse;
|
||||
import org.jboss.resteasy.spi.ResteasyAsynchronousResponse;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.container.ContainerResponseFilter;
|
||||
import javax.ws.rs.core.EntityTag;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.Link;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.NewCookie;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.URI;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class DubboContainerResponseContextImpl implements SuspendableContainerResponseContext {
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DubboContainerResponseContextImpl.class);
|
||||
|
||||
protected final HttpRequest request;
|
||||
protected final HttpResponse httpResponse;
|
||||
protected final BuiltResponse jaxrsResponse;
|
||||
private ResponseContainerRequestContext requestContext;
|
||||
private ContainerResponseFilter[] responseFilters;
|
||||
private ServerResponseWriter.RunnableWithIOException continuation;
|
||||
private int currentFilter;
|
||||
private boolean suspended;
|
||||
private boolean filterReturnIsMeaningful = true;
|
||||
private Map<Class<?>, Object> contextDataMap;
|
||||
private boolean inFilter;
|
||||
private Throwable throwable;
|
||||
private Consumer<Throwable> onComplete;
|
||||
private boolean weSuspended;
|
||||
|
||||
|
||||
public DubboContainerResponseContextImpl(final HttpRequest request, final HttpResponse httpResponse, final BuiltResponse serverResponse,
|
||||
final ResponseContainerRequestContext requestContext, final ContainerResponseFilter[] responseFilters,
|
||||
final Consumer<Throwable> onComplete, final ServerResponseWriter.RunnableWithIOException continuation) {
|
||||
this.request = request;
|
||||
this.httpResponse = httpResponse;
|
||||
this.jaxrsResponse = serverResponse;
|
||||
this.requestContext = requestContext;
|
||||
this.responseFilters = responseFilters;
|
||||
this.continuation = continuation;
|
||||
this.onComplete = onComplete;
|
||||
contextDataMap = ResteasyProviderFactory.getContextDataMap();
|
||||
}
|
||||
|
||||
public BuiltResponse getJaxrsResponse() {
|
||||
return jaxrsResponse;
|
||||
}
|
||||
|
||||
public HttpResponse getHttpResponse() {
|
||||
return httpResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStatus() {
|
||||
return jaxrsResponse.getStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(int code) {
|
||||
httpResponse.setStatus(code);
|
||||
jaxrsResponse.setStatus(code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response.StatusType getStatusInfo() {
|
||||
return jaxrsResponse.getStatusInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatusInfo(Response.StatusType statusInfo) {
|
||||
httpResponse.setStatus(statusInfo.getStatusCode());
|
||||
jaxrsResponse.setStatus(statusInfo.getStatusCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getEntityClass() {
|
||||
return jaxrsResponse.getEntityClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getEntityType() {
|
||||
return jaxrsResponse.getGenericType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntity(Object entity) {
|
||||
//if (entity != null) logger.info("*** setEntity(Object) " + entity.toString());
|
||||
if (entity != null && jaxrsResponse.getEntity() != null) {
|
||||
logger.info("Dubbo container response context filter set entity ,before entity is: " + jaxrsResponse.getEntity() + "and after entity is: " + entity);
|
||||
|
||||
}
|
||||
jaxrsResponse.setEntity(entity);
|
||||
// it resets the entity in a response filter which results
|
||||
// in a bad content-length being sent back to the client
|
||||
// so, we'll remove any content-length setting
|
||||
getHeaders().remove(HttpHeaders.CONTENT_LENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntity(Object entity, Annotation[] annotations, MediaType mediaType) {
|
||||
//if (entity != null) logger.info("*** setEntity(Object, Annotation[], MediaType) " + entity.toString() + ", " + mediaType);
|
||||
if (entity != null && jaxrsResponse.getEntity() != null) {
|
||||
logger.info("Dubbo container response context filter set entity ,before entity is: " + jaxrsResponse.getEntity() + "and after entity is: " + entity);
|
||||
}
|
||||
jaxrsResponse.setEntity(entity);
|
||||
jaxrsResponse.setAnnotations(annotations);
|
||||
jaxrsResponse.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, mediaType);
|
||||
// it resets the entity in a response filter which results
|
||||
// in a bad content-length being sent back to the client
|
||||
// so, we'll remove any content-length setting
|
||||
getHeaders().remove(HttpHeaders.CONTENT_LENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultivaluedMap<String, Object> getHeaders() {
|
||||
return jaxrsResponse.getMetadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAllowedMethods() {
|
||||
return jaxrsResponse.getAllowedMethods();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getDate() {
|
||||
return jaxrsResponse.getDate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getLanguage() {
|
||||
return jaxrsResponse.getLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
return jaxrsResponse.getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaType getMediaType() {
|
||||
return jaxrsResponse.getMediaType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, NewCookie> getCookies() {
|
||||
return jaxrsResponse.getCookies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityTag getEntityTag() {
|
||||
return jaxrsResponse.getEntityTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getLastModified() {
|
||||
return jaxrsResponse.getLastModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getLocation() {
|
||||
return jaxrsResponse.getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Link> getLinks() {
|
||||
return jaxrsResponse.getLinks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLink(String relation) {
|
||||
return jaxrsResponse.hasLink(relation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Link getLink(String relation) {
|
||||
return jaxrsResponse.getLink(relation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Link.Builder getLinkBuilder(String relation) {
|
||||
return jaxrsResponse.getLinkBuilder(relation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEntity() {
|
||||
return !jaxrsResponse.isClosed() && jaxrsResponse.hasEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEntity() {
|
||||
return !jaxrsResponse.isClosed() ? jaxrsResponse.getEntity() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getEntityStream() {
|
||||
try {
|
||||
return httpResponse.getOutputStream();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntityStream(OutputStream entityStream) {
|
||||
httpResponse.setOutputStream(entityStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Annotation[] getEntityAnnotations() {
|
||||
return jaxrsResponse.getAnnotations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultivaluedMap<String, String> getStringHeaders() {
|
||||
return jaxrsResponse.getStringHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeaderString(String name) {
|
||||
return jaxrsResponse.getHeaderString(name);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized void suspend() {
|
||||
if (continuation == null)
|
||||
throw new RuntimeException("Suspend not supported yet");
|
||||
suspended = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void resume() {
|
||||
if (!suspended)
|
||||
throw new RuntimeException("Cannot resume: not suspended");
|
||||
if (inFilter) {
|
||||
// suspend/resume within filter, same thread: just ignore and move on
|
||||
suspended = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// go on, but with proper exception handling
|
||||
try (ResteasyProviderFactory.CloseableContext c = ResteasyProviderFactory.addCloseableContextDataLevel(contextDataMap)) {
|
||||
filter();
|
||||
} catch (Throwable t) {
|
||||
// don't throw to client
|
||||
writeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void resume(Throwable t) {
|
||||
if (!suspended)
|
||||
throw new RuntimeException("Cannot resume: not suspended");
|
||||
if (inFilter) {
|
||||
// not suspended, or suspend/abortWith within filter, same thread: collect and move on
|
||||
throwable = t;
|
||||
suspended = false;
|
||||
} else {
|
||||
try (ResteasyProviderFactory.CloseableContext c = ResteasyProviderFactory.addCloseableContextDataLevel(contextDataMap)) {
|
||||
writeException(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeException(Throwable t) {
|
||||
/*
|
||||
* Here we cannot call AsyncResponse.resume(t) because that would invoke the response filters
|
||||
* and we should not invoke them because we're already in them.
|
||||
*/
|
||||
HttpResponse httpResponse = (HttpResponse) contextDataMap.get(HttpResponse.class);
|
||||
SynchronousDispatcher dispatcher = (SynchronousDispatcher) contextDataMap.get(Dispatcher.class);
|
||||
ResteasyAsynchronousResponse asyncResponse = request.getAsyncContext().getAsyncResponse();
|
||||
|
||||
dispatcher.unhandledAsynchronousException(httpResponse, t);
|
||||
onComplete.accept(t);
|
||||
asyncResponse.complete();
|
||||
asyncResponse.completionCallbacks(t);
|
||||
}
|
||||
|
||||
public synchronized void filter() throws IOException {
|
||||
while (currentFilter < responseFilters.length) {
|
||||
ContainerResponseFilter filter = responseFilters[currentFilter++];
|
||||
try {
|
||||
suspended = false;
|
||||
throwable = null;
|
||||
inFilter = true;
|
||||
filter.filter(requestContext, this);
|
||||
} catch (IOException e) {
|
||||
throw new ApplicationException(e);
|
||||
} finally {
|
||||
inFilter = false;
|
||||
}
|
||||
if (suspended) {
|
||||
if (!request.getAsyncContext().isSuspended()) {
|
||||
request.getAsyncContext().suspend();
|
||||
weSuspended = true;
|
||||
}
|
||||
// ignore any abort request until we are resumed
|
||||
filterReturnIsMeaningful = false;
|
||||
return;
|
||||
}
|
||||
if (throwable != null) {
|
||||
// handle the case where we've been suspended by a previous filter
|
||||
if (filterReturnIsMeaningful)
|
||||
SynchronousDispatcher.rethrow(throwable);
|
||||
else {
|
||||
writeException(throwable);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// here it means we reached the last filter
|
||||
|
||||
// some frameworks don't support async request filters, in which case suspend() is forbidden
|
||||
// so if we get here we're still synchronous and don't have a continuation, which must be in
|
||||
// the caller
|
||||
if (continuation == null)
|
||||
return;
|
||||
|
||||
// if we've never been suspended, the caller is valid so let it handle any exception
|
||||
if (filterReturnIsMeaningful) {
|
||||
continuation.run();
|
||||
onComplete.accept(null);
|
||||
return;
|
||||
}
|
||||
// if we've been suspended then the caller is a filter and have to invoke our continuation
|
||||
// try to write it out
|
||||
try {
|
||||
continuation.run();
|
||||
onComplete.accept(null);
|
||||
if (weSuspended) {
|
||||
// if we're the ones who turned the request async, nobody will call complete() for us, so we have to
|
||||
HttpServletRequest httpServletRequest = (HttpServletRequest) contextDataMap.get(HttpServletRequest.class);
|
||||
httpServletRequest.getAsyncContext().complete();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("", "Dubbo container response context filter error", "request method is: " + request.getHttpMethod() + "and request uri is:" + request.getUri().getPath(), "", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
* 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.rpc.protocol.rest.extension.resteasy.filter;
|
||||
|
||||
import org.jboss.resteasy.core.interception.jaxrs.SuspendableContainerRequestContext;
|
||||
import org.jboss.resteasy.plugins.server.netty.NettyHttpRequest;
|
||||
import org.jboss.resteasy.specimpl.BuiltResponse;
|
||||
import org.jboss.resteasy.specimpl.ResteasyHttpHeaders;
|
||||
import org.jboss.resteasy.spi.ApplicationException;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
|
||||
import javax.ws.rs.container.ContainerRequestFilter;
|
||||
import javax.ws.rs.core.Cookie;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Request;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.SecurityContext;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class DubboPreMatchContainerRequestContext implements SuspendableContainerRequestContext {
|
||||
protected final NettyHttpRequest httpRequest;
|
||||
protected Response response;
|
||||
private ContainerRequestFilter[] requestFilters;
|
||||
private int currentFilter;
|
||||
private boolean suspended;
|
||||
private boolean filterReturnIsMeaningful = true;
|
||||
private Supplier<BuiltResponse> continuation;
|
||||
private Map<Class<?>, Object> contextDataMap;
|
||||
private boolean inFilter;
|
||||
private Throwable throwable;
|
||||
private boolean startedContinuation;
|
||||
|
||||
|
||||
public DubboPreMatchContainerRequestContext(final NettyHttpRequest request,
|
||||
final ContainerRequestFilter[] requestFilters, final Supplier<BuiltResponse> continuation) {
|
||||
this.httpRequest = request;
|
||||
this.requestFilters = requestFilters;
|
||||
this.continuation = continuation;
|
||||
contextDataMap = ResteasyProviderFactory.getContextDataMap();
|
||||
}
|
||||
|
||||
public NettyHttpRequest getHttpRequest() {
|
||||
return httpRequest;
|
||||
}
|
||||
|
||||
public Response getResponseAbortedWith() {
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProperty(String name) {
|
||||
return httpRequest.getAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getPropertyNames() {
|
||||
ArrayList<String> names = new ArrayList<String>();
|
||||
Enumeration<String> enames = httpRequest.getAttributeNames();
|
||||
while (enames.hasMoreElements()) {
|
||||
names.add(enames.nextElement());
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(String name, Object object) {
|
||||
httpRequest.setAttribute(name, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeProperty(String name) {
|
||||
httpRequest.removeAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UriInfo getUriInfo() {
|
||||
return httpRequest.getUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRequestUri(URI requestUri) throws IllegalStateException {
|
||||
httpRequest.setRequestUri(requestUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRequestUri(URI baseUri, URI requestUri) throws IllegalStateException {
|
||||
httpRequest.setRequestUri(baseUri, requestUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethod() {
|
||||
return httpRequest.getHttpMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMethod(String method) {
|
||||
httpRequest.setHttpMethod(method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultivaluedMap<String, String> getHeaders() {
|
||||
return ((ResteasyHttpHeaders) httpRequest.getHttpHeaders()).getMutableHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getDate() {
|
||||
return httpRequest.getHttpHeaders().getDate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getLanguage() {
|
||||
return httpRequest.getHttpHeaders().getLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
return httpRequest.getHttpHeaders().getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaType getMediaType() {
|
||||
return httpRequest.getHttpHeaders().getMediaType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MediaType> getAcceptableMediaTypes() {
|
||||
return httpRequest.getHttpHeaders().getAcceptableMediaTypes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Locale> getAcceptableLanguages() {
|
||||
return httpRequest.getHttpHeaders().getAcceptableLanguages();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Cookie> getCookies() {
|
||||
return httpRequest.getHttpHeaders().getCookies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEntity() {
|
||||
return getMediaType() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getEntityStream() {
|
||||
return httpRequest.getInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntityStream(InputStream entityStream) {
|
||||
httpRequest.setInputStream(entityStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityContext getSecurityContext() {
|
||||
return ResteasyProviderFactory.getContextData(SecurityContext.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSecurityContext(SecurityContext context) {
|
||||
ResteasyProviderFactory.pushContext(SecurityContext.class, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request getRequest() {
|
||||
return ResteasyProviderFactory.getContextData(Request.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeaderString(String name) {
|
||||
return httpRequest.getHttpHeaders().getHeaderString(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void suspend() {
|
||||
if (continuation == null)
|
||||
throw new RuntimeException("Suspend not supported yet");
|
||||
suspended = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void abortWith(Response response) {
|
||||
if (suspended && !inFilter) {
|
||||
try (ResteasyProviderFactory.CloseableContext c = ResteasyProviderFactory.addCloseableContextDataLevel(contextDataMap)) {
|
||||
httpRequest.getAsyncContext().getAsyncResponse().resume(response);
|
||||
}
|
||||
} else {
|
||||
// not suspended, or suspend/abortWith within filter, same thread: collect and move on
|
||||
this.response = response;
|
||||
suspended = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void resume() {
|
||||
if (!suspended)
|
||||
throw new RuntimeException("Cannot resume: not suspended");
|
||||
if (inFilter) {
|
||||
// suspend/resume within filter, same thread: just ignore and move on
|
||||
suspended = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// go on, but with proper exception handling
|
||||
try (ResteasyProviderFactory.CloseableContext c = ResteasyProviderFactory.addCloseableContextDataLevel(contextDataMap)) {
|
||||
filter();
|
||||
} catch (Throwable t) {
|
||||
// don't throw to client
|
||||
writeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void resume(Throwable t) {
|
||||
if (!suspended)
|
||||
throw new RuntimeException("Cannot resume: not suspended");
|
||||
if (inFilter) {
|
||||
// not suspended, or suspend/abortWith within filter, same thread: collect and move on
|
||||
throwable = t;
|
||||
suspended = false;
|
||||
} else {
|
||||
try (ResteasyProviderFactory.CloseableContext c = ResteasyProviderFactory.addCloseableContextDataLevel(contextDataMap)) {
|
||||
writeException(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeException(Throwable t) {
|
||||
/*
|
||||
* Here, contrary to ContainerResponseContextImpl.writeException, we can use the async response
|
||||
* to write the exception, because it calls the right response filters, complete() and callbacks
|
||||
*/
|
||||
httpRequest.getAsyncContext().getAsyncResponse().resume(t);
|
||||
}
|
||||
|
||||
public synchronized BuiltResponse filter() throws Throwable {
|
||||
while (currentFilter < requestFilters.length) {
|
||||
ContainerRequestFilter filter = requestFilters[currentFilter++];
|
||||
try {
|
||||
suspended = false;
|
||||
response = null;
|
||||
throwable = null;
|
||||
inFilter = true;
|
||||
filter.filter(this);
|
||||
} catch (IOException e) {
|
||||
throw new ApplicationException(e);
|
||||
} finally {
|
||||
inFilter = false;
|
||||
}
|
||||
if (suspended) {
|
||||
if (!httpRequest.getAsyncContext().isSuspended())
|
||||
// ignore any abort request until we are resumed
|
||||
filterReturnIsMeaningful = false;
|
||||
response = null;
|
||||
return null;
|
||||
}
|
||||
BuiltResponse serverResponse = (BuiltResponse) getResponseAbortedWith();
|
||||
if (serverResponse != null) {
|
||||
// handle the case where we've been suspended by a previous filter
|
||||
return serverResponse;
|
||||
}
|
||||
|
||||
if (throwable != null) {
|
||||
// handle the case where we've been suspended by a previous filter
|
||||
throw throwable;
|
||||
}
|
||||
}
|
||||
// here it means we reached the last filter
|
||||
// some frameworks don't support async request filters, in which case suspend() is forbidden
|
||||
// so if we get here we're still synchronous and don't have a continuation, which must be in
|
||||
// the caller
|
||||
startedContinuation = true;
|
||||
if (continuation == null)
|
||||
return null;
|
||||
// in any case, return the continuation: sync will use it, and async will ignore it
|
||||
return continuation.get();
|
||||
}
|
||||
|
||||
public boolean startedContinuation() {
|
||||
return startedContinuation;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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.rpc.protocol.rest.extension.resteasy.filter;
|
||||
|
||||
import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
|
||||
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
|
||||
import org.jboss.resteasy.spi.HttpResponse;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.NewCookie;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ResteasyNettyHttpResponse implements HttpResponse {
|
||||
|
||||
private NettyHttpResponse response;
|
||||
|
||||
private MultivaluedMap<String, Object> multivaluedMap = new MultivaluedMapImpl<>();
|
||||
|
||||
public ResteasyNettyHttpResponse(NettyHttpResponse response) {
|
||||
this.response = response;
|
||||
Map<String, List<String>> outputHeaders = response.getOutputHeaders();
|
||||
|
||||
for (Map.Entry<String, List<String>> headers : outputHeaders.entrySet()) {
|
||||
String key = headers.getKey();
|
||||
List<String> value = headers.getValue();
|
||||
multivaluedMap.add(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getStatus() {
|
||||
return response.getStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(int status) {
|
||||
|
||||
response.setStatus(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultivaluedMap<String, Object> getOutputHeaders() {
|
||||
return multivaluedMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
return response.getOutputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOutputStream(OutputStream os) {
|
||||
response.setOutputStream(os);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNewCookie(NewCookie cookie) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendError(int status) throws IOException {
|
||||
|
||||
response.sendError(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendError(int status, String message) throws IOException {
|
||||
response.sendError(status, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCommitted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
|
||||
response.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushBuffer() throws IOException {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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.rpc.protocol.rest.extension.resteasy.filter;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
|
||||
import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.ResteasyContext;
|
||||
import org.apache.dubbo.rpc.protocol.rest.filter.RestRequestFilter;
|
||||
import org.apache.dubbo.rpc.protocol.rest.filter.context.RestFilterContext;
|
||||
import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
|
||||
import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
|
||||
import org.jboss.resteasy.specimpl.BuiltResponse;
|
||||
|
||||
import javax.ws.rs.container.ContainerRequestFilter;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.RESTEASY_NETTY_HTTP_REQUEST_ATTRIBUTE_KEY;
|
||||
|
||||
|
||||
@Activate(value = "resteasy", onClass = {"javax.ws.rs.container.ContainerRequestFilter", "org.jboss.resteasy.plugins.server.netty.NettyHttpRequest", "org.jboss.resteasy.plugins.server.netty.NettyHttpResponse"}, order = Integer.MAX_VALUE - 1)
|
||||
public class ResteasyRequestContainerFilterAdapter implements RestRequestFilter, ResteasyContext {
|
||||
|
||||
|
||||
@Override
|
||||
public void filter(RestFilterContext restFilterContext) throws Exception {
|
||||
|
||||
ServiceDeployer serviceDeployer = restFilterContext.getServiceDeployer();
|
||||
RequestFacade requestFacade = restFilterContext.getRequestFacade();
|
||||
URL url = restFilterContext.getUrl();
|
||||
NettyHttpResponse response = restFilterContext.getResponse();
|
||||
|
||||
List<ContainerRequestFilter> containerRequestFilters = getExtension(serviceDeployer, ContainerRequestFilter.class);
|
||||
|
||||
if (containerRequestFilters.isEmpty()) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
DubboPreMatchContainerRequestContext containerRequestContext = convertHttpRequestToContainerRequestContext(requestFacade, containerRequestFilters.toArray(new ContainerRequestFilter[0]));
|
||||
|
||||
RpcContext.getServiceContext().setObjectAttachment(RESTEASY_NETTY_HTTP_REQUEST_ATTRIBUTE_KEY, containerRequestContext.getHttpRequest());
|
||||
|
||||
try {
|
||||
BuiltResponse restResponse = containerRequestContext.filter();
|
||||
|
||||
if (restResponse == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
addResponseHeaders(response, restResponse.getHeaders());
|
||||
writeResteasyResponse(url, requestFacade, response, restResponse);
|
||||
// completed
|
||||
restFilterContext.setComplete(true);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("dubbo rest resteasy ContainerRequestFilter write response encode error", e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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.rpc.protocol.rest.extension.resteasy.filter;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
|
||||
import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.ResteasyContext;
|
||||
import org.apache.dubbo.rpc.protocol.rest.filter.RestResponseFilter;
|
||||
import org.apache.dubbo.rpc.protocol.rest.filter.context.RestFilterContext;
|
||||
import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
|
||||
import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
|
||||
import org.jboss.resteasy.specimpl.BuiltResponse;
|
||||
import org.jboss.resteasy.spi.HttpResponse;
|
||||
|
||||
import javax.ws.rs.container.ContainerResponseFilter;
|
||||
import java.util.List;
|
||||
|
||||
@Activate(value = "resteasy", order = Integer.MAX_VALUE - 1000, onClass = {"org.jboss.resteasy.specimpl.BuiltResponse", "javax.ws.rs.container.ContainerResponseFilter", "org.jboss.resteasy.spi.HttpResponse", "org.jboss.resteasy.plugins.server.netty.NettyHttpResponse"})
|
||||
public class ResteasyResponseContainerFilterAdapter implements RestResponseFilter, ResteasyContext {
|
||||
@Override
|
||||
public void filter(RestFilterContext restFilterContext) throws Exception {
|
||||
|
||||
|
||||
ServiceDeployer serviceDeployer = restFilterContext.getServiceDeployer();
|
||||
RequestFacade requestFacade = restFilterContext.getRequestFacade();
|
||||
NettyHttpResponse response = restFilterContext.getResponse();
|
||||
URL url = restFilterContext.getUrl();
|
||||
List<ContainerResponseFilter> containerRequestFilters = getExtension(serviceDeployer, ContainerResponseFilter.class);
|
||||
|
||||
|
||||
if (containerRequestFilters.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// response filter entity first
|
||||
|
||||
|
||||
// empty jaxrsResponse
|
||||
BuiltResponse jaxrsResponse = new BuiltResponse();
|
||||
// NettyHttpResponse wrapper
|
||||
HttpResponse httpResponse = new ResteasyNettyHttpResponse(response);
|
||||
DubboContainerResponseContextImpl containerResponseContext = createContainerResponseContext(requestFacade, httpResponse, jaxrsResponse, containerRequestFilters.toArray(new ContainerResponseFilter[0]));
|
||||
containerResponseContext.filter();
|
||||
if (jaxrsResponse.getEntity() != null) {
|
||||
// clean output stream data
|
||||
restOutputStream(response);
|
||||
writeResteasyResponse(url, requestFacade, response, jaxrsResponse);
|
||||
}
|
||||
addResponseHeaders(response, httpResponse.getOutputHeaders());
|
||||
|
||||
restFilterContext.setComplete(true);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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.rpc.protocol.rest.extension.resteasy.intercept;
|
||||
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.jboss.resteasy.core.interception.ServerWriterInterceptorContext;
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.ext.WriterInterceptor;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class DubboServerWriterInterceptorContext extends ServerWriterInterceptorContext {
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DubboServerWriterInterceptorContext.class);
|
||||
|
||||
public DubboServerWriterInterceptorContext(WriterInterceptor[] interceptors, ResteasyProviderFactory providerFactory, Object entity, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> headers, OutputStream outputStream, HttpRequest request) {
|
||||
super(interceptors, providerFactory, entity, type, genericType, annotations, mediaType, headers, outputStream, request);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void proceed() throws IOException, WebApplicationException {
|
||||
logger.debug("Dubbo server writer intercept context: " + getClass().getName() + " Method : proceed");
|
||||
|
||||
if (interceptors == null || index >= interceptors.length) {
|
||||
return;
|
||||
} else {
|
||||
|
||||
logger.debug("Dubbo server writer intercept context WriterInterceptor: " + interceptors[index].getClass().getName());
|
||||
interceptors[index++].aroundWriteTo(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* 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.rpc.protocol.rest.extension.resteasy.intercept;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum;
|
||||
import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
|
||||
import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.ResteasyContext;
|
||||
import org.apache.dubbo.rpc.protocol.rest.filter.RestResponseInterceptor;
|
||||
import org.apache.dubbo.rpc.protocol.rest.filter.context.RestInterceptContext;
|
||||
import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
|
||||
import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
|
||||
import org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext;
|
||||
import org.jboss.resteasy.plugins.server.netty.NettyHttpRequest;
|
||||
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.ext.WriterInterceptor;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.RESTEASY_NETTY_HTTP_REQUEST_ATTRIBUTE_KEY;
|
||||
|
||||
@Activate(value = "resteasy", onClass = {"javax.ws.rs.ext.WriterInterceptorContext", "org.jboss.resteasy.plugins.server.netty.NettyHttpRequest", "org.jboss.resteasy.plugins.server.netty.NettyHttpResponse"})
|
||||
public class ResteasyWriterInterceptorAdapter implements RestResponseInterceptor, ResteasyContext {
|
||||
|
||||
private ResteasyProviderFactory resteasyProviderFactory = getResteasyProviderFactory();
|
||||
|
||||
|
||||
@Override
|
||||
public void intercept(RestInterceptContext restResponseInterceptor) throws Exception {
|
||||
|
||||
RpcInvocation rpcInvocation = restResponseInterceptor.getRpcInvocation();
|
||||
ServiceDeployer serviceDeployer = restResponseInterceptor.getServiceDeployer();
|
||||
RequestFacade request = restResponseInterceptor.getRequestFacade();
|
||||
NettyHttpResponse response = restResponseInterceptor.getResponse();
|
||||
Object result = restResponseInterceptor.getResult();
|
||||
|
||||
Class<?> type = rpcInvocation.getReturnType();
|
||||
|
||||
|
||||
List<WriterInterceptor> extension = serviceDeployer.getExtensions(WriterInterceptor.class);
|
||||
|
||||
if (extension.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
NettyHttpRequest nettyHttpRequest = (NettyHttpRequest) RpcContext.getServiceContext().getObjectAttachment(RESTEASY_NETTY_HTTP_REQUEST_ATTRIBUTE_KEY);
|
||||
|
||||
HttpRequest restRequest = nettyHttpRequest == null ? createNettyHttpRequest(request) : nettyHttpRequest;
|
||||
|
||||
MultivaluedMap<String, Object> headers = new MultivaluedMapImpl();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
|
||||
// get content-type
|
||||
String value = getAcceptMediaType(request, type).value;
|
||||
|
||||
MediaType mediaType = MediaType.valueOf(value);
|
||||
|
||||
AbstractWriterInterceptorContext writerContext = getAbstractWriterInterceptorContext(restRequest, extension, result, type, type, mediaType, os, headers);
|
||||
|
||||
writerContext.proceed();
|
||||
ByteArrayOutputStream outputStream = (ByteArrayOutputStream) writerContext.getOutputStream();
|
||||
|
||||
addResponseHeaders(response, writerContext.getHeaders());
|
||||
|
||||
if (outputStream.size() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// intercept response first
|
||||
restOutputStream(response);
|
||||
|
||||
byte[] bytes = outputStream.toByteArray();
|
||||
response.getOutputStream().write(bytes);
|
||||
response.addOutputHeaders(RestHeaderEnum.CONTENT_TYPE.getHeader(), value);
|
||||
|
||||
restResponseInterceptor.setComplete(true);
|
||||
} finally {
|
||||
IOUtils.close(os);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private AbstractWriterInterceptorContext getAbstractWriterInterceptorContext(HttpRequest request,
|
||||
List<WriterInterceptor> extension,
|
||||
Object entity,
|
||||
Class type,
|
||||
Type genericType,
|
||||
MediaType mediaType,
|
||||
ByteArrayOutputStream os,
|
||||
MultivaluedMap<String, Object> headers) {
|
||||
AbstractWriterInterceptorContext writerContext = new DubboServerWriterInterceptorContext(extension.toArray(new WriterInterceptor[0]),
|
||||
resteasyProviderFactory, entity, type, genericType, new Annotation[0], mediaType,
|
||||
headers, os, request);
|
||||
return writerContext;
|
||||
}
|
||||
|
||||
protected ResteasyProviderFactory getResteasyProviderFactory() {
|
||||
return new ResteasyProviderFactory();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.rpc.protocol.rest.filter;
|
||||
|
||||
import org.apache.dubbo.rpc.protocol.rest.filter.context.RestFilterContext;
|
||||
|
||||
/**
|
||||
* Rest filter is extended by rest request & response filter
|
||||
*/
|
||||
public interface RestFilter {
|
||||
|
||||
void filter(RestFilterContext restFilterContext) throws Exception;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.rpc.protocol.rest.filter;
|
||||
|
||||
import org.apache.dubbo.common.extension.ExtensionScope;
|
||||
import org.apache.dubbo.common.extension.SPI;
|
||||
|
||||
/**
|
||||
* Rest filter will be invoked before http handler
|
||||
*/
|
||||
@SPI(scope = ExtensionScope.FRAMEWORK)
|
||||
public interface RestRequestFilter extends RestFilter {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.rpc.protocol.rest.filter;
|
||||
|
||||
import org.apache.dubbo.common.extension.ExtensionScope;
|
||||
import org.apache.dubbo.common.extension.SPI;
|
||||
|
||||
/**
|
||||
* Rest response filter will be invoked when response is written to channel
|
||||
*/
|
||||
@SPI(scope = ExtensionScope.FRAMEWORK)
|
||||
public interface RestResponseFilter extends RestFilter {
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue