* 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)master
@@ -15,7 +15,7 @@ | |||||
<properties> | <properties> | ||||
<java.source.version>1.8</java.source.version> | <java.source.version>1.8</java.source.version> | ||||
<java.target.version>1.8</java.target.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> | </properties> | ||||
<dependencies> | <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; | 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.common.extension.Activate; | ||||
import org.apache.dubbo.rpc.Filter; | import org.apache.dubbo.rpc.Filter; | ||||
import org.apache.dubbo.rpc.Invocation; | import org.apache.dubbo.rpc.Invocation; | ||||
@@ -34,7 +34,7 @@ public class DubboAppContextFilter implements Filter { | |||||
@Override | @Override | ||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { | 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) { | if (application != null) { | ||||
RpcContext.getContext().setAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, application); | RpcContext.getContext().setAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, application); | ||||
} | } | ||||
@@ -15,6 +15,7 @@ | |||||
*/ | */ | ||||
package com.alibaba.csp.sentinel.adapter.dubbo; | package com.alibaba.csp.sentinel.adapter.dubbo; | ||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig; | |||||
import com.alibaba.csp.sentinel.util.StringUtil; | import com.alibaba.csp.sentinel.util.StringUtil; | ||||
import org.apache.dubbo.rpc.Invocation; | import org.apache.dubbo.rpc.Invocation; | ||||
import org.apache.dubbo.rpc.Invoker; | import org.apache.dubbo.rpc.Invoker; | ||||
@@ -25,6 +26,8 @@ import org.apache.dubbo.rpc.Invoker; | |||||
public final class DubboUtils { | public final class DubboUtils { | ||||
public static final String SENTINEL_DUBBO_APPLICATION_KEY = "dubboApplication"; | 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) { | public static String getApplication(Invocation invocation, String defaultValue) { | ||||
if (invocation == null || invocation.getAttachments() == null) { | if (invocation == null || invocation.getAttachments() == null) { | ||||
@@ -33,12 +36,17 @@ public final class DubboUtils { | |||||
return invocation.getAttachment(SENTINEL_DUBBO_APPLICATION_KEY, defaultValue); | 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); | 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; | boolean isFirst = true; | ||||
for (Class<?> clazz : invocation.getParameterTypes()) { | for (Class<?> clazz : invocation.getParameterTypes()) { | ||||
if (!isFirst) { | if (!isFirst) { | ||||
@@ -55,13 +63,12 @@ public final class DubboUtils { | |||||
if (StringUtil.isNotBlank(prefix)) { | if (StringUtil.isNotBlank(prefix)) { | ||||
return new StringBuilder(64) | return new StringBuilder(64) | ||||
.append(prefix) | .append(prefix) | ||||
.append(getResourceName(invoker, invocation)) | |||||
.append(getResourceName(invoker, invocation, DubboConfig.getDubboInterfaceGroupAndVersionEnabled())) | |||||
.toString(); | .toString(); | ||||
} else { | } else { | ||||
return getResourceName(invoker, invocation); | |||||
return getResourceName(invoker, invocation, DubboConfig.getDubboInterfaceGroupAndVersionEnabled()); | |||||
} | } | ||||
} | } | ||||
private DubboUtils() { | private DubboUtils() { | ||||
} | } | ||||
} | } |
@@ -19,17 +19,18 @@ import com.alibaba.csp.sentinel.Entry; | |||||
import com.alibaba.csp.sentinel.EntryType; | import com.alibaba.csp.sentinel.EntryType; | ||||
import com.alibaba.csp.sentinel.ResourceTypeConstants; | import com.alibaba.csp.sentinel.ResourceTypeConstants; | ||||
import com.alibaba.csp.sentinel.SphU; | 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.config.DubboConfig; | ||||
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry; | import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry; | ||||
import com.alibaba.csp.sentinel.log.RecordLog; | import com.alibaba.csp.sentinel.log.RecordLog; | ||||
import com.alibaba.csp.sentinel.slots.block.BlockException; | import com.alibaba.csp.sentinel.slots.block.BlockException; | ||||
import org.apache.dubbo.common.extension.Activate; | import org.apache.dubbo.common.extension.Activate; | ||||
import org.apache.dubbo.rpc.Filter; | |||||
import org.apache.dubbo.rpc.Invocation; | import org.apache.dubbo.rpc.Invocation; | ||||
import org.apache.dubbo.rpc.InvokeMode; | |||||
import org.apache.dubbo.rpc.Invoker; | import org.apache.dubbo.rpc.Invoker; | ||||
import org.apache.dubbo.rpc.Result; | import org.apache.dubbo.rpc.Result; | ||||
import org.apache.dubbo.rpc.RpcContext; | |||||
import org.apache.dubbo.rpc.RpcException; | 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> | * <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 | * @author Eric Zhao | ||||
*/ | */ | ||||
@Activate(group = "consumer") | @Activate(group = "consumer") | ||||
public class SentinelDubboConsumerFilter implements Filter { | |||||
public class SentinelDubboConsumerFilter extends BaseSentinelDubboFilter { | |||||
public SentinelDubboConsumerFilter() { | public SentinelDubboConsumerFilter() { | ||||
RecordLog.info("Sentinel Apache Dubbo consumer filter initialized"); | 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 { | public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { | ||||
Entry interfaceEntry = null; | Entry interfaceEntry = null; | ||||
Entry methodEntry = null; | Entry methodEntry = null; | ||||
RpcContext rpcContext = RpcContext.getContext(); | |||||
try { | 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) { | } catch (BlockException e) { | ||||
return DubboFallbackRegistry.getConsumerFallback().handle(invoker, invocation, 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(); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -19,17 +19,16 @@ import com.alibaba.csp.sentinel.Entry; | |||||
import com.alibaba.csp.sentinel.EntryType; | import com.alibaba.csp.sentinel.EntryType; | ||||
import com.alibaba.csp.sentinel.ResourceTypeConstants; | import com.alibaba.csp.sentinel.ResourceTypeConstants; | ||||
import com.alibaba.csp.sentinel.SphU; | 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.config.DubboConfig; | ||||
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry; | import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry; | ||||
import com.alibaba.csp.sentinel.context.ContextUtil; | import com.alibaba.csp.sentinel.context.ContextUtil; | ||||
import com.alibaba.csp.sentinel.log.RecordLog; | import com.alibaba.csp.sentinel.log.RecordLog; | ||||
import com.alibaba.csp.sentinel.slots.block.BlockException; | import com.alibaba.csp.sentinel.slots.block.BlockException; | ||||
import org.apache.dubbo.common.extension.Activate; | import org.apache.dubbo.common.extension.Activate; | ||||
import org.apache.dubbo.rpc.Filter; | |||||
import org.apache.dubbo.rpc.Invocation; | import org.apache.dubbo.rpc.Invocation; | ||||
import org.apache.dubbo.rpc.Invoker; | import org.apache.dubbo.rpc.Invoker; | ||||
import org.apache.dubbo.rpc.Result; | import org.apache.dubbo.rpc.Result; | ||||
import org.apache.dubbo.rpc.RpcContext; | |||||
import org.apache.dubbo.rpc.RpcException; | import org.apache.dubbo.rpc.RpcException; | ||||
/** | /** | ||||
@@ -45,7 +44,7 @@ import org.apache.dubbo.rpc.RpcException; | |||||
* @author Eric Zhao | * @author Eric Zhao | ||||
*/ | */ | ||||
@Activate(group = "provider") | @Activate(group = "provider") | ||||
public class SentinelDubboProviderFilter implements Filter { | |||||
public class SentinelDubboProviderFilter extends BaseSentinelDubboFilter { | |||||
public SentinelDubboProviderFilter() { | public SentinelDubboProviderFilter() { | ||||
RecordLog.info("Sentinel Apache Dubbo provider filter initialized"); | 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 { | public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { | ||||
// Get origin caller. | // Get origin caller. | ||||
String application = DubboUtils.getApplication(invocation, ""); | String application = DubboUtils.getApplication(invocation, ""); | ||||
RpcContext rpcContext = RpcContext.getContext(); | |||||
Entry interfaceEntry = null; | Entry interfaceEntry = null; | ||||
Entry methodEntry = null; | Entry methodEntry = null; | ||||
try { | 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 | // Only need to create entrance context at provider side, as context will take effect | ||||
// at entrance of invocation chain only (for inbound traffic). | // 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) { | } catch (BlockException e) { | ||||
return DubboFallbackRegistry.getProviderFallback().handle(invoker, invocation, 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_PROVIDER_PREFIX = "dubbo:provider:"; | ||||
private static final String DEFAULT_DUBBO_CONSUMER_PREFIX = "dubbo:consumer:"; | 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)); | return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_USE_PREFIX)); | ||||
} | } | ||||
@@ -58,5 +62,16 @@ public final class DubboConfig { | |||||
return null; | 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; | 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 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 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 | * Base test class, provide common methods for subClass | ||||
* The package is same as CtSph, to call CtSph.resetChainMap() method for test | * The package is same as CtSph, to call CtSph.resetChainMap() method for test | ||||
* | |||||
* <p> | |||||
* Note: Only for test. DO NOT USE IN PRODUCTION! | * Note: Only for test. DO NOT USE IN PRODUCTION! | ||||
* | * | ||||
* @author cdfive | * @author cdfive | ||||
*/ | */ | ||||
public class BaseTest { | 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 | * Clean up resources for context, clusterNodeMap, processorSlotChainMap | ||||
*/ | */ | ||||
protected static void cleanUpAll() { | 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.config.DubboConfig; | ||||
import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService; | import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService; | ||||
import com.alibaba.csp.sentinel.config.SentinelConfig; | 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.Invocation; | ||||
import org.apache.dubbo.rpc.Invoker; | import org.apache.dubbo.rpc.Invoker; | ||||
import org.junit.After; | import org.junit.After; | ||||
@@ -29,7 +31,9 @@ import java.util.HashMap; | |||||
import static org.junit.Assert.assertEquals; | import static org.junit.Assert.assertEquals; | ||||
import static org.junit.Assert.fail; | 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 | * @author cdfive | ||||
@@ -41,6 +45,7 @@ public class DubboUtilsTest { | |||||
SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "true"); | SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "true"); | ||||
SentinelConfig.setConfig(DubboConfig.DUBBO_PROVIDER_PREFIX, ""); | SentinelConfig.setConfig(DubboConfig.DUBBO_PROVIDER_PREFIX, ""); | ||||
SentinelConfig.setConfig(DubboConfig.DUBBO_CONSUMER_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("csp.sentinel.dubbo.resource.use.prefix", "false"); | ||||
SentinelConfig.setConfig(DubboConfig.DUBBO_PROVIDER_PREFIX, ""); | SentinelConfig.setConfig(DubboConfig.DUBBO_PROVIDER_PREFIX, ""); | ||||
SentinelConfig.setConfig(DubboConfig.DUBBO_CONSUMER_PREFIX, ""); | SentinelConfig.setConfig(DubboConfig.DUBBO_CONSUMER_PREFIX, ""); | ||||
SentinelConfig.setConfig(DubboConfig.DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false"); | |||||
} | } | ||||
@@ -78,27 +84,49 @@ public class DubboUtilsTest { | |||||
} | } | ||||
@Test | @Test | ||||
public void testGetResourceName() { | |||||
public void testGetResourceName() throws NoSuchMethodException { | |||||
Invoker invoker = mock(Invoker.class); | Invoker invoker = mock(Invoker.class); | ||||
when(invoker.getInterface()).thenReturn(DemoService.class); | when(invoker.getInterface()).thenReturn(DemoService.class); | ||||
Invocation invocation = mock(Invocation.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.getMethodName()).thenReturn(method.getName()); | ||||
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes()); | when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes()); | ||||
String resourceName = DubboUtils.getResourceName(invoker, invocation); | String resourceName = DubboUtils.getResourceName(invoker, invocation); | ||||
assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName); | assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName); | ||||
} | } | ||||
@Test | @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); | Invoker invoker = mock(Invoker.class); | ||||
when(invoker.getInterface()).thenReturn(DemoService.class); | when(invoker.getInterface()).thenReturn(DemoService.class); | ||||
Invocation invocation = mock(Invocation.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.getMethodName()).thenReturn(method.getName()); | ||||
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes()); | 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); | 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; | package com.alibaba.csp.sentinel.adapter.dubbo; | ||||
import com.alibaba.csp.sentinel.BaseTest; | import com.alibaba.csp.sentinel.BaseTest; | ||||
import com.alibaba.csp.sentinel.Constants; | |||||
import com.alibaba.csp.sentinel.Entry; | import com.alibaba.csp.sentinel.Entry; | ||||
import com.alibaba.csp.sentinel.EntryType; | 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.adapter.dubbo.provider.DemoService; | ||||
import com.alibaba.csp.sentinel.context.Context; | import com.alibaba.csp.sentinel.context.Context; | ||||
import com.alibaba.csp.sentinel.context.ContextUtil; | 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.Node; | ||||
import com.alibaba.csp.sentinel.node.StatisticNode; | import com.alibaba.csp.sentinel.node.StatisticNode; | ||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; | 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.Invocation; | ||||
import org.apache.dubbo.rpc.Invoker; | import org.apache.dubbo.rpc.Invoker; | ||||
import org.apache.dubbo.rpc.Result; | 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.After; | ||||
import org.junit.Before; | import org.junit.Before; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||
import java.util.ArrayList; | |||||
import java.util.LinkedList; | |||||
import java.util.List; | |||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Queue; | |||||
import java.util.Set; | 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 | * @author cdfive | ||||
@@ -49,9 +71,12 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
private SentinelDubboConsumerFilter filter = new SentinelDubboConsumerFilter(); | private SentinelDubboConsumerFilter filter = new SentinelDubboConsumerFilter(); | ||||
@Before | @Before | ||||
public void setUp() { | public void setUp() { | ||||
cleanUpAll(); | cleanUpAll(); | ||||
initFallback(); | |||||
constructInvokerAndInvocation(); | |||||
} | } | ||||
@After | @After | ||||
@@ -59,18 +84,177 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
cleanUpAll(); | cleanUpAll(); | ||||
} | } | ||||
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); | |||||
} | |||||
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 | @Test | ||||
public void testInvoke() { | |||||
final Invoker invoker = mock(Invoker.class); | |||||
when(invoker.getInterface()).thenReturn(DemoService.class); | |||||
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); | |||||
} | |||||
final Invocation invocation = mock(Invocation.class); | |||||
Method method = DemoService.class.getMethods()[0]; | |||||
when(invocation.getMethodName()).thenReturn(method.getName()); | |||||
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes()); | |||||
@Test | |||||
public void testInvokeSync() { | |||||
final Result result = mock(Result.class); | final Result result = mock(Result.class); | ||||
when(result.hasException()).thenReturn(false); | when(result.hasException()).thenReturn(false); | ||||
when(result.getException()).thenReturn(new Exception()); | |||||
when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> { | when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> { | ||||
verifyInvocationStructure(invoker, invocation); | verifyInvocationStructure(invoker, invocation); | ||||
return result; | return result; | ||||
@@ -79,6 +263,7 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
filter.invoke(invoker, invocation); | filter.invoke(invoker, invocation); | ||||
verify(invoker).invoke(invocation); | verify(invoker).invoke(invocation); | ||||
filter.listener().onResponse(result, invoker, invocation); | |||||
Context context = ContextUtil.getContext(); | Context context = ContextUtil.getContext(); | ||||
assertNull(context); | assertNull(context); | ||||
} | } | ||||
@@ -92,31 +277,32 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
private void verifyInvocationStructure(Invoker invoker, Invocation invocation) { | private void verifyInvocationStructure(Invoker invoker, Invocation invocation) { | ||||
Context context = ContextUtil.getContext(); | Context context = ContextUtil.getContext(); | ||||
assertNotNull(context); | assertNotNull(context); | ||||
// As not call ContextUtil.enter(resourceName, application) in SentinelDubboConsumerFilter, use default 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 | // 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 | // 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()); | assertEquals("", context.getOrigin()); | ||||
DefaultNode entranceNode = context.getEntranceNode(); | DefaultNode entranceNode = context.getEntranceNode(); | ||||
ResourceWrapper entranceResource = entranceNode.getId(); | 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()); | assertSame(EntryType.IN, entranceResource.getEntryType()); | ||||
// As SphU.entry(interfaceName, EntryType.OUT); | // As SphU.entry(interfaceName, EntryType.OUT); | ||||
Set<Node> childList = entranceNode.getChildList(); | Set<Node> childList = entranceNode.getChildList(); | ||||
assertEquals(1, childList.size()); | assertEquals(1, childList.size()); | ||||
DefaultNode interfaceNode = (DefaultNode) childList.iterator().next(); | |||||
DefaultNode interfaceNode = getNode(invoker.getUrl().getColonSeparatedKey(), entranceNode); | |||||
ResourceWrapper interfaceResource = interfaceNode.getId(); | ResourceWrapper interfaceResource = interfaceNode.getId(); | ||||
assertEquals(DemoService.class.getName(), interfaceResource.getName()); | |||||
assertEquals(invoker.getUrl().getColonSeparatedKey(), interfaceResource.getName()); | |||||
assertSame(EntryType.OUT, interfaceResource.getEntryType()); | assertSame(EntryType.OUT, interfaceResource.getEntryType()); | ||||
// As SphU.entry(resourceName, EntryType.OUT); | // As SphU.entry(resourceName, EntryType.OUT); | ||||
childList = interfaceNode.getChildList(); | childList = interfaceNode.getChildList(); | ||||
assertEquals(1, childList.size()); | assertEquals(1, childList.size()); | ||||
DefaultNode methodNode = (DefaultNode) childList.iterator().next(); | |||||
DefaultNode methodNode = getNode(resourceName, entranceNode); | |||||
ResourceWrapper methodResource = methodNode.getId(); | ResourceWrapper methodResource = methodNode.getId(); | ||||
assertEquals(resourceName, methodResource.getName()); | assertEquals(resourceName, methodResource.getName()); | ||||
assertSame(EntryType.OUT, methodResource.getEntryType()); | assertSame(EntryType.OUT, methodResource.getEntryType()); | ||||
@@ -139,4 +325,82 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
Map<String, StatisticNode> interfaceOriginCountMap = interfaceClusterNode.getOriginCountMap(); | Map<String, StatisticNode> interfaceOriginCountMap = interfaceClusterNode.getOriginCountMap(); | ||||
assertEquals(0, interfaceOriginCountMap.size()); | 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.Node; | ||||
import com.alibaba.csp.sentinel.node.StatisticNode; | import com.alibaba.csp.sentinel.node.StatisticNode; | ||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; | 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.Invocation; | ||||
import org.apache.dubbo.rpc.Invoker; | import org.apache.dubbo.rpc.Invoker; | ||||
import org.apache.dubbo.rpc.Result; | import org.apache.dubbo.rpc.Result; | ||||
@@ -38,8 +39,15 @@ import java.lang.reflect.Method; | |||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Set; | 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 | * @author cdfive | ||||
@@ -50,6 +58,7 @@ public class SentinelDubboProviderFilterTest extends BaseTest { | |||||
@Before | @Before | ||||
public void setUp() { | public void setUp() { | ||||
constructInvokerAndInvocation(); | |||||
cleanUpAll(); | cleanUpAll(); | ||||
} | } | ||||
@@ -62,18 +71,16 @@ public class SentinelDubboProviderFilterTest extends BaseTest { | |||||
public void testInvoke() { | public void testInvoke() { | ||||
final String originApplication = "consumerA"; | 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, "")) | when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, "")) | ||||
.thenReturn(originApplication); | |||||
.thenReturn(originApplication); | |||||
final Result result = mock(Result.class); | final Result result = mock(Result.class); | ||||
when(result.hasException()).thenReturn(false); | when(result.hasException()).thenReturn(false); | ||||
when(result.getException()).thenReturn(new Exception()); | |||||
when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> { | when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> { | ||||
verifyInvocationStructure(originApplication, invoker, invocation); | verifyInvocationStructure(originApplication, invoker, invocation); | ||||
return result; | return result; | ||||
@@ -82,6 +89,7 @@ public class SentinelDubboProviderFilterTest extends BaseTest { | |||||
filter.invoke(invoker, invocation); | filter.invoke(invoker, invocation); | ||||
verify(invoker).invoke(invocation); | verify(invoker).invoke(invocation); | ||||
filter.listener().onResponse(result, invoker, invocation); | |||||
Context context = ContextUtil.getContext(); | Context context = ContextUtil.getContext(); | ||||
assertNull(context); | assertNull(context); | ||||
} | } | ||||
@@ -97,7 +105,7 @@ public class SentinelDubboProviderFilterTest extends BaseTest { | |||||
assertNotNull(context); | assertNotNull(context); | ||||
// As ContextUtil.enter(resourceName, application) in SentinelDubboProviderFilter | // 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(resourceName, context.getName()); | ||||
assertEquals(originApplication, context.getOrigin()); | assertEquals(originApplication, context.getOrigin()); | ||||
@@ -111,7 +119,8 @@ public class SentinelDubboProviderFilterTest extends BaseTest { | |||||
assertEquals(1, childList.size()); | assertEquals(1, childList.size()); | ||||
DefaultNode interfaceNode = (DefaultNode) childList.iterator().next(); | DefaultNode interfaceNode = (DefaultNode) childList.iterator().next(); | ||||
ResourceWrapper interfaceResource = interfaceNode.getId(); | ResourceWrapper interfaceResource = interfaceNode.getId(); | ||||
assertEquals(DemoService.class.getName(), interfaceResource.getName()); | |||||
assertEquals(invoker.getUrl().getColonSeparatedKey(), interfaceResource.getName()); | |||||
assertSame(EntryType.IN, interfaceResource.getEntryType()); | assertSame(EntryType.IN, interfaceResource.getEntryType()); | ||||
// As SphU.entry(resourceName, EntryType.IN, 1, invocation.getArguments()); | // 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.BlockException; | ||||
import com.alibaba.csp.sentinel.slots.block.SentinelRpcException; | import com.alibaba.csp.sentinel.slots.block.SentinelRpcException; | ||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowException; | 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.Result; | ||||
import org.apache.dubbo.rpc.RpcResult; | |||||
import org.junit.Assert; | import org.junit.Assert; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
@@ -41,7 +40,7 @@ public class DubboFallbackRegistryTest { | |||||
public void testCustomFallback() { | public void testCustomFallback() { | ||||
BlockException ex = new FlowException("xxx"); | BlockException ex = new FlowException("xxx"); | ||||
DubboFallbackRegistry.setConsumerFallback( | 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() | Result result = DubboFallbackRegistry.getConsumerFallback() | ||||
.handle(null, null, ex); | .handle(null, null, ex); | ||||
Assert.assertFalse("The invocation should not fail", result.hasException()); | Assert.assertFalse("The invocation should not fail", result.hasException()); | ||||
@@ -20,4 +20,5 @@ package com.alibaba.csp.sentinel.adapter.dubbo.provider; | |||||
*/ | */ | ||||
public interface DemoService { | public interface DemoService { | ||||
String sayHello(String name, int n); | 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) { | public String sayHello(String name, int n) { | ||||
return "Hello " + name + ", " + n; | return "Hello " + name + ", " + n; | ||||
} | } | ||||
@Override | |||||
public String sayHi(String name, int n) { | |||||
return "Hi " + name + ", " + n; | |||||
} | |||||
} | } |
@@ -15,7 +15,7 @@ | |||||
<dependency> | <dependency> | ||||
<groupId>org.apache.dubbo</groupId> | <groupId>org.apache.dubbo</groupId> | ||||
<artifactId>dubbo</artifactId> | <artifactId>dubbo</artifactId> | ||||
<version>2.7.1</version> | |||||
<version>2.7.3</version> | |||||
</dependency> | </dependency> | ||||
<!-- Dubbo provides qos plugin and is enable by default. --> | <!-- Dubbo provides qos plugin and is enable by default. --> | ||||
@@ -15,12 +15,22 @@ | |||||
*/ | */ | ||||
package com.alibaba.csp.sentinel.demo.apache.dubbo; | 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.ConsumerConfiguration; | ||||
import com.alibaba.csp.sentinel.demo.apache.dubbo.consumer.FooServiceConsumer; | 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.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 org.springframework.context.annotation.AnnotationConfigApplicationContext; | ||||
import java.util.ArrayList; | |||||
import java.util.Collections; | |||||
import java.util.List; | |||||
/** | /** | ||||
* Please add the following VM arguments: | * Please add the following VM arguments: | ||||
* <pre> | * <pre> | ||||
@@ -33,10 +43,14 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext | |||||
*/ | */ | ||||
public class FooConsumerBootstrap { | 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(); | AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext(); | ||||
consumerContext.register(ConsumerConfiguration.class); | consumerContext.register(ConsumerConfiguration.class); | ||||
consumerContext.refresh(); | consumerContext.refresh(); | ||||
initFlowRule(10, false); | |||||
FooServiceConsumer service = consumerContext.getBean(FooServiceConsumer.class); | FooServiceConsumer service = consumerContext.getBean(FooServiceConsumer.class); | ||||
@@ -50,5 +64,97 @@ public class FooConsumerBootstrap { | |||||
ex.printStackTrace(); | 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 { | 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) { | public static void main(String[] args) { | ||||
// Users don't need to manually call this method. | // Users don't need to manually call this method. | ||||
// Only for eager initialization. | // Only for eager initialization. | ||||
InitExecutor.doInit(); | InitExecutor.doInit(); | ||||
initFlowRule(); | |||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); | AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); | ||||
context.register(ProviderConfiguration.class); | context.register(ProviderConfiguration.class); | ||||
@@ -54,10 +50,4 @@ public class FooProviderBootstrap { | |||||
System.out.println("Service provider is ready"); | 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 sayHello(String name); | ||||
String doAnother(); | String doAnother(); | ||||
String exceptionTest(boolean biz, boolean timeout); | |||||
} | } |
@@ -16,7 +16,6 @@ | |||||
package com.alibaba.csp.sentinel.demo.apache.dubbo.consumer; | package com.alibaba.csp.sentinel.demo.apache.dubbo.consumer; | ||||
import com.alibaba.csp.sentinel.demo.apache.dubbo.FooService; | import com.alibaba.csp.sentinel.demo.apache.dubbo.FooService; | ||||
import org.apache.dubbo.config.annotation.Reference; | import org.apache.dubbo.config.annotation.Reference; | ||||
/** | /** | ||||
@@ -24,7 +23,7 @@ import org.apache.dubbo.config.annotation.Reference; | |||||
*/ | */ | ||||
public class FooServiceConsumer { | 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; | private FooService fooService; | ||||
public String sayHello(String name) { | public String sayHello(String name) { | ||||
@@ -34,4 +33,8 @@ public class FooServiceConsumer { | |||||
public String doAnother() { | public String doAnother() { | ||||
return fooService.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; | package com.alibaba.csp.sentinel.demo.apache.dubbo.provider; | ||||
import java.time.LocalDateTime; | |||||
import com.alibaba.csp.sentinel.demo.apache.dubbo.FooService; | import com.alibaba.csp.sentinel.demo.apache.dubbo.FooService; | ||||
import org.apache.dubbo.config.annotation.Service; | import org.apache.dubbo.config.annotation.Service; | ||||
import java.time.LocalDateTime; | |||||
/** | /** | ||||
* @author Eric Zhao | * @author Eric Zhao | ||||
*/ | */ | ||||
@@ -36,4 +35,20 @@ public class FooServiceImpl implements FooService { | |||||
public String doAnother() { | public String doAnother() { | ||||
return LocalDateTime.now().toString(); | 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"; | |||||
} | |||||
} | } |