Improve async invocation support in sentinel-apache-dubbo-adapter (#1124)
* Improve async support for Dubbo 2.7.2 and above (not compatible with 2.7.0 and 2.7.1 due to the bad compatibility design of Dubbo Filter)
This commit is contained in:
parent
a5d6773cbc
commit
47100e64ba
|
|
@ -15,7 +15,7 @@
|
|||
<properties>
|
||||
<java.source.version>1.8</java.source.version>
|
||||
<java.target.version>1.8</java.target.version>
|
||||
<apache.dubbo.version>2.7.1</apache.dubbo.version>
|
||||
<apache.dubbo.version>2.7.3</apache.dubbo.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
47
sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java
Executable file → Normal file
47
sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java
Executable file → Normal file
|
|
@ -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;
|
||||
|
||||
/**
|
||||
* <p>Dubbo service consumer filter for Sentinel. Auto activated by default.</p>
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
46
sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java
Executable file → Normal file
46
sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java
Executable file → Normal file
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
* <p>
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<FlowRule> 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<DegradeRule> 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<Node> 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<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 = 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<Node> 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<String, StatisticNode> methodOriginCountMap = methodClusterNode.getOriginCountMap();
|
||||
assertEquals(0, methodOriginCountMap.size());
|
||||
|
||||
Map<String, StatisticNode> 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<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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo</artifactId>
|
||||
<version>2.7.1</version>
|
||||
<version>2.7.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Dubbo provides qos plugin and is enable by default. -->
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
* <pre>
|
||||
|
|
@ -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<FlowRule> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
* <pre>
|
||||
* -Djava.net.preferIPv4Stack=true
|
||||
* -Dcsp.sentinel.api.port=8721
|
||||
* -Dproject.name=dubbo-consumer-demo
|
||||
* </pre>
|
||||
*
|
||||
* @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<String> 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<String> 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));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,4 +23,6 @@ public interface FooService {
|
|||
String sayHello(String name);
|
||||
|
||||
String doAnother();
|
||||
|
||||
String exceptionTest(boolean biz, boolean timeout);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue