Add Sentinel SOFARPC adapter module (#1307)
This commit is contained in:
parent
cd1d9be0ea
commit
18acb1d154
|
|
@ -18,6 +18,7 @@
|
||||||
<module>sentinel-web-servlet</module>
|
<module>sentinel-web-servlet</module>
|
||||||
<module>sentinel-dubbo-adapter</module>
|
<module>sentinel-dubbo-adapter</module>
|
||||||
<module>sentinel-apache-dubbo-adapter</module>
|
<module>sentinel-apache-dubbo-adapter</module>
|
||||||
|
<module>sentinel-sofa-rpc-adapter</module>
|
||||||
<module>sentinel-grpc-adapter</module>
|
<module>sentinel-grpc-adapter</module>
|
||||||
<module>sentinel-zuul-adapter</module>
|
<module>sentinel-zuul-adapter</module>
|
||||||
<module>sentinel-reactor-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-command-handler</module>
|
||||||
<module>sentinel-demo-spring-webflux</module>
|
<module>sentinel-demo-spring-webflux</module>
|
||||||
<module>sentinel-demo-apache-dubbo</module>
|
<module>sentinel-demo-apache-dubbo</module>
|
||||||
|
<module>sentinel-demo-sofa-rpc</module>
|
||||||
<module>sentinel-demo-spring-cloud-gateway</module>
|
<module>sentinel-demo-spring-cloud-gateway</module>
|
||||||
<module>sentinel-demo-zuul-gateway</module>
|
<module>sentinel-demo-zuul-gateway</module>
|
||||||
<module>sentinel-demo-etcd-datasource</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
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue