Skip to content

Commit

Permalink
Add Sentinel SOFARPC adapter module (alibaba#1307)
Browse files Browse the repository at this point in the history
  • Loading branch information
cdfive authored Mar 2, 2020
1 parent cd1d9be commit 18acb1d
Show file tree
Hide file tree
Showing 30 changed files with 1,558 additions and 0 deletions.
1 change: 1 addition & 0 deletions sentinel-adapter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<module>sentinel-web-servlet</module>
<module>sentinel-dubbo-adapter</module>
<module>sentinel-apache-dubbo-adapter</module>
<module>sentinel-sofa-rpc-adapter</module>
<module>sentinel-grpc-adapter</module>
<module>sentinel-zuul-adapter</module>
<module>sentinel-reactor-adapter</module>
Expand Down
63 changes: 63 additions & 0 deletions sentinel-adapter/sentinel-sofa-rpc-adapter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Sentinel SOFARPC Adapter

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

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

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

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

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

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

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

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

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

## SOFARPC resources

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

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

## Flow control based on caller

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

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

## Global fallback

Sentinel SOFARPC Adapter supports global fallback configuration.
The global fallback will handle exceptions and give replacement result when blocked by
flow control, degrade or system load protection. You can implement your own `SofaRpcFallback` interface
and then register to `SofaRpcFallbackRegistry`. If no fallback is configured, Sentinel will wrap the `BlockException`
then directly throw it out.
43 changes: 43 additions & 0 deletions sentinel-adapter/sentinel-sofa-rpc-adapter/pom.xml
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
Loading

0 comments on commit 18acb1d

Please sign in to comment.