Browse Source

Add Sentinel SOFARPC adapter module (#1307)

master
cdfive GitHub 4 years ago
parent
commit
18acb1d154
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1558 additions and 0 deletions
  1. +1
    -0
      sentinel-adapter/pom.xml
  2. +63
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/README.md
  3. +43
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/pom.xml
  4. +57
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/AbstractSofaRpcFilter.java
  5. +82
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/SentinelSofaRpcConsumerFilter.java
  6. +93
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/SentinelSofaRpcProviderFilter.java
  7. +59
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/SofaRpcUtils.java
  8. +9
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/config/SofaRpcConfig.java
  9. +37
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/fallback/DefaultSofaRpcFallback.java
  10. +39
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/fallback/SofaRpcFallback.java
  11. +46
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/fallback/SofaRpcFallbackRegistry.java
  12. +3
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.filter.Filter
  13. +109
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/AbstractSofaRpcFilterTest.java
  14. +58
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/BaseTest.java
  15. +155
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/SentinelSofaRpcConsumerFilterTest.java
  16. +154
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/SentinelSofaRpcProviderFilterTest.java
  17. +68
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/SofaRpcUtilsTest.java
  18. +48
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/fallback/DefaultSofaRpcFallbackTest.java
  19. +74
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/fallback/SofaRpcFallbackRegistryTest.java
  20. +24
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/service/DemoService.java
  21. +29
    -0
      sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/service/impl/DemoServiceImpl.java
  22. +1
    -0
      sentinel-demo/pom.xml
  23. +42
    -0
      sentinel-demo/sentinel-demo-sofa-rpc/README.md
  24. +44
    -0
      sentinel-demo/sentinel-demo-sofa-rpc/pom.xml
  25. +79
    -0
      sentinel-demo/sentinel-demo-sofa-rpc/src/main/java/com/alibaba/csp/sentinel/demo/sofa/rpc/DemoConsumer.java
  26. +54
    -0
      sentinel-demo/sentinel-demo-sofa-rpc/src/main/java/com/alibaba/csp/sentinel/demo/sofa/rpc/DemoProvider.java
  27. +24
    -0
      sentinel-demo/sentinel-demo-sofa-rpc/src/main/java/com/alibaba/csp/sentinel/demo/sofa/rpc/service/DemoService.java
  28. +41
    -0
      sentinel-demo/sentinel-demo-sofa-rpc/src/main/java/com/alibaba/csp/sentinel/demo/sofa/rpc/service/impl/DemoServiceImpl.java
  29. +16
    -0
      sentinel-demo/sentinel-demo-sofa-rpc/src/main/resources/log4j.xml
  30. +6
    -0
      sentinel-demo/sentinel-demo-sofa-rpc/src/main/resources/sofa-rpc/rpc-config.json

+ 1
- 0
sentinel-adapter/pom.xml View File

@@ -18,6 +18,7 @@
<module>sentinel-web-servlet</module>
<module>sentinel-dubbo-adapter</module>
<module>sentinel-apache-dubbo-adapter</module>
<module>sentinel-sofa-rpc-adapter</module>
<module>sentinel-grpc-adapter</module>
<module>sentinel-zuul-adapter</module>
<module>sentinel-reactor-adapter</module>


+ 63
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/README.md View File

@@ -0,0 +1,63 @@
# Sentinel SOFARPC Adapter

Sentinel SOFARPC Adapter provides service provider filter and consumer filter
for [SOFARPC](https://www.sofastack.tech/projects/sofa-rpc) services.

**Note: This adapter supports SOFARPC 5.4.x version and above, and 5.6.x is officially recommended.**

To use Sentinel SOFARPC Adapter, you can simply add the following dependency to your `pom.xml`:

```xml
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-sofa-rpc-adapter</artifactId>
<version>x.y.z</version>
</dependency>
```

The Sentinel filters are **enabled by default**. Once you add the dependency,
the SOFARPC services and methods will become protected resources in Sentinel,
which can leverage Sentinel's flow control and guard ability when rules are configured.
Demos can be found in [sentinel-demo-sofa-rpc](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-sofa-rpc).

If you don't want the filters enabled, you can manually disable them. For example:

```java
providerConfig.setParameter("sofa.rpc.sentinel.enabled", "false");
consumerConfig.setParameter("sofa.rpc.sentinel.enabled", "false");
```

or add setting in `rpc-config.json` file, and its priority is lower than above.
```json
{
"sofa.rpc.sentinel.enabled": true
}
```

For more details of SOFARPC filter, see [here](https://www.sofastack.tech/projects/sofa-rpc/custom-filter/).

## SOFARPC resources

The resource for SOFARPC services has two granularities: service interface and service method.

- Service interface:resourceName format is `interfaceName`,e.g. `com.alibaba.csp.sentinel.demo.sofa.rpc.DemoService`
- Service method:resourceName format is `interfaceName#methodSignature`,e.g. `com.alibaba.csp.sentinel.demo.sofa.rpc.DemoService#sayHello(java.lang.Integer,java.lang.String,int)`

## Flow control based on caller

In many circumstances, it's also significant to control traffic flow based on the **caller**.
For example, assuming that there are two services A and B, both of them initiate remote call requests to the service provider.
If we want to limit the calls from service B only, we can set the `limitApp` of flow rule as the identifier of service B (e.g. service name).

Sentinel SOFARPC Adapter will automatically resolve the SOFARPC consumer's *application name* as the caller's name (`origin`),
and will bring the caller's name when doing resource protection.
If `limitApp` of flow rules is not configured (`default`), flow control will take effects on all callers.
If `limitApp` of a flow rule is configured with a caller, then the corresponding flow rule will only take effect on the specific caller.

## Global fallback

Sentinel SOFARPC Adapter supports global fallback configuration.
The global fallback will handle exceptions and give replacement result when blocked by
flow control, degrade or system load protection. You can implement your own `SofaRpcFallback` interface
and then register to `SofaRpcFallbackRegistry`. If no fallback is configured, Sentinel will wrap the `BlockException`
then directly throw it out.

+ 43
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/pom.xml View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>sentinel-adapter</artifactId>
<groupId>com.alibaba.csp</groupId>
<version>1.7.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>sentinel-sofa-rpc-adapter</artifactId>

<properties>
<sofa-rpc-all.version>5.6.4</sofa-rpc-all.version>
</properties>

<dependencies>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
</dependency>

<dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>sofa-rpc-all</artifactId>
<version>${sofa-rpc-all.version}</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

+ 57
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/AbstractSofaRpcFilter.java View File

@@ -0,0 +1,57 @@
package com.alibaba.csp.sentinel.adapter.sofa.rpc;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.adapter.sofa.rpc.config.SofaRpcConfig;
import com.alipay.sofa.rpc.common.RpcConfigs;
import com.alipay.sofa.rpc.common.utils.StringUtils;
import com.alipay.sofa.rpc.config.AbstractInterfaceConfig;
import com.alipay.sofa.rpc.core.exception.RpcErrorType;
import com.alipay.sofa.rpc.core.exception.SofaRpcException;
import com.alipay.sofa.rpc.core.response.SofaResponse;
import com.alipay.sofa.rpc.filter.Filter;
import com.alipay.sofa.rpc.filter.FilterInvoker;

/**
* @author cdfive
*/
abstract class AbstractSofaRpcFilter extends Filter {

@Override
public boolean needToLoad(FilterInvoker invoker) {
AbstractInterfaceConfig config = invoker.getConfig();

String enabled = config.getParameter(SofaRpcConfig.SOFA_RPC_SENTINEL_ENABLED);
if (StringUtils.isNotBlank(enabled)) {
return Boolean.valueOf(enabled);
}

return RpcConfigs.getOrDefaultValue(SofaRpcConfig.SOFA_RPC_SENTINEL_ENABLED, true);
}

protected void traceResponseException(SofaResponse response, Entry interfaceEntry, Entry methodEntry) {
if (response.isError()) {
SofaRpcException rpcException = new SofaRpcException(RpcErrorType.SERVER_FILTER, response.getErrorMsg());
Tracer.traceEntry(rpcException, interfaceEntry);
Tracer.traceEntry(rpcException, methodEntry);
} else {
Object appResponse = response.getAppResponse();
if (appResponse instanceof Throwable) {
Tracer.traceEntry((Throwable) appResponse, interfaceEntry);
Tracer.traceEntry((Throwable) appResponse, methodEntry);
}
}
}

protected SofaRpcException traceOtherException(Throwable t, Entry interfaceEntry, Entry methodEntry) {
SofaRpcException rpcException;
if (t instanceof SofaRpcException) {
rpcException = (SofaRpcException) t;
} else {
rpcException = new SofaRpcException(RpcErrorType.SERVER_FILTER, t);
}
Tracer.traceEntry(rpcException, interfaceEntry);
Tracer.traceEntry(rpcException, methodEntry);
return rpcException;
}
}

+ 82
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/SentinelSofaRpcConsumerFilter.java View File

@@ -0,0 +1,82 @@
/*
* 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.sofa.rpc;

import com.alibaba.csp.sentinel.*;
import com.alibaba.csp.sentinel.adapter.sofa.rpc.fallback.SofaRpcFallbackRegistry;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alipay.sofa.rpc.common.RpcConstants;
import com.alipay.sofa.rpc.core.exception.SofaRpcException;
import com.alipay.sofa.rpc.core.request.SofaRequest;
import com.alipay.sofa.rpc.core.response.SofaResponse;
import com.alipay.sofa.rpc.ext.Extension;
import com.alipay.sofa.rpc.filter.AutoActive;
import com.alipay.sofa.rpc.filter.FilterInvoker;

import static com.alibaba.csp.sentinel.adapter.sofa.rpc.SofaRpcUtils.getInterfaceResourceName;
import static com.alibaba.csp.sentinel.adapter.sofa.rpc.SofaRpcUtils.getMethodResourceName;
import static com.alibaba.csp.sentinel.adapter.sofa.rpc.SofaRpcUtils.getMethodArguments;

/**
* SOFARPC service consumer filter for Sentinel, auto activated by default.
*
* If you want to disable the consumer filter, you can configure:
* <pre>ConsumerConfig.setParameter("sofa.rpc.sentinel.enabled", "false");</pre>
*
* or add setting in rpc-config.json:
* <pre>"sofa.rpc.sentinel.enabled": false </pre>
*
* @author cdfive
*/
@Extension(value = "consumerSentinel", order = -1000)
@AutoActive(consumerSide = true)
public class SentinelSofaRpcConsumerFilter extends AbstractSofaRpcFilter {

@Override
public SofaResponse invoke(FilterInvoker invoker, SofaRequest request) throws SofaRpcException {
// Now only support sync invoke.
if (request.getInvokeType() != null && !RpcConstants.INVOKER_TYPE_SYNC.equals(request.getInvokeType())) {
return invoker.invoke(request);
}

String interfaceResourceName = getInterfaceResourceName(request);
String methodResourceName = getMethodResourceName(request);

Entry interfaceEntry = null;
Entry methodEntry = null;
try {
interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT, getMethodArguments(request));

SofaResponse response = invoker.invoke(request);

traceResponseException(response, interfaceEntry, methodEntry);
return response;
} catch (BlockException e) {
return SofaRpcFallbackRegistry.getConsumerFallback().handle(invoker, request, e);
} catch (Throwable t) {
throw traceOtherException(t, interfaceEntry, methodEntry);
} finally {
if (methodEntry != null) {
methodEntry.exit(1, getMethodArguments(request));
}

if (interfaceEntry != null) {
interfaceEntry.exit();
}
}
}
}

+ 93
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/SentinelSofaRpcProviderFilter.java View File

@@ -0,0 +1,93 @@
/*
* 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.sofa.rpc;

import com.alibaba.csp.sentinel.*;
import com.alibaba.csp.sentinel.adapter.sofa.rpc.fallback.SofaRpcFallbackRegistry;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alipay.sofa.rpc.common.RpcConstants;
import com.alipay.sofa.rpc.core.exception.SofaRpcException;
import com.alipay.sofa.rpc.core.request.SofaRequest;
import com.alipay.sofa.rpc.core.response.SofaResponse;
import com.alipay.sofa.rpc.ext.Extension;
import com.alipay.sofa.rpc.filter.AutoActive;
import com.alipay.sofa.rpc.filter.FilterInvoker;

import static com.alibaba.csp.sentinel.adapter.sofa.rpc.SofaRpcUtils.getApplicationName;
import static com.alibaba.csp.sentinel.adapter.sofa.rpc.SofaRpcUtils.getInterfaceResourceName;
import static com.alibaba.csp.sentinel.adapter.sofa.rpc.SofaRpcUtils.getMethodResourceName;
import static com.alibaba.csp.sentinel.adapter.sofa.rpc.SofaRpcUtils.getMethodArguments;

/**
* SOFARPC service provider filter for Sentinel, auto activated by default.
*
* If you want to disable the provider filter, you can configure:
* <pre>ProviderConfig.setParameter("sofa.rpc.sentinel.enabled", "false");</pre>
*
* or add setting in rpc-config.json file:
* <pre>
* {
* "sofa.rpc.sentinel.enabled": false
* }
* </pre>
*
* @author cdfive
*/
@Extension(value = "providerSentinel", order = -1000)
@AutoActive(providerSide = true)
public class SentinelSofaRpcProviderFilter extends AbstractSofaRpcFilter {

@Override
public SofaResponse invoke(FilterInvoker invoker, SofaRequest request) throws SofaRpcException {
// Now only support sync invoke.
if (request.getInvokeType() != null && !RpcConstants.INVOKER_TYPE_SYNC.equals(request.getInvokeType())) {
return invoker.invoke(request);
}

String applicationName = getApplicationName(request);
String interfaceResourceName = getInterfaceResourceName(request);
String methodResourceName = getMethodResourceName(request);

Entry interfaceEntry = null;
Entry methodEntry = null;
try {
ContextUtil.enter(methodResourceName, applicationName);

interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN);
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN, getMethodArguments(request));

SofaResponse response = invoker.invoke(request);

traceResponseException(response, interfaceEntry, methodEntry);
return response;
} catch (BlockException e) {
return SofaRpcFallbackRegistry.getProviderFallback().handle(invoker, request, e);
} catch (Throwable t) {
throw traceOtherException(t, interfaceEntry, methodEntry);
} finally {
if (methodEntry != null) {
methodEntry.exit(1, getMethodArguments(request));
}

if (interfaceEntry != null) {
interfaceEntry.exit();
}

ContextUtil.exit();
}
}
}

