- Fix NPE bug in consumer filter (when non-biz error occurred) - Improve default fallback in Dubbo 2.7.x adapter: convert the BlockException to a simple RuntimeException (with necessary message) - Polish code and comments Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -1,4 +1,4 @@ | |||||
# Sentinel Apache Dubbo Adapter | |||||
# Sentinel Apache Dubbo Adapter (for 2.7.x+) | |||||
> Note: 中文文档请见[此处](https://github.com/alibaba/Sentinel/wiki/主流框架的适配#dubbo)。 | > Note: 中文文档请见[此处](https://github.com/alibaba/Sentinel/wiki/主流框架的适配#dubbo)。 | ||||
@@ -21,7 +21,7 @@ To use Sentinel Dubbo Adapter, you can simply add the following dependency to yo | |||||
The Sentinel filters are **enabled by default**. Once you add the dependency, | The Sentinel filters are **enabled by default**. Once you add the dependency, | ||||
the Dubbo services and methods will become protected resources in Sentinel, | the Dubbo services and methods will become protected resources in Sentinel, | ||||
which can leverage Sentinel's flow control and guard ability when rules are configured. | which can leverage Sentinel's flow control and guard ability when rules are configured. | ||||
Demos can be found in [sentinel-demo-dubbo](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-dubbo). | |||||
Demos can be found in [sentinel-demo-apache-dubbo](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-apache-dubbo). | |||||
If you don't want the filters enabled, you can manually disable them. For example: | If you don't want the filters enabled, you can manually disable them. For example: | ||||
@@ -37,8 +37,8 @@ For more details of Dubbo filter, see [here](http://dubbo.apache.org/en-us/docs/ | |||||
The resource for Dubbo services has two granularities: service interface and service method. | The resource for Dubbo services has two granularities: service interface and service method. | ||||
- Service interface:resourceName format is `interfaceName`,e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService` | |||||
- Service method:resourceName format is `interfaceName:methodSignature`,e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String)` | |||||
- Service interface: resourceName format is `interfaceName`, e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService` | |||||
- Service method: resourceName format is `interfaceName:methodSignature`, e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String)` | |||||
## Flow control based on caller | ## Flow control based on caller | ||||
@@ -21,11 +21,10 @@ import org.apache.dubbo.rpc.Invocation; | |||||
import org.apache.dubbo.rpc.Invoker; | import org.apache.dubbo.rpc.Invoker; | ||||
/** | /** | ||||
* Base Class of the {@link SentinelDubboProviderFilter} and {@link SentinelDubboConsumerFilter}. | |||||
* Base class of the {@link SentinelDubboProviderFilter} and {@link SentinelDubboConsumerFilter}. | |||||
* | * | ||||
* @author Zechao Zheng | * @author Zechao Zheng | ||||
*/ | */ | ||||
public abstract class BaseSentinelDubboFilter implements Filter { | public abstract class BaseSentinelDubboFilter implements Filter { | ||||
@@ -19,11 +19,13 @@ import com.alibaba.csp.sentinel.*; | |||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig; | import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig; | ||||
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.*; | import org.apache.dubbo.rpc.*; | ||||
import org.apache.dubbo.rpc.support.RpcUtils; | import org.apache.dubbo.rpc.support.RpcUtils; | ||||
import java.util.LinkedList; | import java.util.LinkedList; | ||||
import java.util.Optional; | |||||
import java.util.function.BiConsumer; | import java.util.function.BiConsumer; | ||||
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; | import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; | ||||
@@ -38,6 +40,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; | |||||
* | * | ||||
* @author Carpenter Lee | * @author Carpenter Lee | ||||
* @author Eric Zhao | * @author Eric Zhao | ||||
* @author Lin Liang | |||||
*/ | */ | ||||
@Activate(group = CONSUMER) | @Activate(group = CONSUMER) | ||||
public class SentinelDubboConsumerFilter extends BaseSentinelDubboFilter { | public class SentinelDubboConsumerFilter extends BaseSentinelDubboFilter { | ||||
@@ -64,7 +67,6 @@ public class SentinelDubboConsumerFilter extends BaseSentinelDubboFilter { | |||||
} else { | } else { | ||||
return asyncInvoke(invoker, invocation); | return asyncInvoke(invoker, invocation); | ||||
} | } | ||||
} | } | ||||
private Result syncInvoke(Invoker<?> invoker, Invocation invocation) { | private Result syncInvoke(Invoker<?> invoker, Invocation invocation) { | ||||
@@ -75,7 +77,8 @@ public class SentinelDubboConsumerFilter extends BaseSentinelDubboFilter { | |||||
String methodResourceName = getMethodName(invoker, invocation, prefix); | String methodResourceName = getMethodName(invoker, invocation, prefix); | ||||
try { | try { | ||||
interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT); | interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT); | ||||
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT, invocation.getArguments()); | |||||
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT, | |||||
invocation.getArguments()); | |||||
Result result = invoker.invoke(invocation); | Result result = invoker.invoke(invocation); | ||||
if (result.hasException()) { | if (result.hasException()) { | ||||
Tracer.traceEntry(result.getException(), interfaceEntry); | Tracer.traceEntry(result.getException(), interfaceEntry); | ||||
@@ -98,24 +101,27 @@ public class SentinelDubboConsumerFilter extends BaseSentinelDubboFilter { | |||||
} | } | ||||
} | } | ||||
private Result asyncInvoke(Invoker<?> invoker, Invocation invocation) { | private Result asyncInvoke(Invoker<?> invoker, Invocation invocation) { | ||||
LinkedList<EntryHolder> queue = new LinkedList<>(); | LinkedList<EntryHolder> queue = new LinkedList<>(); | ||||
String prefix = DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey(); | String prefix = DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey(); | ||||
String interfaceResourceName = getInterfaceName(invoker, prefix); | String interfaceResourceName = getInterfaceName(invoker, prefix); | ||||
String methodResourceName = getMethodName(invoker, invocation, prefix); | String methodResourceName = getMethodName(invoker, invocation, prefix); | ||||
try { | try { | ||||
queue.push(new EntryHolder(SphU.asyncEntry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT), null)); | |||||
queue.push(new EntryHolder(SphU.asyncEntry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT, 1, invocation.getArguments()), invocation.getArguments())); | |||||
queue.push(new EntryHolder( | |||||
SphU.asyncEntry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT), null)); | |||||
queue.push(new EntryHolder( | |||||
SphU.asyncEntry(methodResourceName, ResourceTypeConstants.COMMON_RPC, | |||||
EntryType.OUT, 1, invocation.getArguments()), invocation.getArguments())); | |||||
Result result = invoker.invoke(invocation); | Result result = invoker.invoke(invocation); | ||||
result.whenCompleteWithContext(new BiConsumer<Result, Throwable>() { | |||||
@Override | |||||
public void accept(Result result, Throwable throwable) { | |||||
while (!queue.isEmpty()) { | |||||
EntryHolder holder = queue.pop(); | |||||
Tracer.traceEntry(result.getException(), holder.entry); | |||||
exitEntry(holder); | |||||
} | |||||
result.whenCompleteWithContext((r, throwable) -> { | |||||
Throwable error = throwable; | |||||
if (error == null) { | |||||
error = Optional.ofNullable(r).map(Result::getException).orElse(null); | |||||
} | |||||
while (!queue.isEmpty()) { | |||||
EntryHolder holder = queue.pop(); | |||||
Tracer.traceEntry(error, holder.entry); | |||||
exitEntry(holder); | |||||
} | } | ||||
}); | }); | ||||
return result; | return result; | ||||
@@ -127,10 +133,9 @@ public class SentinelDubboConsumerFilter extends BaseSentinelDubboFilter { | |||||
} | } | ||||
} | } | ||||
class EntryHolder { | |||||
static class EntryHolder { | |||||
final private Entry entry; | final private Entry entry; | ||||
final private Object[] params; | final private Object[] params; | ||||
public EntryHolder(Entry entry, Object[] params) { | public EntryHolder(Entry entry, Object[] params) { | ||||
@@ -20,6 +20,7 @@ import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig; | |||||
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.Invocation; | import org.apache.dubbo.rpc.Invocation; | ||||
import org.apache.dubbo.rpc.Invoker; | import org.apache.dubbo.rpc.Invoker; | ||||
@@ -74,7 +75,8 @@ public class SentinelDubboProviderFilter extends BaseSentinelDubboFilter { | |||||
// at entrance of invocation chain only (for inbound traffic). | // at entrance of invocation chain only (for inbound traffic). | ||||
ContextUtil.enter(methodResourceName, origin); | ContextUtil.enter(methodResourceName, origin); | ||||
interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN); | interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN); | ||||
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN, invocation.getArguments()); | |||||
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN, | |||||
invocation.getArguments()); | |||||
Result result = invoker.invoke(invocation); | Result result = invoker.invoke(invocation); | ||||
if (result.hasException()) { | if (result.hasException()) { | ||||
Tracer.traceEntry(result.getException(), interfaceEntry); | Tracer.traceEntry(result.getException(), interfaceEntry); | ||||
@@ -98,6 +100,5 @@ public class SentinelDubboProviderFilter extends BaseSentinelDubboFilter { | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -43,13 +43,11 @@ public final class DubboAdapterGlobalConfig { | |||||
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 final String DUBBO_INTERFACE_GROUP_VERSION_ENABLED = "csp.sentinel.dubbo.interface.group.version.enabled"; | ||||
public static final String TRACE_BIZ_EXCEPTION_ENABLED = "csp.sentinel.dubbo.trace.biz.exception.enabled"; | |||||
private static volatile DubboFallback consumerFallback = new DefaultDubboFallback(); | private static volatile DubboFallback consumerFallback = new DefaultDubboFallback(); | ||||
private static volatile DubboFallback providerFallback = new DefaultDubboFallback(); | private static volatile DubboFallback providerFallback = new DefaultDubboFallback(); | ||||
private static volatile DubboOriginParser originParser = new DefaultDubboOriginParser(); | private static volatile DubboOriginParser originParser = new DefaultDubboOriginParser(); | ||||
public static boolean isUsePrefix() { | public static boolean isUsePrefix() { | ||||
return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_RES_NAME_WITH_PREFIX_KEY)); | return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_RES_NAME_WITH_PREFIX_KEY)); | ||||
} | } | ||||
@@ -74,16 +72,6 @@ public final class DubboAdapterGlobalConfig { | |||||
return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED)); | 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; | |||||
} | |||||
public static DubboFallback getConsumerFallback() { | public static DubboFallback getConsumerFallback() { | ||||
return consumerFallback; | return consumerFallback; | ||||
} | } | ||||
@@ -16,7 +16,7 @@ | |||||
package com.alibaba.csp.sentinel.adapter.dubbo.fallback; | 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 org.apache.dubbo.rpc.AsyncRpcResult; | 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; | ||||
@@ -30,6 +30,6 @@ public class DefaultDubboFallback implements DubboFallback { | |||||
@Override | @Override | ||||
public Result handle(Invoker<?> invoker, Invocation invocation, BlockException ex) { | public Result handle(Invoker<?> invoker, Invocation invocation, BlockException ex) { | ||||
// Just wrap the exception. | // Just wrap the exception. | ||||
return AsyncRpcResult.newDefaultAsyncResult(null, new SentinelRpcException(ex), invocation); | |||||
return AsyncRpcResult.newDefaultAsyncResult(ex.toRuntimeException(), invocation); | |||||
} | } | ||||
} | } |
@@ -16,18 +16,14 @@ | |||||
package com.alibaba.csp.sentinel.adapter.dubbo.fallback; | package com.alibaba.csp.sentinel.adapter.dubbo.fallback; | ||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig; | import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig; | ||||
import com.alibaba.csp.sentinel.util.AssertUtil; | |||||
/** | /** | ||||
* <p>Global fallback registry for Dubbo.</p> | * <p>Global fallback registry for Dubbo.</p> | ||||
* | * | ||||
* <p> | |||||
* Note: Circuit breaking is mainly designed for consumer. The provider should not | |||||
* give fallback result in most circumstances. | |||||
* </p> | |||||
* | |||||
* @author Eric Zhao | * @author Eric Zhao | ||||
* @deprecated use {@link DubboAdapterGlobalConfig} instead since 1.8.0. | |||||
*/ | */ | ||||
@Deprecated | |||||
public final class DubboFallbackRegistry { | public final class DubboFallbackRegistry { | ||||
public static DubboFallback getConsumerFallback() { | public static DubboFallback getConsumerFallback() { | ||||
@@ -35,7 +31,6 @@ public final class DubboFallbackRegistry { | |||||
} | } | ||||
public static void setConsumerFallback(DubboFallback consumerFallback) { | public static void setConsumerFallback(DubboFallback consumerFallback) { | ||||
AssertUtil.notNull(consumerFallback, "consumerFallback cannot be null"); | |||||
DubboAdapterGlobalConfig.setConsumerFallback(consumerFallback); | DubboAdapterGlobalConfig.setConsumerFallback(consumerFallback); | ||||
} | } | ||||
@@ -44,7 +39,6 @@ public final class DubboFallbackRegistry { | |||||
} | } | ||||
public static void setProviderFallback(DubboFallback providerFallback) { | public static void setProviderFallback(DubboFallback providerFallback) { | ||||
AssertUtil.notNull(providerFallback, "providerFallback cannot be null"); | |||||
DubboAdapterGlobalConfig.setProviderFallback(providerFallback); | DubboAdapterGlobalConfig.setProviderFallback(providerFallback); | ||||
} | } | ||||
@@ -19,6 +19,7 @@ import com.alibaba.csp.sentinel.BaseTest; | |||||
import com.alibaba.csp.sentinel.DubboTestUtil; | import com.alibaba.csp.sentinel.DubboTestUtil; | ||||
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.DubboAdapterGlobalConfig; | |||||
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallback; | 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.fallback.DubboFallbackRegistry; | ||||
import com.alibaba.csp.sentinel.context.Context; | import com.alibaba.csp.sentinel.context.Context; | ||||
@@ -34,6 +35,7 @@ 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.degrade.DegradeRuleManager; | ||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; | import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; | ||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; | import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; | ||||
import org.apache.dubbo.rpc.*; | import org.apache.dubbo.rpc.*; | ||||
import org.apache.dubbo.rpc.support.RpcUtils; | import org.apache.dubbo.rpc.support.RpcUtils; | ||||
import org.junit.After; | import org.junit.After; | ||||
@@ -53,8 +55,7 @@ import static org.mockito.Mockito.*; | |||||
*/ | */ | ||||
public class SentinelDubboConsumerFilterTest extends BaseTest { | public class SentinelDubboConsumerFilterTest extends BaseTest { | ||||
private SentinelDubboConsumerFilter consumerFilter = new SentinelDubboConsumerFilter(); | |||||
private final SentinelDubboConsumerFilter consumerFilter = new SentinelDubboConsumerFilter(); | |||||
@Before | @Before | ||||
public void setUp() { | public void setUp() { | ||||
@@ -67,7 +68,6 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
cleanUpAll(); | cleanUpAll(); | ||||
} | } | ||||
@Test | @Test | ||||
public void testInterfaceLevelFollowControlAsync() throws InterruptedException { | public void testInterfaceLevelFollowControlAsync() throws InterruptedException { | ||||
@@ -105,7 +105,6 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
verifyInvocationStructureForCallFinish(invoker, invocation); | verifyInvocationStructureForCallFinish(invoker, invocation); | ||||
assertEquals("normal", result.getValue()); | assertEquals("normal", result.getValue()); | ||||
// inc the clusterNode's exception to trigger the fallback | // inc the clusterNode's exception to trigger the fallback | ||||
for (int i = 0; i < 5; i++) { | for (int i = 0; i < 5; i++) { | ||||
invokeDubboRpc(true, invoker, invocation); | invokeDubboRpc(true, invoker, invocation); | ||||
@@ -153,7 +152,6 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
assertNull(context); | assertNull(context); | ||||
} | } | ||||
@Test | @Test | ||||
public void testMethodFlowControlAsync() { | public void testMethodFlowControlAsync() { | ||||
@@ -175,10 +173,8 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
assertEquals("fallback", fallback.getValue()); | assertEquals("fallback", fallback.getValue()); | ||||
verifyInvocationStructureForCallFinish(invoker, invocation); | verifyInvocationStructureForCallFinish(invoker, invocation); | ||||
} | } | ||||
@Test | @Test | ||||
public void testInvokeAsync() { | public void testInvokeAsync() { | ||||
@@ -190,7 +186,7 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
when(result.hasException()).thenReturn(false); | when(result.hasException()).thenReturn(false); | ||||
when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> { | when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> { | ||||
verifyInvocationStructureForAsyncCall(invoker, invocation); | verifyInvocationStructureForAsyncCall(invoker, invocation); | ||||
return result; | |||||
return result; | |||||
}); | }); | ||||
consumerFilter.invoke(invoker, invocation); | consumerFilter.invoke(invoker, invocation); | ||||
verify(invoker).invoke(invocation); | verify(invoker).invoke(invocation); | ||||
@@ -220,7 +216,6 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
assertNull(context); | assertNull(context); | ||||
} | } | ||||
/** | /** | ||||
* Simply verify invocation structure in memory: | * Simply verify invocation structure in memory: | ||||
* EntranceNode(defaultContextName) | * EntranceNode(defaultContextName) | ||||
@@ -231,7 +226,8 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
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 = consumerFilter.getMethodName(invoker, invocation, null); | String resourceName = consumerFilter.getMethodName(invoker, invocation, null); | ||||
assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, context.getName()); | assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, context.getName()); | ||||
@@ -269,7 +265,8 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
// Verify clusterNode | // Verify clusterNode | ||||
ClusterNode methodClusterNode = methodNode.getClusterNode(); | ClusterNode methodClusterNode = methodNode.getClusterNode(); | ||||
ClusterNode interfaceClusterNode = interfaceNode.getClusterNode(); | ClusterNode interfaceClusterNode = interfaceNode.getClusterNode(); | ||||
assertNotSame(methodClusterNode, interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode | |||||
assertNotSame(methodClusterNode, | |||||
interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode | |||||
// As context origin is "", the StatisticNode should not be created in originCountMap of ClusterNode | // As context origin is "", the StatisticNode should not be created in originCountMap of ClusterNode | ||||
Map<String, StatisticNode> methodOriginCountMap = methodClusterNode.getOriginCountMap(); | Map<String, StatisticNode> methodOriginCountMap = methodClusterNode.getOriginCountMap(); | ||||
@@ -284,7 +281,8 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
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 = consumerFilter.getMethodName(invoker, invocation, null); | String resourceName = consumerFilter.getMethodName(invoker, invocation, null); | ||||
assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, context.getName()); | assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, context.getName()); | ||||
@@ -319,7 +317,8 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
// Verify clusterNode | // Verify clusterNode | ||||
ClusterNode methodClusterNode = methodNode.getClusterNode(); | ClusterNode methodClusterNode = methodNode.getClusterNode(); | ||||
ClusterNode interfaceClusterNode = interfaceNode.getClusterNode(); | ClusterNode interfaceClusterNode = interfaceNode.getClusterNode(); | ||||
assertNotSame(methodClusterNode, interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode | |||||
assertNotSame(methodClusterNode, | |||||
interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode | |||||
// As context origin is "", the StatisticNode should not be created in originCountMap of ClusterNode | // As context origin is "", the StatisticNode should not be created in originCountMap of ClusterNode | ||||
Map<String, StatisticNode> methodOriginCountMap = methodClusterNode.getOriginCountMap(); | Map<String, StatisticNode> methodOriginCountMap = methodClusterNode.getOriginCountMap(); | ||||
@@ -329,7 +328,6 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
assertEquals(0, interfaceOriginCountMap.size()); | assertEquals(0, interfaceOriginCountMap.size()); | ||||
} | } | ||||
private void verifyInvocationStructureForCallFinish(Invoker invoker, Invocation invocation) { | private void verifyInvocationStructureForCallFinish(Invoker invoker, Invocation invocation) { | ||||
Context context = ContextUtil.getContext(); | Context context = ContextUtil.getContext(); | ||||
assertNull(context); | assertNull(context); | ||||
@@ -338,7 +336,6 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
assertNull(entries); | assertNull(entries); | ||||
} | } | ||||
private DefaultNode getNode(String resourceName, DefaultNode root) { | private DefaultNode getNode(String resourceName, DefaultNode root) { | ||||
Queue<DefaultNode> queue = new LinkedList<>(); | Queue<DefaultNode> queue = new LinkedList<>(); | ||||
@@ -355,7 +352,6 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
return null; | return null; | ||||
} | } | ||||
private void initFlowRule(String resource) { | private void initFlowRule(String resource) { | ||||
FlowRule flowRule = new FlowRule(resource); | FlowRule flowRule = new FlowRule(resource); | ||||
flowRule.setCount(1); | flowRule.setCount(1); | ||||
@@ -367,24 +363,18 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
private void initDegradeRule(String resource) { | private void initDegradeRule(String resource) { | ||||
DegradeRule degradeRule = new DegradeRule(resource) | DegradeRule degradeRule = new DegradeRule(resource) | ||||
.setCount(0.5) | |||||
.setGrade(DEGRADE_GRADE_EXCEPTION_RATIO); | |||||
.setCount(0.5) | |||||
.setGrade(DEGRADE_GRADE_EXCEPTION_RATIO); | |||||
List<DegradeRule> degradeRules = new ArrayList<>(); | List<DegradeRule> degradeRules = new ArrayList<>(); | ||||
degradeRules.add(degradeRule); | degradeRules.add(degradeRule); | ||||
degradeRule.setTimeWindow(1); | degradeRule.setTimeWindow(1); | ||||
DegradeRuleManager.loadRules(degradeRules); | DegradeRuleManager.loadRules(degradeRules); | ||||
} | } | ||||
private void initFallback() { | private 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; | |||||
} | |||||
DubboAdapterGlobalConfig.setConsumerFallback((invoker, invocation, ex) -> { | |||||
// boolean async = RpcUtils.isAsync(invoker.getUrl(), invocation); | |||||
return AsyncRpcResult.newDefaultAsyncResult("fallback", invocation); | |||||
}); | }); | ||||
} | } | ||||
@@ -394,13 +384,11 @@ public class SentinelDubboConsumerFilterTest extends BaseTest { | |||||
if (InvokeMode.SYNC == invokeMode) { | if (InvokeMode.SYNC == invokeMode) { | ||||
result = exception ? new AppResponse(new Exception("error")) : new AppResponse("normal"); | result = exception ? new AppResponse(new Exception("error")) : new AppResponse("normal"); | ||||
} else { | } else { | ||||
result = exception ? AsyncRpcResult.newDefaultAsyncResult(new Exception("error"), invocation) : AsyncRpcResult.newDefaultAsyncResult("normal", invocation); | |||||
result = exception ? AsyncRpcResult.newDefaultAsyncResult(new Exception("error"), invocation) | |||||
: AsyncRpcResult.newDefaultAsyncResult("normal", invocation); | |||||
} | } | ||||
when(invoker.invoke(invocation)).thenReturn(result); | when(invoker.invoke(invocation)).thenReturn(result); | ||||
return consumerFilter.invoke(invoker, invocation); | return consumerFilter.invoke(invoker, invocation); | ||||
} | } | ||||
} | } |
@@ -17,8 +17,8 @@ package com.alibaba.csp.sentinel.adapter.dubbo.fallback; | |||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig; | import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig; | ||||
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.flow.FlowException; | import com.alibaba.csp.sentinel.slots.block.flow.FlowException; | ||||
import org.apache.dubbo.rpc.AsyncRpcResult; | import org.apache.dubbo.rpc.AsyncRpcResult; | ||||
import org.apache.dubbo.rpc.Result; | import org.apache.dubbo.rpc.Result; | ||||
import org.junit.After; | import org.junit.After; | ||||
@@ -33,12 +33,12 @@ public class DubboFallbackRegistryTest { | |||||
@Before | @Before | ||||
public void setUp() { | public void setUp() { | ||||
DubboFallbackRegistry.setConsumerFallback(new DefaultDubboFallback()); | |||||
DubboAdapterGlobalConfig.setConsumerFallback(new DefaultDubboFallback()); | |||||
} | } | ||||
@After | @After | ||||
public void tearDown() { | public void tearDown() { | ||||
DubboFallbackRegistry.setConsumerFallback(new DefaultDubboFallback()); | |||||
DubboAdapterGlobalConfig.setConsumerFallback(new DefaultDubboFallback()); | |||||
} | } | ||||
@Test | @Test | ||||
@@ -46,15 +46,17 @@ public class DubboFallbackRegistryTest { | |||||
// Test for default fallback. | // Test for default fallback. | ||||
BlockException ex = new FlowException("xxx"); | BlockException ex = new FlowException("xxx"); | ||||
Result result = new DefaultDubboFallback().handle(null, null, ex); | Result result = new DefaultDubboFallback().handle(null, null, ex); | ||||
Assert.assertTrue("The invocation should not fail",result.hasException()); | |||||
Assert.assertEquals(SentinelRpcException.class, result.getException().getClass()); | |||||
Assert.assertTrue("The result should carry exception", result.hasException()); | |||||
Assert.assertTrue(BlockException.isBlockException(result.getException())); | |||||
Assert.assertTrue(result.getException().getMessage().contains(ex.getClass().getSimpleName())); | |||||
} | } | ||||
@Test | @Test | ||||
public void testCustomFallback() { | public void testCustomFallback() { | ||||
BlockException ex = new FlowException("xxx"); | BlockException ex = new FlowException("xxx"); | ||||
DubboAdapterGlobalConfig.setConsumerFallback( | DubboAdapterGlobalConfig.setConsumerFallback( | ||||
(invoker, invocation, e) -> AsyncRpcResult.newDefaultAsyncResult("Error: " + e.getClass().getName(), invocation)); | |||||
(invoker, invocation, e) -> AsyncRpcResult | |||||
.newDefaultAsyncResult("Error: " + e.getClass().getName(), invocation)); | |||||
Result result = DubboAdapterGlobalConfig.getConsumerFallback() | Result result = DubboAdapterGlobalConfig.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()); | ||||