ソースを参照

Improve async invocation support in sentinel-apache-dubbo-adapter (#1124)

* Improve async support for Dubbo 2.7.2 and above (not compatible with 2.7.0 and 2.7.1 due to the bad compatibility design of Dubbo Filter)
master
Mr.Z Eric Zhao 5年前
コミット
47100e64ba
21個のファイルの変更798行の追加130行の削除
  1. +1
    -1
      sentinel-adapter/sentinel-apache-dubbo-adapter/pom.xml
  2. +77
    -0
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/BaseSentinelDubboFilter.java
  3. +2
    -2
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAppContextFilter.java
  4. +15
    -8
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtils.java
  5. +22
    -25
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java
  6. +15
    -31
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java
  7. +16
    -1
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/config/DubboConfig.java
  8. +52
    -5
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/BaseTest.java
  9. +34
    -6
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtilsTest.java
  10. +282
    -18
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java
  11. +21
    -12
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilterTest.java
  12. +2
    -3
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java
  13. +1
    -0
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/DemoService.java
  14. +5
    -0
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/impl/DemoServiceImpl.java
  15. +1
    -1
      sentinel-demo/sentinel-demo-apache-dubbo/pom.xml
  16. +108
    -2
      sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooConsumerBootstrap.java
  17. +119
    -0
      sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooConsumerExceptionDegradeBootstrap.java
  18. +0
    -10
      sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooProviderBootstrap.java
  19. +2
    -0
      sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooService.java
  20. +5
    -2
      sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/consumer/FooServiceConsumer.java
  21. +18
    -3
      sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/provider/FooServiceImpl.java

+ 1
- 1
sentinel-adapter/sentinel-apache-dubbo-adapter/pom.xml ファイルの表示

@@ -15,7 +15,7 @@
<properties>
<java.source.version>1.8</java.source.version>
<java.target.version>1.8</java.target.version>
<apache.dubbo.version>2.7.1</apache.dubbo.version>
<apache.dubbo.version>2.7.3</apache.dubbo.version>
</properties>

<dependencies>


+ 77
- 0
sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/BaseSentinelDubboFilter.java ファイルの表示

@@ -0,0 +1,77 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.adapter.dubbo;


import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
import com.alibaba.csp.sentinel.context.ContextUtil;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ListenableFilter;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;

/**
* Base Class of the {@link SentinelDubboProviderFilter} and {@link SentinelDubboConsumerFilter}.
*
* @author Zechao Zheng
*/

public abstract class BaseSentinelDubboFilter extends ListenableFilter {
public BaseSentinelDubboFilter() {
this.listener = new SentinelDubboListener();
}

static class SentinelDubboListener implements Listener {

@Override
public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
if (DubboConfig.getDubboBizExceptionTraceEnabled()) {
traceAndExit(appResponse.getException(), invoker.getUrl());
} else {
traceAndExit(null, invoker.getUrl());
}
}

@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
traceAndExit(t, invoker.getUrl());
}

}

static void traceAndExit(Throwable throwable, URL url) {
Entry interfaceEntry = (Entry) RpcContext.getContext().get(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY);
Entry methodEntry = (Entry) RpcContext.getContext().get(DubboUtils.DUBBO_METHOD_ENTRY_KEY);
if (methodEntry != null) {
Tracer.traceEntry(throwable, methodEntry);
methodEntry.exit();
RpcContext.getContext().remove(DubboUtils.DUBBO_METHOD_ENTRY_KEY);
}
if (interfaceEntry != null) {
Tracer.traceEntry(throwable, interfaceEntry);
interfaceEntry.exit();
RpcContext.getContext().remove(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY);
}
if (CommonConstants.PROVIDER_SIDE.equals(url.getParameter(CommonConstants.SIDE_KEY))) {
ContextUtil.exit();
}
}
}

+ 2
- 2
sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAppContextFilter.java ファイルの表示

@@ -15,7 +15,7 @@
*/
package com.alibaba.csp.sentinel.adapter.dubbo;

import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
@@ -34,7 +34,7 @@ public class DubboAppContextFilter implements Filter {

@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
String application = invoker.getUrl().getParameter(Constants.APPLICATION_KEY);
String application = invoker.getUrl().getParameter(CommonConstants.APPLICATION_KEY);
if (application != null) {
RpcContext.getContext().setAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, application);
}


+ 15
- 8
sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtils.java ファイルの表示

@@ -15,6 +15,7 @@
*/
package com.alibaba.csp.sentinel.adapter.dubbo;