+ 59
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/SofaRpcUtils.java View File

@@ -0,0 +1,59 @@
/*
* 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.sofa.rpc;

import com.alipay.sofa.rpc.common.RemotingConstants;
import com.alipay.sofa.rpc.core.request.SofaRequest;

/**
* @author cdfive
*/
public class SofaRpcUtils {

public static String getApplicationName(SofaRequest request) {
String appName = (String) request.getRequestProp(RemotingConstants.HEAD_APP_NAME);
return appName == null ? "" : appName;
}

public static String getInterfaceResourceName(SofaRequest request) {
return request.getInterfaceName();
}

public static String getMethodResourceName(SofaRequest request) {
StringBuilder buf = new StringBuilder(64);
buf.append(request.getInterfaceName())
.append("#")
.append(request.getMethodName())
.append("(");

boolean isFirst = true;
for (String methodArgSig : request.getMethodArgSigs()) {
if (!isFirst) {
buf.append(",");
} else {
isFirst = false;
}

buf.append(methodArgSig);
}
buf.append(")");
return buf.toString();
}

public static Object[] getMethodArguments(SofaRequest request) {
return request.getMethodArgs();
}
}

+ 9
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/config/SofaRpcConfig.java View File

@@ -0,0 +1,9 @@
package com.alibaba.csp.sentinel.adapter.sofa.rpc.config;

