@@ -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> | |||
@@ -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. |
@@ -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> |
@@ -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; | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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"; | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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() {} | |||
} | |||
@@ -0,0 +1,3 @@ | |||
# name # order | |||
com.alibaba.csp.sentinel.adapter.sofa.rpc.SentinelSofaRpcProviderFilter # -1000 | |||
com.alibaba.csp.sentinel.adapter.sofa.rpc.SentinelSofaRpcConsumerFilter # -1000 |
@@ -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 | |||
} | |||
} | |||
} |
@@ -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 | |||
} | |||
} | |||
} |
@@ -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()); | |||
} | |||
} |
@@ -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)); | |||
} | |||
} |
@@ -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]); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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()); | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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> | |||
@@ -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/控制台). |
@@ -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> |
@@ -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); | |||
} | |||
} |
@@ -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!"); | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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]"; | |||
} | |||
} |
@@ -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> |
@@ -0,0 +1,6 @@ | |||
{ | |||
"rpc.config.order": 999, | |||
"logger.impl": "com.alipay.sofa.rpc.log.SLF4JLoggerImpl", | |||
// 是否启用Sentinel,不设置默认为true | |||
"sofa.rpc.sentinel.enabled": true | |||
} |