import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
import com.alibaba.csp.sentinel.util.StringUtil;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
@@ -25,6 +26,8 @@ import org.apache.dubbo.rpc.Invoker;
public final class DubboUtils {

public static final String SENTINEL_DUBBO_APPLICATION_KEY = "dubboApplication";
public static final String DUBBO_METHOD_ENTRY_KEY = "dubboMethodEntry";
public static final String DUBBO_INTERFACE_ENTRY_KEY = "dubboInterfaceEntry";

public static String getApplication(Invocation invocation, String defaultValue) {
if (invocation == null || invocation.getAttachments() == null) {
@@ -33,12 +36,17 @@ public final class DubboUtils {
return invocation.getAttachment(SENTINEL_DUBBO_APPLICATION_KEY, defaultValue);
}

public static String getResourceName(Invoker<?> invoker, Invocation invocation) {
public static String getResourceName(Invoker<?> invoker, Invocation invocation){
return getResourceName(invoker, invocation, false);
}

public static String getResourceName(Invoker<?> invoker, Invocation invocation, Boolean useGroupAndVersion) {
StringBuilder buf = new StringBuilder(64);
buf.append(invoker.getInterface().getName())
.append(":")
.append(invocation.getMethodName())
.append("(");
String interfaceResource = useGroupAndVersion ? invoker.getUrl().getColonSeparatedKey() : invoker.getInterface().getName();
buf.append(interfaceResource)
.append(":")
.append(invocation.getMethodName())
.append("(");
boolean isFirst = true;
for (Class<?> clazz : invocation.getParameterTypes()) {
if (!isFirst) {
@@ -55,13 +63,12 @@ public final class DubboUtils {
if (StringUtil.isNotBlank(prefix)) {
return new StringBuilder(64)
.append(prefix)
.append(getResourceName(invoker, invocation))
.append(getResourceName(invoker, invocation, DubboConfig.getDubboInterfaceGroupAndVersionEnabled()))
.toString();
} else {
return getResourceName(invoker, invocation);
return getResourceName(invoker, invocation, DubboConfig.getDubboInterfaceGroupAndVersionEnabled());
}
}

private DubboUtils() {
}
}

+ 22
- 25
sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java ファイルの表示

@@ -19,17 +19,18 @@ import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.ResourceTypeConstants;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.InvokeMode;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.support.RpcUtils;

/**
* <p>Dubbo service consumer filter for Sentinel. Auto activated by default.</p>
@@ -43,7 +44,7 @@ import org.apache.dubbo.rpc.RpcException;
* @author Eric Zhao
*/
@Activate(group = "consumer")
public class SentinelDubboConsumerFilter implements Filter {
public class SentinelDubboConsumerFilter extends BaseSentinelDubboFilter {

public SentinelDubboConsumerFilter() {
RecordLog.info("Sentinel Apache Dubbo consumer filter initialized");
@@ -53,33 +54,29 @@ public class SentinelDubboConsumerFilter implements Filter {
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
Entry interfaceEntry = null;
Entry methodEntry = null;
RpcContext rpcContext = RpcContext.getContext();
try {
String resourceName = DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboConsumerPrefix());
interfaceEntry = SphU.entry(invoker.getInterface().getName(),
ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
methodEntry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
String methodResourceName = DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboConsumerPrefix());
String interfaceResourceName = DubboConfig.getDubboInterfaceGroupAndVersionEnabled() ? invoker.getUrl().getColonSeparatedKey()
: invoker.getInterface().getName();
InvokeMode invokeMode = RpcUtils.getInvokeMode(invoker.getUrl(), invocation);

Result result = invoker.invoke(invocation);
if (result.hasException()) {
Throwable e = result.getException();
// Record common exception.
Tracer.traceEntry(e, interfaceEntry);
Tracer.traceEntry(e, methodEntry);
if (InvokeMode.SYNC == invokeMode) {
interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
rpcContext.set(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY, interfaceEntry);
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT, invocation.getArguments());
} else {
// should generate the AsyncEntry when the invoke model in future or async
interfaceEntry = SphU.asyncEntry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
rpcContext.set(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY, interfaceEntry);
methodEntry = SphU.asyncEntry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT, 1, invocation.getArguments());
}
return result;
rpcContext.set(DubboUtils.DUBBO_METHOD_ENTRY_KEY, methodEntry);
return invoker.invoke(invocation);
} catch (BlockException e) {
return DubboFallbackRegistry.getConsumerFallback().handle(invoker, invocation, e);
} catch (RpcException e) {
Tracer.traceEntry(e, interfaceEntry);
Tracer.traceEntry(e, methodEntry);
throw e;
} finally {
if (methodEntry != null) {
methodEntry.exit();
}
if (interfaceEntry != null) {
interfaceEntry.exit();
}
}
}
}



+ 15
- 31
sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java ファイルの表示

@@ -19,17 +19,16 @@ import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.ResourceTypeConstants;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;

/**
@@ -45,7 +44,7 @@ import org.apache.dubbo.rpc.RpcException;
* @author Eric Zhao
*/
@Activate(group = "provider")
public class SentinelDubboProviderFilter implements Filter {
public class SentinelDubboProviderFilter extends BaseSentinelDubboFilter {

public SentinelDubboProviderFilter() {
RecordLog.info("Sentinel Apache Dubbo provider filter initialized");
@@ -55,41 +54,26 @@ public class SentinelDubboProviderFilter implements Filter {
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
// Get origin caller.
String application = DubboUtils.getApplication(invocation, "");
RpcContext rpcContext = RpcContext.getContext();
Entry interfaceEntry = null;
Entry methodEntry = null;
try {
String resourceName = DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboProviderPrefix());
String interfaceName = invoker.getInterface().getName();
String methodResourceName = DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboProviderPrefix());
String interfaceResourceName = DubboConfig.getDubboInterfaceGroupAndVersionEnabled() ? invoker.getUrl().getColonSeparatedKey()
: invoker.getInterface().getName();
// Only need to create entrance context at provider side, as context will take effect
// at entrance of invocation chain only (for inbound traffic).
ContextUtil.enter(resourceName, application);
interfaceEntry = SphU.entry(interfaceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN);
methodEntry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_RPC,
EntryType.IN, invocation.getArguments());

Result result = invoker.invoke(invocation);
if (result.hasException()) {
Throwable e = result.getException();
// Record common exception.
Tracer.traceEntry(e, interfaceEntry);
Tracer.traceEntry(e, methodEntry);
}
return result;
ContextUtil.enter(methodResourceName, application);
interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN);
rpcContext.set(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY, interfaceEntry);
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN, invocation.getArguments());
rpcContext.set(DubboUtils.DUBBO_METHOD_ENTRY_KEY, methodEntry);
return invoker.invoke(invocation);
} catch (BlockException e) {
return DubboFallbackRegistry.getProviderFallback().handle(invoker, invocation, e);
} catch (RpcException e) {
Tracer.traceEntry(e, interfaceEntry);
Tracer.traceEntry(e, methodEntry);
throw e;
} finally {
if (methodEntry != null) {
methodEntry.exit(1, invocation.getArguments());
}
if (interfaceEntry != null) {
interfaceEntry.exit();
}
ContextUtil.exit();
}
}


}


+ 16
- 1
sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/config/DubboConfig.java ファイルの表示

@@ -37,8 +37,12 @@ public final class DubboConfig {
private static final String DEFAULT_DUBBO_PROVIDER_PREFIX = "dubbo:provider:";
private static final String DEFAULT_DUBBO_CONSUMER_PREFIX = "dubbo:consumer:";

public static final String DUBBO_INTERFACE_GROUP_VERSION_ENABLED = "csp.sentinel.dubbo.interface.group.version.enabled";

public static boolean isUsePrefix(){
public static final String TRACE_BIZ_EXCEPTION_ENABLED = "csp.sentinel.dubbo.trace.biz.exception.enabled";


public static boolean isUsePrefix() {
return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_USE_PREFIX));
}

@@ -58,5 +62,16 @@ public final class DubboConfig {
return null;
}

public static Boolean getDubboInterfaceGroupAndVersionEnabled() {
return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED));
}

public static Boolean getDubboBizExceptionTraceEnabled() {
String traceBizExceptionEnabled = SentinelConfig.getConfig(TRACE_BIZ_EXCEPTION_ENABLED);
if (StringUtil.isNotBlank(traceBizExceptionEnabled)) {
return TRUE_STR.equalsIgnoreCase(traceBizExceptionEnabled);
}
return true;
}

}