/**
* @author cdfive
*/
public class SofaRpcConfig {

public static final String SOFA_RPC_SENTINEL_ENABLED = "sofa.rpc.sentinel.enabled";
}

+ 37
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/fallback/DefaultSofaRpcFallback.java View File

@@ -0,0 +1,37 @@
/*
* 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.sofa.rpc.fallback;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
import com.alipay.sofa.rpc.core.request.SofaRequest;
import com.alipay.sofa.rpc.core.response.SofaResponse;
import com.alipay.sofa.rpc.filter.FilterInvoker;

/**
* Default Sentinel fallback handler for SOFARPC services.
* Just wrap and throw the exception.
*
* @author cdfive
*/
public class DefaultSofaRpcFallback implements SofaRpcFallback {

@Override
public SofaResponse handle(FilterInvoker invoker, SofaRequest request, BlockException ex) {
// Just wrap and throw the exception.
throw new SentinelRpcException(ex);
}
}

+ 39
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/fallback/SofaRpcFallback.java View File

@@ -0,0 +1,39 @@
/*
* 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.sofa.rpc.fallback;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alipay.sofa.rpc.core.request.SofaRequest;
import com.alipay.sofa.rpc.core.response.SofaResponse;
import com.alipay.sofa.rpc.filter.FilterInvoker;

/**
* Sentinel fallback handler for SOFARPC services.
*
* @author cdfive
*/
public interface SofaRpcFallback {

/**
* Handle the block exception and provide fallback result.
*
* @param invoker FilterInvoker
* @param request SofaRequest
* @param ex block exception
* @return fallback result
*/
SofaResponse handle(FilterInvoker invoker, SofaRequest request, BlockException ex);
}

+ 46
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/fallback/SofaRpcFallbackRegistry.java View File

@@ -0,0 +1,46 @@
/*
* 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.sofa.rpc.fallback;

/**
* Global Sentinel fallback registry for SOFARPC services.
*
* @author cdfive
*/
public final class SofaRpcFallbackRegistry {

private static volatile SofaRpcFallback providerFallback = new DefaultSofaRpcFallback();
private static volatile SofaRpcFallback consumerFallback = new DefaultSofaRpcFallback();

public static SofaRpcFallback getProviderFallback() {
return providerFallback;
}

public static void setProviderFallback(SofaRpcFallback providerFallback) {
SofaRpcFallbackRegistry.providerFallback = providerFallback;
}

public static SofaRpcFallback getConsumerFallback() {
return consumerFallback;
}

public static void setConsumerFallback(SofaRpcFallback consumerFallback) {
SofaRpcFallbackRegistry.consumerFallback = consumerFallback;
}

private SofaRpcFallbackRegistry() {}
}


+ 3
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/main/resources/META-INF/services/sofa-rpc/com.alipay.sofa.rpc.filter.Filter View File

@@ -0,0 +1,3 @@
# name # order
com.alibaba.csp.sentinel.adapter.sofa.rpc.SentinelSofaRpcProviderFilter # -1000
com.alibaba.csp.sentinel.adapter.sofa.rpc.SentinelSofaRpcConsumerFilter # -1000

+ 109
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/AbstractSofaRpcFilterTest.java View File

@@ -0,0 +1,109 @@
package com.alibaba.csp.sentinel.adapter.sofa.rpc;

import com.alibaba.csp.sentinel.adapter.sofa.rpc.config.SofaRpcConfig;
import com.alipay.sofa.rpc.codec.Serializer;
import com.alipay.sofa.rpc.common.RpcConfigs;
import com.alipay.sofa.rpc.config.ConsumerConfig;
import com.alipay.sofa.rpc.config.ProviderConfig;
import com.alipay.sofa.rpc.filter.FilterInvoker;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.lang.reflect.Method;

import static org.junit.Assert.*;

/**
* Test cases for {@link AbstractSofaRpcFilter}.
*
* @author cdfive
*/
public class AbstractSofaRpcFilterTest {

@Before
public void setUp() {
removeRpcConfig(SofaRpcConfig.SOFA_RPC_SENTINEL_ENABLED);
}

@After
public void cleanUp() {
removeRpcConfig(SofaRpcConfig.SOFA_RPC_SENTINEL_ENABLED);
}

@Test
public void testNeedToLoadProvider() {
SentinelSofaRpcProviderFilter providerFilter = new SentinelSofaRpcProviderFilter();
ProviderConfig providerConfig = new ProviderConfig();
providerConfig.setInterfaceId(Serializer.class.getName());
providerConfig.setId("AAA");
FilterInvoker invoker = new FilterInvoker(null, null, providerConfig);
assertTrue(providerFilter.needToLoad(invoker));

providerConfig.setParameter(SofaRpcConfig.SOFA_RPC_SENTINEL_ENABLED, "false");
assertFalse(providerFilter.needToLoad(invoker));

providerConfig.setParameter(SofaRpcConfig.SOFA_RPC_SENTINEL_ENABLED, "");
assertTrue(providerFilter.needToLoad(invoker));

RpcConfigs.putValue(SofaRpcConfig.SOFA_RPC_SENTINEL_ENABLED, "false");
assertFalse(providerFilter.needToLoad(invoker));
}

@Test
public void testNeedToLoadConsumer() {
SentinelSofaRpcConsumerFilter consumerFilter = new SentinelSofaRpcConsumerFilter();
ConsumerConfig consumerConfig = new ConsumerConfig();
consumerConfig.setInterfaceId(Serializer.class.getName());
consumerConfig.setId("BBB");
FilterInvoker invoker = new FilterInvoker(null, null, consumerConfig);
assertTrue(consumerFilter.needToLoad(invoker));

consumerConfig.setParameter(SofaRpcConfig.SOFA_RPC_SENTINEL_ENABLED, "false");
assertFalse(consumerFilter.needToLoad(invoker));

consumerConfig.setParameter(SofaRpcConfig.SOFA_RPC_SENTINEL_ENABLED, "");
assertTrue(consumerFilter.needToLoad(invoker));

RpcConfigs.putValue(SofaRpcConfig.SOFA_RPC_SENTINEL_ENABLED, "false");
assertFalse(consumerFilter.needToLoad(invoker));
}

@Test
public void testNeedToLoadProviderAndConsumer() {
SentinelSofaRpcProviderFilter providerFilter = new SentinelSofaRpcProviderFilter();
ProviderConfig providerConfig = new ProviderConfig();
providerConfig.setInterfaceId(Serializer.class.getName());
providerConfig.setId("AAA");
FilterInvoker providerInvoker = new FilterInvoker(null, null, providerConfig);
assertTrue(providerFilter.needToLoad(providerInvoker));

SentinelSofaRpcConsumerFilter consumerFilter = new SentinelSofaRpcConsumerFilter();
ConsumerConfig consumerConfig = new ConsumerConfig();
consumerConfig.setInterfaceId(Serializer.class.getName());
consumerConfig.setId("BBB");
FilterInvoker consumerInvoker = new FilterInvoker(null, null, consumerConfig);
assertTrue(consumerFilter.needToLoad(consumerInvoker));

providerConfig.setParameter(SofaRpcConfig.SOFA_RPC_SENTINEL_ENABLED, "false");
assertFalse(providerFilter.needToLoad(providerInvoker));
assertTrue(consumerFilter.needToLoad(consumerInvoker));

providerConfig.setParameter(SofaRpcConfig.SOFA_RPC_SENTINEL_ENABLED, "");
assertTrue(providerFilter.needToLoad(providerInvoker));

RpcConfigs.putValue(SofaRpcConfig.SOFA_RPC_SENTINEL_ENABLED, "false");
assertFalse(providerFilter.needToLoad(providerInvoker));
assertFalse(consumerFilter.needToLoad(consumerInvoker));
}

private void removeRpcConfig(String key) {
try {
Method removeValueMethod = RpcConfigs.class.getDeclaredMethod("removeValue", String.class);
removeValueMethod.setAccessible(true);
removeValueMethod.invoke(null, key);
} catch (Exception e) {
// Empty
}
}
}

+ 58
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/BaseTest.java View File

