diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/BaseSentinelDubboFilter.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/BaseSentinelDubboFilter.java
new file mode 100644
index 00000000..9ea01e59
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/BaseSentinelDubboFilter.java
@@ -0,0 +1,77 @@
+/*
+ * 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.Entry;
+import com.alibaba.csp.sentinel.Tracer;
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+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.ListenableFilter;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcContext;
+
+/**
+ * Base Class of the {@link SentinelDubboProviderFilter} and {@link SentinelDubboConsumerFilter}.
+ *
+ * @author Zechao Zheng
+ */
+
+public abstract class BaseSentinelDubboFilter extends ListenableFilter {
+ public BaseSentinelDubboFilter() {
+ this.listener = new SentinelDubboListener();
+ }
+
+ static class SentinelDubboListener implements Listener {
+
+ @Override
+ public void onResponse(Result appResponse, Invoker> invoker, Invocation invocation) {
+ if (DubboConfig.getDubboBizExceptionTraceEnabled()) {
+ traceAndExit(appResponse.getException(), invoker.getUrl());
+ } else {
+ traceAndExit(null, invoker.getUrl());
+ }
+ }
+
+ @Override
+ public void onError(Throwable t, Invoker> invoker, Invocation invocation) {
+ traceAndExit(t, invoker.getUrl());
+ }
+
+ }
+
+ static void traceAndExit(Throwable throwable, URL url) {
+ Entry interfaceEntry = (Entry) RpcContext.getContext().get(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY);
+ Entry methodEntry = (Entry) RpcContext.getContext().get(DubboUtils.DUBBO_METHOD_ENTRY_KEY);
+ if (methodEntry != null) {
+ Tracer.traceEntry(throwable, methodEntry);
+ methodEntry.exit();
+ RpcContext.getContext().remove(DubboUtils.DUBBO_METHOD_ENTRY_KEY);
+ }
+ if (interfaceEntry != null) {
+ Tracer.traceEntry(throwable, interfaceEntry);
+ interfaceEntry.exit();
+ RpcContext.getContext().remove(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY);
+ }
+ if (CommonConstants.PROVIDER_SIDE.equals(url.getParameter(CommonConstants.SIDE_KEY))) {
+ ContextUtil.exit();
+ }
+ }
+}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAppContextFilter.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAppContextFilter.java
index 97ae51ba..789bb96e 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAppContextFilter.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAppContextFilter.java
@@ -15,7 +15,7 @@
*/
package com.alibaba.csp.sentinel.adapter.dubbo;
-import org.apache.dubbo.common.Constants;
+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;
@@ -34,7 +34,7 @@ public class DubboAppContextFilter implements Filter {
@Override
public Result invoke(Invoker> invoker, Invocation invocation) throws RpcException {
- String application = invoker.getUrl().getParameter(Constants.APPLICATION_KEY);
+ String application = invoker.getUrl().getParameter(CommonConstants.APPLICATION_KEY);
if (application != null) {
RpcContext.getContext().setAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, application);
}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtils.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtils.java
index 362925d2..d2c325fa 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtils.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtils.java
@@ -15,6 +15,7 @@
*/
package com.alibaba.csp.sentinel.adapter.dubbo;
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
import com.alibaba.csp.sentinel.util.StringUtil;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
@@ -25,6 +26,8 @@ import org.apache.dubbo.rpc.Invoker;
public final class DubboUtils {
public static final String SENTINEL_DUBBO_APPLICATION_KEY = "dubboApplication";
+ public static final String DUBBO_METHOD_ENTRY_KEY = "dubboMethodEntry";
+ public static final String DUBBO_INTERFACE_ENTRY_KEY = "dubboInterfaceEntry";
public static String getApplication(Invocation invocation, String defaultValue) {
if (invocation == null || invocation.getAttachments() == null) {
@@ -33,12 +36,17 @@ public final class DubboUtils {
return invocation.getAttachment(SENTINEL_DUBBO_APPLICATION_KEY, defaultValue);
}
- public static String getResourceName(Invoker> invoker, Invocation invocation) {
+ public static String getResourceName(Invoker> invoker, Invocation invocation){
+ return getResourceName(invoker, invocation, false);
+ }
+
+ public static String getResourceName(Invoker> invoker, Invocation invocation, Boolean useGroupAndVersion) {
StringBuilder buf = new StringBuilder(64);
- buf.append(invoker.getInterface().getName())
- .append(":")
- .append(invocation.getMethodName())
- .append("(");
+ 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) {
@@ -55,13 +63,12 @@ public final class DubboUtils {
if (StringUtil.isNotBlank(prefix)) {
return new StringBuilder(64)
.append(prefix)
- .append(getResourceName(invoker, invocation))
+ .append(getResourceName(invoker, invocation, DubboConfig.getDubboInterfaceGroupAndVersionEnabled()))
.toString();
} else {
- return getResourceName(invoker, invocation);
+ return getResourceName(invoker, invocation, DubboConfig.getDubboInterfaceGroupAndVersionEnabled());
}
}
-
private DubboUtils() {
}
}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java
old mode 100755
new mode 100644
index ef312bc0..8b317e90
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java
@@ -19,17 +19,18 @@ import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.ResourceTypeConstants;
import com.alibaba.csp.sentinel.SphU;
-import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
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.Filter;
import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.InvokeMode;
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 org.apache.dubbo.rpc.support.RpcUtils;
/**
* Dubbo service consumer filter for Sentinel. Auto activated by default.
@@ -43,7 +44,7 @@ import org.apache.dubbo.rpc.RpcException;
* @author Eric Zhao
*/
@Activate(group = "consumer")
-public class SentinelDubboConsumerFilter implements Filter {
+public class SentinelDubboConsumerFilter extends BaseSentinelDubboFilter {
public SentinelDubboConsumerFilter() {
RecordLog.info("Sentinel Apache Dubbo consumer filter initialized");
@@ -53,33 +54,29 @@ public class SentinelDubboConsumerFilter implements Filter {
public Result invoke(Invoker> invoker, Invocation invocation) throws RpcException {
Entry interfaceEntry = null;
Entry methodEntry = null;
+ RpcContext rpcContext = RpcContext.getContext();
try {
- String resourceName = DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboConsumerPrefix());
- interfaceEntry = SphU.entry(invoker.getInterface().getName(),
- ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
- methodEntry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
+ String methodResourceName = DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboConsumerPrefix());
+ String interfaceResourceName = DubboConfig.getDubboInterfaceGroupAndVersionEnabled() ? invoker.getUrl().getColonSeparatedKey()
+ : invoker.getInterface().getName();
+ InvokeMode invokeMode = RpcUtils.getInvokeMode(invoker.getUrl(), invocation);
- Result result = invoker.invoke(invocation);
- if (result.hasException()) {
- Throwable e = result.getException();
- // Record common exception.
- Tracer.traceEntry(e, interfaceEntry);
- Tracer.traceEntry(e, methodEntry);
+ if (InvokeMode.SYNC == invokeMode) {
+ interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
+ rpcContext.set(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY, interfaceEntry);
+ methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT, invocation.getArguments());
+ } else {
+ // should generate the AsyncEntry when the invoke model in future or async
+ interfaceEntry = SphU.asyncEntry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
+ rpcContext.set(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY, interfaceEntry);
+ methodEntry = SphU.asyncEntry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT, 1, invocation.getArguments());
}
- return result;
+ rpcContext.set(DubboUtils.DUBBO_METHOD_ENTRY_KEY, methodEntry);
+ return invoker.invoke(invocation);
} catch (BlockException e) {
return DubboFallbackRegistry.getConsumerFallback().handle(invoker, invocation, e);
- } catch (RpcException e) {
- Tracer.traceEntry(e, interfaceEntry);
- Tracer.traceEntry(e, methodEntry);
- throw e;
- } finally {
- if (methodEntry != null) {
- methodEntry.exit();
- }
- if (interfaceEntry != null) {
- interfaceEntry.exit();
- }
}
}
}
+
+
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java
old mode 100755
new mode 100644
index d5a2745e..479b0954
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java
@@ -19,17 +19,16 @@ import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.ResourceTypeConstants;
import com.alibaba.csp.sentinel.SphU;
-import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
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.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;
/**
@@ -45,7 +44,7 @@ import org.apache.dubbo.rpc.RpcException;
* @author Eric Zhao
*/
@Activate(group = "provider")
-public class SentinelDubboProviderFilter implements Filter {
+public class SentinelDubboProviderFilter extends BaseSentinelDubboFilter {
public SentinelDubboProviderFilter() {
RecordLog.info("Sentinel Apache Dubbo provider filter initialized");
@@ -55,41 +54,26 @@ public class SentinelDubboProviderFilter implements Filter {
public Result invoke(Invoker> invoker, Invocation invocation) throws RpcException {
// Get origin caller.
String application = DubboUtils.getApplication(invocation, "");
-
+ RpcContext rpcContext = RpcContext.getContext();
Entry interfaceEntry = null;
Entry methodEntry = null;
try {
- String resourceName = DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboProviderPrefix());
- String interfaceName = invoker.getInterface().getName();
+ String methodResourceName = DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboProviderPrefix());
+ String interfaceResourceName = DubboConfig.getDubboInterfaceGroupAndVersionEnabled() ? invoker.getUrl().getColonSeparatedKey()
+ : invoker.getInterface().getName();
// 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(resourceName, application);
- interfaceEntry = SphU.entry(interfaceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN);
- methodEntry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_RPC,
- EntryType.IN, invocation.getArguments());
-
- Result result = invoker.invoke(invocation);
- if (result.hasException()) {
- Throwable e = result.getException();
- // Record common exception.
- Tracer.traceEntry(e, interfaceEntry);
- Tracer.traceEntry(e, methodEntry);
- }
- return result;
+ ContextUtil.enter(methodResourceName, application);
+ interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN);
+ rpcContext.set(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY, interfaceEntry);
+ methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN, invocation.getArguments());
+ rpcContext.set(DubboUtils.DUBBO_METHOD_ENTRY_KEY, methodEntry);
+ return invoker.invoke(invocation);
} catch (BlockException e) {
return DubboFallbackRegistry.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();
}
}
+
+
}
+
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/config/DubboConfig.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/config/DubboConfig.java
index 515bca3e..9b2b0218 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/config/DubboConfig.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/config/DubboConfig.java
@@ -37,8 +37,12 @@ public final class DubboConfig {
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";
- public static boolean isUsePrefix(){
+ public static final String TRACE_BIZ_EXCEPTION_ENABLED = "csp.sentinel.dubbo.trace.biz.exception.enabled";
+
+
+ public static boolean isUsePrefix() {
return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_USE_PREFIX));
}
@@ -58,5 +62,16 @@ public final class DubboConfig {
return null;
}
+ public static Boolean getDubboInterfaceGroupAndVersionEnabled() {
+ return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED));
+ }
+
+ public static Boolean getDubboBizExceptionTraceEnabled() {
+ String traceBizExceptionEnabled = SentinelConfig.getConfig(TRACE_BIZ_EXCEPTION_ENABLED);
+ if (StringUtil.isNotBlank(traceBizExceptionEnabled)) {
+ return TRUE_STR.equalsIgnoreCase(traceBizExceptionEnabled);
+ }
+ return true;
+ }
}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/BaseTest.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/BaseTest.java
index a61f3b4c..7793a656 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/BaseTest.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/BaseTest.java
@@ -15,26 +15,73 @@
*/
package com.alibaba.csp.sentinel;
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
+import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
+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.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.RpcContext;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
/**
* Base test class, provide common methods for subClass
* The package is same as CtSph, to call CtSph.resetChainMap() method for test
- *
+ *
* Note: Only for test. DO NOT USE IN PRODUCTION!
*
* @author cdfive
*/
public class BaseTest {
+
+ protected Invoker invoker;
+ protected Invocation invocation;
+
+ public void constructInvokerAndInvocation() {
+ 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 = mock(Invocation.class);
+ Method method = DemoService.class.getMethods()[0];
+ when(invocation.getMethodName()).thenReturn(method.getName());
+ when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
+
+ }
+
/**
* Clean up resources for context, clusterNodeMap, processorSlotChainMap
*/
protected static void cleanUpAll() {
- RpcContext.removeContext();
- ClusterBuilderSlot.getClusterNodeMap().clear();
- CtSph.resetChainMap();
+ try {
+ RpcContext.removeContext();
+ ClusterBuilderSlot.getClusterNodeMap().clear();
+ CtSph.resetChainMap();
+ Method method = ContextUtil.class.getDeclaredMethod("resetContextMap");
+ method.setAccessible(true);
+ method.invoke(null, null);
+ ContextUtil.exit();
+ SentinelConfig.setConfig(DubboConfig.DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "true");
+ FlowRuleManager.loadRules(new ArrayList<>());
+ DegradeRuleManager.loadRules(new ArrayList<>());
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ e.printStackTrace();
+ }
}
}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtilsTest.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtilsTest.java
index 95933fcf..ece46fde 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtilsTest.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtilsTest.java
@@ -18,6 +18,8 @@ package com.alibaba.csp.sentinel.adapter.dubbo;
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
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;
@@ -29,7 +31,9 @@ import java.util.HashMap;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
/**
* @author cdfive
@@ -41,6 +45,7 @@ public class DubboUtilsTest {
SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "true");
SentinelConfig.setConfig(DubboConfig.DUBBO_PROVIDER_PREFIX, "");
SentinelConfig.setConfig(DubboConfig.DUBBO_CONSUMER_PREFIX, "");
+ SentinelConfig.setConfig(DubboConfig.DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
}
@@ -49,6 +54,7 @@ public class DubboUtilsTest {
SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "false");
SentinelConfig.setConfig(DubboConfig.DUBBO_PROVIDER_PREFIX, "");
SentinelConfig.setConfig(DubboConfig.DUBBO_CONSUMER_PREFIX, "");
+ SentinelConfig.setConfig(DubboConfig.DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
}
@@ -78,27 +84,49 @@ public class DubboUtilsTest {
}
@Test
- public void testGetResourceName() {
+ 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.getMethods()[0];
+ 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.getResourceName(invoker, invocation);
assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
+
}
@Test
- public void testGetResourceNameWithPrefix() {
+ 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.getResourceName(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.getMethods()[0];
+ Method method = DemoService.class.getDeclaredMethod("sayHello", String.class, int.class);
when(invocation.getMethodName()).thenReturn(method.getName());
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
@@ -118,4 +146,4 @@ public class DubboUtilsTest {
assertEquals("my:dubbo:consumer:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
}
-}
+}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java
index 55b87a59..31318cb2 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java
@@ -16,9 +16,11 @@
package com.alibaba.csp.sentinel.adapter.dubbo;
import com.alibaba.csp.sentinel.BaseTest;
-import com.alibaba.csp.sentinel.Constants;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
+import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallback;
+import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.context.ContextUtil;
@@ -27,20 +29,40 @@ 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.AsyncRpcResult;
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.support.RpcUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
+import java.util.Queue;
import java.util.Set;
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
+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.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
/**
* @author cdfive
@@ -49,9 +71,12 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
private SentinelDubboConsumerFilter filter = new SentinelDubboConsumerFilter();
+
@Before
public void setUp() {
cleanUpAll();
+ initFallback();
+ constructInvokerAndInvocation();
}
@After
@@ -59,18 +84,177 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
cleanUpAll();
}
- @Test
- public void testInvoke() {
- final Invoker invoker = mock(Invoker.class);
- when(invoker.getInterface()).thenReturn(DemoService.class);
+ public void initFlowRule(String resource) {
+ FlowRule flowRule = new FlowRule(resource);
+ flowRule.setCount(1);
+ flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ List flowRules = new ArrayList<>();
+ flowRules.add(flowRule);
+ FlowRuleManager.loadRules(flowRules);
+ }
- final Invocation invocation = mock(Invocation.class);
- Method method = DemoService.class.getMethods()[0];
- when(invocation.getMethodName()).thenReturn(method.getName());
- when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
+ public void initDegradeRule(String resource) {
+ DegradeRule degradeRule = new DegradeRule(resource)
+ .setCount(0.5)
+ .setGrade(DEGRADE_GRADE_EXCEPTION_RATIO);
+ List degradeRules = new ArrayList<>();
+ degradeRules.add(degradeRule);
+ degradeRule.setTimeWindow(1);
+ DegradeRuleManager.loadRules(degradeRules);
+ }
+
+
+ public void initFallback() {
+ DubboFallbackRegistry.setConsumerFallback(new DubboFallback() {
+ @Override
+ public Result handle(Invoker> invoker, Invocation invocation, BlockException ex) {
+ boolean async = RpcUtils.isAsync(invoker.getUrl(), invocation);
+ Result fallbackResult = null;
+ fallbackResult = AsyncRpcResult.newDefaultAsyncResult("fallback", invocation);
+ return fallbackResult;
+ }
+ });
+ }
+
+ @Test
+ public void testInterfaceLevelFollowControlAsync() throws InterruptedException {
+ when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
+ initFlowRule(invoker.getUrl().getColonSeparatedKey());
+ Result result1 = responseBack(requestGo(false, invocation));
+ assertEquals("normal", result1.getValue());
+ // should fallback because the qps > 1
+ Result result2 = responseBack(requestGo(false, invocation));
+ assertEquals("fallback", result2.getValue());
+ // sleeping 1000 ms to reset qps
+ Thread.sleep(1000);
+ Result result3 = responseBack(requestGo(false, invocation));
+ assertEquals("normal", result3.getValue());
+
+ verifyInvocationStructureForCallFinish();
+ }
+
+ @Test
+ public void testDegradeAsync() throws InterruptedException {
+ when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
+
+ initDegradeRule(invoker.getUrl().getColonSeparatedKey());
+ Result result = requestGo(false, invocation);
+ verifyInvocationStructureForAsyncCall(invoker, invocation);
+ responseBack(result);
+ assertEquals("normal", result.getValue());
+ // inc the clusterNode's exception to trigger the fallback
+ for (int i = 0; i < 5; i++) {
+ responseBack(requestGo(true, invocation));
+ verifyInvocationStructureForCallFinish();
+ }
+ Result result2 = responseBack(requestGo(false, invocation));
+ assertEquals("fallback", result2.getValue());
+ // sleeping 1000 ms to reset exception
+ Thread.sleep(1000);
+
+ Result result3 = responseBack(requestGo(false, invocation));
+ assertEquals("normal", result3.getValue());
+
+ Context context = ContextUtil.getContext();
+ assertNull(context);
+ }
+
+ @Test
+ public void testDegradeSync() throws InterruptedException {
+
+ initDegradeRule(invoker.getUrl().getColonSeparatedKey());
+ Result result = requestGo(false, invocation);
+ verifyInvocationStructure(invoker, invocation);
+ responseBack(result);
+ assertEquals("normal", result.getValue());
+ // inc the clusterNode's exception to trigger the fallback
+ for (int i = 0; i < 5; i++) {
+ responseBack(requestGo(true, invocation));
+ verifyInvocationStructureForCallFinish();
+ }
+ Result result2 = responseBack(requestGo(false, invocation));
+ assertEquals("fallback", result2.getValue());
+ // sleeping 1000 ms to reset exception
+ Thread.sleep(1000);
+
+ Result result3 = responseBack(requestGo(false, invocation));
+ assertEquals("normal", result3.getValue());
+
+ Context context = ContextUtil.getContext();
+ assertNull(context);
+ }
+
+
+ @Test
+ public void testMethodFlowControlAsync() {
+ when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
+ initFlowRule(DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboConsumerPrefix()));
+ responseBack(requestGo(false, invocation));
+
+ responseBack(requestGo(false, invocation));
+
+ Invocation invocation2 = mock(Invocation.class);
+ Method method = DemoService.class.getMethods()[1];
+ when(invocation2.getMethodName()).thenReturn(method.getName());
+ when(invocation2.getParameterTypes()).thenReturn(method.getParameterTypes());
+ Result result2 = responseBack(requestGo(false, invocation2));
+ verifyInvocationStructureForCallFinish();
+ assertEquals("normal", result2.getValue());
+
+ // the method of invocation should be blocked
+ Result fallback = requestGo(false, invocation);
+ assertNotNull(RpcContext.getContext().get(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY));
+ assertNull(RpcContext.getContext().get(DubboUtils.DUBBO_METHOD_ENTRY_KEY));
+ responseBack(fallback);
+ assertEquals("fallback", fallback.getValue());
+ verifyInvocationStructureForCallFinish();
+
+
+ }
+
+ public Result requestGo(boolean exception, Invocation currentInvocation) {
+ AsyncRpcResult result = null;
+
+ if (exception) {
+ result = AsyncRpcResult.newDefaultAsyncResult(new Exception("error"), currentInvocation);
+ } else {
+ result = AsyncRpcResult.newDefaultAsyncResult("normal", currentInvocation);
+ }
+ when(invoker.invoke(currentInvocation)).thenReturn(result);
+ return filter.invoke(invoker, currentInvocation);
+ }
+
+ public Result responseBack(Result result) {
+ filter.listener().onResponse(result, invoker, invocation);
+ return result;
+ }
+
+
+ @Test
+ public void testInvokeAsync() throws InterruptedException {
+
+ 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;
+ });
+
+ filter.invoke(invoker, invocation);
+ verify(invoker).invoke(invocation);
+
+ Context context = ContextUtil.getContext();
+ assertNotNull(context);
+ }
+
+ @Test
+ public void testInvokeSync() {
+
+ 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;
@@ -79,6 +263,7 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
filter.invoke(invoker, invocation);
verify(invoker).invoke(invocation);
+ filter.listener().onResponse(result, invoker, invocation);
Context context = ContextUtil.getContext();
assertNull(context);
}
@@ -92,31 +277,32 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
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 = DubboUtils.getResourceName(invoker, invocation);
- assertEquals(Constants.CONTEXT_DEFAULT_NAME, context.getName());
+ String resourceName = DubboUtils.getResourceName(invoker, invocation, true);
+ assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, context.getName());
assertEquals("", context.getOrigin());
DefaultNode entranceNode = context.getEntranceNode();
ResourceWrapper entranceResource = entranceNode.getId();
- assertEquals(Constants.CONTEXT_DEFAULT_NAME, entranceResource.getName());
+
+ assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, entranceResource.getName());
assertSame(EntryType.IN, entranceResource.getEntryType());
// As SphU.entry(interfaceName, EntryType.OUT);
Set childList = entranceNode.getChildList();
assertEquals(1, childList.size());
- DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
+ DefaultNode interfaceNode = getNode(invoker.getUrl().getColonSeparatedKey(), entranceNode);
ResourceWrapper interfaceResource = interfaceNode.getId();
- assertEquals(DemoService.class.getName(), interfaceResource.getName());
+
+ assertEquals(invoker.getUrl().getColonSeparatedKey(), interfaceResource.getName());
assertSame(EntryType.OUT, interfaceResource.getEntryType());
// As SphU.entry(resourceName, EntryType.OUT);
childList = interfaceNode.getChildList();
assertEquals(1, childList.size());
- DefaultNode methodNode = (DefaultNode) childList.iterator().next();
+ DefaultNode methodNode = getNode(resourceName, entranceNode);
ResourceWrapper methodResource = methodNode.getId();
assertEquals(resourceName, methodResource.getName());
assertSame(EntryType.OUT, methodResource.getEntryType());
@@ -139,4 +325,82 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
Map 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 = DubboUtils.getResourceName(invoker, invocation, true);
+ 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 childList = entranceNode.getChildList();
+ assertEquals(2, childList.size());
+ DefaultNode interfaceNode = getNode(invoker.getUrl().getColonSeparatedKey(), entranceNode);
+ ResourceWrapper interfaceResource = interfaceNode.getId();
+ assertEquals(invoker.getUrl().getColonSeparatedKey(), 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 methodOriginCountMap = methodClusterNode.getOriginCountMap();
+ assertEquals(0, methodOriginCountMap.size());
+
+ Map interfaceOriginCountMap = interfaceClusterNode.getOriginCountMap();
+ assertEquals(0, interfaceOriginCountMap.size());
+ }
+
+
+ private void verifyInvocationStructureForCallFinish() {
+ Context context = ContextUtil.getContext();
+ assertNull(context);
+ Entry interfaceEntry = (Entry) RpcContext.getContext().get(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY);
+ Entry methodEntry = (Entry) RpcContext.getContext().get(DubboUtils.DUBBO_METHOD_ENTRY_KEY);
+ assertNull(interfaceEntry);
+ assertNull(methodEntry);
+ }
+
+
+ public DefaultNode getNode(String resourceName, DefaultNode root) {
+
+ Queue 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;
+ }
+
}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilterTest.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilterTest.java
index d8a6f00d..1a7000f1 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilterTest.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilterTest.java
@@ -26,7 +26,8 @@ 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;
@@ -38,8 +39,15 @@ import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
/**
* @author cdfive
@@ -50,6 +58,7 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
@Before
public void setUp() {
+ constructInvokerAndInvocation();
cleanUpAll();
}
@@ -62,18 +71,16 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
public void testInvoke() {
final String originApplication = "consumerA";
- final Invoker invoker = mock(Invoker.class);
- when(invoker.getInterface()).thenReturn(DemoService.class);
+ URL url = invoker.getUrl()
+ .addParameter(CommonConstants.SIDE_KEY, CommonConstants.PROVIDER_SIDE);
+ when(invoker.getUrl()).thenReturn(url);
- final Invocation invocation = mock(Invocation.class);
- Method method = DemoService.class.getMethods()[0];
- when(invocation.getMethodName()).thenReturn(method.getName());
- when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, ""))
- .thenReturn(originApplication);
+ .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;
@@ -82,6 +89,7 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
filter.invoke(invoker, invocation);
verify(invoker).invoke(invocation);
+ filter.listener().onResponse(result, invoker, invocation);
Context context = ContextUtil.getContext();
assertNull(context);
}
@@ -97,7 +105,7 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
assertNotNull(context);
// As ContextUtil.enter(resourceName, application) in SentinelDubboProviderFilter
- String resourceName = DubboUtils.getResourceName(invoker, invocation);
+ String resourceName = DubboUtils.getResourceName(invoker, invocation, true);
assertEquals(resourceName, context.getName());
assertEquals(originApplication, context.getOrigin());
@@ -111,7 +119,8 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
assertEquals(1, childList.size());
DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
ResourceWrapper interfaceResource = interfaceNode.getId();
- assertEquals(DemoService.class.getName(), interfaceResource.getName());
+
+ assertEquals(invoker.getUrl().getColonSeparatedKey(), interfaceResource.getName());
assertSame(EntryType.IN, interfaceResource.getEntryType());
// As SphU.entry(resourceName, EntryType.IN, 1, invocation.getArguments());
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java
index bc06ebf1..ce5019e1 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java
@@ -18,9 +18,8 @@ package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
-
+import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Result;
-import org.apache.dubbo.rpc.RpcResult;
import org.junit.Assert;
import org.junit.Test;
@@ -41,7 +40,7 @@ public class DubboFallbackRegistryTest {
public void testCustomFallback() {
BlockException ex = new FlowException("xxx");
DubboFallbackRegistry.setConsumerFallback(
- (invoker, invocation, e) -> new RpcResult("Error: " + e.getClass().getName()));
+ (invoker, invocation, e) -> AsyncRpcResult.newDefaultAsyncResult("Error: " + e.getClass().getName(), invocation));
Result result = DubboFallbackRegistry.getConsumerFallback()
.handle(null, null, ex);
Assert.assertFalse("The invocation should not fail", result.hasException());
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/DemoService.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/DemoService.java
index 2a024cc9..26e84479 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/DemoService.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/DemoService.java
@@ -20,4 +20,5 @@ package com.alibaba.csp.sentinel.adapter.dubbo.provider;
*/
public interface DemoService {
String sayHello(String name, int n);
+ String sayHi(String name,int n);
}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/impl/DemoServiceImpl.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/impl/DemoServiceImpl.java
index f804c2c4..b09e08be 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/impl/DemoServiceImpl.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/impl/DemoServiceImpl.java
@@ -24,4 +24,9 @@ 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;
+ }
}
diff --git a/sentinel-demo/sentinel-demo-apache-dubbo/pom.xml b/sentinel-demo/sentinel-demo-apache-dubbo/pom.xml
index 3d9b1053..b65359ff 100644
--- a/sentinel-demo/sentinel-demo-apache-dubbo/pom.xml
+++ b/sentinel-demo/sentinel-demo-apache-dubbo/pom.xml
@@ -15,7 +15,7 @@
org.apache.dubbo
dubbo
- 2.7.1
+ 2.7.3
diff --git a/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooConsumerBootstrap.java b/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooConsumerBootstrap.java
index dfe1dbcb..38de4892 100644
--- a/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooConsumerBootstrap.java
+++ b/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooConsumerBootstrap.java
@@ -15,12 +15,22 @@
*/
package com.alibaba.csp.sentinel.demo.apache.dubbo;
+import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
import com.alibaba.csp.sentinel.demo.apache.dubbo.consumer.ConsumerConfiguration;
import com.alibaba.csp.sentinel.demo.apache.dubbo.consumer.FooServiceConsumer;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
-
+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.AsyncRpcResult;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
/**
* Please add the following VM arguments:
*
@@ -33,10 +43,14 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
*/
public class FooConsumerBootstrap {
- public static void main(String[] args) {
+ private static final String INTERFACE_RES_KEY = FooService.class.getName();
+ private static final String RES_KEY = INTERFACE_RES_KEY + ":sayHello(java.lang.String)";
+
+ public static void main(String[] args) throws InterruptedException {
AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext();
consumerContext.register(ConsumerConfiguration.class);
consumerContext.refresh();
+ initFlowRule(10, false);
FooServiceConsumer service = consumerContext.getBean(FooServiceConsumer.class);
@@ -50,5 +64,97 @@ public class FooConsumerBootstrap {
ex.printStackTrace();
}
}
+
+ // method flowcontrol
+ Thread.sleep(1000);
+ initFlowRule(20, true);
+ for (int i = 0; i < 10; i++) {
+ try {
+ String message = service.sayHello("Eric");
+ System.out.println("Success: " + message);
+ } catch (SentinelRpcException ex) {
+ System.out.println("Blocked");
+ System.out.println("fallback:" + service.doAnother());
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ // fallback to result
+ Thread.sleep(1000);
+ registryCustomFallback();
+
+ for (int i = 0; i < 10; i++) {
+ try {
+ String message = service.sayHello("Eric");
+ System.out.println("Result: " + message);
+ } catch (SentinelRpcException ex) {
+ System.out.println("Blocked");
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ // fallback to exception
+ Thread.sleep(1000);
+ registryCustomFallbackForCustomException();
+
+ for (int i = 0; i < 10; i++) {
+ try {
+ String message = service.sayHello("Eric");
+ System.out.println("Result: " + message);
+ } catch (SentinelRpcException ex) {
+ System.out.println("Blocked");
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ Thread.sleep(1000);
+ registryCustomFallbackWhenFallbackError();
+ for (int i = 0; i < 10; i++) {
+ try {
+ String message = service.sayHello("Eric");
+ System.out.println("Result: " + message);
+ } catch (SentinelRpcException ex) {
+ System.out.println("Blocked");
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ public static void registryCustomFallback() {
+ DubboFallbackRegistry.setConsumerFallback(
+ (invoker, invocation, ex) -> AsyncRpcResult.newDefaultAsyncResult("fallback", invocation));
+
+ }
+
+ public static void registryCustomFallbackForCustomException() {
+ DubboFallbackRegistry.setConsumerFallback(
+ (invoker, invocation, ex) -> AsyncRpcResult.newDefaultAsyncResult(new RuntimeException("fallback"), invocation));
+ }
+
+ public static void registryCustomFallbackWhenFallbackError() {
+ DubboFallbackRegistry.setConsumerFallback(
+ (invoker, invocation, ex) -> {
+ throw new RuntimeException("fallback");
+ });
+ }
+
+
+ private static void initFlowRule(int interfaceFlowLimit, boolean method) {
+ FlowRule flowRule = new FlowRule(INTERFACE_RES_KEY)
+ .setCount(interfaceFlowLimit)
+ .setGrade(RuleConstant.FLOW_GRADE_QPS);
+ List list = new ArrayList<>();
+ if (method) {
+ FlowRule flowRule1 = new FlowRule(RES_KEY)
+ .setCount(5)
+ .setGrade(RuleConstant.FLOW_GRADE_QPS);
+ list.add(flowRule1);
+ }
+ list.add(flowRule);
+ FlowRuleManager.loadRules(list);
}
}
diff --git a/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooConsumerExceptionDegradeBootstrap.java b/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooConsumerExceptionDegradeBootstrap.java
new file mode 100644
index 00000000..2df72d84
--- /dev/null
+++ b/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooConsumerExceptionDegradeBootstrap.java
@@ -0,0 +1,119 @@
+/*
+ * 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.demo.apache.dubbo;
+
+import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
+import com.alibaba.csp.sentinel.demo.apache.dubbo.consumer.ConsumerConfiguration;
+import com.alibaba.csp.sentinel.demo.apache.dubbo.consumer.FooServiceConsumer;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
+import org.apache.dubbo.rpc.AsyncRpcResult;
+import org.apache.dubbo.rpc.RpcContext;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+import java.util.Collections;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Please add the following VM arguments:
+ *
+ * -Djava.net.preferIPv4Stack=true
+ * -Dcsp.sentinel.api.port=8721
+ * -Dproject.name=dubbo-consumer-demo
+ *
+ *
+ * @author Zechao zheng
+ */
+public class FooConsumerExceptionDegradeBootstrap {
+
+ private static final String INTERFACE_RES_KEY = FooService.class.getName();
+ private static final String RES_KEY = INTERFACE_RES_KEY + ":sayHello(java.lang.String)";
+
+ public static void main(String[] args) throws InterruptedException, ExecutionException {
+ AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext();
+ consumerContext.register(ConsumerConfiguration.class);
+ consumerContext.refresh();
+
+ FooServiceConsumer service = consumerContext.getBean(FooServiceConsumer.class);
+ initExceptionFallback(3);
+ registryCustomFallback();
+ for (int i = 0; i < 10; i++) {
+ try {
+ String message = service.exceptionTest(true, false);
+ System.out.println("Result: " + message);
+ } catch (SentinelRpcException ex) {
+ System.out.println("Blocked");
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ // sleep 3s to skip the time window
+ initExceptionFallback(3);
+ Thread.sleep(3000);
+ for (int i = 0; i < 10; i++) {
+ try {
+ String message = service.exceptionTest(false, true);
+ System.out.println("Result: " + message);
+ } catch (SentinelRpcException ex) {
+ System.out.println("Blocked");
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ initExceptionFallback(3);
+ Thread.sleep(3000);
+
+ try {
+ // timeout to trigger the fallback
+ CompletableFuture completableFuture = RpcContext.getContext().asyncCall(() -> service.exceptionTest(false, true));
+ System.out.println("Result: " + completableFuture.get());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ for (int i = 0; i < 10; i++) {
+ try {
+ CompletableFuture result = RpcContext.getContext().asyncCall(() -> service.exceptionTest(false, true));
+ System.out.println("Result: " + result.get());
+ } catch (SentinelRpcException ex) {
+ System.out.println("Blocked");
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ }
+
+ public static void registryCustomFallback() {
+ DubboFallbackRegistry.setConsumerFallback(
+ (invoker, invocation, ex) -> AsyncRpcResult.newDefaultAsyncResult("fallback", invocation));
+
+ }
+
+ public static void initExceptionFallback(int timewindow) {
+ DegradeRule degradeRule = new DegradeRule(INTERFACE_RES_KEY)
+ .setCount(0.5)
+ .setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO)
+ .setTimeWindow(timewindow)
+ .setMinRequestAmount(1);
+ DegradeRuleManager.loadRules(Collections.singletonList(degradeRule));
+
+ }
+}
diff --git a/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooProviderBootstrap.java b/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooProviderBootstrap.java
index a23c0ed0..1e4619a9 100644
--- a/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooProviderBootstrap.java
+++ b/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooProviderBootstrap.java
@@ -37,15 +37,11 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
*/
public class FooProviderBootstrap {
- private static final String INTERFACE_RES_KEY = FooService.class.getName();
- private static final String RES_KEY = INTERFACE_RES_KEY + ":sayHello(java.lang.String)";
-
public static void main(String[] args) {
// Users don't need to manually call this method.
// Only for eager initialization.
InitExecutor.doInit();
- initFlowRule();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(ProviderConfiguration.class);
@@ -54,10 +50,4 @@ public class FooProviderBootstrap {
System.out.println("Service provider is ready");
}
- private static void initFlowRule() {
- FlowRule flowRule = new FlowRule(INTERFACE_RES_KEY)
- .setCount(10)
- .setGrade(RuleConstant.FLOW_GRADE_QPS);
- FlowRuleManager.loadRules(Collections.singletonList(flowRule));
- }
}
diff --git a/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooService.java b/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooService.java
index dcdabc8c..1ccff084 100644
--- a/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooService.java
+++ b/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooService.java
@@ -23,4 +23,6 @@ public interface FooService {
String sayHello(String name);
String doAnother();
+
+ String exceptionTest(boolean biz, boolean timeout);
}
diff --git a/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/consumer/FooServiceConsumer.java b/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/consumer/FooServiceConsumer.java
index f83d98a0..5a224d5b 100644
--- a/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/consumer/FooServiceConsumer.java
+++ b/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/consumer/FooServiceConsumer.java
@@ -16,7 +16,6 @@
package com.alibaba.csp.sentinel.demo.apache.dubbo.consumer;
import com.alibaba.csp.sentinel.demo.apache.dubbo.FooService;
-
import org.apache.dubbo.config.annotation.Reference;
/**
@@ -24,7 +23,7 @@ import org.apache.dubbo.config.annotation.Reference;
*/
public class FooServiceConsumer {
- @Reference(url = "dubbo://127.0.0.1:25758", timeout = 3000)
+ @Reference(url = "dubbo://127.0.0.1:25758", timeout = 500)
private FooService fooService;
public String sayHello(String name) {
@@ -34,4 +33,8 @@ public class FooServiceConsumer {
public String doAnother() {
return fooService.doAnother();
}
+
+ public String exceptionTest(boolean biz, boolean timeout) {
+ return fooService.exceptionTest(biz, timeout);
+ }
}
diff --git a/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/provider/FooServiceImpl.java b/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/provider/FooServiceImpl.java
index bd92567a..f73184b0 100644
--- a/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/provider/FooServiceImpl.java
+++ b/sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/provider/FooServiceImpl.java
@@ -15,12 +15,11 @@
*/
package com.alibaba.csp.sentinel.demo.apache.dubbo.provider;
-import java.time.LocalDateTime;
-
import com.alibaba.csp.sentinel.demo.apache.dubbo.FooService;
-
import org.apache.dubbo.config.annotation.Service;
+import java.time.LocalDateTime;
+
/**
* @author Eric Zhao
*/
@@ -36,4 +35,20 @@ public class FooServiceImpl implements FooService {
public String doAnother() {
return LocalDateTime.now().toString();
}
+
+ @Override
+ public String exceptionTest(boolean biz, boolean timeout) {
+ if (biz) {
+ throw new RuntimeException("biz exception");
+ }
+ if (timeout) {
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ return "Success";
+ }
+
}