+ 52
- 5
sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/BaseTest.java ファイルの表示

@@ -15,26 +15,73 @@
*/
package com.alibaba.csp.sentinel;

import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcContext;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

/**
* Base test class, provide common methods for subClass
* The package is same as CtSph, to call CtSph.resetChainMap() method for test
*
* <p>
* Note: Only for test. DO NOT USE IN PRODUCTION!
*
* @author cdfive
*/
public class BaseTest {


protected Invoker invoker;
protected Invocation invocation;

public void constructInvokerAndInvocation() {
invoker = mock(Invoker.class);
URL url = URL.valueOf("dubbo://127.0.0.1:2181")
.addParameter(CommonConstants.VERSION_KEY, "1.0.0")
.addParameter(CommonConstants.GROUP_KEY, "grp1")
.addParameter(CommonConstants.INTERFACE_KEY, DemoService.class.getName());
when(invoker.getUrl()).thenReturn(url);
when(invoker.getInterface()).thenReturn(DemoService.class);

invocation = mock(Invocation.class);
Method method = DemoService.class.getMethods()[0];
when(invocation.getMethodName()).thenReturn(method.getName());
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());

}

/**
* Clean up resources for context, clusterNodeMap, processorSlotChainMap
*/
protected static void cleanUpAll() {
RpcContext.removeContext();
ClusterBuilderSlot.getClusterNodeMap().clear();
CtSph.resetChainMap();
try {
RpcContext.removeContext();
ClusterBuilderSlot.getClusterNodeMap().clear();
CtSph.resetChainMap();
Method method = ContextUtil.class.getDeclaredMethod("resetContextMap");
method.setAccessible(true);
method.invoke(null, null);
ContextUtil.exit();
SentinelConfig.setConfig(DubboConfig.DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "true");
FlowRuleManager.loadRules(new ArrayList<>());
DegradeRuleManager.loadRules(new ArrayList<>());
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}

+ 34
- 6
sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtilsTest.java ファイルの表示

@@ -18,6 +18,8 @@ package com.alibaba.csp.sentinel.adapter.dubbo;
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.junit.After;
@@ -29,7 +31,9 @@ import java.util.HashMap;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
* @author cdfive
@@ -41,6 +45,7 @@ public class DubboUtilsTest {
SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "true");
SentinelConfig.setConfig(DubboConfig.DUBBO_PROVIDER_PREFIX, "");
SentinelConfig.setConfig(DubboConfig.DUBBO_CONSUMER_PREFIX, "");
SentinelConfig.setConfig(DubboConfig.DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
}


@@ -49,6 +54,7 @@ public class DubboUtilsTest {
SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "false");
SentinelConfig.setConfig(DubboConfig.DUBBO_PROVIDER_PREFIX, "");
SentinelConfig.setConfig(DubboConfig.DUBBO_CONSUMER_PREFIX, "");
SentinelConfig.setConfig(DubboConfig.DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
}


@@ -78,27 +84,49 @@ public class DubboUtilsTest {
}

@Test
public void testGetResourceName() {
public void testGetResourceName() throws NoSuchMethodException {
Invoker invoker = mock(Invoker.class);
when(invoker.getInterface()).thenReturn(DemoService.class);

Invocation invocation = mock(Invocation.class);
Method method = DemoService.class.getMethods()[0];
Method method = DemoService.class.getDeclaredMethod("sayHello", String.class, int.class);
when(invocation.getMethodName()).thenReturn(method.getName());
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());

String resourceName = DubboUtils.getResourceName(invoker, invocation);

assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);

}

@Test
public void testGetResourceNameWithPrefix() {
public void testGetResourceNameWithGroupAndVersion() throws NoSuchMethodException {
Invoker invoker = mock(Invoker.class);
URL url = URL.valueOf("dubbo://127.0.0.1:2181")
.addParameter(CommonConstants.VERSION_KEY, "1.0.0")
.addParameter(CommonConstants.GROUP_KEY, "grp1")
.addParameter(CommonConstants.INTERFACE_KEY, DemoService.class.getName());
when(invoker.getUrl()).thenReturn(url);
when(invoker.getInterface()).thenReturn(DemoService.class);

Invocation invocation = mock(Invocation.class);
Method method = DemoService.class.getDeclaredMethod("sayHello", String.class, int.class);
when(invocation.getMethodName()).thenReturn(method.getName());
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());

String resourceNameUseGroupAndVersion = DubboUtils.getResourceName(invoker, invocation, true);

assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:1.0.0:grp1:sayHello(java.lang.String,int)", resourceNameUseGroupAndVersion);
}


@Test
public void testGetResourceNameWithPrefix() throws NoSuchMethodException {
Invoker invoker = mock(Invoker.class);
when(invoker.getInterface()).thenReturn(DemoService.class);

Invocation invocation = mock(Invocation.class);
Method method = DemoService.class.getMethods()[0];
Method method = DemoService.class.getDeclaredMethod("sayHello", String.class, int.class);
when(invocation.getMethodName()).thenReturn(method.getName());
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());