@@ -0,0 +1,58 @@
/*
* 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.sofa.rpc;

import com.alibaba.csp.sentinel.Constants;
import com.alibaba.csp.sentinel.CtSph;
import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;

import java.lang.reflect.Method;

/**
* Base test class, provide common methods for sub test class.
*
* Note: Only for test. DO NOT USE IN PRODUCTION!
*
* @author cdfive
*/
public class BaseTest {

/**
* Clean up resources.
*/
protected static void cleanUpAll() {
Context context = ContextUtil.getContext();
if (context != null) {
context.setCurEntry(null);
ContextUtil.exit();
}

Constants.ROOT.removeChildList();

ClusterBuilderSlot.getClusterNodeMap().clear();

// Clear chainMap in CtSph
try {
Method resetChainMapMethod = CtSph.class.getDeclaredMethod("resetChainMap");
resetChainMapMethod.setAccessible(true);
resetChainMapMethod.invoke(null);
} catch (Exception e) {
// Empty
}
}
}

+ 155
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/SentinelSofaRpcConsumerFilterTest.java View File

@@ -0,0 +1,155 @@
/*
* 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.sofa.rpc;

import com.alibaba.csp.sentinel.Constants;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.node.ClusterNode;
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.alipay.sofa.rpc.common.RpcConstants;
import com.alipay.sofa.rpc.core.request.SofaRequest;
import com.alipay.sofa.rpc.core.response.SofaResponse;
import com.alipay.sofa.rpc.filter.FilterInvoker;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import java.util.Map;
import java.util.Set;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

/**
* Test cases for {@link SentinelSofaRpcConsumerFilter}.
*
* @author cdfive
*/
public class SentinelSofaRpcConsumerFilterTest extends BaseTest {

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

@After
public void cleanUp() {
cleanUpAll();
}

@Test
public void testInvokeSentinelWorks() {
SentinelSofaRpcConsumerFilter filter = new SentinelSofaRpcConsumerFilter();

final String interfaceResourceName = "com.alibaba.csp.sentinel.adapter.sofa.rpc.service.DemoService";
final String methodResourceName = "com.alibaba.csp.sentinel.adapter.sofa.rpc.service.DemoService#sayHello(java.lang.String,int)";

SofaRequest request = mock(SofaRequest.class);
when(request.getInvokeType()).thenReturn(RpcConstants.INVOKER_TYPE_SYNC);
when(request.getInterfaceName()).thenReturn(interfaceResourceName);
when(request.getMethodName()).thenReturn("sayHello");
when(request.getMethodArgSigs()).thenReturn(new String[]{"java.lang.String", "int"});
when(request.getMethodArgs()).thenReturn(new Object[]{"Sentinel", 2020});

FilterInvoker filterInvoker = mock(FilterInvoker.class);
when(filterInvoker.invoke(request)).thenAnswer(new Answer<SofaResponse>() {
@Override
public SofaResponse answer(InvocationOnMock invocationOnMock) throws Throwable {
verifyInvocationStructure(interfaceResourceName, methodResourceName);
SofaResponse response = new SofaResponse();
response.setAppResponse("Hello Sentinel 2020");
return response;
}
});

// Before invoke
assertNull(ContextUtil.getContext());

// Do invoke
SofaResponse response = filter.invoke(filterInvoker, request);
assertEquals("Hello Sentinel 2020", response.getAppResponse());
verify(filterInvoker).invoke(request);

// After invoke, make sure exit context
assertNull(ContextUtil.getContext());
}

/**
* Verify Sentinel invocation structure in memory:
* EntranceNode(defaultContextName)
* --InterfaceNode(interfaceName)
* ----MethodNode(resourceName)
*/
private void verifyInvocationStructure(String interfaceResourceName, String methodResourceName) {
Context context = ContextUtil.getContext();
assertNotNull(context);

// As not call ContextUtil.enter(methodResourceName, applicationName) in SentinelSofaRpcConsumerFilter, use default context
// In actual project, a consumer is usually also a provider, the context will be created by SentinelSofaRpcProviderFilter
// If consumer is on the top of SOFARPC invocation chain, use default context
assertEquals(Constants.CONTEXT_DEFAULT_NAME, context.getName());
assertEquals("", context.getOrigin());

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

// As SphU.entry(interfaceResourceName, EntryType.OUT);
Set<Node> childList = entranceNode.getChildList();
assertEquals(1, childList.size());
DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
ResourceWrapper interfaceResource = interfaceNode.getId();
assertEquals(interfaceResourceName, interfaceResource.getName());
assertSame(EntryType.OUT, interfaceResource.getEntryType());

// As SphU.entry(methodResourceName, EntryType.OUT);
childList = interfaceNode.getChildList();
assertEquals(1, childList.size());
DefaultNode methodNode = (DefaultNode) childList.iterator().next();
ResourceWrapper methodResource = methodNode.getId();
assertEquals(methodResourceName, methodResource.getName());
assertSame(EntryType.OUT, methodResource.getEntryType());

// Verify curEntry
Entry curEntry = context.getCurEntry();
assertSame(methodNode, curEntry.getCurNode());
assertSame(interfaceNode, curEntry.getLastNode());
// As context origin is not "", no originNode should be created in curEntry
assertNull(curEntry.getOriginNode());

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

// 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());
}
}

+ 154
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/SentinelSofaRpcProviderFilterTest.java View File

@@ -0,0 +1,154 @@
/*
* 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.sofa.rpc;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.node.ClusterNode;
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.alipay.sofa.rpc.common.RpcConstants;
import com.alipay.sofa.rpc.core.request.SofaRequest;
import com.alipay.sofa.rpc.core.response.SofaResponse;
import com.alipay.sofa.rpc.filter.FilterInvoker;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import java.util.Map;
import java.util.Set;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

/**
* Test cases for {@link SentinelSofaRpcProviderFilter}.
*
* @author cdfive
*/
public class SentinelSofaRpcProviderFilterTest extends BaseTest {

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

@After
public void cleanUp() {
cleanUpAll();
}

@Test
public void testInvokeSentinelWorks() {
SentinelSofaRpcProviderFilter filter = new SentinelSofaRpcProviderFilter();

final String applicationName = "demo-provider";
final String interfaceResourceName = "com.alibaba.csp.sentinel.adapter.sofa.rpc.service.DemoService";
final String methodResourceName = "com.alibaba.csp.sentinel.adapter.sofa.rpc.service.DemoService#sayHello(java.lang.String,int)";

SofaRequest request = mock(SofaRequest.class);
when(request.getRequestProp("app")).thenReturn(applicationName);
when(request.getInvokeType()).thenReturn(RpcConstants.INVOKER_TYPE_SYNC);
when(request.getInterfaceName()).thenReturn(interfaceResourceName);
when(request.getMethodName()).thenReturn("sayHello");
when(request.getMethodArgSigs()).thenReturn(new String[]{"java.lang.String", "int"});
when(request.getMethodArgs()).thenReturn(new Object[]{"Sentinel", 2020});

FilterInvoker filterInvoker = mock(FilterInvoker.class);
when(filterInvoker.invoke(request)).thenAnswer(new Answer<SofaResponse>() {
@Override
public SofaResponse answer(InvocationOnMock invocationOnMock) throws Throwable {
verifyInvocationStructure(applicationName, interfaceResourceName, methodResourceName);
SofaResponse response = new SofaResponse();
response.setAppResponse("Hello Sentinel 2020");
return response;
}
});

// Before invoke
assertNull(ContextUtil.getContext());

// Do invoke
SofaResponse response = filter.invoke(filterInvoker, request);
assertEquals("Hello Sentinel 2020", response.getAppResponse());
verify(filterInvoker).invoke(request);

// After invoke, make sure exit context
assertNull(ContextUtil.getContext());
}

/**
* Verify Sentinel invocation structure in memory:
* EntranceNode(methodResourceName)
* --InterfaceNode(interfaceResourceName)
* ----MethodNode(methodResourceName)
*/
private void verifyInvocationStructure(String applicationName, String interfaceResourceName, String methodResourceName) {
Context context = ContextUtil.getContext();
assertNotNull(context);

assertEquals(methodResourceName, context.getName());
assertEquals(applicationName, context.getOrigin());

DefaultNode entranceNode = context.getEntranceNode();
ResourceWrapper entranceResource = entranceNode.getId();
assertEquals(methodResourceName, entranceResource.getName());
assertSame(EntryType.IN, entranceResource.getEntryType());

// As SphU.entry(interfaceResourceName, EntryType.IN);
Set<Node> childList = entranceNode.getChildList();
assertEquals(1, childList.size());
DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
ResourceWrapper interfaceResource = interfaceNode.getId();
assertEquals(interfaceResourceName, interfaceResource.getName());
assertSame(EntryType.IN, interfaceResource.getEntryType());

// As SphU.entry(methodResourceName, EntryType.IN, 1, methodArguments);
childList = interfaceNode.getChildList();
assertEquals(1, childList.size());
DefaultNode methodNode = (DefaultNode) childList.iterator().next();
ResourceWrapper methodResource = methodNode.getId();
assertEquals(methodResourceName, methodResource.getName());
assertSame(EntryType.IN, methodResource.getEntryType());

// Verify curEntry
Entry curEntry = context.getCurEntry();
assertSame(methodNode, curEntry.getCurNode());
assertSame(interfaceNode, curEntry.getLastNode());
// As context origin is not "", originNode should be created
assertNotNull(curEntry.getOriginNode());

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

// As context origin is not "", the StatisticNode should be created in originCountMap of ClusterNode
Map<String, StatisticNode> methodOriginCountMap = methodClusterNode.getOriginCountMap();
assertEquals(1, methodOriginCountMap.size());
assertTrue(methodOriginCountMap.containsKey(applicationName));
Map<String, StatisticNode> interfaceOriginCountMap = interfaceClusterNode.getOriginCountMap();
assertEquals(1, interfaceOriginCountMap.size());
assertTrue(interfaceOriginCountMap.containsKey(applicationName));
}
}

+ 68
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/SofaRpcUtilsTest.java View File

@@ -0,0 +1,68 @@
/*
* 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.sofa.rpc;

import com.alipay.sofa.rpc.core.request.SofaRequest;
import org.junit.Test;

import static org.junit.Assert.*;

/**
* Test cases for {@link SofaRpcUtils}.
*
* @author cdfive
*/
public class SofaRpcUtilsTest {

@Test
public void testGetApplicationName() {
SofaRequest request = new SofaRequest();
String applicationName = SofaRpcUtils.getApplicationName(request);
assertEquals("", applicationName);

request.addRequestProp("app", "test-app");
applicationName = SofaRpcUtils.getApplicationName(request);
assertEquals("test-app", applicationName);
}

@Test
public void testGetInterfaceResourceName() {
SofaRequest request = new SofaRequest();
request.setInterfaceName("com.alibaba.csp.sentinel.adapter.sofa.rpc.service.DemoService");
String interfaceResourceName = SofaRpcUtils.getInterfaceResourceName(request);
assertEquals("com.alibaba.csp.sentinel.adapter.sofa.rpc.service.DemoService", interfaceResourceName);
}

@Test
public void testGetMethodResourceName() {
SofaRequest request = new SofaRequest();
request.setInterfaceName("com.alibaba.csp.sentinel.adapter.sofa.rpc.service.DemoService");
request.setMethodName("sayHello");
request.setMethodArgSigs(new String[]{"java.lang.String", "int"});
String methodResourceName = SofaRpcUtils.getMethodResourceName(request);
assertEquals("com.alibaba.csp.sentinel.adapter.sofa.rpc.service.DemoService#sayHello(java.lang.String,int)", methodResourceName);
}

@Test
public void testGetMethodArguments() {
SofaRequest request = new SofaRequest();
request.setMethodArgs(new Object[]{"Sentinel", 2020});
Object[] arguments = SofaRpcUtils.getMethodArguments(request);
assertEquals(arguments.length, 2);
assertEquals("Sentinel", arguments[0]);
assertEquals(2020, arguments[1]);
}
}

+ 48
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/fallback/DefaultSofaRpcFallbackTest.java View File

@@ -0,0 +1,48 @@
/*
* 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.sofa.rpc.fallback;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
import org.junit.Test;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

/**
* Test cases for {@link DefaultSofaRpcFallback}.
*
* @author cdfive
*/
public class DefaultSofaRpcFallbackTest {

@Test
public void testHandle() {
SofaRpcFallback sofaRpcFallback = new DefaultSofaRpcFallback();
BlockException blockException = mock(BlockException.class);

boolean throwSentinelRpcException = false;
boolean causeIsBlockException = false;
try {
sofaRpcFallback.handle(null, null, blockException);
} catch (Exception e) {
throwSentinelRpcException = e instanceof SentinelRpcException;
causeIsBlockException = e.getCause() instanceof BlockException;
}
assertTrue(throwSentinelRpcException);
assertTrue(causeIsBlockException);
}
}

