init
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
# Sentinel SOFARPC Adapter
|
||||
|
||||
Sentinel SOFARPC Adapter provides service provider filter and consumer filter
|
||||
for [SOFARPC](https://www.sofastack.tech/projects/sofa-rpc) services.
|
||||
|
||||
**Note: This adapter supports SOFARPC 5.4.x version and above, and 5.6.x is officially recommended.**
|
||||
|
||||
To use Sentinel SOFARPC Adapter, you can simply add the following dependency to your `pom.xml`:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-sofa-rpc-adapter</artifactId>
|
||||
<version>x.y.z</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
The Sentinel filters are **enabled by default**. Once you add the dependency,
|
||||
the SOFARPC services and methods will become protected resources in Sentinel,
|
||||
which can leverage Sentinel's flow control and guard ability when rules are configured.
|
||||
Demos can be found in [sentinel-demo-sofa-rpc](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-sofa-rpc).
|
||||
|
||||
If you don't want the filters enabled, you can manually disable them. For example:
|
||||
|
||||
```java
|
||||
providerConfig.setParameter("sofa.rpc.sentinel.enabled", "false");
|
||||
consumerConfig.setParameter("sofa.rpc.sentinel.enabled", "false");
|
||||
```
|
||||
|
||||
or add setting in `rpc-config.json` file, and its priority is lower than above.
|
||||
|
||||
```json
|
||||
{
|
||||
"sofa.rpc.sentinel.enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
For more details of SOFARPC filter, see [here](https://www.sofastack.tech/projects/sofa-rpc/custom-filter/).
|
||||
|
||||
## SOFARPC resources
|
||||
|
||||
The resource for SOFARPC services has two granularities: service interface and service method.
|
||||
|
||||
- Service interface:resourceName format is `interfaceName`,e.g. `com.alibaba.csp.sentinel.demo.sofa.rpc.DemoService`
|
||||
- Service method:resourceName format is `interfaceName#methodSignature`,e.g. `com.alibaba.csp.sentinel.demo.sofa.rpc.DemoService#sayHello(java.lang.Integer,java.lang.String,int)`
|
||||
|
||||
## Flow control based on caller
|
||||
|
||||
In many circumstances, it's also significant to control traffic flow based on the **caller**.
|
||||
For example, assuming that there are two services A and B, both of them initiate remote call requests to the service provider.
|
||||
If we want to limit the calls from service B only, we can set the `limitApp` of flow rule as the identifier of service B (e.g. service name).
|
||||
|
||||
Sentinel SOFARPC Adapter will automatically resolve the SOFARPC consumer's *application name* as the caller's name (`origin`),
|
||||
and will bring the caller's name when doing resource protection.
|
||||
If `limitApp` of flow rules is not configured (`default`), flow control will take effects on all callers.
|
||||
If `limitApp` of a flow rule is configured with a caller, then the corresponding flow rule will only take effect on the specific caller.
|
||||
|
||||
## Global fallback
|
||||
|
||||
Sentinel SOFARPC Adapter supports global fallback configuration.
|
||||
The global fallback will handle exceptions and give replacement result when blocked by
|
||||
flow control, degrade or system load protection. You can implement your own `SofaRpcFallback` interface
|
||||
and then register to `SofaRpcFallbackRegistry`. If no fallback is configured, Sentinel will wrap the `BlockException`
|
||||
then directly throw it out.
|
||||
43
sentinel/sentinel-adapter/sentinel-sofa-rpc-adapter/pom.xml
Normal file
43
sentinel/sentinel-adapter/sentinel-sofa-rpc-adapter/pom.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>sentinel-adapter</artifactId>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<version>1.8.3</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,72 @@
|
||||
/*
|
||||
* Copyright 1999-2020 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
|
||||
*
|
||||
* https://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.Tracer;
|
||||
|
||||
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(SentinelConstants.SOFA_RPC_SENTINEL_ENABLED);
|
||||
if (StringUtils.isNotBlank(enabled)) {
|
||||
return Boolean.parseBoolean(enabled);
|
||||
}
|
||||
|
||||
return RpcConfigs.getOrDefaultValue(SentinelConstants.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,27 @@
|
||||
/*
|
||||
* Copyright 1999-2020 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
|
||||
*
|
||||
* https://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;
|
||||
|
||||
/**
|
||||
* @author cdfive
|
||||
* @since 1.7.2
|
||||
*/
|
||||
public final class SentinelConstants {
|
||||
|
||||
public static final String SOFA_RPC_SENTINEL_ENABLED = "sofa.rpc.sentinel.enabled";
|
||||
|
||||
private SentinelConstants() {}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 callerApp = getApplicationName(request);
|
||||
String interfaceResourceName = getInterfaceResourceName(request);
|
||||
String methodResourceName = getMethodResourceName(request);
|
||||
|
||||
Entry interfaceEntry = null;
|
||||
Entry methodEntry = null;
|
||||
try {
|
||||
ContextUtil.enter(methodResourceName, callerApp);
|
||||
|
||||
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,61 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
private SofaRpcUtils() {}
|
||||
}
|
||||
@@ -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,50 @@
|
||||
/*
|
||||
* 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.util.AssertUtil;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
AssertUtil.notNull(providerFallback, "providerFallback cannot be null");
|
||||
SofaRpcFallbackRegistry.providerFallback = providerFallback;
|
||||
}
|
||||
|
||||
public static SofaRpcFallback getConsumerFallback() {
|
||||
return consumerFallback;
|
||||
}
|
||||
|
||||
public static void setConsumerFallback(SofaRpcFallback consumerFallback) {
|
||||
AssertUtil.notNull(consumerFallback, "consumerFallback cannot be null");
|
||||
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,108 @@
|
||||
package com.alibaba.csp.sentinel.adapter.sofa.rpc;
|
||||
|
||||
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(SentinelConstants.SOFA_RPC_SENTINEL_ENABLED);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
removeRpcConfig(SentinelConstants.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(SentinelConstants.SOFA_RPC_SENTINEL_ENABLED, "false");
|
||||
assertFalse(providerFilter.needToLoad(invoker));
|
||||
|
||||
providerConfig.setParameter(SentinelConstants.SOFA_RPC_SENTINEL_ENABLED, "");
|
||||
assertTrue(providerFilter.needToLoad(invoker));
|
||||
|
||||
RpcConfigs.putValue(SentinelConstants.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(SentinelConstants.SOFA_RPC_SENTINEL_ENABLED, "false");
|
||||
assertFalse(consumerFilter.needToLoad(invoker));
|
||||
|
||||
consumerConfig.setParameter(SentinelConstants.SOFA_RPC_SENTINEL_ENABLED, "");
|
||||
assertTrue(consumerFilter.needToLoad(invoker));
|
||||
|
||||
RpcConfigs.putValue(SentinelConstants.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(SentinelConstants.SOFA_RPC_SENTINEL_ENABLED, "false");
|
||||
assertFalse(providerFilter.needToLoad(providerInvoker));
|
||||
assertTrue(consumerFilter.needToLoad(consumerInvoker));
|
||||
|
||||
providerConfig.setParameter(SentinelConstants.SOFA_RPC_SENTINEL_ENABLED, "");
|
||||
assertTrue(providerFilter.needToLoad(providerInvoker));
|
||||
|
||||
RpcConfigs.putValue(SentinelConstants.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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user