@@ -118,4 +146,4 @@ public class DubboUtilsTest {
assertEquals("my:dubbo:consumer:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);

}
}
}

+ 282
- 18
sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java ファイルの表示

@@ -16,9 +16,11 @@
package com.alibaba.csp.sentinel.adapter.dubbo;

import com.alibaba.csp.sentinel.BaseTest;
import com.alibaba.csp.sentinel.Constants;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallback;
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.context.ContextUtil;
@@ -27,20 +29,40 @@ import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.node.Node;
import com.alibaba.csp.sentinel.node.StatisticNode;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.support.RpcUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static com.alibaba.csp.sentinel.slots.block.RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO;
import static org.apache.dubbo.rpc.Constants.ASYNC_KEY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
* @author cdfive
@@ -49,9 +71,12 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {

private SentinelDubboConsumerFilter filter = new SentinelDubboConsumerFilter();


@Before
public void setUp() {
cleanUpAll();
initFallback();
constructInvokerAndInvocation();
}

@After
@@ -59,18 +84,177 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
cleanUpAll();
}

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
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);
when(result.hasException()).thenReturn(false);
when(result.getException()).thenReturn(new Exception());
when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> {
verifyInvocationStructure(invoker, invocation);
return result;
@@ -79,6 +263,7 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
filter.invoke(invoker, invocation);
verify(invoker).invoke(invocation);

filter.listener().onResponse(result, invoker, invocation);
Context context = ContextUtil.getContext();
assertNull(context);
}
@@ -92,31 +277,32 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
private void verifyInvocationStructure(Invoker invoker, Invocation invocation) {
Context context = ContextUtil.getContext();
assertNotNull(context);

// As not call ContextUtil.enter(resourceName, application) in SentinelDubboConsumerFilter, use default context
// In actual project, a consumer is usually also a provider, the context will be created by SentinelDubboProviderFilter
// If consumer is on the top of Dubbo RPC invocation chain, use default context
String resourceName = DubboUtils.getResourceName(invoker, invocation);
assertEquals(Constants.CONTEXT_DEFAULT_NAME, context.getName());
String resourceName = DubboUtils.getResourceName(invoker, invocation, true);
assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, context.getName());
assertEquals("", context.getOrigin());

DefaultNode entranceNode = context.getEntranceNode();
ResourceWrapper entranceResource = entranceNode.getId();
assertEquals(Constants.CONTEXT_DEFAULT_NAME, entranceResource.getName());

assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, entranceResource.getName());
assertSame(EntryType.IN, entranceResource.getEntryType());

// As SphU.entry(interfaceName, EntryType.OUT);
Set<Node> childList = entranceNode.getChildList();
assertEquals(1, childList.size());
DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
DefaultNode interfaceNode = getNode(invoker.getUrl().getColonSeparatedKey(), entranceNode);
ResourceWrapper interfaceResource = interfaceNode.getId();
assertEquals(DemoService.class.getName(), interfaceResource.getName());

assertEquals(invoker.getUrl().getColonSeparatedKey(), interfaceResource.getName());
assertSame(EntryType.OUT, interfaceResource.getEntryType());

// As SphU.entry(resourceName, EntryType.OUT);
childList = interfaceNode.getChildList();
assertEquals(1, childList.size());
DefaultNode methodNode = (DefaultNode) childList.iterator().next();
DefaultNode methodNode = getNode(resourceName, entranceNode);
ResourceWrapper methodResource = methodNode.getId();
assertEquals(resourceName, methodResource.getName());
assertSame(EntryType.OUT, methodResource.getEntryType());
@@ -139,4 +325,82 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
Map<String, StatisticNode> interfaceOriginCountMap = interfaceClusterNode.getOriginCountMap();
assertEquals(0, interfaceOriginCountMap.size());
}

private void verifyInvocationStructureForAsyncCall(Invoker invoker, Invocation invocation) {
Context context = ContextUtil.getContext();
assertNotNull(context);

// As not call ContextUtil.enter(resourceName, application) in SentinelDubboConsumerFilter, use default context
// In actual project, a consumer is usually also a provider, the context will be created by SentinelDubboProviderFilter
// If consumer is on the top of Dubbo RPC invocation chain, use default context
String resourceName = DubboUtils.getResourceName(invoker, invocation, true);
assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, context.getName());
assertEquals("", context.getOrigin());

DefaultNode entranceNode = context.getEntranceNode();
ResourceWrapper entranceResource = entranceNode.getId();
assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, entranceResource.getName());
assertSame(EntryType.IN, entranceResource.getEntryType());