+ 74
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/fallback/SofaRpcFallbackRegistryTest.java View File

@@ -0,0 +1,74 @@
/*
* 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.sofa.rpc.fallback;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alipay.sofa.rpc.core.request.SofaRequest;
import com.alipay.sofa.rpc.core.response.SofaResponse;
import com.alipay.sofa.rpc.filter.FilterInvoker;
import org.junit.Test;

import static org.junit.Assert.*;

/**
* Test cases for {@link SofaRpcFallbackRegistry}.
*
* @author cdfive
*/
public class SofaRpcFallbackRegistryTest {

@Test
public void testDefaultfallback() {
// Test get default provider fallback
SofaRpcFallback providerFallback = SofaRpcFallbackRegistry.getProviderFallback();
assertNotNull(providerFallback);
assertTrue(providerFallback instanceof DefaultSofaRpcFallback);

// Test get default consumer fallback
SofaRpcFallback consumerFallback = SofaRpcFallbackRegistry.getConsumerFallback();
assertNotNull(consumerFallback);
assertTrue(consumerFallback instanceof DefaultSofaRpcFallback);
}

@Test
public void testCustomFallback() {
// Test invoke custom provider fallback
SofaRpcFallbackRegistry.setProviderFallback(new SofaRpcFallback() {
@Override
public SofaResponse handle(FilterInvoker invoker, SofaRequest request, BlockException ex) {
SofaResponse response = new SofaResponse();
response.setAppResponse("test provider response");
return response;
}
});
SofaResponse providerResponse = SofaRpcFallbackRegistry.getProviderFallback().handle(null, null, null);
assertNotNull(providerResponse);
assertEquals("test provider response", providerResponse.getAppResponse());

// Test invoke custom consumer fallback
SofaRpcFallbackRegistry.setConsumerFallback(new SofaRpcFallback() {
@Override
public SofaResponse handle(FilterInvoker invoker, SofaRequest request, BlockException ex) {
SofaResponse response = new SofaResponse();
response.setAppResponse("test consumer response");
return response;
}
});
SofaResponse consumerResponse = SofaRpcFallbackRegistry.getConsumerFallback().handle(null, null, null);
assertNotNull(consumerResponse);
assertEquals("test consumer response", consumerResponse.getAppResponse());
}
}

+ 24
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/service/DemoService.java View File

@@ -0,0 +1,24 @@
/*
* 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.sofa.rpc.service;

/**
* @author cdfive
*/
public interface DemoService {

String sayHello(String name, int year);
}

+ 29
- 0
sentinel-adapter/sentinel-sofa-rpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/sofa/rpc/service/impl/DemoServiceImpl.java View File

@@ -0,0 +1,29 @@
/*
* 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.sofa.rpc.service.impl;

import com.alibaba.csp.sentinel.adapter.sofa.rpc.service.DemoService;

/**
* @author cdfive
*/
public class DemoServiceImpl implements DemoService {

@Override
public String sayHello(String name, int year) {
return "Hello " + name + " " + year;
}
}

+ 1
- 0
sentinel-demo/pom.xml View File

@@ -32,6 +32,7 @@
<module>sentinel-demo-command-handler</module>
<module>sentinel-demo-spring-webflux</module>
<module>sentinel-demo-apache-dubbo</module>
<module>sentinel-demo-sofa-rpc</module>
<module>sentinel-demo-spring-cloud-gateway</module>
<module>sentinel-demo-zuul-gateway</module>
<module>sentinel-demo-etcd-datasource</module>


+ 42
- 0
sentinel-demo/sentinel-demo-sofa-rpc/README.md View File

@@ -0,0 +1,42 @@
# Sentinel SOFARPC Demo

Sentinel 提供了与 SOFARPC 整合的模块 - `sentinel-sofa-rpc-adapter`,主要包括针对 Service Provider 和 Service Consumer 实现的 Filter。使用时用户只需引入以下模块(以 Maven 为例):

```xml
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-sofa-rpc-adapter</artifactId>
<version>x.y.z</version>
</dependency>
```

引入此依赖后,SOFARPC 的服务接口和方法(包括调用端和服务端)就会成为 Sentinel 中的资源,在配置了规则后就可以自动享受到 Sentinel 的防护能力。

> **注:若希望接入 Dashboard,请参考demo中的注释添加VM参数,只引入`sentinel-sofa-rpc-adapter`无法接入控制台!**

若不希望开启 Sentinel SOFARPC Adapter 中的某个 Filter,可以手动关闭对应的 Filter,比如:

```java
providerConfig.setParameter("sofa.rpc.sentinel.enabled", "false");
consumerConfig.setParameter("sofa.rpc.sentinel.enabled", "false");
```

或者在`rpc-config.json`文件中设置,它的优先级要低一些。

```json
{
"sofa.rpc.sentinel.enabled": true
}
```

# 运行Demo

1. 启动控制台,运行`DashboardApplication`

2. 启动Provider,运行`DemoProvider`(VM参数:`-Dproject.name=DemoProvider -Dcsp.sentinel.dashboard.server=localhost:8080`)

3. 启动Consumer,运行`DemoConsumer`(VM参数:`-Dproject.name=DemoConsumer -Dcsp.sentinel.dashboard.server=localhost:8080`)

通过控制台实时监控、簇点链路菜单观察接口调用、资源情况;对资源设置不同流控规则,进行观察和调试。

参考:[Sentinel控制台](https://github.com/alibaba/Sentinel/wiki/控制台).

+ 44
- 0
sentinel-demo/sentinel-demo-sofa-rpc/pom.xml View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>sentinel-demo</artifactId>
<groupId>com.alibaba.csp</groupId>
<version>1.7.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>sentinel-demo-sofa-rpc</artifactId>

<properties>
<sofa-rpc-all.version>5.6.4</sofa-rpc-all.version>
<slf4j-log4j12.version>1.7.21</slf4j-log4j12.version>
</properties>

<dependencies>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-sofa-rpc-adapter</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>sofa-rpc-all</artifactId>
<version>${sofa-rpc-all.version}</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-log4j12.version}</version>
</dependency>
</dependencies>
</project>

