init
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
# Sentinel Apache Dubbo Adapter (for 2.7.x+)
|
||||
|
||||
> Note: 中文文档请见[此处](https://github.com/alibaba/Sentinel/wiki/主流框架的适配#dubbo)。
|
||||
|
||||
Sentinel Dubbo Adapter provides service consumer filter and provider filter
|
||||
for [Apache Dubbo](https://dubbo.apache.org/en-us/) services.
|
||||
|
||||
**Note: This adapter only supports Apache Dubbo 2.7.x and above.** For legacy `com.alibaba:dubbo` 2.6.x,
|
||||
please use `sentinel-dubbo-adapter` module instead.
|
||||
|
||||
To use Sentinel Dubbo Adapter, you can simply add the following dependency to your `pom.xml`:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-apache-dubbo-adapter</artifactId>
|
||||
<version>x.y.z</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
The Sentinel filters are **enabled by default**. Once you add the dependency,
|
||||
the Dubbo services and methods will become protected resources in Sentinel,
|
||||
which can leverage Sentinel's flow control and guard ability when rules are configured.
|
||||
Demos can be found in [sentinel-demo-apache-dubbo](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-apache-dubbo).
|
||||
|
||||
If you don't want the filters enabled, you can manually disable them. For example:
|
||||
|
||||
```xml
|
||||
<dubbo:consumer filter="-sentinel.dubbo.consumer.filter"/>
|
||||
|
||||
<dubbo:provider filter="-sentinel.dubbo.provider.filter"/>
|
||||
```
|
||||
|
||||
For more details of Dubbo filter, see [here](http://dubbo.apache.org/en-us/docs/dev/impls/filter.html).
|
||||
|
||||
## Dubbo resources
|
||||
|
||||
The resource for Dubbo services has two granularities: service interface and service method.
|
||||
|
||||
- Service interface: resourceName format is `interfaceName`, e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService`
|
||||
- Service method: resourceName format is `interfaceName:methodSignature`, e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String)`
|
||||
|
||||
## Flow control based on caller
|
||||
|
||||
In many circumstances, it's also significant to control traffic flow based on the **caller**.
|
||||
For example, assuming that there are two services A and B, both of them initiate remote call requests to the service provider.
|
||||
If we want to limit the calls from service B only, we can set the `limitApp` of flow rule as the identifier of service B (e.g. service name).
|
||||
|
||||
Sentinel Dubbo Adapter will automatically resolve the Dubbo consumer's *application name* as the caller's name (`origin`),
|
||||
and will bring the caller's name when doing resource protection.
|
||||
If `limitApp` of flow rules is not configured (`default`), flow control will take effects on all callers.
|
||||
If `limitApp` of a flow rule is configured with a caller, then the corresponding flow rule will only take effect on the specific caller.
|
||||
|
||||
> Note: Dubbo consumer does not provide its Dubbo application name when doing RPC,
|
||||
> so developers should manually put the application name into *attachment* at consumer side,
|
||||
> then extract it at provider side. Sentinel Dubbo Adapter has implemented a filter (`DubboAppContextFilter`)
|
||||
> where consumer can carry application name information to provider automatically.
|
||||
> If the consumer does not use Sentinel Dubbo Adapter but requires flow control based on caller,
|
||||
> developers can manually put the application name into attachment with the key `dubboApplication`.
|
||||
>
|
||||
> Since 1.8.0, the adapter provides support for customizing origin parsing logic. You may register your own `DubboOriginParser`
|
||||
> implementation to `DubboAdapterGlobalConfig`.
|
||||
|
||||
## Global fallback
|
||||
|
||||
Sentinel Dubbo Adapter supports global fallback configuration.
|
||||
The global fallback will handle exceptions and give replacement result when blocked by
|
||||
flow control, degrade or system load protection. You can implement your own `DubboFallback` interface
|
||||
and then register to `DubboAdapterGlobalConfig`.
|
||||
If no fallback is configured, Sentinel will wrap the `BlockException` as the fallback result.
|
||||
|
||||
Besides, we can also leverage [Dubbo mock mechanism](http://dubbo.apache.org/en-us/docs/user/demos/local-mock.html) to provide fallback implementation of degraded Dubbo services.
|
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>sentinel-adapter</artifactId>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<version>1.8.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>sentinel-apache-dubbo-adapter</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<java.source.version>1.8</java.source.version>
|
||||
<java.target.version>1.8</java.target.version>
|
||||
<apache.dubbo.version>2.7.13</apache.dubbo.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo</artifactId>
|
||||
<version>${apache.dubbo.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
|
||||
/**
|
||||
* Base class of the {@link SentinelDubboProviderFilter} and {@link SentinelDubboConsumerFilter}.
|
||||
*
|
||||
* @author Zechao Zheng
|
||||
*/
|
||||
public abstract class BaseSentinelDubboFilter implements Filter {
|
||||
|
||||
|
||||
/**
|
||||
* Get method name of dubbo rpc
|
||||
*
|
||||
* @param invoker
|
||||
* @param invocation
|
||||
* @return
|
||||
*/
|
||||
abstract String getMethodName(Invoker invoker, Invocation invocation, String prefix);
|
||||
|
||||
/**
|
||||
* Get interface name of dubbo rpc
|
||||
*
|
||||
* @param invoker
|
||||
* @return
|
||||
*/
|
||||
abstract String getInterfaceName(Invoker invoker, String prefix);
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import org.apache.dubbo.common.constants.CommonConstants;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
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.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
|
||||
|
||||
/**
|
||||
* Puts current consumer's application name in the attachment of each invocation.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
@Activate(group = CONSUMER)
|
||||
public class DubboAppContextFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
String application = invoker.getUrl().getParameter(CommonConstants.APPLICATION_KEY);
|
||||
if (application != null) {
|
||||
RpcContext.getContext().setAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, application);
|
||||
}
|
||||
return invoker.invoke(invocation);
|
||||
}
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public final class DubboUtils {
|
||||
|
||||
public static final String SENTINEL_DUBBO_APPLICATION_KEY = "dubboApplication";
|
||||
|
||||
public static String getApplication(Invocation invocation, String defaultValue) {
|
||||
if (invocation == null || invocation.getAttachments() == null) {
|
||||
throw new IllegalArgumentException("Bad invocation instance");
|
||||
}
|
||||
return invocation.getAttachment(SENTINEL_DUBBO_APPLICATION_KEY, defaultValue);
|
||||
}
|
||||
|
||||
public static String getMethodResourceName(Invoker<?> invoker, Invocation invocation){
|
||||
return getMethodResourceName(invoker, invocation, false);
|
||||
}
|
||||
|
||||
public static String getMethodResourceName(Invoker<?> invoker, Invocation invocation, Boolean useGroupAndVersion) {
|
||||
StringBuilder buf = new StringBuilder(64);
|
||||
String interfaceResource = useGroupAndVersion ? invoker.getUrl().getColonSeparatedKey() : invoker.getInterface().getName();
|
||||
buf.append(interfaceResource)
|
||||
.append(":")
|
||||
.append(invocation.getMethodName())
|
||||
.append("(");
|
||||
boolean isFirst = true;
|
||||
for (Class<?> clazz : invocation.getParameterTypes()) {
|
||||
if (!isFirst) {
|
||||
buf.append(",");
|
||||
}
|
||||
buf.append(clazz.getName());
|
||||
isFirst = false;
|
||||
}
|
||||
buf.append(")");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String getMethodResourceName(Invoker<?> invoker, Invocation invocation, String prefix) {
|
||||
if (StringUtil.isNotBlank(prefix)) {
|
||||
return new StringBuilder(64)
|
||||
.append(prefix)
|
||||
.append(getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboInterfaceGroupAndVersionEnabled()))
|
||||
.toString();
|
||||
} else {
|
||||
return getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboInterfaceGroupAndVersionEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String getInterfaceName(Invoker invoker) {
|
||||
return getInterfaceName(invoker, false);
|
||||
}
|
||||
|
||||
public static String getInterfaceName(Invoker<?> invoker, Boolean useGroupAndVersion) {
|
||||
StringBuilder buf = new StringBuilder(64);
|
||||
return useGroupAndVersion ? invoker.getUrl().getColonSeparatedKey() : invoker.getInterface().getName();
|
||||
}
|
||||
|
||||
public static String getInterfaceName(Invoker<?> invoker, String prefix) {
|
||||
if (StringUtil.isNotBlank(prefix)) {
|
||||
return new StringBuilder(64)
|
||||
.append(prefix)
|
||||
.append(getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboInterfaceGroupAndVersionEnabled()))
|
||||
.toString();
|
||||
} else {
|
||||
return getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboInterfaceGroupAndVersionEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private DubboUtils() {
|
||||
}
|
||||
}
|
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.*;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.rpc.*;
|
||||
import org.apache.dubbo.rpc.support.RpcUtils;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
|
||||
|
||||
/**
|
||||
* <p>Dubbo service consumer filter for Sentinel. Auto activated by default.</p>
|
||||
* <p>
|
||||
* If you want to disable the consumer filter, you can configure:
|
||||
* <pre>
|
||||
* <dubbo:consumer filter="-sentinel.dubbo.consumer.filter"/>
|
||||
* </pre>
|
||||
*
|
||||
* @author Carpenter Lee
|
||||
* @author Eric Zhao
|
||||
* @author Lin Liang
|
||||
*/
|
||||
@Activate(group = CONSUMER)
|
||||
public class SentinelDubboConsumerFilter extends BaseSentinelDubboFilter {
|
||||
|
||||
public SentinelDubboConsumerFilter() {
|
||||
RecordLog.info("Sentinel Apache Dubbo consumer filter initialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
String getMethodName(Invoker invoker, Invocation invocation, String prefix) {
|
||||
return DubboUtils.getMethodResourceName(invoker, invocation, prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
String getInterfaceName(Invoker invoker, String prefix) {
|
||||
return DubboUtils.getInterfaceName(invoker, prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
InvokeMode invokeMode = RpcUtils.getInvokeMode(invoker.getUrl(), invocation);
|
||||
if (InvokeMode.SYNC == invokeMode) {
|
||||
return syncInvoke(invoker, invocation);
|
||||
} else {
|
||||
return asyncInvoke(invoker, invocation);
|
||||
}
|
||||
}
|
||||
|
||||
private Result syncInvoke(Invoker<?> invoker, Invocation invocation) {
|
||||
Entry interfaceEntry = null;
|
||||
Entry methodEntry = null;
|
||||
String prefix = DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey();
|
||||
String interfaceResourceName = getInterfaceName(invoker, prefix);
|
||||
String methodResourceName = getMethodName(invoker, invocation, prefix);
|
||||
try {
|
||||
interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
|
||||
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT,
|
||||
invocation.getArguments());
|
||||
Result result = invoker.invoke(invocation);
|
||||
if (result.hasException()) {
|
||||
Tracer.traceEntry(result.getException(), interfaceEntry);
|
||||
Tracer.traceEntry(result.getException(), methodEntry);
|
||||
}
|
||||
return result;
|
||||
} catch (BlockException e) {
|
||||
return DubboAdapterGlobalConfig.getConsumerFallback().handle(invoker, invocation, e);
|
||||
} catch (RpcException e) {
|
||||
Tracer.traceEntry(e, interfaceEntry);
|
||||
Tracer.traceEntry(e, methodEntry);
|
||||
throw e;
|
||||
} finally {
|
||||
if (methodEntry != null) {
|
||||
methodEntry.exit(1, invocation.getArguments());
|
||||
}
|
||||
if (interfaceEntry != null) {
|
||||
interfaceEntry.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Result asyncInvoke(Invoker<?> invoker, Invocation invocation) {
|
||||
LinkedList<EntryHolder> queue = new LinkedList<>();
|
||||
String prefix = DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey();
|
||||
String interfaceResourceName = getInterfaceName(invoker, prefix);
|
||||
String methodResourceName = getMethodName(invoker, invocation, prefix);
|
||||
try {
|
||||
queue.push(new EntryHolder(
|
||||
SphU.asyncEntry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT), null));
|
||||
queue.push(new EntryHolder(
|
||||
SphU.asyncEntry(methodResourceName, ResourceTypeConstants.COMMON_RPC,
|
||||
EntryType.OUT, 1, invocation.getArguments()), invocation.getArguments()));
|
||||
Result result = invoker.invoke(invocation);
|
||||
result.whenCompleteWithContext((r, throwable) -> {
|
||||
Throwable error = throwable;
|
||||
if (error == null) {
|
||||
error = Optional.ofNullable(r).map(Result::getException).orElse(null);
|
||||
}
|
||||
while (!queue.isEmpty()) {
|
||||
EntryHolder holder = queue.pop();
|
||||
Tracer.traceEntry(error, holder.entry);
|
||||
exitEntry(holder);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
} catch (BlockException e) {
|
||||
while (!queue.isEmpty()) {
|
||||
exitEntry(queue.pop());
|
||||
}
|
||||
return DubboAdapterGlobalConfig.getConsumerFallback().handle(invoker, invocation, e);
|
||||
}
|
||||
}
|
||||
|
||||
static class EntryHolder {
|
||||
|
||||
final private Entry entry;
|
||||
final private Object[] params;
|
||||
|
||||
public EntryHolder(Entry entry, Object[] params) {
|
||||
this.entry = entry;
|
||||
this.params = params;
|
||||
}
|
||||
}
|
||||
|
||||
private void exitEntry(EntryHolder holder) {
|
||||
if (holder.params != null) {
|
||||
holder.entry.exit(1, holder.params);
|
||||
} else {
|
||||
holder.entry.exit();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.*;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
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;
|
||||
|
||||
/**
|
||||
* <p>Apache Dubbo service provider filter that enables integration with Sentinel. Auto activated by default.</p>
|
||||
* <p>Note: this only works for Apache Dubbo 2.7.x or above version.</p>
|
||||
* <p>
|
||||
* If you want to disable the provider filter, you can configure:
|
||||
* <pre>
|
||||
* <dubbo:provider filter="-sentinel.dubbo.provider.filter"/>
|
||||
* </pre>
|
||||
*
|
||||
* @author Carpenter Lee
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
@Activate(group = PROVIDER)
|
||||
public class SentinelDubboProviderFilter extends BaseSentinelDubboFilter {
|
||||
|
||||
public SentinelDubboProviderFilter() {
|
||||
RecordLog.info("Sentinel Apache Dubbo provider filter initialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
String getMethodName(Invoker invoker, Invocation invocation, String prefix) {
|
||||
return DubboUtils.getMethodResourceName(invoker, invocation, prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
String getInterfaceName(Invoker invoker, String prefix) {
|
||||
return DubboUtils.getInterfaceName(invoker, prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
// Get origin caller.
|
||||
String origin = DubboAdapterGlobalConfig.getOriginParser().parse(invoker, invocation);
|
||||
if (null == origin) {
|
||||
origin = "";
|
||||
}
|
||||
Entry interfaceEntry = null;
|
||||
Entry methodEntry = null;
|
||||
String prefix = DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey();
|
||||
String interfaceResourceName = getInterfaceName(invoker, prefix);
|
||||
String methodResourceName = getMethodName(invoker, invocation, prefix);
|
||||
try {
|
||||
// Only need to create entrance context at provider side, as context will take effect
|
||||
// at entrance of invocation chain only (for inbound traffic).
|
||||
ContextUtil.enter(methodResourceName, origin);
|
||||
interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN);
|
||||
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN,
|
||||
invocation.getArguments());
|
||||
Result result = invoker.invoke(invocation);
|
||||
if (result.hasException()) {
|
||||
Tracer.traceEntry(result.getException(), interfaceEntry);
|
||||
Tracer.traceEntry(result.getException(), methodEntry);
|
||||
}
|
||||
return result;
|
||||
} catch (BlockException e) {
|
||||
return DubboAdapterGlobalConfig.getProviderFallback().handle(invoker, invocation, e);
|
||||
} catch (RpcException e) {
|
||||
Tracer.traceEntry(e, interfaceEntry);
|
||||
Tracer.traceEntry(e, methodEntry);
|
||||
throw e;
|
||||
} finally {
|
||||
if (methodEntry != null) {
|
||||
methodEntry.exit(1, invocation.getArguments());
|
||||
}
|
||||
if (interfaceEntry != null) {
|
||||
interfaceEntry.exit();
|
||||
}
|
||||
ContextUtil.exit();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.config;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DefaultDubboFallback;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallback;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.origin.DefaultDubboOriginParser;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.origin.DubboOriginParser;
|
||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Responsible for dubbo service provider, consumer attribute configuration
|
||||
* </p>
|
||||
*
|
||||
* @author lianglin
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public final class DubboAdapterGlobalConfig {
|
||||
|
||||
private static final String TRUE_STR = "true";
|
||||
|
||||
public static final String DUBBO_RES_NAME_WITH_PREFIX_KEY = "csp.sentinel.dubbo.resource.use.prefix";
|
||||
public static final String DUBBO_PROVIDER_RES_NAME_PREFIX_KEY = "csp.sentinel.dubbo.resource.provider.prefix";
|
||||
public static final String DUBBO_CONSUMER_RES_NAME_PREFIX_KEY = "csp.sentinel.dubbo.resource.consumer.prefix";
|
||||
|
||||
private static final String DEFAULT_DUBBO_PROVIDER_PREFIX = "dubbo:provider:";
|
||||
private static final String DEFAULT_DUBBO_CONSUMER_PREFIX = "dubbo:consumer:";
|
||||
|
||||
public static final String DUBBO_INTERFACE_GROUP_VERSION_ENABLED = "csp.sentinel.dubbo.interface.group.version.enabled";
|
||||
|
||||
private static volatile DubboFallback consumerFallback = new DefaultDubboFallback();
|
||||
private static volatile DubboFallback providerFallback = new DefaultDubboFallback();
|
||||
private static volatile DubboOriginParser originParser = new DefaultDubboOriginParser();
|
||||
|
||||
public static boolean isUsePrefix() {
|
||||
return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_RES_NAME_WITH_PREFIX_KEY));
|
||||
}
|
||||
|
||||
public static String getDubboProviderResNamePrefixKey() {
|
||||
if (isUsePrefix()) {
|
||||
String config = SentinelConfig.getConfig(DUBBO_PROVIDER_RES_NAME_PREFIX_KEY);
|
||||
return StringUtil.isNotBlank(config) ? config : DEFAULT_DUBBO_PROVIDER_PREFIX;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getDubboConsumerResNamePrefixKey() {
|
||||
if (isUsePrefix()) {
|
||||
String config = SentinelConfig.getConfig(DUBBO_CONSUMER_RES_NAME_PREFIX_KEY);
|
||||
return StringUtil.isNotBlank(config) ? config : DEFAULT_DUBBO_CONSUMER_PREFIX;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Boolean getDubboInterfaceGroupAndVersionEnabled() {
|
||||
return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED));
|
||||
}
|
||||
|
||||
public static DubboFallback getConsumerFallback() {
|
||||
return consumerFallback;
|
||||
}
|
||||
|
||||
public static void setConsumerFallback(DubboFallback consumerFallback) {
|
||||
AssertUtil.notNull(consumerFallback, "consumerFallback cannot be null");
|
||||
DubboAdapterGlobalConfig.consumerFallback = consumerFallback;
|
||||
}
|
||||
|
||||
public static DubboFallback getProviderFallback() {
|
||||
return providerFallback;
|
||||
}
|
||||
|
||||
public static void setProviderFallback(DubboFallback providerFallback) {
|
||||
AssertUtil.notNull(providerFallback, "providerFallback cannot be null");
|
||||
DubboAdapterGlobalConfig.providerFallback = providerFallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the origin parser of Dubbo adapter.
|
||||
*
|
||||
* @return the origin parser
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public static DubboOriginParser getOriginParser() {
|
||||
return originParser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the origin parser of Dubbo adapter.
|
||||
*
|
||||
* @param originParser the origin parser
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public static void setOriginParser(DubboOriginParser originParser) {
|
||||
AssertUtil.notNull(originParser, "originParser cannot be null");
|
||||
DubboAdapterGlobalConfig.originParser = originParser;
|
||||
}
|
||||
|
||||
private DubboAdapterGlobalConfig() {}
|
||||
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class DefaultDubboFallback implements DubboFallback {
|
||||
|
||||
@Override
|
||||
public Result handle(Invoker<?> invoker, Invocation invocation, BlockException ex) {
|
||||
// Just wrap the exception.
|
||||
return AsyncRpcResult.newDefaultAsyncResult(ex.toRuntimeException(), invocation);
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
|
||||
/**
|
||||
* Fallback handler for Dubbo services.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DubboFallback {
|
||||
|
||||
/**
|
||||
* Handle the block exception and provide fallback result.
|
||||
*
|
||||
* @param invoker Dubbo invoker
|
||||
* @param invocation Dubbo invocation
|
||||
* @param ex block exception
|
||||
* @return fallback result
|
||||
*/
|
||||
Result handle(Invoker<?> invoker, Invocation invocation, BlockException ex);
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
|
||||
/**
|
||||
* <p>Global fallback registry for Dubbo.</p>
|
||||
*
|
||||
* @author Eric Zhao
|
||||
* @deprecated use {@link DubboAdapterGlobalConfig} instead since 1.8.0.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class DubboFallbackRegistry {
|
||||
|
||||
public static DubboFallback getConsumerFallback() {
|
||||
return DubboAdapterGlobalConfig.getConsumerFallback();
|
||||
}
|
||||
|
||||
public static void setConsumerFallback(DubboFallback consumerFallback) {
|
||||
DubboAdapterGlobalConfig.setConsumerFallback(consumerFallback);
|
||||
}
|
||||
|
||||
public static DubboFallback getProviderFallback() {
|
||||
return DubboAdapterGlobalConfig.getProviderFallback();
|
||||
}
|
||||
|
||||
public static void setProviderFallback(DubboFallback providerFallback) {
|
||||
DubboAdapterGlobalConfig.setProviderFallback(providerFallback);
|
||||
}
|
||||
|
||||
private DubboFallbackRegistry() {}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.origin;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.DubboUtils;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
|
||||
/**
|
||||
* Default Dubbo origin parser.
|
||||
*
|
||||
* @author jingzian
|
||||
*/
|
||||
public class DefaultDubboOriginParser implements DubboOriginParser {
|
||||
|
||||
@Override
|
||||
public String parse(Invoker<?> invoker, Invocation invocation) {
|
||||
return DubboUtils.getApplication(invocation, "");
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.origin;
|
||||
|
||||
import com.alibaba.csp.sentinel.context.Context;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
|
||||
/**
|
||||
* Customized origin parser for Dubbo provider filter.{@link Context#getOrigin()}
|
||||
*
|
||||
* @author jingzian
|
||||
*/
|
||||
public interface DubboOriginParser {
|
||||
|
||||
/**
|
||||
* Parses the origin (caller) from Dubbo invocation.
|
||||
*
|
||||
* @param invoker Dubbo invoker
|
||||
* @param invocation Dubbo invocation
|
||||
* @return the parsed origin
|
||||
*/
|
||||
String parse(Invoker<?> invoker, Invocation invocation);
|
||||
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
sentinel.dubbo.provider.filter=com.alibaba.csp.sentinel.adapter.dubbo.SentinelDubboProviderFilter
|
||||
sentinel.dubbo.consumer.filter=com.alibaba.csp.sentinel.adapter.dubbo.SentinelDubboConsumerFilter
|
||||
dubbo.application.context.name.filter=com.alibaba.csp.sentinel.adapter.dubbo.DubboAppContextFilter
|
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DefaultDubboFallback;
|
||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Base test class, provide common methods for subClass
|
||||
* The package is same as CtSph, to call CtSph.resetChainMap() method for test
|
||||
* <p>
|
||||
* Note: Only for test. DO NOT USE IN PRODUCTION!
|
||||
*
|
||||
* @author cdfive
|
||||
* @author lianglin
|
||||
*/
|
||||
public class BaseTest {
|
||||
|
||||
|
||||
/**
|
||||
* Clean up resources for context, clusterNodeMap, processorSlotChainMap
|
||||
*/
|
||||
public void cleanUpAll() {
|
||||
try {
|
||||
clearDubboContext();
|
||||
cleanUpCstContext();
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanUpCstContext() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
ClusterBuilderSlot.getClusterNodeMap().clear();
|
||||
CtSph.resetChainMap();
|
||||
Method method = ContextUtil.class.getDeclaredMethod("resetContextMap");
|
||||
method.setAccessible(true);
|
||||
method.invoke(null, null);
|
||||
ContextUtil.exit();
|
||||
FlowRuleManager.loadRules(new ArrayList<>());
|
||||
DegradeRuleManager.loadRules(new ArrayList<>());
|
||||
}
|
||||
|
||||
private void clearDubboContext() {
|
||||
SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "false");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
|
||||
DubboAdapterGlobalConfig.setConsumerFallback(new DefaultDubboFallback());
|
||||
RpcContext.removeContext();
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.constants.CommonConstants;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author lianglin
|
||||
*/
|
||||
public class DubboTestUtil {
|
||||
|
||||
|
||||
public static Class<?> DEFAULT_TEST_SERVICE = DemoService.class;
|
||||
public static Method DEFAULT_TEST_METHOD_ONE = DEFAULT_TEST_SERVICE.getMethods()[0];
|
||||
public static Method DEFAULT_TEST_METHOD_TWO = DEFAULT_TEST_SERVICE.getMethods()[1];
|
||||
|
||||
public static Invoker getMockInvoker(URL url, Class<?> cls) {
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
when(invoker.getUrl()).thenReturn(url);
|
||||
when(invoker.getInterface()).thenReturn(cls);
|
||||
return invoker;
|
||||
}
|
||||
|
||||
public static Invoker getDefaultMockInvoker() {
|
||||
return getMockInvoker(getDefaultTestURL(), DEFAULT_TEST_SERVICE);
|
||||
}
|
||||
|
||||
public static Invocation getMockInvocation(Method method) {
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
when(invocation.getMethodName()).thenReturn(method.getName());
|
||||
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
|
||||
return invocation;
|
||||
}
|
||||
|
||||
public static Invocation getDefaultMockInvocationOne() {
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
when(invocation.getMethodName()).thenReturn(DEFAULT_TEST_METHOD_ONE.getName());
|
||||
when(invocation.getParameterTypes()).thenReturn(DEFAULT_TEST_METHOD_ONE.getParameterTypes());
|
||||
return invocation;
|
||||
}
|
||||
|
||||
public static Invocation getDefaultMockInvocationTwo() {
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
when(invocation.getMethodName()).thenReturn(DEFAULT_TEST_METHOD_TWO.getName());
|
||||
when(invocation.getParameterTypes()).thenReturn(DEFAULT_TEST_METHOD_TWO.getParameterTypes());
|
||||
return invocation;
|
||||
}
|
||||
|
||||
public static URL getDefaultTestURL() {
|
||||
URL url = URL.valueOf("dubbo://127.0.0.1:2181")
|
||||
.addParameter(CommonConstants.VERSION_KEY, "1.0.0")
|
||||
.addParameter(CommonConstants.GROUP_KEY, "grp1")
|
||||
.addParameter(CommonConstants.INTERFACE_KEY, DEFAULT_TEST_SERVICE.getName());
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.BaseTest;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* @author cdfive
|
||||
*/
|
||||
public class DubboAppContextFilterTest extends BaseTest {
|
||||
|
||||
private DubboAppContextFilter filter = new DubboAppContextFilter();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
cleanUpAll();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
cleanUpAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeApplicationKey() {
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
URL url = URL.valueOf("test://test:111/test?application=serviceA");
|
||||
when(invoker.getUrl()).thenReturn(url);
|
||||
|
||||
filter.invoke(invoker, invocation);
|
||||
verify(invoker).invoke(invocation);
|
||||
|
||||
String application = RpcContext.getContext().getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY);
|
||||
assertEquals("serviceA", application);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeNullApplicationKey() {
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
URL url = URL.valueOf("test://test:111/test?application=");
|
||||
when(invoker.getUrl()).thenReturn(url);
|
||||
|
||||
filter.invoke(invoker, invocation);
|
||||
verify(invoker).invoke(invocation);
|
||||
|
||||
String application = RpcContext.getContext().getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY);
|
||||
assertEquals(application, "");
|
||||
}
|
||||
}
|
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
|
||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.constants.CommonConstants;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig.DUBBO_INTERFACE_GROUP_VERSION_ENABLED;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* @author cdfive
|
||||
*/
|
||||
public class DubboUtilsTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "true");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "");
|
||||
SentinelConfig.setConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
|
||||
}
|
||||
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "false");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "");
|
||||
SentinelConfig.setConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetApplication() {
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
when(invocation.getAttachments()).thenReturn(new HashMap<>());
|
||||
when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, ""))
|
||||
.thenReturn("consumerA");
|
||||
|
||||
String application = DubboUtils.getApplication(invocation, "");
|
||||
verify(invocation).getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, "");
|
||||
|
||||
assertEquals("consumerA", application);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testGetApplicationNoAttachments() {
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
when(invocation.getAttachments()).thenReturn(null);
|
||||
when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, ""))
|
||||
.thenReturn("consumerA");
|
||||
|
||||
DubboUtils.getApplication(invocation, "");
|
||||
|
||||
fail("No attachments in invocation, IllegalArgumentException should be thrown!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceName() throws NoSuchMethodException {
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
when(invoker.getInterface()).thenReturn(DemoService.class);
|
||||
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
Method method = DemoService.class.getDeclaredMethod("sayHello", String.class, int.class);
|
||||
when(invocation.getMethodName()).thenReturn(method.getName());
|
||||
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
|
||||
|
||||
String resourceName = DubboUtils.getMethodResourceName(invoker, invocation);
|
||||
|
||||
assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceNameWithGroupAndVersion() throws NoSuchMethodException {
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
URL url = URL.valueOf("dubbo://127.0.0.1:2181")
|
||||
.addParameter(CommonConstants.VERSION_KEY, "1.0.0")
|
||||
.addParameter(CommonConstants.GROUP_KEY, "grp1")
|
||||
.addParameter(CommonConstants.INTERFACE_KEY, DemoService.class.getName());
|
||||
when(invoker.getUrl()).thenReturn(url);
|
||||
when(invoker.getInterface()).thenReturn(DemoService.class);
|
||||
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
Method method = DemoService.class.getDeclaredMethod("sayHello", String.class, int.class);
|
||||
when(invocation.getMethodName()).thenReturn(method.getName());
|
||||
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
|
||||
|
||||
String resourceNameUseGroupAndVersion = DubboUtils.getMethodResourceName(invoker, invocation, true);
|
||||
|
||||
assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:1.0.0:grp1:sayHello(java.lang.String,int)", resourceNameUseGroupAndVersion);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetResourceNameWithPrefix() throws NoSuchMethodException {
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
when(invoker.getInterface()).thenReturn(DemoService.class);
|
||||
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
Method method = DemoService.class.getDeclaredMethod("sayHello", String.class, int.class);
|
||||
when(invocation.getMethodName()).thenReturn(method.getName());
|
||||
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
|
||||
|
||||
//test with default prefix
|
||||
String resourceName = DubboUtils.getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey());
|
||||
assertEquals("dubbo:provider:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
|
||||
resourceName = DubboUtils.getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey());
|
||||
assertEquals("dubbo:consumer:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
|
||||
|
||||
|
||||
//test with custom prefix
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "my:dubbo:provider:");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "my:dubbo:consumer:");
|
||||
resourceName = DubboUtils.getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey());
|
||||
assertEquals("my:dubbo:provider:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
|
||||
resourceName = DubboUtils.getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey());
|
||||
assertEquals("my:dubbo:consumer:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInterfaceName() {
|
||||
|
||||
URL url = URL.valueOf("dubbo://127.0.0.1:2181")
|
||||
.addParameter(CommonConstants.VERSION_KEY, "1.0.0")
|
||||
.addParameter(CommonConstants.GROUP_KEY, "grp1")
|
||||
.addParameter(CommonConstants.INTERFACE_KEY, DemoService.class.getName());
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
when(invoker.getUrl()).thenReturn(url);
|
||||
when(invoker.getInterface()).thenReturn(DemoService.class);
|
||||
|
||||
SentinelConfig.setConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
|
||||
assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService", DubboUtils.getInterfaceName(invoker));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInterfaceNameWithGroupAndVersion() throws NoSuchMethodException {
|
||||
|
||||
URL url = URL.valueOf("dubbo://127.0.0.1:2181")
|
||||
.addParameter(CommonConstants.VERSION_KEY, "1.0.0")
|
||||
.addParameter(CommonConstants.GROUP_KEY, "grp1")
|
||||
.addParameter(CommonConstants.INTERFACE_KEY, DemoService.class.getName());
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
when(invoker.getUrl()).thenReturn(url);
|
||||
when(invoker.getInterface()).thenReturn(DemoService.class);
|
||||
|
||||
SentinelConfig.setConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "true");
|
||||
assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:1.0.0:grp1", DubboUtils.getInterfaceName(invoker, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInterfaceNameWithPrefix() throws NoSuchMethodException {
|
||||
URL url = URL.valueOf("dubbo://127.0.0.1:2181")
|
||||
.addParameter(CommonConstants.VERSION_KEY, "1.0.0")
|
||||
.addParameter(CommonConstants.GROUP_KEY, "grp1")
|
||||
.addParameter(CommonConstants.INTERFACE_KEY, DemoService.class.getName());
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
when(invoker.getUrl()).thenReturn(url);
|
||||
when(invoker.getInterface()).thenReturn(DemoService.class);
|
||||
|
||||
|
||||
//test with default prefix
|
||||
String resourceName = DubboUtils.getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey());
|
||||
assertEquals("dubbo:provider:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService", resourceName);
|
||||
resourceName = DubboUtils.getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey());
|
||||
assertEquals("dubbo:consumer:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService", resourceName);
|
||||
|
||||
|
||||
//test with custom prefix
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "my:dubbo:provider:");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "my:dubbo:consumer:");
|
||||
resourceName = DubboUtils.getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey());
|
||||
assertEquals("my:dubbo:provider:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService", resourceName);
|
||||
resourceName = DubboUtils.getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey());
|
||||
assertEquals("my:dubbo:consumer:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService", resourceName);
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.BaseTest;
|
||||
import com.alibaba.csp.sentinel.DubboTestUtil;
|
||||
import com.alibaba.csp.sentinel.Entry;
|
||||
import com.alibaba.csp.sentinel.EntryType;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallback;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
|
||||
import com.alibaba.csp.sentinel.context.Context;
|
||||
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||
import com.alibaba.csp.sentinel.node.ClusterNode;
|
||||
import com.alibaba.csp.sentinel.node.DefaultNode;
|
||||
import com.alibaba.csp.sentinel.node.Node;
|
||||
import com.alibaba.csp.sentinel.node.StatisticNode;
|
||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
||||
|
||||
import org.apache.dubbo.rpc.*;
|
||||
import org.apache.dubbo.rpc.support.RpcUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.alibaba.csp.sentinel.slots.block.RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO;
|
||||
import static org.apache.dubbo.rpc.Constants.ASYNC_KEY;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* @author cdfive
|
||||
* @author lianglin
|
||||
*/
|
||||
public class SentinelDubboConsumerFilterTest extends BaseTest {
|
||||
|
||||
private final SentinelDubboConsumerFilter consumerFilter = new SentinelDubboConsumerFilter();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
cleanUpAll();
|
||||
initFallback();
|
||||
}
|
||||
|
||||
@After
|
||||
public void destroy() {
|
||||
cleanUpAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInterfaceLevelFollowControlAsync() throws InterruptedException {
|
||||
|
||||
Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
|
||||
Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
|
||||
|
||||
when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
|
||||
initFlowRule(DubboUtils.getInterfaceName(invoker));
|
||||
|
||||
Result result1 = invokeDubboRpc(false, invoker, invocation);
|
||||
assertEquals("normal", result1.getValue());
|
||||
|
||||
// should fallback because the qps > 1
|
||||
Result result2 = invokeDubboRpc(false, invoker, invocation);
|
||||
assertEquals("fallback", result2.getValue());
|
||||
|
||||
// sleeping 1000 ms to reset qps
|
||||
Thread.sleep(1000);
|
||||
Result result3 = invokeDubboRpc(false, invoker, invocation);
|
||||
assertEquals("normal", result3.getValue());
|
||||
|
||||
verifyInvocationStructureForCallFinish(invoker, invocation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDegradeAsync() throws InterruptedException {
|
||||
|
||||
Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
|
||||
Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
|
||||
|
||||
when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
|
||||
initDegradeRule(DubboUtils.getInterfaceName(invoker));
|
||||
|
||||
Result result = invokeDubboRpc(false, invoker, invocation);
|
||||
verifyInvocationStructureForCallFinish(invoker, invocation);
|
||||
assertEquals("normal", result.getValue());
|
||||
|
||||
// inc the clusterNode's exception to trigger the fallback
|
||||
for (int i = 0; i < 5; i++) {
|
||||
invokeDubboRpc(true, invoker, invocation);
|
||||
verifyInvocationStructureForCallFinish(invoker, invocation);
|
||||
}
|
||||
|
||||
Result result2 = invokeDubboRpc(false, invoker, invocation);
|
||||
assertEquals("fallback", result2.getValue());
|
||||
|
||||
// sleeping 1000 ms to reset exception
|
||||
Thread.sleep(1000);
|
||||
Result result3 = invokeDubboRpc(false, invoker, invocation);
|
||||
assertEquals("normal", result3.getValue());
|
||||
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNull(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDegradeSync() throws InterruptedException {
|
||||
|
||||
Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
|
||||
Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
|
||||
initDegradeRule(DubboUtils.getInterfaceName(invoker));
|
||||
|
||||
Result result = invokeDubboRpc(false, invoker, invocation);
|
||||
verifyInvocationStructureForCallFinish(invoker, invocation);
|
||||
assertEquals("normal", result.getValue());
|
||||
|
||||
// inc the clusterNode's exception to trigger the fallback
|
||||
for (int i = 0; i < 5; i++) {
|
||||
invokeDubboRpc(true, invoker, invocation);
|
||||
verifyInvocationStructureForCallFinish(invoker, invocation);
|
||||
}
|
||||
|
||||
Result result2 = invokeDubboRpc(false, invoker, invocation);
|
||||
assertEquals("fallback", result2.getValue());
|
||||
|
||||
// sleeping 1000 ms to reset exception
|
||||
Thread.sleep(1000);
|
||||
Result result3 = invokeDubboRpc(false, invoker, invocation);
|
||||
assertEquals("normal", result3.getValue());
|
||||
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNull(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodFlowControlAsync() {
|
||||
|
||||
Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
|
||||
Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
|
||||
|
||||
when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
|
||||
initFlowRule(consumerFilter.getMethodName(invoker, invocation, null));
|
||||
invokeDubboRpc(false, invoker, invocation);
|
||||
invokeDubboRpc(false, invoker, invocation);
|
||||
|
||||
Invocation invocation2 = DubboTestUtil.getDefaultMockInvocationTwo();
|
||||
Result result2 = invokeDubboRpc(false, invoker, invocation2);
|
||||
verifyInvocationStructureForCallFinish(invoker, invocation2);
|
||||
assertEquals("normal", result2.getValue());
|
||||
|
||||
// the method of invocation should be blocked
|
||||
Result fallback = invokeDubboRpc(false, invoker, invocation);
|
||||
assertEquals("fallback", fallback.getValue());
|
||||
verifyInvocationStructureForCallFinish(invoker, invocation);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeAsync() {
|
||||
|
||||
Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
|
||||
Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
|
||||
|
||||
when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
|
||||
final Result result = mock(Result.class);
|
||||
when(result.hasException()).thenReturn(false);
|
||||
when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> {
|
||||
verifyInvocationStructureForAsyncCall(invoker, invocation);
|
||||
return result;
|
||||
});
|
||||
consumerFilter.invoke(invoker, invocation);
|
||||
verify(invoker).invoke(invocation);
|
||||
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNotNull(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeSync() {
|
||||
|
||||
Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
|
||||
Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
|
||||
|
||||
final Result result = mock(Result.class);
|
||||
when(result.hasException()).thenReturn(false);
|
||||
when(result.getException()).thenReturn(new Exception());
|
||||
when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> {
|
||||
verifyInvocationStructure(invoker, invocation);
|
||||
return result;
|
||||
});
|
||||
|
||||
consumerFilter.invoke(invoker, invocation);
|
||||
verify(invoker).invoke(invocation);
|
||||
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNull(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply verify invocation structure in memory:
|
||||
* EntranceNode(defaultContextName)
|
||||
* --InterfaceNode(interfaceName)
|
||||
* ----MethodNode(resourceName)
|
||||
*/
|
||||
private void verifyInvocationStructure(Invoker invoker, Invocation invocation) {
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNotNull(context);
|
||||
// As not call ContextUtil.enter(resourceName, application) in SentinelDubboConsumerFilter, use default context
|
||||
// In actual project, a consumer is usually also a provider, the context will be created by
|
||||
//SentinelDubboProviderFilter
|
||||
// If consumer is on the top of Dubbo RPC invocation chain, use default context
|
||||
String resourceName = consumerFilter.getMethodName(invoker, invocation, null);
|
||||
assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, context.getName());
|
||||
assertEquals("", context.getOrigin());
|
||||
|
||||
DefaultNode entranceNode = context.getEntranceNode();
|
||||
ResourceWrapper entranceResource = entranceNode.getId();
|
||||
|
||||
assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, entranceResource.getName());
|
||||
assertSame(EntryType.IN, entranceResource.getEntryType());
|
||||
|
||||
// As SphU.entry(interfaceName, EntryType.OUT);
|
||||
Set<Node> childList = entranceNode.getChildList();
|
||||
assertEquals(1, childList.size());
|
||||
DefaultNode interfaceNode = getNode(DubboUtils.getInterfaceName(invoker), entranceNode);
|
||||
ResourceWrapper interfaceResource = interfaceNode.getId();
|
||||
|
||||
assertEquals(DubboUtils.getInterfaceName(invoker), interfaceResource.getName());
|
||||
assertSame(EntryType.OUT, interfaceResource.getEntryType());
|
||||
|
||||
// As SphU.entry(resourceName, EntryType.OUT);
|
||||
childList = interfaceNode.getChildList();
|
||||
assertEquals(1, childList.size());
|
||||
DefaultNode methodNode = getNode(resourceName, entranceNode);
|
||||
ResourceWrapper methodResource = methodNode.getId();
|
||||
assertEquals(resourceName, methodResource.getName());
|
||||
assertSame(EntryType.OUT, methodResource.getEntryType());
|
||||
|
||||
// Verify curEntry
|
||||
Entry curEntry = context.getCurEntry();
|
||||
assertSame(methodNode, curEntry.getCurNode());
|
||||
assertSame(interfaceNode, curEntry.getLastNode());
|
||||
assertNull(curEntry.getOriginNode());// As context origin is not "", no originNode should be created in curEntry
|
||||
|
||||
// Verify clusterNode
|
||||
ClusterNode methodClusterNode = methodNode.getClusterNode();
|
||||
ClusterNode interfaceClusterNode = interfaceNode.getClusterNode();
|
||||
assertNotSame(methodClusterNode,
|
||||
interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode
|
||||
|
||||
// As context origin is "", the StatisticNode should not be created in originCountMap of ClusterNode
|
||||
Map<String, StatisticNode> methodOriginCountMap = methodClusterNode.getOriginCountMap();
|
||||
assertEquals(0, methodOriginCountMap.size());
|
||||
|
||||
Map<String, StatisticNode> interfaceOriginCountMap = interfaceClusterNode.getOriginCountMap();
|
||||
assertEquals(0, interfaceOriginCountMap.size());
|
||||
}
|
||||
|
||||
private void verifyInvocationStructureForAsyncCall(Invoker invoker, Invocation invocation) {
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNotNull(context);
|
||||
|
||||
// As not call ContextUtil.enter(resourceName, application) in SentinelDubboConsumerFilter, use default context
|
||||
// In actual project, a consumer is usually also a provider, the context will be created by
|
||||
//SentinelDubboProviderFilter
|
||||
// If consumer is on the top of Dubbo RPC invocation chain, use default context
|
||||
String resourceName = consumerFilter.getMethodName(invoker, invocation, null);
|
||||
assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, context.getName());
|
||||
assertEquals("", context.getOrigin());
|
||||
|
||||
DefaultNode entranceNode = context.getEntranceNode();
|
||||
ResourceWrapper entranceResource = entranceNode.getId();
|
||||
assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, entranceResource.getName());
|
||||
assertSame(EntryType.IN, entranceResource.getEntryType());
|
||||
|
||||
// As SphU.entry(interfaceName, EntryType.OUT);
|
||||
Set<Node> childList = entranceNode.getChildList();
|
||||
assertEquals(2, childList.size());
|
||||
DefaultNode interfaceNode = getNode(DubboUtils.getInterfaceName(invoker), entranceNode);
|
||||
ResourceWrapper interfaceResource = interfaceNode.getId();
|
||||
assertEquals(DubboUtils.getInterfaceName(invoker), interfaceResource.getName());
|
||||
assertSame(EntryType.OUT, interfaceResource.getEntryType());
|
||||
|
||||
// As SphU.entry(resourceName, EntryType.OUT);
|
||||
childList = interfaceNode.getChildList();
|
||||
assertEquals(0, childList.size());
|
||||
DefaultNode methodNode = getNode(resourceName, entranceNode);
|
||||
ResourceWrapper methodResource = methodNode.getId();
|
||||
assertEquals(resourceName, methodResource.getName());
|
||||
assertSame(EntryType.OUT, methodResource.getEntryType());
|
||||
|
||||
// Verify curEntry
|
||||
// nothing will bind to local context when use the AsyncEntry
|
||||
Entry curEntry = context.getCurEntry();
|
||||
assertNull(curEntry);
|
||||
|
||||
// Verify clusterNode
|
||||
ClusterNode methodClusterNode = methodNode.getClusterNode();
|
||||
ClusterNode interfaceClusterNode = interfaceNode.getClusterNode();
|
||||
assertNotSame(methodClusterNode,
|
||||
interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode
|
||||
|
||||
// As context origin is "", the StatisticNode should not be created in originCountMap of ClusterNode
|
||||
Map<String, StatisticNode> methodOriginCountMap = methodClusterNode.getOriginCountMap();
|
||||
assertEquals(0, methodOriginCountMap.size());
|
||||
|
||||
Map<String, StatisticNode> interfaceOriginCountMap = interfaceClusterNode.getOriginCountMap();
|
||||
assertEquals(0, interfaceOriginCountMap.size());
|
||||
}
|
||||
|
||||
private void verifyInvocationStructureForCallFinish(Invoker invoker, Invocation invocation) {
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNull(context);
|
||||
String methodResourceName = consumerFilter.getMethodName(invoker, invocation, null);
|
||||
Entry[] entries = (Entry[]) RpcContext.getContext().get(methodResourceName);
|
||||
assertNull(entries);
|
||||
}
|
||||
|
||||
private DefaultNode getNode(String resourceName, DefaultNode root) {
|
||||
|
||||
Queue<DefaultNode> queue = new LinkedList<>();
|
||||
queue.offer(root);
|
||||
while (!queue.isEmpty()) {
|
||||
DefaultNode temp = queue.poll();
|
||||
if (temp.getId().getName().equals(resourceName)) {
|
||||
return temp;
|
||||
}
|
||||
for (Node node : temp.getChildList()) {
|
||||
queue.offer((DefaultNode) node);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void initFlowRule(String resource) {
|
||||
FlowRule flowRule = new FlowRule(resource);
|
||||
flowRule.setCount(1);
|
||||
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
|
||||
List<FlowRule> flowRules = new ArrayList<>();
|
||||
flowRules.add(flowRule);
|
||||
FlowRuleManager.loadRules(flowRules);
|
||||
}
|
||||
|
||||
private void initDegradeRule(String resource) {
|
||||
DegradeRule degradeRule = new DegradeRule(resource)
|
||||
.setCount(0.5)
|
||||
.setGrade(DEGRADE_GRADE_EXCEPTION_RATIO);
|
||||
List<DegradeRule> degradeRules = new ArrayList<>();
|
||||
degradeRules.add(degradeRule);
|
||||
degradeRule.setTimeWindow(1);
|
||||
DegradeRuleManager.loadRules(degradeRules);
|
||||
}
|
||||
|
||||
private void initFallback() {
|
||||
DubboAdapterGlobalConfig.setConsumerFallback((invoker, invocation, ex) -> {
|
||||
// boolean async = RpcUtils.isAsync(invoker.getUrl(), invocation);
|
||||
return AsyncRpcResult.newDefaultAsyncResult("fallback", invocation);
|
||||
});
|
||||
}
|
||||
|
||||
private Result invokeDubboRpc(boolean exception, Invoker invoker, Invocation invocation) {
|
||||
Result result = null;
|
||||
InvokeMode invokeMode = RpcUtils.getInvokeMode(invoker.getUrl(), invocation);
|
||||
if (InvokeMode.SYNC == invokeMode) {
|
||||
result = exception ? new AppResponse(new Exception("error")) : new AppResponse("normal");
|
||||
} else {
|
||||
result = exception ? AsyncRpcResult.newDefaultAsyncResult(new Exception("error"), invocation)
|
||||
: AsyncRpcResult.newDefaultAsyncResult("normal", invocation);
|
||||
}
|
||||
when(invoker.invoke(invocation)).thenReturn(result);
|
||||
return consumerFilter.invoke(invoker, invocation);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.BaseTest;
|
||||
import com.alibaba.csp.sentinel.DubboTestUtil;
|
||||
import com.alibaba.csp.sentinel.Entry;
|
||||
import com.alibaba.csp.sentinel.EntryType;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
|
||||
import com.alibaba.csp.sentinel.context.Context;
|
||||
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||
import com.alibaba.csp.sentinel.node.ClusterNode;
|
||||
import com.alibaba.csp.sentinel.node.DefaultNode;
|
||||
import com.alibaba.csp.sentinel.node.Node;
|
||||
import com.alibaba.csp.sentinel.node.StatisticNode;
|
||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.constants.CommonConstants;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* @author cdfive
|
||||
* @author lianglin
|
||||
*/
|
||||
public class SentinelDubboProviderFilterTest extends BaseTest {
|
||||
|
||||
|
||||
private SentinelDubboProviderFilter filter = new SentinelDubboProviderFilter();
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
cleanUpAll();
|
||||
}
|
||||
|
||||
@After
|
||||
public void destroy() {
|
||||
cleanUpAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvoke() {
|
||||
|
||||
final String originApplication = "consumerA";
|
||||
|
||||
URL url = DubboTestUtil.getDefaultTestURL();
|
||||
url = url.addParameter(CommonConstants.SIDE_KEY, CommonConstants.PROVIDER_SIDE);
|
||||
Invoker invoker = DubboTestUtil.getMockInvoker(url, DemoService.class);
|
||||
|
||||
Invocation invocation = DubboTestUtil.getMockInvocation(DemoService.class.getMethods()[0]);
|
||||
when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, ""))
|
||||
.thenReturn(originApplication);
|
||||
|
||||
final Result result = mock(Result.class);
|
||||
when(result.hasException()).thenReturn(false);
|
||||
when(result.getException()).thenReturn(new Exception());
|
||||
|
||||
when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> {
|
||||
verifyInvocationStructure(originApplication, invoker, invocation);
|
||||
return result;
|
||||
});
|
||||
|
||||
filter.invoke(invoker, invocation);
|
||||
verify(invoker).invoke(invocation);
|
||||
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNull(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply verify invocation structure in memory:
|
||||
* EntranceNode(methodResourceName)
|
||||
* --InterfaceNode(interfaceName)
|
||||
* ----MethodNode(methodResourceName)
|
||||
*/
|
||||
private void verifyInvocationStructure(String originApplication, Invoker invoker, Invocation invocation) {
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNotNull(context);
|
||||
|
||||
// As ContextUtil.enter(resourceName, application) in SentinelDubboProviderFilter
|
||||
String methodResourceName = filter.getMethodName(invoker, invocation, null);
|
||||
assertEquals(methodResourceName, context.getName());
|
||||
assertEquals(originApplication, context.getOrigin());
|
||||
|
||||
DefaultNode entranceNode = context.getEntranceNode();
|
||||
ResourceWrapper entranceResource = entranceNode.getId();
|
||||
assertEquals(methodResourceName, entranceResource.getName());
|
||||
assertSame(EntryType.IN, entranceResource.getEntryType());
|
||||
|
||||
// As SphU.entry(interfaceName, EntryType.IN);
|
||||
Set<Node> childList = entranceNode.getChildList();
|
||||
assertEquals(1, childList.size());
|
||||
DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
|
||||
ResourceWrapper interfaceResource = interfaceNode.getId();
|
||||
|
||||
assertEquals(filter.getInterfaceName(invoker, null), interfaceResource.getName());
|
||||
assertSame(EntryType.IN, interfaceResource.getEntryType());
|
||||
|
||||
// As SphU.entry(resourceName, EntryType.IN, 1, invocation.getArguments());
|
||||
childList = interfaceNode.getChildList();
|
||||
assertEquals(1, childList.size());
|
||||
DefaultNode methodNode = (DefaultNode) childList.iterator().next();
|
||||
ResourceWrapper methodResource = methodNode.getId();
|
||||
assertEquals(methodResourceName, methodResource.getName());
|
||||
assertSame(EntryType.IN, methodResource.getEntryType());
|
||||
|
||||
// Verify curEntry
|
||||
Entry curEntry = context.getCurEntry();
|
||||
assertSame(methodNode, curEntry.getCurNode());
|
||||
assertSame(interfaceNode, curEntry.getLastNode());
|
||||
assertNotNull(curEntry.getOriginNode());// As context origin is not "", originNode should be created
|
||||
|
||||
// Verify clusterNode
|
||||
ClusterNode methodClusterNode = methodNode.getClusterNode();
|
||||
ClusterNode interfaceClusterNode = interfaceNode.getClusterNode();
|
||||
assertNotSame(methodClusterNode, interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode
|
||||
|
||||
// As context origin is not "", the StatisticNode should be created in originCountMap of ClusterNode
|
||||
Map<String, StatisticNode> methodOriginCountMap = methodClusterNode.getOriginCountMap();
|
||||
assertEquals(1, methodOriginCountMap.size());
|
||||
assertTrue(methodOriginCountMap.containsKey(originApplication));
|
||||
|
||||
Map<String, StatisticNode> interfaceOriginCountMap = interfaceClusterNode.getOriginCountMap();
|
||||
assertEquals(1, interfaceOriginCountMap.size());
|
||||
assertTrue(interfaceOriginCountMap.containsKey(originApplication));
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
|
||||
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class DubboFallbackRegistryTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
DubboAdapterGlobalConfig.setConsumerFallback(new DefaultDubboFallback());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
DubboAdapterGlobalConfig.setConsumerFallback(new DefaultDubboFallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultFallback() {
|
||||
// Test for default fallback.
|
||||
BlockException ex = new FlowException("xxx");
|
||||
Result result = new DefaultDubboFallback().handle(null, null, ex);
|
||||
Assert.assertTrue("The result should carry exception", result.hasException());
|
||||
Assert.assertTrue(BlockException.isBlockException(result.getException()));
|
||||
Assert.assertTrue(result.getException().getMessage().contains(ex.getClass().getSimpleName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomFallback() {
|
||||
BlockException ex = new FlowException("xxx");
|
||||
DubboAdapterGlobalConfig.setConsumerFallback(
|
||||
(invoker, invocation, e) -> AsyncRpcResult
|
||||
.newDefaultAsyncResult("Error: " + e.getClass().getName(), invocation));
|
||||
Result result = DubboAdapterGlobalConfig.getConsumerFallback()
|
||||
.handle(null, null, ex);
|
||||
Assert.assertFalse("The invocation should not fail", result.hasException());
|
||||
Assert.assertEquals("Error: " + ex.getClass().getName(), result.getValue());
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.origin;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.DubboUtils;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
import com.alibaba.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author tiecheng
|
||||
*/
|
||||
public class DubboOriginRegistryTest {
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
DubboAdapterGlobalConfig.setOriginParser(new DefaultDubboOriginParser());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testDefaultOriginParserFail() {
|
||||
DubboAdapterGlobalConfig.getOriginParser().parse(null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultOriginParserSuccess() {
|
||||
RpcInvocation invocation = new RpcInvocation();
|
||||
String dubboName = "sentinel";
|
||||
invocation.setAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, dubboName);
|
||||
String origin = DubboAdapterGlobalConfig.getOriginParser().parse(null, invocation);
|
||||
Assert.assertEquals(dubboName, origin);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomOriginParser() {
|
||||
DubboAdapterGlobalConfig.setOriginParser(new DubboOriginParser() {
|
||||
@Override
|
||||
public String parse(Invoker<?> invoker, Invocation invocation) {
|
||||
return invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, "default") + "_" + invocation
|
||||
.getMethodName();
|
||||
}
|
||||
});
|
||||
|
||||
RpcInvocation invocation = new RpcInvocation();
|
||||
String origin = DubboAdapterGlobalConfig.getOriginParser().parse(null, invocation);
|
||||
Assert.assertEquals("default_null", origin);
|
||||
|
||||
String dubboName = "sentinel";
|
||||
invocation.setAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, dubboName);
|
||||
origin = DubboAdapterGlobalConfig.getOriginParser().parse(null, invocation);
|
||||
Assert.assertEquals(dubboName + "_null", origin);
|
||||
|
||||
invocation.setMethodName("hello");
|
||||
origin = DubboAdapterGlobalConfig.getOriginParser().parse(null, invocation);
|
||||
Assert.assertEquals(dubboName + "_hello", origin);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.provider;
|
||||
|
||||
/**
|
||||
* @author leyou
|
||||
*/
|
||||
public interface DemoService {
|
||||
String sayHello(String name, int n);
|
||||
String sayHi(String name,int n);
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.provider.impl;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
|
||||
|
||||
/**
|
||||
* @author leyou
|
||||
*/
|
||||
public class DemoServiceImpl implements DemoService {
|
||||
public String sayHello(String name, int n) {
|
||||
return "Hello " + name + ", " + n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sayHi(String name, int n) {
|
||||
return "Hi " + name + ", " + n;
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://code.alibabatech.com/schema/dubbo
|
||||
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
|
||||
|
||||
<dubbo:application name="demo-consumer"/>
|
||||
<dubbo:registry address="multicast://224.5.6.7:1234"/>
|
||||
<dubbo:protocol name="dubbo" port="20880"/>
|
||||
<dubbo:service interface="com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService" ref="demoServiceImp" />
|
||||
<bean id="demoServiceImp" class="com.alibaba.csp.sentinel.adapter.dubbo.provider.impl.DemoServiceImpl"/>
|
||||
|
||||
<dubbo:reference id="demoService" interface="com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService"/>
|
||||
|
||||
</beans>
|
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://code.alibabatech.com/schema/dubbo
|
||||
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
|
||||
|
||||
<dubbo:application name="demo-provider"/>
|
||||
<dubbo:registry address="multicast://224.5.6.7:1234"/>
|
||||
<dubbo:protocol name="dubbo" port="20880"/>
|
||||
<dubbo:service interface="com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService" ref="demoServiceImp" />
|
||||
<bean id="demoServiceImp" class="com.alibaba.csp.sentinel.adapter.dubbo.provider.impl.DemoServiceImpl"/>
|
||||
|
||||
<dubbo:reference id="demoService" interface="com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService"/>
|
||||
|
||||
</beans>
|
Reference in New Issue
Block a user