// As SphU.entry(interfaceName, EntryType.OUT);
Set<Node> childList = entranceNode.getChildList();
assertEquals(2, childList.size());
DefaultNode interfaceNode = getNode(invoker.getUrl().getColonSeparatedKey(), entranceNode);
ResourceWrapper interfaceResource = interfaceNode.getId();
assertEquals(invoker.getUrl().getColonSeparatedKey(), interfaceResource.getName());
assertSame(EntryType.OUT, interfaceResource.getEntryType());

// As SphU.entry(resourceName, EntryType.OUT);
childList = interfaceNode.getChildList();
assertEquals(0, childList.size());
DefaultNode methodNode = getNode(resourceName, entranceNode);
ResourceWrapper methodResource = methodNode.getId();
assertEquals(resourceName, methodResource.getName());
assertSame(EntryType.OUT, methodResource.getEntryType());

// Verify curEntry
// nothing will bind to local context when use the AsyncEntry
Entry curEntry = context.getCurEntry();
assertNull(curEntry);

// Verify clusterNode
ClusterNode methodClusterNode = methodNode.getClusterNode();
ClusterNode interfaceClusterNode = interfaceNode.getClusterNode();
assertNotSame(methodClusterNode, interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode

// As context origin is "", the StatisticNode should not be created in originCountMap of ClusterNode
Map<String, StatisticNode> methodOriginCountMap = methodClusterNode.getOriginCountMap();
assertEquals(0, methodOriginCountMap.size());

Map<String, StatisticNode> interfaceOriginCountMap = interfaceClusterNode.getOriginCountMap();
assertEquals(0, interfaceOriginCountMap.size());
}


private void verifyInvocationStructureForCallFinish() {
Context context = ContextUtil.getContext();
assertNull(context);
Entry interfaceEntry = (Entry) RpcContext.getContext().get(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY);
Entry methodEntry = (Entry) RpcContext.getContext().get(DubboUtils.DUBBO_METHOD_ENTRY_KEY);
assertNull(interfaceEntry);
assertNull(methodEntry);
}


public DefaultNode getNode(String resourceName, DefaultNode root) {

Queue<DefaultNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
DefaultNode temp = queue.poll();
if (temp.getId().getName().equals(resourceName)) {
return temp;
}
for (Node node : temp.getChildList()) {
queue.offer((DefaultNode) node);
}
}
return null;
}

}

+ 21
- 12
sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilterTest.java ファイルの表示

@@ -26,7 +26,8 @@ import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.node.Node;
import com.alibaba.csp.sentinel.node.StatisticNode;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
@@ -38,8 +39,15 @@ import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
* @author cdfive
@@ -50,6 +58,7 @@ public class SentinelDubboProviderFilterTest extends BaseTest {

@Before
public void setUp() {
constructInvokerAndInvocation();
cleanUpAll();
}

@@ -62,18 +71,16 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
public void testInvoke() {
final String originApplication = "consumerA";

final Invoker invoker = mock(Invoker.class);
when(invoker.getInterface()).thenReturn(DemoService.class);
URL url = invoker.getUrl()
.addParameter(CommonConstants.SIDE_KEY, CommonConstants.PROVIDER_SIDE);
when(invoker.getUrl()).thenReturn(url);

final Invocation invocation = mock(Invocation.class);
Method method = DemoService.class.getMethods()[0];
when(invocation.getMethodName()).thenReturn(method.getName());
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, ""))
.thenReturn(originApplication);
.thenReturn(originApplication);

final Result result = mock(Result.class);
when(result.hasException()).thenReturn(false);
when(result.getException()).thenReturn(new Exception());
when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> {
verifyInvocationStructure(originApplication, invoker, invocation);
return result;
@@ -82,6 +89,7 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
filter.invoke(invoker, invocation);
verify(invoker).invoke(invocation);

filter.listener().onResponse(result, invoker, invocation);
Context context = ContextUtil.getContext();
assertNull(context);
}
@@ -97,7 +105,7 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
assertNotNull(context);

// As ContextUtil.enter(resourceName, application) in SentinelDubboProviderFilter
String resourceName = DubboUtils.getResourceName(invoker, invocation);
String resourceName = DubboUtils.getResourceName(invoker, invocation, true);
assertEquals(resourceName, context.getName());
assertEquals(originApplication, context.getOrigin());

@@ -111,7 +119,8 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
assertEquals(1, childList.size());
DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
ResourceWrapper interfaceResource = interfaceNode.getId();
assertEquals(DemoService.class.getName(), interfaceResource.getName());