+ 79
- 0
sentinel-demo/sentinel-demo-sofa-rpc/src/main/java/com/alibaba/csp/sentinel/demo/sofa/rpc/DemoConsumer.java View File

@@ -0,0 +1,79 @@
/*
* 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.sofa.rpc;

import com.alibaba.csp.sentinel.demo.sofa.rpc.service.DemoService;
import com.alipay.sofa.rpc.common.RpcConstants;
import com.alipay.sofa.rpc.config.ApplicationConfig;
import com.alipay.sofa.rpc.config.ConsumerConfig;

import java.util.concurrent.TimeUnit;

/**
* Demo consumer of SOFARPC.
*
* Interact with Sentinel Dashboard, add the following VM arguments:
* <pre>
* -Dproject.name=DemoProvider -Dcsp.sentinel.dashboard.server=localhost:8080
* </pre>
*
* @author cdfive
*/
public class DemoConsumer {

public static void main(String[] args) throws Exception {
ApplicationConfig application = new ApplicationConfig().setAppName("DemoConsumer");

ConsumerConfig<DemoService> consumerConfig = new ConsumerConfig<DemoService>()
.setApplication(application)
.setInterfaceId(DemoService.class.getName())
.setProtocol("bolt")
.setDirectUrl("bolt://127.0.0.1:12001")
.setInvokeType(RpcConstants.INVOKER_TYPE_SYNC);

// 设置是否启用Sentinel,默认启用
// 也可在rpc-config.json全局设置
// consumerConfig.setParameter("sofa.rpc.sentinel.enabled", "false");

DemoService helloService = consumerConfig.refer();

System.out.println("DemoConsumer started!");

long sleepMs = 5;
int total = 5000;
int index = 0;
System.out.println("Total call " + total + " times and sleep " + sleepMs + "ms after each call.");

while (true) {
try {
index++;
String result = helloService.sayHello(index, "SOFARPC", 2020);
System.out.println("[" + index + "][Consumer]receive response: " + result);
} catch (Exception e) {
System.out.println("[" + index + "][Consumer]receive exception: " + e.getMessage());
}

TimeUnit.MILLISECONDS.sleep(sleepMs);

if (index == total) {
break;
}
}

System.out.println("DemoConsumer exit!");
System.exit(0);
}
}

+ 54
- 0
sentinel-demo/sentinel-demo-sofa-rpc/src/main/java/com/alibaba/csp/sentinel/demo/sofa/rpc/DemoProvider.java View File

@@ -0,0 +1,54 @@
/*
* 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.sofa.rpc;

import com.alibaba.csp.sentinel.demo.sofa.rpc.service.DemoService;
import com.alibaba.csp.sentinel.demo.sofa.rpc.service.impl.DemoServiceImpl;
import com.alipay.sofa.rpc.config.ProviderConfig;
import com.alipay.sofa.rpc.config.ServerConfig;

/**
* Demo provider of SOFARPC
*
* Interact with Sentinel Dashboard, add the following VM arguments:
* <pre>
* -Dproject.name=DemoProvider -Dcsp.sentinel.dashboard.server=localhost:8080
* </pre>
*
* @author cdfive
*/
public class DemoProvider {

public static void main(String[] args) {
ServerConfig serverConfig = new ServerConfig()
.setProtocol("bolt")
.setPort(12001)
.setDaemon(false);

ProviderConfig<DemoService> providerConfig = new ProviderConfig<DemoService>()
.setInterfaceId(DemoService.class.getName())
.setRef(new DemoServiceImpl())
.setServer(serverConfig);

// 设置是否启用Sentinel,默认启用
// 也可在rpc-config.json全局设置
// providerConfig.setParameter("sofa.rpc.sentinel.enabled", "false");

providerConfig.export();

System.out.println("DemoProvider started!");
}
}

+ 24
- 0
sentinel-demo/sentinel-demo-sofa-rpc/src/main/java/com/alibaba/csp/sentinel/demo/sofa/rpc/service/DemoService.java View File

@@ -0,0 +1,24 @@
/*
* 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.sofa.rpc.service;

/**
* @author cdfive
*/
public interface DemoService {

String sayHello(Integer index, String name, int year);
}

+ 41
- 0
sentinel-demo/sentinel-demo-sofa-rpc/src/main/java/com/alibaba/csp/sentinel/demo/sofa/rpc/service/impl/DemoServiceImpl.java View File

@@ -0,0 +1,41 @@
/*
* 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.sofa.rpc.service.impl;

import com.alibaba.csp.sentinel.demo.sofa.rpc.service.DemoService;

import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

/**
* @author cdfive
*/
public class DemoServiceImpl implements DemoService {

@Override
public String sayHello(Integer index, String name, int year) {
System.out.println("[" + index + "][Provider]receive request: " + name + "," + year);

int sleepMs = ThreadLocalRandom.current().nextInt(50);
try {
TimeUnit.MILLISECONDS.sleep(sleepMs);
} catch (InterruptedException e) {
System.err.println(e.getMessage());
}

return "Hello " + name + " " + year + "[" + sleepMs + "ms]";
}
}

+ 16
- 0
sentinel-demo/sentinel-demo-sofa-rpc/src/main/resources/log4j.xml View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">

<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %t %5p [%c:%M:%L] - %m%n"/>
</layout>
</appender>

<root>
<level value="INFO"/>
<appender-ref ref="CONSOLE"/>
</root>

</log4j:configuration>

+ 6
- 0
sentinel-demo/sentinel-demo-sofa-rpc/src/main/resources/sofa-rpc/rpc-config.json View File

@@ -0,0 +1,6 @@
{
"rpc.config.order": 999,
"logger.impl": "com.alipay.sofa.rpc.log.SLF4JLoggerImpl",
// 是否启用Sentinel,不设置默认为true
"sofa.rpc.sentinel.enabled": true
}

Loading…
Cancel
Save