assertEquals(invoker.getUrl().getColonSeparatedKey(), interfaceResource.getName());
assertSame(EntryType.IN, interfaceResource.getEntryType());

// As SphU.entry(resourceName, EntryType.IN, 1, invocation.getArguments());


+ 2
- 3
sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java ファイルの表示

@@ -18,9 +18,8 @@ package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcResult;
import org.junit.Assert;
import org.junit.Test;

@@ -41,7 +40,7 @@ public class DubboFallbackRegistryTest {
public void testCustomFallback() {
BlockException ex = new FlowException("xxx");
DubboFallbackRegistry.setConsumerFallback(
(invoker, invocation, e) -> new RpcResult("Error: " + e.getClass().getName()));
(invoker, invocation, e) -> AsyncRpcResult.newDefaultAsyncResult("Error: " + e.getClass().getName(), invocation));
Result result = DubboFallbackRegistry.getConsumerFallback()
.handle(null, null, ex);
Assert.assertFalse("The invocation should not fail", result.hasException());


+ 1
- 0
sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/DemoService.java ファイルの表示

@@ -20,4 +20,5 @@ package com.alibaba.csp.sentinel.adapter.dubbo.provider;
*/
public interface DemoService {
String sayHello(String name, int n);
String sayHi(String name,int n);
}

+ 5
- 0
sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/impl/DemoServiceImpl.java ファイルの表示

@@ -24,4 +24,9 @@ public class DemoServiceImpl implements DemoService {
public String sayHello(String name, int n) {
return "Hello " + name + ", " + n;
}

@Override
public String sayHi(String name, int n) {
return "Hi " + name + ", " + n;
}
}

+ 1
- 1
sentinel-demo/sentinel-demo-apache-dubbo/pom.xml ファイルの表示

@@ -15,7 +15,7 @@
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.1</version>
<version>2.7.3</version>
</dependency>

<!-- Dubbo provides qos plugin and is enable by default. -->


+ 108
- 2
sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooConsumerBootstrap.java ファイルの表示

@@ -15,12 +15,22 @@
*/
package com.alibaba.csp.sentinel.demo.apache.dubbo;

import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
import com.alibaba.csp.sentinel.demo.apache.dubbo.consumer.ConsumerConfiguration;
import com.alibaba.csp.sentinel.demo.apache.dubbo.consumer.FooServiceConsumer;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;

import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* Please add the following VM arguments:
* <pre>
@@ -33,10 +43,14 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
*/
public class FooConsumerBootstrap {

public static void main(String[] args) {
private static final String INTERFACE_RES_KEY = FooService.class.getName();
private static final String RES_KEY = INTERFACE_RES_KEY + ":sayHello(java.lang.String)";

public static void main(String[] args) throws InterruptedException {
AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext();
consumerContext.register(ConsumerConfiguration.class);
consumerContext.refresh();
initFlowRule(10, false);

FooServiceConsumer service = consumerContext.getBean(FooServiceConsumer.class);

@@ -50,5 +64,97 @@ public class FooConsumerBootstrap {
ex.printStackTrace();
}
}

// method flowcontrol
Thread.sleep(1000);
initFlowRule(20, true);
for (int i = 0; i < 10; i++) {
try {
String message = service.sayHello("Eric");
System.out.println("Success: " + message);
} catch (SentinelRpcException ex) {
System.out.println("Blocked");
System.out.println("fallback:" + service.doAnother());

} catch (Exception ex) {
ex.printStackTrace();
}
}

// fallback to result
Thread.sleep(1000);
registryCustomFallback();

for (int i = 0; i < 10; i++) {
try {
String message = service.sayHello("Eric");
System.out.println("Result: " + message);
} catch (SentinelRpcException ex) {
System.out.println("Blocked");
} catch (Exception ex) {
ex.printStackTrace();
}
}
// fallback to exception
Thread.sleep(1000);
registryCustomFallbackForCustomException();

for (int i = 0; i < 10; i++) {
try {
String message = service.sayHello("Eric");
System.out.println("Result: " + message);
} catch (SentinelRpcException ex) {
System.out.println("Blocked");
} catch (Exception ex) {
ex.printStackTrace();
}
}

Thread.sleep(1000);
registryCustomFallbackWhenFallbackError();
for (int i = 0; i < 10; i++) {
try {
String message = service.sayHello("Eric");
System.out.println("Result: " + message);
} catch (SentinelRpcException ex) {
System.out.println("Blocked");
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

public static void registryCustomFallback() {
DubboFallbackRegistry.setConsumerFallback(
(invoker, invocation, ex) -> AsyncRpcResult.newDefaultAsyncResult("fallback", invocation));

}

public static void registryCustomFallbackForCustomException() {
DubboFallbackRegistry.setConsumerFallback(
(invoker, invocation, ex) -> AsyncRpcResult.newDefaultAsyncResult(new RuntimeException("fallback"), invocation));
}

public static void registryCustomFallbackWhenFallbackError() {
DubboFallbackRegistry.setConsumerFallback(
(invoker, invocation, ex) -> {
throw new RuntimeException("fallback");
});
}


private static void initFlowRule(int interfaceFlowLimit, boolean method) {
FlowRule flowRule = new FlowRule(INTERFACE_RES_KEY)
.setCount(interfaceFlowLimit)
.setGrade(RuleConstant.FLOW_GRADE_QPS);
List<FlowRule> list = new ArrayList<>();
if (method) {
FlowRule flowRule1 = new FlowRule(RES_KEY)
.setCount(5)
.setGrade(RuleConstant.FLOW_GRADE_QPS);
list.add(flowRule1);
}
list.add(flowRule);
FlowRuleManager.loadRules(list);
}
}

+ 119
- 0
sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooConsumerExceptionDegradeBootstrap.java ファイルの表示

@@ -0,0 +1,119 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.demo.apache.dubbo;

import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
import com.alibaba.csp.sentinel.demo.apache.dubbo.consumer.ConsumerConfiguration;
import com.alibaba.csp.sentinel.demo.apache.dubbo.consumer.FooServiceConsumer;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.RpcContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
* Please add the following VM arguments:
* <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));

}
}

+ 0
- 10
sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooProviderBootstrap.java ファイルの表示

@@ -37,15 +37,11 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
*/
public class FooProviderBootstrap {

private static final String INTERFACE_RES_KEY = FooService.class.getName();
private static final String RES_KEY = INTERFACE_RES_KEY + ":sayHello(java.lang.String)";

public static void main(String[] args) {
// Users don't need to manually call this method.
// Only for eager initialization.
InitExecutor.doInit();

initFlowRule();

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(ProviderConfiguration.class);
@@ -54,10 +50,4 @@ public class FooProviderBootstrap {
System.out.println("Service provider is ready");
}

private static void initFlowRule() {
FlowRule flowRule = new FlowRule(INTERFACE_RES_KEY)
.setCount(10)
.setGrade(RuleConstant.FLOW_GRADE_QPS);
FlowRuleManager.loadRules(Collections.singletonList(flowRule));
}
}

+ 2
- 0
sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/FooService.java ファイルの表示

@@ -23,4 +23,6 @@ public interface FooService {
String sayHello(String name);

String doAnother();

String exceptionTest(boolean biz, boolean timeout);
}

+ 5
- 2
sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/consumer/FooServiceConsumer.java ファイルの表示

@@ -16,7 +16,6 @@
package com.alibaba.csp.sentinel.demo.apache.dubbo.consumer;

import com.alibaba.csp.sentinel.demo.apache.dubbo.FooService;

import org.apache.dubbo.config.annotation.Reference;

/**
@@ -24,7 +23,7 @@ import org.apache.dubbo.config.annotation.Reference;
*/
public class FooServiceConsumer {

@Reference(url = "dubbo://127.0.0.1:25758", timeout = 3000)
@Reference(url = "dubbo://127.0.0.1:25758", timeout = 500)
private FooService fooService;

public String sayHello(String name) {
@@ -34,4 +33,8 @@ public class FooServiceConsumer {
public String doAnother() {
return fooService.doAnother();
}

public String exceptionTest(boolean biz, boolean timeout) {
return fooService.exceptionTest(biz, timeout);
}
}

+ 18
- 3
sentinel-demo/sentinel-demo-apache-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/apache/dubbo/provider/FooServiceImpl.java ファイルの表示

@@ -15,12 +15,11 @@
*/
package com.alibaba.csp.sentinel.demo.apache.dubbo.provider;

import java.time.LocalDateTime;

import com.alibaba.csp.sentinel.demo.apache.dubbo.FooService;

import org.apache.dubbo.config.annotation.Service;

import java.time.LocalDateTime;

/**
* @author Eric Zhao
*/
@@ -36,4 +35,20 @@ public class FooServiceImpl implements FooService {
public String doAnother() {
return LocalDateTime.now().toString();
}

@Override
public String exceptionTest(boolean biz, boolean timeout) {
if (biz) {
throw new RuntimeException("biz exception");
}
if (timeout) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "Success";
}

}

読み込み中…
キャンセル
保存