diff --git a/.gitignore b/.gitignore
index 84c94f6903..44f7205303 100755
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,9 @@
out
gen
+# Visual Studio Code
+.history/
+
# Maven
target/
pom.xml.tag
diff --git a/.travis.yml b/.travis.yml
index 0f940c4d27..158f71de31 100755
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,11 +9,17 @@ matrix:
env: BUILD_JDK=ORACLE_JDK_8
- jdk: oraclejdk11
env: BUILD_JDK=ORACLE_JDK_11
+ - arch: arm64
allow_failures:
- env: BUILD_JDK=ORACLE_JDK_11
# https://docs.travis-ci.com/user/languages/java/#maven-dependency-management
install:
+ - if [ "${TRAVIS_CPU_ARCH}" == "arm64" ]; then
+ sudo apt-get install -y maven openjdk-11-jdk;
+ export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-arm64;
+ export PATH=$JAVA_HOME/bin:$PATH;
+ fi
- mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -DminimumPriority=1
after_success:
diff --git a/README.md b/README.md
index d635a97be9..2c31657374 100755
--- a/README.md
+++ b/README.md
@@ -11,13 +11,15 @@
## Introduction
As distributed systems become increasingly popular, the reliability between services is becoming more important than ever before.
-Sentinel takes "flow" as breakthrough point, and works on multiple fields including **flow control**, **circuit breaking** and **system adaptive protection**, to guarantee reliability of microservices.
+Sentinel takes "flow" as breakthrough point, and works on multiple fields including **flow control**,
+**traffic shaping**, **circuit breaking** and **system adaptive protection**, to guarantee reliability and resilience for microservices.
Sentinel has the following features:
- **Rich applicable scenarios**: Sentinel has been wildly used in Alibaba, and has covered almost all the core-scenarios in Double-11 (11.11) Shopping Festivals in the past 10 years, such as “Second Kill” which needs to limit burst flow traffic to meet the system capacity, message peak clipping and valley fills, circuit breaking for unreliable downstream services, cluster flow control, etc.
- **Real-time monitoring**: Sentinel also provides real-time monitoring ability. You can see the runtime information of a single machine in real-time, and the aggregated runtime info of a cluster with less than 500 nodes.
- **Widespread open-source ecosystem**: Sentinel provides out-of-box integrations with commonly-used frameworks and libraries such as Spring Cloud, Dubbo and gRPC. You can easily use Sentinel by simply add the adapter dependency to your services.
+- **Polyglot support**: Sentinel has provided native support for Java, [Go](https://github.com/alibaba/sentinel-golang) and [C++](https://github.com/alibaba/sentinel-cpp).
- **Various SPI extensions**: Sentinel provides easy-to-use SPI extension interfaces that allow you to quickly customize your logic, for example, custom rule management, adapting data sources, and so on.
Features overview:
@@ -47,16 +49,16 @@ Below is a simple demo that guides new users to use Sentinel in just 3 steps. It
### 1. Add Dependency
-**Note:** Sentinel requires Java 7 or later.
+**Note:** Sentinel Core requires Java 7 or later.
-If your application is build in Maven, just add the following dependency in `pom.xml`.
+If your're using Maven, just add the following dependency in `pom.xml`.
```xml
com.alibaba.cspsentinel-core
- 1.7.1
+ 1.8.0
```
@@ -78,7 +80,7 @@ try (Entry entry = SphU.entry("HelloWorld")) {
// try-with-resources auto exit
```
-So far the code modification is done. We also provide [annotation support module](https://github.com/alibaba/Sentinel/blob/master/sentinel-extension/sentinel-annotation-aspectj/README.md) to define resource easier.
+So far the code modification is done. We've also provided [annotation support module](https://github.com/alibaba/Sentinel/blob/master/sentinel-extension/sentinel-annotation-aspectj/README.md) to define resource easier.
### 3. Define Rules
@@ -124,6 +126,8 @@ Samples can be found in the [sentinel-demo](https://github.com/alibaba/Sentinel/
### 5. Start Dashboard
+> Note: Java 8 is required for building or running the dashboard.
+
Sentinel also provides a simple dashboard application, on which you can monitor the clients and configure the rules in real time.
![dashboard](https://user-images.githubusercontent.com/9434884/55449295-84866d80-55fd-11e9-94e5-d3441f4a2b63.png)
@@ -139,11 +143,11 @@ All the information can be found in [logs](https://github.com/alibaba/Sentinel/w
For bug report, questions and discussions please submit [GitHub Issues](https://github.com/alibaba/sentinel/issues).
-Contact us: sentinel@linux.alibaba.com
+Contact us via [Gitter](https://gitter.im/alibaba/Sentinel) or [Email](mailto:sentinel@linux.alibaba.com).
## Contributing
-Contributions are always welcomed! Please see [CONTRIBUTING](./CONTRIBUTING.md) for detailed guidelines.
+Contributions are always welcomed! Please refer to [CONTRIBUTING](./CONTRIBUTING.md) for detailed guidelines.
You can start with the issues labeled with [`good first issue`](https://github.com/alibaba/Sentinel/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
@@ -155,18 +159,21 @@ And thanks for all [contributors](https://github.com/alibaba/Sentinel/graphs/con
## Who is using
-These are only part of the companies using Sentinel, for reference only. If you are using Sentinel, please [add your company here](https://github.com/alibaba/Sentinel/issues/18) to tell us your scenario to make Sentinel better :)
+These are only part of the companies using Sentinel, for reference only.
+If you are using Sentinel, please [add your company here](https://github.com/alibaba/Sentinel/issues/18) to tell us your scenario to make Sentinel better :)
![Alibaba Group](https://docs.alibabagroup.com/assets2/images/en/global/logo_header.png)
+![AntFin](https://user-images.githubusercontent.com/9434884/90598732-30961c00-e226-11ea-8c86-0b1d7f7875c7.png)
![Taiping Renshou](http://www.cntaiping.com/tplresource/cms/www/taiping/img/home_new/tp_logo_img.png)
+![拼多多](http://cdn.pinduoduo.com/assets/img/pdd_logo_v3.png)
+![爱奇艺](https://user-images.githubusercontent.com/9434884/90598445-a51c8b00-e225-11ea-9327-3543525f3f2a.png)
![Shunfeng Technology](https://user-images.githubusercontent.com/9434884/48463502-2f48eb80-e817-11e8-984f-2f9b1b789e2d.png)
-![Mandao](https://user-images.githubusercontent.com/9434884/48463559-6cad7900-e817-11e8-87e4-42952b074837.png)
-![每日优鲜](https://home.missfresh.cn/statics/img/logo.png)
![二维火](https://user-images.githubusercontent.com/9434884/49358468-bc43de00-f70d-11e8-97fe-0bf05865f29f.png)
+![Mandao](https://user-images.githubusercontent.com/9434884/48463559-6cad7900-e817-11e8-87e4-42952b074837.png)
![文轩在线](http://static.winxuancdn.com/css/v2/images/logo.png)
![客如云](https://www.keruyun.com/static/krynew/images/logo.png)
![亲宝宝](https://stlib.qbb6.com/wclt/img/home_hd/version1/title_logo.png)
![杭州光云科技](https://www.raycloud.com/images/logo.png)
![金汇金融](https://res.jinhui365.com/r/images/logo2.png?v=1.527)
![闪电购](http://cdn.52shangou.com/shandianbang/official-source/3.1.1/build/images/logo.png)
-![拼多多](http://cdn.pinduoduo.com/assets/img/pdd_logo_v3.png)
+
diff --git a/doc/image/sentinel-opensource-eco-landscape-en.png b/doc/image/sentinel-opensource-eco-landscape-en.png
index fbf0314e1d..a00ec3edaa 100644
Binary files a/doc/image/sentinel-opensource-eco-landscape-en.png and b/doc/image/sentinel-opensource-eco-landscape-en.png differ
diff --git a/pom.xml b/pom.xml
index 2d80de934b..6a6cc820d6 100755
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.alibaba.cspsentinel-parent
- 1.7.2-SNAPSHOT
+ 1.8.1-SNAPSHOTpom${project.artifactId}
@@ -41,11 +41,12 @@
- 1.2.62
+ 1.2.71
+ 1.3.24.12
- 2.21.0
+ 2.28.23.12.13.1.52.0.0
@@ -71,11 +72,14 @@
sentinel-extensionsentinel-transportsentinel-adapter
+ sentinel-cluster
+ sentinel-logging
+
sentinel-dashboardsentinel-demosentinel-benchmark
- sentinel-cluster
+
@@ -95,6 +99,11 @@
sentinel-annotation-aspectj${project.version}
+
+ com.alibaba.csp
+ sentinel-annotation-cdi-interceptor
+ ${project.version}
+ com.alibaba.cspsentinel-parameter-flow-control
@@ -263,6 +272,7 @@
@{argLine} -Xms1024m -Xmx2048m
+ -Dfile.encoding=UTF-8false
diff --git a/sentinel-adapter/pom.xml b/sentinel-adapter/pom.xml
index d1bdc9e63c..2b35586da5 100755
--- a/sentinel-adapter/pom.xml
+++ b/sentinel-adapter/pom.xml
@@ -7,7 +7,7 @@
com.alibaba.cspsentinel-parent
- 1.7.2-SNAPSHOT
+ 1.8.1-SNAPSHOTsentinel-adapterpom
@@ -18,6 +18,7 @@
sentinel-web-servletsentinel-dubbo-adaptersentinel-apache-dubbo-adapter
+ sentinel-apache-httpclient-adaptersentinel-sofa-rpc-adaptersentinel-grpc-adaptersentinel-zuul-adapter
@@ -26,6 +27,10 @@
sentinel-api-gateway-adapter-commonsentinel-spring-cloud-gateway-adaptersentinel-spring-webmvc-adapter
+ sentinel-zuul2-adapter
+ sentinel-okhttp-adapter
+ sentinel-jax-rs-adapter
+ sentinel-quarkus-adapter
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/README.md b/sentinel-adapter/sentinel-apache-dubbo-adapter/README.md
index fe97e9f48b..9d232bef03 100755
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/README.md
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/README.md
@@ -1,4 +1,4 @@
-# Sentinel Apache Dubbo Adapter
+# Sentinel Apache Dubbo Adapter (for 2.7.x+)
> Note: 中文文档请见[此处](https://github.com/alibaba/Sentinel/wiki/主流框架的适配#dubbo)。
@@ -21,7 +21,7 @@ To use Sentinel Dubbo Adapter, you can simply add the following dependency to yo
The Sentinel filters are **enabled by default**. Once you add the dependency,
the Dubbo 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-dubbo](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-dubbo).
+Demos can be found in [sentinel-demo-apache-dubbo](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-apache-dubbo).
If you don't want the filters enabled, you can manually disable them. For example:
@@ -37,8 +37,8 @@ For more details of Dubbo filter, see [here](http://dubbo.apache.org/en-us/docs/
The resource for Dubbo services has two granularities: service interface and service method.
-- Service interface:resourceName format is `interfaceName`,e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService`
-- Service method:resourceName format is `interfaceName:methodSignature`,e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String)`
+- Service interface: resourceName format is `interfaceName`, e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService`
+- Service method: resourceName format is `interfaceName:methodSignature`, e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String)`
## Flow control based on caller
@@ -52,17 +52,21 @@ If `limitApp` of flow rules is not configured (`default`), flow control will tak
If `limitApp` of a flow rule is configured with a caller, then the corresponding flow rule will only take effect on the specific caller.
> Note: Dubbo consumer does not provide its Dubbo application name when doing RPC,
-so developers should manually put the application name into *attachment* at consumer side,
-then extract it at provider side. Sentinel Dubbo Adapter has implemented a filter (`DubboAppContextFilter`)
-where consumer can carry application name information to provider automatically.
-If the consumer does not use Sentinel Dubbo Adapter but requires flow control based on caller, developers can manually put the application name into attachment with the key `dubboApplication`.
+> so developers should manually put the application name into *attachment* at consumer side,
+> then extract it at provider side. Sentinel Dubbo Adapter has implemented a filter (`DubboAppContextFilter`)
+> where consumer can carry application name information to provider automatically.
+> If the consumer does not use Sentinel Dubbo Adapter but requires flow control based on caller,
+> developers can manually put the application name into attachment with the key `dubboApplication`.
+>
+> Since 1.8.0, the adapter provides support for customizing origin parsing logic. You may register your own `DubboOriginParser`
+> implementation to `DubboAdapterGlobalConfig`.
## Global fallback
Sentinel Dubbo 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 `DubboFallback` interface
-and then register to `DubboFallbackRegistry`. If no fallback is configured, Sentinel will wrap the `BlockException`
-then directly throw it out.
+and then register to `DubboAdapterGlobalConfig`.
+If no fallback is configured, Sentinel will wrap the `BlockException` as the fallback result.
Besides, we can also leverage [Dubbo mock mechanism](http://dubbo.apache.org/en-us/docs/user/demos/local-mock.html) to provide fallback implementation of degraded Dubbo services.
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/pom.xml b/sentinel-adapter/sentinel-apache-dubbo-adapter/pom.xml
index 44f799db5e..cc7d379969 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/pom.xml
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/pom.xml
@@ -5,7 +5,7 @@
sentinel-adaptercom.alibaba.csp
- 1.7.2-SNAPSHOT
+ 1.8.1-SNAPSHOT4.0.0
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/BaseSentinelDubboFilter.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/BaseSentinelDubboFilter.java
index bc2101ffb2..8d4009f23a 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/BaseSentinelDubboFilter.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/BaseSentinelDubboFilter.java
@@ -16,66 +16,34 @@
package com.alibaba.csp.sentinel.adapter.dubbo;
-import com.alibaba.csp.sentinel.Entry;
-import com.alibaba.csp.sentinel.Tracer;
-import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
-import com.alibaba.csp.sentinel.context.ContextUtil;
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.constants.CommonConstants;
-import org.apache.dubbo.rpc.*;
+import org.apache.dubbo.rpc.Filter;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
/**
- * Base Class of the {@link SentinelDubboProviderFilter} and {@link SentinelDubboConsumerFilter}.
+ * Base class of the {@link SentinelDubboProviderFilter} and {@link SentinelDubboConsumerFilter}.
*
* @author Zechao Zheng
*/
+public abstract class BaseSentinelDubboFilter implements Filter {
-public abstract class BaseSentinelDubboFilter extends ListenableFilter {
- public BaseSentinelDubboFilter() {
- this.listener = new SentinelDubboListener();
- }
- static class SentinelDubboListener implements Listener {
+ /**
+ * Get method name of dubbo rpc
+ *
+ * @param invoker
+ * @param invocation
+ * @return
+ */
+ abstract String getMethodName(Invoker invoker, Invocation invocation, String prefix);
- public void onResponse(Result appResponse, Invoker> invoker, Invocation invocation) {
- onSuccess(appResponse, invoker);
- }
+ /**
+ * Get interface name of dubbo rpc
+ *
+ * @param invoker
+ * @return
+ */
+ abstract String getInterfaceName(Invoker invoker, String prefix);
- //for compatible dubbo 2.7.5 rename onResponse to onMessage
- public void onMessage(Result appResponse, Invoker> invoker, Invocation invocation) {
- onSuccess(appResponse, invoker);
- }
- private void onSuccess(Result appResponse, Invoker> invoker) {
- if (DubboConfig.getDubboBizExceptionTraceEnabled()) {
- traceAndExit(appResponse.getException(), invoker.getUrl());
- } else {
- traceAndExit(null, invoker.getUrl());
- }
- }
-
- @Override
- public void onError(Throwable t, Invoker> invoker, Invocation invocation) {
- traceAndExit(t, invoker.getUrl());
- }
-
- }
-
- static void traceAndExit(Throwable throwable, URL url) {
- Entry interfaceEntry = (Entry) RpcContext.getContext().get(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY);
- Entry methodEntry = (Entry) RpcContext.getContext().get(DubboUtils.DUBBO_METHOD_ENTRY_KEY);
- if (methodEntry != null) {
- Tracer.traceEntry(throwable, methodEntry);
- methodEntry.exit();
- RpcContext.getContext().remove(DubboUtils.DUBBO_METHOD_ENTRY_KEY);
- }
- if (interfaceEntry != null) {
- Tracer.traceEntry(throwable, interfaceEntry);
- interfaceEntry.exit();
- RpcContext.getContext().remove(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY);
- }
- if (CommonConstants.PROVIDER_SIDE.equals(url.getParameter(CommonConstants.SIDE_KEY))) {
- ContextUtil.exit();
- }
- }
}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtils.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtils.java
index d2c325fafd..f9da92a507 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtils.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtils.java
@@ -15,7 +15,7 @@
*/
package com.alibaba.csp.sentinel.adapter.dubbo;
-import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
import com.alibaba.csp.sentinel.util.StringUtil;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
@@ -26,8 +26,6 @@
public final class DubboUtils {
public static final String SENTINEL_DUBBO_APPLICATION_KEY = "dubboApplication";
- public static final String DUBBO_METHOD_ENTRY_KEY = "dubboMethodEntry";
- public static final String DUBBO_INTERFACE_ENTRY_KEY = "dubboInterfaceEntry";
public static String getApplication(Invocation invocation, String defaultValue) {
if (invocation == null || invocation.getAttachments() == null) {
@@ -36,11 +34,11 @@ public static String getApplication(Invocation invocation, String defaultValue)
return invocation.getAttachment(SENTINEL_DUBBO_APPLICATION_KEY, defaultValue);
}
- public static String getResourceName(Invoker> invoker, Invocation invocation){
- return getResourceName(invoker, invocation, false);
+ public static String getMethodResourceName(Invoker> invoker, Invocation invocation){
+ return getMethodResourceName(invoker, invocation, false);
}
- public static String getResourceName(Invoker> invoker, Invocation invocation, Boolean useGroupAndVersion) {
+ public static String getMethodResourceName(Invoker> invoker, Invocation invocation, Boolean useGroupAndVersion) {
StringBuilder buf = new StringBuilder(64);
String interfaceResource = useGroupAndVersion ? invoker.getUrl().getColonSeparatedKey() : invoker.getInterface().getName();
buf.append(interfaceResource)
@@ -59,16 +57,39 @@ public static String getResourceName(Invoker> invoker, Invocation invocation,
return buf.toString();
}
- public static String getResourceName(Invoker> invoker, Invocation invocation, String prefix) {
+ public static String getMethodResourceName(Invoker> invoker, Invocation invocation, String prefix) {
if (StringUtil.isNotBlank(prefix)) {
return new StringBuilder(64)
.append(prefix)
- .append(getResourceName(invoker, invocation, DubboConfig.getDubboInterfaceGroupAndVersionEnabled()))
+ .append(getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboInterfaceGroupAndVersionEnabled()))
.toString();
} else {
- return getResourceName(invoker, invocation, DubboConfig.getDubboInterfaceGroupAndVersionEnabled());
+ return getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboInterfaceGroupAndVersionEnabled());
}
}
+
+
+ public static String getInterfaceName(Invoker invoker) {
+ return getInterfaceName(invoker, false);
+ }
+
+ public static String getInterfaceName(Invoker> invoker, Boolean useGroupAndVersion) {
+ StringBuilder buf = new StringBuilder(64);
+ return useGroupAndVersion ? invoker.getUrl().getColonSeparatedKey() : invoker.getInterface().getName();
+ }
+
+ public static String getInterfaceName(Invoker> invoker, String prefix) {
+ if (StringUtil.isNotBlank(prefix)) {
+ return new StringBuilder(64)
+ .append(prefix)
+ .append(getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboInterfaceGroupAndVersionEnabled()))
+ .toString();
+ } else {
+ return getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboInterfaceGroupAndVersionEnabled());
+ }
+ }
+
+
private DubboUtils() {
}
}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java
index ffa9fba9cf..02b8c5ad91 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java
@@ -15,23 +15,19 @@
*/
package com.alibaba.csp.sentinel.adapter.dubbo;
-import com.alibaba.csp.sentinel.Entry;
-import com.alibaba.csp.sentinel.EntryType;
-import com.alibaba.csp.sentinel.ResourceTypeConstants;
-import com.alibaba.csp.sentinel.SphU;
-import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
-import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
+import com.alibaba.csp.sentinel.*;
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.slots.block.BlockException;
+
import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.rpc.Invocation;
-import org.apache.dubbo.rpc.InvokeMode;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.Result;
-import org.apache.dubbo.rpc.RpcContext;
-import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.*;
import org.apache.dubbo.rpc.support.RpcUtils;
+import java.util.LinkedList;
+import java.util.Optional;
+import java.util.function.BiConsumer;
+
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
/**
@@ -44,6 +40,7 @@
*
* @author Carpenter Lee
* @author Eric Zhao
+ * @author Lin Liang
*/
@Activate(group = CONSUMER)
public class SentinelDubboConsumerFilter extends BaseSentinelDubboFilter {
@@ -52,33 +49,106 @@ public SentinelDubboConsumerFilter() {
RecordLog.info("Sentinel Apache Dubbo consumer filter initialized");
}
+ @Override
+ String getMethodName(Invoker invoker, Invocation invocation, String prefix) {
+ return DubboUtils.getMethodResourceName(invoker, invocation, prefix);
+ }
+
+ @Override
+ String getInterfaceName(Invoker invoker, String prefix) {
+ return DubboUtils.getInterfaceName(invoker, prefix);
+ }
+
@Override
public Result invoke(Invoker> invoker, Invocation invocation) throws RpcException {
+ InvokeMode invokeMode = RpcUtils.getInvokeMode(invoker.getUrl(), invocation);
+ if (InvokeMode.SYNC == invokeMode) {
+ return syncInvoke(invoker, invocation);
+ } else {
+ return asyncInvoke(invoker, invocation);
+ }
+ }
+
+ private Result syncInvoke(Invoker> invoker, Invocation invocation) {
Entry interfaceEntry = null;
Entry methodEntry = null;
- RpcContext rpcContext = RpcContext.getContext();
+ String prefix = DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey();
+ String interfaceResourceName = getInterfaceName(invoker, prefix);
+ String methodResourceName = getMethodName(invoker, invocation, prefix);
try {
- String methodResourceName = DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboConsumerPrefix());
- String interfaceResourceName = DubboConfig.getDubboInterfaceGroupAndVersionEnabled() ? invoker.getUrl().getColonSeparatedKey()
- : invoker.getInterface().getName();
- InvokeMode invokeMode = RpcUtils.getInvokeMode(invoker.getUrl(), invocation);
-
- if (InvokeMode.SYNC == invokeMode) {
- interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
- rpcContext.set(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY, interfaceEntry);
- methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT, invocation.getArguments());
- } else {
- // should generate the AsyncEntry when the invoke model in future or async
- interfaceEntry = SphU.asyncEntry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
- rpcContext.set(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY, interfaceEntry);
- methodEntry = SphU.asyncEntry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT, 1, invocation.getArguments());
+ interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
+ methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT,
+ invocation.getArguments());
+ Result result = invoker.invoke(invocation);
+ if (result.hasException()) {
+ Tracer.traceEntry(result.getException(), interfaceEntry);
+ Tracer.traceEntry(result.getException(), methodEntry);
+ }
+ return result;
+ } catch (BlockException e) {
+ return DubboAdapterGlobalConfig.getConsumerFallback().handle(invoker, invocation, e);
+ } catch (RpcException e) {
+ Tracer.traceEntry(e, interfaceEntry);
+ Tracer.traceEntry(e, methodEntry);
+ throw e;
+ } finally {
+ if (methodEntry != null) {
+ methodEntry.exit(1, invocation.getArguments());
}
- rpcContext.set(DubboUtils.DUBBO_METHOD_ENTRY_KEY, methodEntry);
- return invoker.invoke(invocation);
+ if (interfaceEntry != null) {
+ interfaceEntry.exit();
+ }
+ }
+ }
+
+ private Result asyncInvoke(Invoker> invoker, Invocation invocation) {
+ LinkedList queue = new LinkedList<>();
+ String prefix = DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey();
+ String interfaceResourceName = getInterfaceName(invoker, prefix);
+ String methodResourceName = getMethodName(invoker, invocation, prefix);
+ try {
+ queue.push(new EntryHolder(
+ SphU.asyncEntry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT), null));
+ queue.push(new EntryHolder(
+ SphU.asyncEntry(methodResourceName, ResourceTypeConstants.COMMON_RPC,
+ EntryType.OUT, 1, invocation.getArguments()), invocation.getArguments()));
+ Result result = invoker.invoke(invocation);
+ result.whenCompleteWithContext((r, throwable) -> {
+ Throwable error = throwable;
+ if (error == null) {
+ error = Optional.ofNullable(r).map(Result::getException).orElse(null);
+ }
+ while (!queue.isEmpty()) {
+ EntryHolder holder = queue.pop();
+ Tracer.traceEntry(error, holder.entry);
+ exitEntry(holder);
+ }
+ });
+ return result;
} catch (BlockException e) {
- return DubboFallbackRegistry.getConsumerFallback().handle(invoker, invocation, e);
+ while (!queue.isEmpty()) {
+ exitEntry(queue.pop());
+ }
+ return DubboAdapterGlobalConfig.getConsumerFallback().handle(invoker, invocation, e);
}
}
-}
+ static class EntryHolder {
+ final private Entry entry;
+ final private Object[] params;
+
+ public EntryHolder(Entry entry, Object[] params) {
+ this.entry = entry;
+ this.params = params;
+ }
+ }
+
+ private void exitEntry(EntryHolder holder) {
+ if (holder.params != null) {
+ holder.entry.exit(1, holder.params);
+ } else {
+ holder.entry.exit();
+ }
+ }
+}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java
index c919978694..867073e147 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java
@@ -15,20 +15,16 @@
*/
package com.alibaba.csp.sentinel.adapter.dubbo;
-import com.alibaba.csp.sentinel.Entry;
-import com.alibaba.csp.sentinel.EntryType;
-import com.alibaba.csp.sentinel.ResourceTypeConstants;
-import com.alibaba.csp.sentinel.SphU;
-import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
-import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
+import com.alibaba.csp.sentinel.*;
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.slots.block.BlockException;
+
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
-import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
@@ -52,30 +48,57 @@ public SentinelDubboProviderFilter() {
RecordLog.info("Sentinel Apache Dubbo provider filter initialized");
}
+ @Override
+ String getMethodName(Invoker invoker, Invocation invocation, String prefix) {
+ return DubboUtils.getMethodResourceName(invoker, invocation, prefix);
+ }
+
+ @Override
+ String getInterfaceName(Invoker invoker, String prefix) {
+ return DubboUtils.getInterfaceName(invoker, prefix);
+ }
+
@Override
public Result invoke(Invoker> invoker, Invocation invocation) throws RpcException {
// Get origin caller.
- String application = DubboUtils.getApplication(invocation, "");
- RpcContext rpcContext = RpcContext.getContext();
+ String origin = DubboAdapterGlobalConfig.getOriginParser().parse(invoker, invocation);
+ if (null == origin) {
+ origin = "";
+ }
Entry interfaceEntry = null;
Entry methodEntry = null;
+ String prefix = DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey();
+ String interfaceResourceName = getInterfaceName(invoker, prefix);
+ String methodResourceName = getMethodName(invoker, invocation, prefix);
try {
- String methodResourceName = DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboProviderPrefix());
- String interfaceResourceName = DubboConfig.getDubboInterfaceGroupAndVersionEnabled() ? invoker.getUrl().getColonSeparatedKey()
- : invoker.getInterface().getName();
// Only need to create entrance context at provider side, as context will take effect
// at entrance of invocation chain only (for inbound traffic).
- ContextUtil.enter(methodResourceName, application);
+ ContextUtil.enter(methodResourceName, origin);
interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN);
- rpcContext.set(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY, interfaceEntry);
- methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN, invocation.getArguments());
- rpcContext.set(DubboUtils.DUBBO_METHOD_ENTRY_KEY, methodEntry);
- return invoker.invoke(invocation);
+ methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN,
+ invocation.getArguments());
+ Result result = invoker.invoke(invocation);
+ if (result.hasException()) {
+ Tracer.traceEntry(result.getException(), interfaceEntry);
+ Tracer.traceEntry(result.getException(), methodEntry);
+ }
+ return result;
} catch (BlockException e) {
- return DubboFallbackRegistry.getProviderFallback().handle(invoker, invocation, e);
+ return DubboAdapterGlobalConfig.getProviderFallback().handle(invoker, invocation, e);
+ } catch (RpcException e) {
+ Tracer.traceEntry(e, interfaceEntry);
+ Tracer.traceEntry(e, methodEntry);
+ throw e;
+ } finally {
+ if (methodEntry != null) {
+ methodEntry.exit(1, invocation.getArguments());
+ }
+ if (interfaceEntry != null) {
+ interfaceEntry.exit();
+ }
+ ContextUtil.exit();
}
}
-
}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/config/DubboAdapterGlobalConfig.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/config/DubboAdapterGlobalConfig.java
new file mode 100644
index 0000000000..43664f0a38
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/config/DubboAdapterGlobalConfig.java
@@ -0,0 +1,116 @@
+/*
+ * 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.dubbo.config;
+
+import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DefaultDubboFallback;
+import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallback;
+import com.alibaba.csp.sentinel.adapter.dubbo.origin.DefaultDubboOriginParser;
+import com.alibaba.csp.sentinel.adapter.dubbo.origin.DubboOriginParser;
+import com.alibaba.csp.sentinel.config.SentinelConfig;
+import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.util.StringUtil;
+
+/**
+ *
+ * Responsible for dubbo service provider, consumer attribute configuration
+ *
+ *
+ * @author lianglin
+ * @since 1.7.0
+ */
+public final class DubboAdapterGlobalConfig {
+
+ private static final String TRUE_STR = "true";
+
+ public static final String DUBBO_RES_NAME_WITH_PREFIX_KEY = "csp.sentinel.dubbo.resource.use.prefix";
+ public static final String DUBBO_PROVIDER_RES_NAME_PREFIX_KEY = "csp.sentinel.dubbo.resource.provider.prefix";
+ public static final String DUBBO_CONSUMER_RES_NAME_PREFIX_KEY = "csp.sentinel.dubbo.resource.consumer.prefix";
+
+ private static final String DEFAULT_DUBBO_PROVIDER_PREFIX = "dubbo:provider:";
+ private static final String DEFAULT_DUBBO_CONSUMER_PREFIX = "dubbo:consumer:";
+
+ public static final String DUBBO_INTERFACE_GROUP_VERSION_ENABLED = "csp.sentinel.dubbo.interface.group.version.enabled";
+
+ private static volatile DubboFallback consumerFallback = new DefaultDubboFallback();
+ private static volatile DubboFallback providerFallback = new DefaultDubboFallback();
+ private static volatile DubboOriginParser originParser = new DefaultDubboOriginParser();
+
+ public static boolean isUsePrefix() {
+ return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_RES_NAME_WITH_PREFIX_KEY));
+ }
+
+ public static String getDubboProviderResNamePrefixKey() {
+ if (isUsePrefix()) {
+ String config = SentinelConfig.getConfig(DUBBO_PROVIDER_RES_NAME_PREFIX_KEY);
+ return StringUtil.isNotBlank(config) ? config : DEFAULT_DUBBO_PROVIDER_PREFIX;
+ }
+ return null;
+ }
+
+ public static String getDubboConsumerResNamePrefixKey() {
+ if (isUsePrefix()) {
+ String config = SentinelConfig.getConfig(DUBBO_CONSUMER_RES_NAME_PREFIX_KEY);
+ return StringUtil.isNotBlank(config) ? config : DEFAULT_DUBBO_CONSUMER_PREFIX;
+ }
+ return null;
+ }
+
+ public static Boolean getDubboInterfaceGroupAndVersionEnabled() {
+ return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED));
+ }
+
+ public static DubboFallback getConsumerFallback() {
+ return consumerFallback;
+ }
+
+ public static void setConsumerFallback(DubboFallback consumerFallback) {
+ AssertUtil.notNull(consumerFallback, "consumerFallback cannot be null");
+ DubboAdapterGlobalConfig.consumerFallback = consumerFallback;
+ }
+
+ public static DubboFallback getProviderFallback() {
+ return providerFallback;
+ }
+
+ public static void setProviderFallback(DubboFallback providerFallback) {
+ AssertUtil.notNull(providerFallback, "providerFallback cannot be null");
+ DubboAdapterGlobalConfig.providerFallback = providerFallback;
+ }
+
+ /**
+ * Get the origin parser of Dubbo adapter.
+ *
+ * @return the origin parser
+ * @since 1.8.0
+ */
+ public static DubboOriginParser getOriginParser() {
+ return originParser;
+ }
+
+ /**
+ * Set the origin parser of Dubbo adapter.
+ *
+ * @param originParser the origin parser
+ * @since 1.8.0
+ */
+ public static void setOriginParser(DubboOriginParser originParser) {
+ AssertUtil.notNull(originParser, "originParser cannot be null");
+ DubboAdapterGlobalConfig.originParser = originParser;
+ }
+
+ private DubboAdapterGlobalConfig() {}
+
+}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/config/DubboConfig.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/config/DubboConfig.java
deleted file mode 100644
index 9b2b021828..0000000000
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/config/DubboConfig.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.dubbo.config;
-
-import com.alibaba.csp.sentinel.config.SentinelConfig;
-import com.alibaba.csp.sentinel.util.StringUtil;
-
-/**
- *
- * Responsible for dubbo service provider, consumer attribute configuration
- *
- *
- * @author lianglin
- * @since 1.7.0
- */
-public final class DubboConfig {
-
- public static final String DUBBO_USE_PREFIX = "csp.sentinel.dubbo.resource.use.prefix";
- private static final String TRUE_STR = "true";
-
- public static final String DUBBO_PROVIDER_PREFIX = "csp.sentinel.dubbo.resource.provider.prefix";
- public static final String DUBBO_CONSUMER_PREFIX = "csp.sentinel.dubbo.resource.consumer.prefix";
-
- private static final String DEFAULT_DUBBO_PROVIDER_PREFIX = "dubbo:provider:";
- private static final String DEFAULT_DUBBO_CONSUMER_PREFIX = "dubbo:consumer:";
-
- public static final String DUBBO_INTERFACE_GROUP_VERSION_ENABLED = "csp.sentinel.dubbo.interface.group.version.enabled";
-
- public static final String TRACE_BIZ_EXCEPTION_ENABLED = "csp.sentinel.dubbo.trace.biz.exception.enabled";
-
-
- public static boolean isUsePrefix() {
- return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_USE_PREFIX));
- }
-
- public static String getDubboProviderPrefix() {
- if (isUsePrefix()) {
- String config = SentinelConfig.getConfig(DUBBO_PROVIDER_PREFIX);
- return StringUtil.isNotBlank(config) ? config : DEFAULT_DUBBO_PROVIDER_PREFIX;
- }
- return null;
- }
-
- public static String getDubboConsumerPrefix() {
- if (isUsePrefix()) {
- String config = SentinelConfig.getConfig(DUBBO_CONSUMER_PREFIX);
- return StringUtil.isNotBlank(config) ? config : DEFAULT_DUBBO_CONSUMER_PREFIX;
- }
- return null;
- }
-
- public static Boolean getDubboInterfaceGroupAndVersionEnabled() {
- return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED));
- }
-
- public static Boolean getDubboBizExceptionTraceEnabled() {
- String traceBizExceptionEnabled = SentinelConfig.getConfig(TRACE_BIZ_EXCEPTION_ENABLED);
- if (StringUtil.isNotBlank(traceBizExceptionEnabled)) {
- return TRUE_STR.equalsIgnoreCase(traceBizExceptionEnabled);
- }
- return true;
- }
-
-}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DefaultDubboFallback.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DefaultDubboFallback.java
index 8a4659f7be..d5099b1d6a 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DefaultDubboFallback.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DefaultDubboFallback.java
@@ -16,8 +16,8 @@
package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
import com.alibaba.csp.sentinel.slots.block.BlockException;
-import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
+import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
@@ -29,7 +29,7 @@ public class DefaultDubboFallback implements DubboFallback {
@Override
public Result handle(Invoker> invoker, Invocation invocation, BlockException ex) {
- // Just wrap and throw the exception.
- throw new SentinelRpcException(ex);
+ // Just wrap the exception.
+ return AsyncRpcResult.newDefaultAsyncResult(ex.toRuntimeException(), invocation);
}
}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistry.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistry.java
index 78c3a0b191..a9fb1f5a44 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistry.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistry.java
@@ -15,39 +15,31 @@
*/
package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
-import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
/**
*
Global fallback registry for Dubbo.
*
- *
- * Note: Circuit breaking is mainly designed for consumer. The provider should not
- * give fallback result in most circumstances.
- *
- *
* @author Eric Zhao
+ * @deprecated use {@link DubboAdapterGlobalConfig} instead since 1.8.0.
*/
+@Deprecated
public final class DubboFallbackRegistry {
- private static volatile DubboFallback consumerFallback = new DefaultDubboFallback();
- private static volatile DubboFallback providerFallback = new DefaultDubboFallback();
-
public static DubboFallback getConsumerFallback() {
- return consumerFallback;
+ return DubboAdapterGlobalConfig.getConsumerFallback();
}
public static void setConsumerFallback(DubboFallback consumerFallback) {
- AssertUtil.notNull(consumerFallback, "consumerFallback cannot be null");
- DubboFallbackRegistry.consumerFallback = consumerFallback;
+ DubboAdapterGlobalConfig.setConsumerFallback(consumerFallback);
}
public static DubboFallback getProviderFallback() {
- return providerFallback;
+ return DubboAdapterGlobalConfig.getProviderFallback();
}
public static void setProviderFallback(DubboFallback providerFallback) {
- AssertUtil.notNull(providerFallback, "providerFallback cannot be null");
- DubboFallbackRegistry.providerFallback = providerFallback;
+ DubboAdapterGlobalConfig.setProviderFallback(providerFallback);
}
private DubboFallbackRegistry() {}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DefaultDubboOriginParser.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DefaultDubboOriginParser.java
new file mode 100644
index 0000000000..5a84fc189e
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DefaultDubboOriginParser.java
@@ -0,0 +1,34 @@
+/*
+ * 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.dubbo.origin;
+
+import com.alibaba.csp.sentinel.adapter.dubbo.DubboUtils;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+
+/**
+ * Default Dubbo origin parser.
+ *
+ * @author jingzian
+ */
+public class DefaultDubboOriginParser implements DubboOriginParser {
+
+ @Override
+ public String parse(Invoker> invoker, Invocation invocation) {
+ return DubboUtils.getApplication(invocation, "");
+ }
+
+}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DubboOriginParser.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DubboOriginParser.java
new file mode 100644
index 0000000000..234ce1c3f0
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DubboOriginParser.java
@@ -0,0 +1,38 @@
+/*
+ * 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.dubbo.origin;
+
+import com.alibaba.csp.sentinel.context.Context;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+
+/**
+ * Customized origin parser for Dubbo provider filter.{@link Context#getOrigin()}
+ *
+ * @author jingzian
+ */
+public interface DubboOriginParser {
+
+ /**
+ * Parses the origin (caller) from Dubbo invocation.
+ *
+ * @param invoker Dubbo invoker
+ * @param invocation Dubbo invocation
+ * @return the parsed origin
+ */
+ String parse(Invoker> invoker, Invocation invocation);
+
+}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/BaseTest.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/BaseTest.java
index 7793a6567e..db78c3666a 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/BaseTest.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/BaseTest.java
@@ -15,26 +15,19 @@
*/
package com.alibaba.csp.sentinel;
-import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
-import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
+import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DefaultDubboFallback;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.constants.CommonConstants;
-import org.apache.dubbo.rpc.Invocation;
-import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcContext;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
/**
* Base test class, provide common methods for subClass
* The package is same as CtSph, to call CtSph.resetChainMap() method for test
@@ -42,46 +35,41 @@
* Note: Only for test. DO NOT USE IN PRODUCTION!
*
* @author cdfive
+ * @author lianglin
*/
public class BaseTest {
- protected Invoker invoker;
- protected Invocation invocation;
-
- public void constructInvokerAndInvocation() {
- invoker = mock(Invoker.class);
- URL url = URL.valueOf("dubbo://127.0.0.1:2181")
- .addParameter(CommonConstants.VERSION_KEY, "1.0.0")
- .addParameter(CommonConstants.GROUP_KEY, "grp1")
- .addParameter(CommonConstants.INTERFACE_KEY, DemoService.class.getName());
- when(invoker.getUrl()).thenReturn(url);
- when(invoker.getInterface()).thenReturn(DemoService.class);
-
- invocation = mock(Invocation.class);
- Method method = DemoService.class.getMethods()[0];
- when(invocation.getMethodName()).thenReturn(method.getName());
- when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
-
- }
-
/**
* Clean up resources for context, clusterNodeMap, processorSlotChainMap
*/
- protected static void cleanUpAll() {
+ public void cleanUpAll() {
try {
- RpcContext.removeContext();
- ClusterBuilderSlot.getClusterNodeMap().clear();
- CtSph.resetChainMap();
- Method method = ContextUtil.class.getDeclaredMethod("resetContextMap");
- method.setAccessible(true);
- method.invoke(null, null);
- ContextUtil.exit();
- SentinelConfig.setConfig(DubboConfig.DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "true");
- FlowRuleManager.loadRules(new ArrayList<>());
- DegradeRuleManager.loadRules(new ArrayList<>());
+ clearDubboContext();
+ cleanUpCstContext();
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
+
+ private void cleanUpCstContext() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+ ClusterBuilderSlot.getClusterNodeMap().clear();
+ CtSph.resetChainMap();
+ Method method = ContextUtil.class.getDeclaredMethod("resetContextMap");
+ method.setAccessible(true);
+ method.invoke(null, null);
+ ContextUtil.exit();
+ FlowRuleManager.loadRules(new ArrayList<>());
+ DegradeRuleManager.loadRules(new ArrayList<>());
+ }
+
+ private void clearDubboContext() {
+ SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "false");
+ SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "");
+ SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "");
+ SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
+ DubboAdapterGlobalConfig.setConsumerFallback(new DefaultDubboFallback());
+ RpcContext.removeContext();
+
+ }
}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/DubboTestUtil.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/DubboTestUtil.java
new file mode 100644
index 0000000000..326f216b24
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/DubboTestUtil.java
@@ -0,0 +1,80 @@
+/*
+ * 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;
+
+import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+
+import java.lang.reflect.Method;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author lianglin
+ */
+public class DubboTestUtil {
+
+
+ public static Class> DEFAULT_TEST_SERVICE = DemoService.class;
+ public static Method DEFAULT_TEST_METHOD_ONE = DEFAULT_TEST_SERVICE.getMethods()[0];
+ public static Method DEFAULT_TEST_METHOD_TWO = DEFAULT_TEST_SERVICE.getMethods()[1];
+
+ public static Invoker getMockInvoker(URL url, Class> cls) {
+ Invoker invoker = mock(Invoker.class);
+ when(invoker.getUrl()).thenReturn(url);
+ when(invoker.getInterface()).thenReturn(cls);
+ return invoker;
+ }
+
+ public static Invoker getDefaultMockInvoker() {
+ return getMockInvoker(getDefaultTestURL(), DEFAULT_TEST_SERVICE);
+ }
+
+ public static Invocation getMockInvocation(Method method) {
+ Invocation invocation = mock(Invocation.class);
+ when(invocation.getMethodName()).thenReturn(method.getName());
+ when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
+ return invocation;
+ }
+
+ public static Invocation getDefaultMockInvocationOne() {
+ Invocation invocation = mock(Invocation.class);
+ when(invocation.getMethodName()).thenReturn(DEFAULT_TEST_METHOD_ONE.getName());
+ when(invocation.getParameterTypes()).thenReturn(DEFAULT_TEST_METHOD_ONE.getParameterTypes());
+ return invocation;
+ }
+
+ public static Invocation getDefaultMockInvocationTwo() {
+ Invocation invocation = mock(Invocation.class);
+ when(invocation.getMethodName()).thenReturn(DEFAULT_TEST_METHOD_TWO.getName());
+ when(invocation.getParameterTypes()).thenReturn(DEFAULT_TEST_METHOD_TWO.getParameterTypes());
+ return invocation;
+ }
+
+ public static URL getDefaultTestURL() {
+ URL url = URL.valueOf("dubbo://127.0.0.1:2181")
+ .addParameter(CommonConstants.VERSION_KEY, "1.0.0")
+ .addParameter(CommonConstants.GROUP_KEY, "grp1")
+ .addParameter(CommonConstants.INTERFACE_KEY, DEFAULT_TEST_SERVICE.getName());
+ return url;
+ }
+
+
+}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtilsTest.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtilsTest.java
index ece46fde3d..4b067c04bf 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtilsTest.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtilsTest.java
@@ -15,7 +15,7 @@
*/
package com.alibaba.csp.sentinel.adapter.dubbo;
-import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import org.apache.dubbo.common.URL;
@@ -29,11 +29,10 @@
import java.lang.reflect.Method;
import java.util.HashMap;
+import static com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig.DUBBO_INTERFACE_GROUP_VERSION_ENABLED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
/**
* @author cdfive
@@ -43,18 +42,18 @@ public class DubboUtilsTest {
@Before
public void setUp() {
SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "true");
- SentinelConfig.setConfig(DubboConfig.DUBBO_PROVIDER_PREFIX, "");
- SentinelConfig.setConfig(DubboConfig.DUBBO_CONSUMER_PREFIX, "");
- SentinelConfig.setConfig(DubboConfig.DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
+ SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "");
+ SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "");
+ SentinelConfig.setConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
}
@After
public void tearDown() {
SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "false");
- SentinelConfig.setConfig(DubboConfig.DUBBO_PROVIDER_PREFIX, "");
- SentinelConfig.setConfig(DubboConfig.DUBBO_CONSUMER_PREFIX, "");
- SentinelConfig.setConfig(DubboConfig.DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
+ SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "");
+ SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "");
+ SentinelConfig.setConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
}
@@ -93,7 +92,7 @@ public void testGetResourceName() throws NoSuchMethodException {
when(invocation.getMethodName()).thenReturn(method.getName());
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
- String resourceName = DubboUtils.getResourceName(invoker, invocation);
+ String resourceName = DubboUtils.getMethodResourceName(invoker, invocation);
assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
@@ -114,7 +113,7 @@ public void testGetResourceNameWithGroupAndVersion() throws NoSuchMethodExceptio
when(invocation.getMethodName()).thenReturn(method.getName());
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
- String resourceNameUseGroupAndVersion = DubboUtils.getResourceName(invoker, invocation, true);
+ String resourceNameUseGroupAndVersion = DubboUtils.getMethodResourceName(invoker, invocation, true);
assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:1.0.0:grp1:sayHello(java.lang.String,int)", resourceNameUseGroupAndVersion);
}
@@ -131,19 +130,78 @@ public void testGetResourceNameWithPrefix() throws NoSuchMethodException {
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
//test with default prefix
- String resourceName = DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboProviderPrefix());
+ String resourceName = DubboUtils.getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey());
assertEquals("dubbo:provider:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
- resourceName = DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboConsumerPrefix());
+ resourceName = DubboUtils.getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey());
assertEquals("dubbo:consumer:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
//test with custom prefix
- SentinelConfig.setConfig(DubboConfig.DUBBO_PROVIDER_PREFIX, "my:dubbo:provider:");
- SentinelConfig.setConfig(DubboConfig.DUBBO_CONSUMER_PREFIX, "my:dubbo:consumer:");
- resourceName = DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboProviderPrefix());
+ SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "my:dubbo:provider:");
+ SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "my:dubbo:consumer:");
+ resourceName = DubboUtils.getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey());
assertEquals("my:dubbo:provider:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
- resourceName = DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboConsumerPrefix());
+ resourceName = DubboUtils.getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey());
assertEquals("my:dubbo:consumer:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
}
+
+ @Test
+ public void testGetInterfaceName() {
+
+ URL url = URL.valueOf("dubbo://127.0.0.1:2181")
+ .addParameter(CommonConstants.VERSION_KEY, "1.0.0")
+ .addParameter(CommonConstants.GROUP_KEY, "grp1")
+ .addParameter(CommonConstants.INTERFACE_KEY, DemoService.class.getName());
+ Invoker invoker = mock(Invoker.class);
+ when(invoker.getUrl()).thenReturn(url);
+ when(invoker.getInterface()).thenReturn(DemoService.class);
+
+ SentinelConfig.setConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
+ assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService", DubboUtils.getInterfaceName(invoker));
+
+ }
+
+ @Test
+ public void testGetInterfaceNameWithGroupAndVersion() throws NoSuchMethodException {
+
+ URL url = URL.valueOf("dubbo://127.0.0.1:2181")
+ .addParameter(CommonConstants.VERSION_KEY, "1.0.0")
+ .addParameter(CommonConstants.GROUP_KEY, "grp1")
+ .addParameter(CommonConstants.INTERFACE_KEY, DemoService.class.getName());
+ Invoker invoker = mock(Invoker.class);
+ when(invoker.getUrl()).thenReturn(url);
+ when(invoker.getInterface()).thenReturn(DemoService.class);
+
+ SentinelConfig.setConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "true");
+ assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:1.0.0:grp1", DubboUtils.getInterfaceName(invoker, true));
+ }
+
+ @Test
+ public void testGetInterfaceNameWithPrefix() throws NoSuchMethodException {
+ URL url = URL.valueOf("dubbo://127.0.0.1:2181")
+ .addParameter(CommonConstants.VERSION_KEY, "1.0.0")
+ .addParameter(CommonConstants.GROUP_KEY, "grp1")
+ .addParameter(CommonConstants.INTERFACE_KEY, DemoService.class.getName());
+ Invoker invoker = mock(Invoker.class);
+ when(invoker.getUrl()).thenReturn(url);
+ when(invoker.getInterface()).thenReturn(DemoService.class);
+
+
+ //test with default prefix
+ String resourceName = DubboUtils.getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey());
+ assertEquals("dubbo:provider:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService", resourceName);
+ resourceName = DubboUtils.getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey());
+ assertEquals("dubbo:consumer:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService", resourceName);
+
+
+ //test with custom prefix
+ SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "my:dubbo:provider:");
+ SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "my:dubbo:consumer:");
+ resourceName = DubboUtils.getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey());
+ assertEquals("my:dubbo:provider:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService", resourceName);
+ resourceName = DubboUtils.getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey());
+ assertEquals("my:dubbo:consumer:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService", resourceName);
+
+ }
}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java
index ea69ab8bdb..a350a47de1 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java
@@ -16,13 +16,12 @@
package com.alibaba.csp.sentinel.adapter.dubbo;
import com.alibaba.csp.sentinel.BaseTest;
+import com.alibaba.csp.sentinel.DubboTestUtil;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
-import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
-import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DefaultDubboFallback;
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallback;
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
-import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.node.ClusterNode;
@@ -36,125 +35,88 @@
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
-import org.apache.dubbo.rpc.AsyncRpcResult;
-import org.apache.dubbo.rpc.Invocation;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.Result;
-import org.apache.dubbo.rpc.RpcContext;
+
+import org.apache.dubbo.rpc.*;
import org.apache.dubbo.rpc.support.RpcUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-import java.util.Set;
+import java.util.*;
import static com.alibaba.csp.sentinel.slots.block.RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO;
import static org.apache.dubbo.rpc.Constants.ASYNC_KEY;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
/**
* @author cdfive
+ * @author lianglin
*/
public class SentinelDubboConsumerFilterTest extends BaseTest {
- private SentinelDubboConsumerFilter filter = new SentinelDubboConsumerFilter();
-
+ private final SentinelDubboConsumerFilter consumerFilter = new SentinelDubboConsumerFilter();
@Before
public void setUp() {
cleanUpAll();
initFallback();
- constructInvokerAndInvocation();
}
@After
- public void cleanUp() {
+ public void destroy() {
cleanUpAll();
- DubboFallbackRegistry.setConsumerFallback(new DefaultDubboFallback());
- }
-
- public void initFlowRule(String resource) {
- FlowRule flowRule = new FlowRule(resource);
- flowRule.setCount(1);
- flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
- List flowRules = new ArrayList<>();
- flowRules.add(flowRule);
- FlowRuleManager.loadRules(flowRules);
- }
-
- public void initDegradeRule(String resource) {
- DegradeRule degradeRule = new DegradeRule(resource)
- .setCount(0.5)
- .setGrade(DEGRADE_GRADE_EXCEPTION_RATIO);
- List degradeRules = new ArrayList<>();
- degradeRules.add(degradeRule);
- degradeRule.setTimeWindow(1);
- DegradeRuleManager.loadRules(degradeRules);
- }
-
-
- public void initFallback() {
- DubboFallbackRegistry.setConsumerFallback(new DubboFallback() {
- @Override
- public Result handle(Invoker> invoker, Invocation invocation, BlockException ex) {
- boolean async = RpcUtils.isAsync(invoker.getUrl(), invocation);
- Result fallbackResult = null;
- fallbackResult = AsyncRpcResult.newDefaultAsyncResult("fallback", invocation);
- return fallbackResult;
- }
- });
}
@Test
public void testInterfaceLevelFollowControlAsync() throws InterruptedException {
+
+ Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
+ Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
+
when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
- initFlowRule(invoker.getUrl().getColonSeparatedKey());
- Result result1 = responseBack(requestGo(false, invocation));
+ initFlowRule(DubboUtils.getInterfaceName(invoker));
+
+ Result result1 = invokeDubboRpc(false, invoker, invocation);
assertEquals("normal", result1.getValue());
+
// should fallback because the qps > 1
- Result result2 = responseBack(requestGo(false, invocation));
+ Result result2 = invokeDubboRpc(false, invoker, invocation);
assertEquals("fallback", result2.getValue());
+
// sleeping 1000 ms to reset qps
Thread.sleep(1000);
- Result result3 = responseBack(requestGo(false, invocation));
+ Result result3 = invokeDubboRpc(false, invoker, invocation);
assertEquals("normal", result3.getValue());
- verifyInvocationStructureForCallFinish();
+ verifyInvocationStructureForCallFinish(invoker, invocation);
}
@Test
public void testDegradeAsync() throws InterruptedException {
+
+ Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
+ Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
+
when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
+ initDegradeRule(DubboUtils.getInterfaceName(invoker));
- initDegradeRule(invoker.getUrl().getColonSeparatedKey());
- Result result = requestGo(false, invocation);
- verifyInvocationStructureForAsyncCall(invoker, invocation);
- responseBack(result);
+ Result result = invokeDubboRpc(false, invoker, invocation);
+ verifyInvocationStructureForCallFinish(invoker, invocation);
assertEquals("normal", result.getValue());
+
// inc the clusterNode's exception to trigger the fallback
for (int i = 0; i < 5; i++) {
- responseBack(requestGo(true, invocation));
- verifyInvocationStructureForCallFinish();
+ invokeDubboRpc(true, invoker, invocation);
+ verifyInvocationStructureForCallFinish(invoker, invocation);
}
- Result result2 = responseBack(requestGo(false, invocation));
+
+ Result result2 = invokeDubboRpc(false, invoker, invocation);
assertEquals("fallback", result2.getValue());
+
// sleeping 1000 ms to reset exception
Thread.sleep(1000);
-
- Result result3 = responseBack(requestGo(false, invocation));
+ Result result3 = invokeDubboRpc(false, invoker, invocation);
assertEquals("normal", result3.getValue());
Context context = ContextUtil.getContext();
@@ -164,87 +126,69 @@ public void testDegradeAsync() throws InterruptedException {
@Test
public void testDegradeSync() throws InterruptedException {
- initDegradeRule(invoker.getUrl().getColonSeparatedKey());
- Result result = requestGo(false, invocation);
- verifyInvocationStructure(invoker, invocation);
- responseBack(result);
+ Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
+ Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
+ initDegradeRule(DubboUtils.getInterfaceName(invoker));
+
+ Result result = invokeDubboRpc(false, invoker, invocation);
+ verifyInvocationStructureForCallFinish(invoker, invocation);
assertEquals("normal", result.getValue());
+
// inc the clusterNode's exception to trigger the fallback
for (int i = 0; i < 5; i++) {
- responseBack(requestGo(true, invocation));
- verifyInvocationStructureForCallFinish();
+ invokeDubboRpc(true, invoker, invocation);
+ verifyInvocationStructureForCallFinish(invoker, invocation);
}
- Result result2 = responseBack(requestGo(false, invocation));
+
+ Result result2 = invokeDubboRpc(false, invoker, invocation);
assertEquals("fallback", result2.getValue());
+
// sleeping 1000 ms to reset exception
Thread.sleep(1000);
-
- Result result3 = responseBack(requestGo(false, invocation));
+ Result result3 = invokeDubboRpc(false, invoker, invocation);
assertEquals("normal", result3.getValue());
Context context = ContextUtil.getContext();
assertNull(context);
}
-
@Test
public void testMethodFlowControlAsync() {
- when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
- initFlowRule(DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboConsumerPrefix()));
- responseBack(requestGo(false, invocation));
- responseBack(requestGo(false, invocation));
+ Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
+ Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
- Invocation invocation2 = mock(Invocation.class);
- Method method = DemoService.class.getMethods()[1];
- when(invocation2.getMethodName()).thenReturn(method.getName());
- when(invocation2.getParameterTypes()).thenReturn(method.getParameterTypes());
- Result result2 = responseBack(requestGo(false, invocation2));
- verifyInvocationStructureForCallFinish();
+ when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
+ initFlowRule(consumerFilter.getMethodName(invoker, invocation, null));
+ invokeDubboRpc(false, invoker, invocation);
+ invokeDubboRpc(false, invoker, invocation);
+
+ Invocation invocation2 = DubboTestUtil.getDefaultMockInvocationTwo();
+ Result result2 = invokeDubboRpc(false, invoker, invocation2);
+ verifyInvocationStructureForCallFinish(invoker, invocation2);
assertEquals("normal", result2.getValue());
// the method of invocation should be blocked
- Result fallback = requestGo(false, invocation);
- assertNotNull(RpcContext.getContext().get(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY));
- assertNull(RpcContext.getContext().get(DubboUtils.DUBBO_METHOD_ENTRY_KEY));
- responseBack(fallback);
+ Result fallback = invokeDubboRpc(false, invoker, invocation);
assertEquals("fallback", fallback.getValue());
- verifyInvocationStructureForCallFinish();
-
-
- }
-
- public Result requestGo(boolean exception, Invocation currentInvocation) {
- AsyncRpcResult result = null;
-
- if (exception) {
- result = AsyncRpcResult.newDefaultAsyncResult(new Exception("error"), currentInvocation);
- } else {
- result = AsyncRpcResult.newDefaultAsyncResult("normal", currentInvocation);
- }
- when(invoker.invoke(currentInvocation)).thenReturn(result);
- return filter.invoke(invoker, currentInvocation);
- }
+ verifyInvocationStructureForCallFinish(invoker, invocation);
- public Result responseBack(Result result) {
- filter.listener().onMessage(result, invoker, invocation);
- return result;
}
-
@Test
- public void testInvokeAsync() throws InterruptedException {
+ public void testInvokeAsync() {
- when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
+ Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
+ Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
+ when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
final Result result = mock(Result.class);
when(result.hasException()).thenReturn(false);
when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> {
verifyInvocationStructureForAsyncCall(invoker, invocation);
return result;
});
-
- filter.invoke(invoker, invocation);
+ consumerFilter.invoke(invoker, invocation);
verify(invoker).invoke(invocation);
Context context = ContextUtil.getContext();
@@ -254,6 +198,9 @@ public void testInvokeAsync() throws InterruptedException {
@Test
public void testInvokeSync() {
+ Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
+ Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
+
final Result result = mock(Result.class);
when(result.hasException()).thenReturn(false);
when(result.getException()).thenReturn(new Exception());
@@ -262,10 +209,9 @@ public void testInvokeSync() {
return result;
});
- filter.invoke(invoker, invocation);
+ consumerFilter.invoke(invoker, invocation);
verify(invoker).invoke(invocation);
- filter.listener().onMessage(result, invoker, invocation);
Context context = ContextUtil.getContext();
assertNull(context);
}
@@ -280,9 +226,10 @@ private void verifyInvocationStructure(Invoker invoker, Invocation invocation) {
Context context = ContextUtil.getContext();
assertNotNull(context);
// As not call ContextUtil.enter(resourceName, application) in SentinelDubboConsumerFilter, use default context
- // In actual project, a consumer is usually also a provider, the context will be created by SentinelDubboProviderFilter
+ // In actual project, a consumer is usually also a provider, the context will be created by
+ //SentinelDubboProviderFilter
// If consumer is on the top of Dubbo RPC invocation chain, use default context
- String resourceName = DubboUtils.getResourceName(invoker, invocation, true);
+ String resourceName = consumerFilter.getMethodName(invoker, invocation, null);
assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, context.getName());
assertEquals("", context.getOrigin());
@@ -295,10 +242,10 @@ private void verifyInvocationStructure(Invoker invoker, Invocation invocation) {
// As SphU.entry(interfaceName, EntryType.OUT);
Set childList = entranceNode.getChildList();
assertEquals(1, childList.size());
- DefaultNode interfaceNode = getNode(invoker.getUrl().getColonSeparatedKey(), entranceNode);
+ DefaultNode interfaceNode = getNode(DubboUtils.getInterfaceName(invoker), entranceNode);
ResourceWrapper interfaceResource = interfaceNode.getId();
- assertEquals(invoker.getUrl().getColonSeparatedKey(), interfaceResource.getName());
+ assertEquals(DubboUtils.getInterfaceName(invoker), interfaceResource.getName());
assertSame(EntryType.OUT, interfaceResource.getEntryType());
// As SphU.entry(resourceName, EntryType.OUT);
@@ -318,7 +265,8 @@ private void verifyInvocationStructure(Invoker invoker, Invocation invocation) {
// Verify clusterNode
ClusterNode methodClusterNode = methodNode.getClusterNode();
ClusterNode interfaceClusterNode = interfaceNode.getClusterNode();
- assertNotSame(methodClusterNode, interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode
+ assertNotSame(methodClusterNode,
+ interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode
// As context origin is "", the StatisticNode should not be created in originCountMap of ClusterNode
Map methodOriginCountMap = methodClusterNode.getOriginCountMap();
@@ -333,9 +281,10 @@ private void verifyInvocationStructureForAsyncCall(Invoker invoker, Invocation i
assertNotNull(context);
// As not call ContextUtil.enter(resourceName, application) in SentinelDubboConsumerFilter, use default context
- // In actual project, a consumer is usually also a provider, the context will be created by SentinelDubboProviderFilter
+ // In actual project, a consumer is usually also a provider, the context will be created by
+ //SentinelDubboProviderFilter
// If consumer is on the top of Dubbo RPC invocation chain, use default context
- String resourceName = DubboUtils.getResourceName(invoker, invocation, true);
+ String resourceName = consumerFilter.getMethodName(invoker, invocation, null);
assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, context.getName());
assertEquals("", context.getOrigin());
@@ -347,9 +296,9 @@ private void verifyInvocationStructureForAsyncCall(Invoker invoker, Invocation i
// As SphU.entry(interfaceName, EntryType.OUT);
Set childList = entranceNode.getChildList();
assertEquals(2, childList.size());
- DefaultNode interfaceNode = getNode(invoker.getUrl().getColonSeparatedKey(), entranceNode);
+ DefaultNode interfaceNode = getNode(DubboUtils.getInterfaceName(invoker), entranceNode);
ResourceWrapper interfaceResource = interfaceNode.getId();
- assertEquals(invoker.getUrl().getColonSeparatedKey(), interfaceResource.getName());
+ assertEquals(DubboUtils.getInterfaceName(invoker), interfaceResource.getName());
assertSame(EntryType.OUT, interfaceResource.getEntryType());
// As SphU.entry(resourceName, EntryType.OUT);
@@ -368,7 +317,8 @@ private void verifyInvocationStructureForAsyncCall(Invoker invoker, Invocation i
// Verify clusterNode
ClusterNode methodClusterNode = methodNode.getClusterNode();
ClusterNode interfaceClusterNode = interfaceNode.getClusterNode();
- assertNotSame(methodClusterNode, interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode
+ assertNotSame(methodClusterNode,
+ interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode
// As context origin is "", the StatisticNode should not be created in originCountMap of ClusterNode
Map methodOriginCountMap = methodClusterNode.getOriginCountMap();
@@ -378,18 +328,15 @@ private void verifyInvocationStructureForAsyncCall(Invoker invoker, Invocation i
assertEquals(0, interfaceOriginCountMap.size());
}
-
- private void verifyInvocationStructureForCallFinish() {
+ private void verifyInvocationStructureForCallFinish(Invoker invoker, Invocation invocation) {
Context context = ContextUtil.getContext();
assertNull(context);
- Entry interfaceEntry = (Entry) RpcContext.getContext().get(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY);
- Entry methodEntry = (Entry) RpcContext.getContext().get(DubboUtils.DUBBO_METHOD_ENTRY_KEY);
- assertNull(interfaceEntry);
- assertNull(methodEntry);
+ String methodResourceName = consumerFilter.getMethodName(invoker, invocation, null);
+ Entry[] entries = (Entry[]) RpcContext.getContext().get(methodResourceName);
+ assertNull(entries);
}
-
- public DefaultNode getNode(String resourceName, DefaultNode root) {
+ private DefaultNode getNode(String resourceName, DefaultNode root) {
Queue queue = new LinkedList<>();
queue.offer(root);
@@ -405,4 +352,43 @@ public DefaultNode getNode(String resourceName, DefaultNode root) {
return null;
}
+ private void initFlowRule(String resource) {
+ FlowRule flowRule = new FlowRule(resource);
+ flowRule.setCount(1);
+ flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ List flowRules = new ArrayList<>();
+ flowRules.add(flowRule);
+ FlowRuleManager.loadRules(flowRules);
+ }
+
+ private void initDegradeRule(String resource) {
+ DegradeRule degradeRule = new DegradeRule(resource)
+ .setCount(0.5)
+ .setGrade(DEGRADE_GRADE_EXCEPTION_RATIO);
+ List degradeRules = new ArrayList<>();
+ degradeRules.add(degradeRule);
+ degradeRule.setTimeWindow(1);
+ DegradeRuleManager.loadRules(degradeRules);
+ }
+
+ private void initFallback() {
+ DubboAdapterGlobalConfig.setConsumerFallback((invoker, invocation, ex) -> {
+ // boolean async = RpcUtils.isAsync(invoker.getUrl(), invocation);
+ return AsyncRpcResult.newDefaultAsyncResult("fallback", invocation);
+ });
+ }
+
+ private Result invokeDubboRpc(boolean exception, Invoker invoker, Invocation invocation) {
+ Result result = null;
+ InvokeMode invokeMode = RpcUtils.getInvokeMode(invoker.getUrl(), invocation);
+ if (InvokeMode.SYNC == invokeMode) {
+ result = exception ? new AppResponse(new Exception("error")) : new AppResponse("normal");
+ } else {
+ result = exception ? AsyncRpcResult.newDefaultAsyncResult(new Exception("error"), invocation)
+ : AsyncRpcResult.newDefaultAsyncResult("normal", invocation);
+ }
+ when(invoker.invoke(invocation)).thenReturn(result);
+ return consumerFilter.invoke(invoker, invocation);
+ }
+
}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilterTest.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilterTest.java
index ed97800a41..741dae6da6 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilterTest.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilterTest.java
@@ -16,6 +16,7 @@
package com.alibaba.csp.sentinel.adapter.dubbo;
import com.alibaba.csp.sentinel.BaseTest;
+import com.alibaba.csp.sentinel.DubboTestUtil;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
@@ -35,52 +36,49 @@
import org.junit.Before;
import org.junit.Test;
-import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
/**
* @author cdfive
+ * @author lianglin
*/
public class SentinelDubboProviderFilterTest extends BaseTest {
+
private SentinelDubboProviderFilter filter = new SentinelDubboProviderFilter();
+
@Before
public void setUp() {
- constructInvokerAndInvocation();
cleanUpAll();
}
@After
- public void cleanUp() {
+ public void destroy() {
cleanUpAll();
}
@Test
public void testInvoke() {
+
final String originApplication = "consumerA";
- URL url = invoker.getUrl()
- .addParameter(CommonConstants.SIDE_KEY, CommonConstants.PROVIDER_SIDE);
- when(invoker.getUrl()).thenReturn(url);
+ URL url = DubboTestUtil.getDefaultTestURL();
+ url = url.addParameter(CommonConstants.SIDE_KEY, CommonConstants.PROVIDER_SIDE);
+ Invoker invoker = DubboTestUtil.getMockInvoker(url, DemoService.class);
+ Invocation invocation = DubboTestUtil.getMockInvocation(DemoService.class.getMethods()[0]);
when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, ""))
.thenReturn(originApplication);
final Result result = mock(Result.class);
when(result.hasException()).thenReturn(false);
when(result.getException()).thenReturn(new Exception());
+
when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> {
verifyInvocationStructure(originApplication, invoker, invocation);
return result;
@@ -89,29 +87,28 @@ public void testInvoke() {
filter.invoke(invoker, invocation);
verify(invoker).invoke(invocation);
- filter.listener().onMessage(result, invoker, invocation);
Context context = ContextUtil.getContext();
assertNull(context);
}
/**
* Simply verify invocation structure in memory:
- * EntranceNode(resourceName)
+ * EntranceNode(methodResourceName)
* --InterfaceNode(interfaceName)
- * ----MethodNode(resourceName)
+ * ----MethodNode(methodResourceName)
*/
private void verifyInvocationStructure(String originApplication, Invoker invoker, Invocation invocation) {
Context context = ContextUtil.getContext();
assertNotNull(context);
// As ContextUtil.enter(resourceName, application) in SentinelDubboProviderFilter
- String resourceName = DubboUtils.getResourceName(invoker, invocation, true);
- assertEquals(resourceName, context.getName());
+ String methodResourceName = filter.getMethodName(invoker, invocation, null);
+ assertEquals(methodResourceName, context.getName());
assertEquals(originApplication, context.getOrigin());
DefaultNode entranceNode = context.getEntranceNode();
ResourceWrapper entranceResource = entranceNode.getId();
- assertEquals(resourceName, entranceResource.getName());
+ assertEquals(methodResourceName, entranceResource.getName());
assertSame(EntryType.IN, entranceResource.getEntryType());
// As SphU.entry(interfaceName, EntryType.IN);
@@ -120,7 +117,7 @@ private void verifyInvocationStructure(String originApplication, Invoker invoker
DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
ResourceWrapper interfaceResource = interfaceNode.getId();
- assertEquals(invoker.getUrl().getColonSeparatedKey(), interfaceResource.getName());
+ assertEquals(filter.getInterfaceName(invoker, null), interfaceResource.getName());
assertSame(EntryType.IN, interfaceResource.getEntryType());
// As SphU.entry(resourceName, EntryType.IN, 1, invocation.getArguments());
@@ -128,7 +125,7 @@ private void verifyInvocationStructure(String originApplication, Invoker invoker
assertEquals(1, childList.size());
DefaultNode methodNode = (DefaultNode) childList.iterator().next();
ResourceWrapper methodResource = methodNode.getId();
- assertEquals(resourceName, methodResource.getName());
+ assertEquals(methodResourceName, methodResource.getName());
assertSame(EntryType.IN, methodResource.getEntryType());
// Verify curEntry
@@ -151,4 +148,6 @@ private void verifyInvocationStructure(String originApplication, Invoker invoker
assertEquals(1, interfaceOriginCountMap.size());
assertTrue(interfaceOriginCountMap.containsKey(originApplication));
}
+
+
}
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java
index e1f199b729..6c2f427c96 100644
--- a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java
@@ -15,9 +15,10 @@
*/
package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
import com.alibaba.csp.sentinel.slots.block.BlockException;
-import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
+
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Result;
import org.junit.After;
@@ -32,28 +33,31 @@ public class DubboFallbackRegistryTest {
@Before
public void setUp() {
- DubboFallbackRegistry.setConsumerFallback(new DefaultDubboFallback());
+ DubboAdapterGlobalConfig.setConsumerFallback(new DefaultDubboFallback());
}
@After
public void tearDown() {
- DubboFallbackRegistry.setConsumerFallback(new DefaultDubboFallback());
+ DubboAdapterGlobalConfig.setConsumerFallback(new DefaultDubboFallback());
}
- @Test(expected = SentinelRpcException.class)
+ @Test
public void testDefaultFallback() {
- // Test for default.
+ // Test for default fallback.
BlockException ex = new FlowException("xxx");
- DubboFallbackRegistry.getConsumerFallback()
- .handle(null, null, ex);
+ Result result = new DefaultDubboFallback().handle(null, null, ex);
+ Assert.assertTrue("The result should carry exception", result.hasException());
+ Assert.assertTrue(BlockException.isBlockException(result.getException()));
+ Assert.assertTrue(result.getException().getMessage().contains(ex.getClass().getSimpleName()));
}
@Test
public void testCustomFallback() {
BlockException ex = new FlowException("xxx");
- DubboFallbackRegistry.setConsumerFallback(
- (invoker, invocation, e) -> AsyncRpcResult.newDefaultAsyncResult("Error: " + e.getClass().getName(), invocation));
- Result result = DubboFallbackRegistry.getConsumerFallback()
+ DubboAdapterGlobalConfig.setConsumerFallback(
+ (invoker, invocation, e) -> AsyncRpcResult
+ .newDefaultAsyncResult("Error: " + e.getClass().getName(), invocation));
+ Result result = DubboAdapterGlobalConfig.getConsumerFallback()
.handle(null, null, ex);
Assert.assertFalse("The invocation should not fail", result.hasException());
Assert.assertEquals("Error: " + ex.getClass().getName(), result.getValue());
diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DubboOriginRegistryTest.java b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DubboOriginRegistryTest.java
new file mode 100644
index 0000000000..8143a43bc7
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DubboOriginRegistryTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.dubbo.origin;
+
+import com.alibaba.csp.sentinel.adapter.dubbo.DubboUtils;
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author tiecheng
+ */
+public class DubboOriginRegistryTest {
+
+ @After
+ public void cleanUp() {
+ DubboAdapterGlobalConfig.setOriginParser(new DefaultDubboOriginParser());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDefaultOriginParserFail() {
+ DubboAdapterGlobalConfig.getOriginParser().parse(null, null);
+ }
+
+ @Test
+ public void testDefaultOriginParserSuccess() {
+ RpcInvocation invocation = new RpcInvocation();
+ String dubboName = "sentinel";
+ invocation.setAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, dubboName);
+ String origin = DubboAdapterGlobalConfig.getOriginParser().parse(null, invocation);
+ Assert.assertEquals(dubboName, origin);
+ }
+
+ @Test
+ public void testCustomOriginParser() {
+ DubboAdapterGlobalConfig.setOriginParser(new DubboOriginParser() {
+ @Override
+ public String parse(Invoker> invoker, Invocation invocation) {
+ return invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, "default") + "_" + invocation
+ .getMethodName();
+ }
+ });
+
+ RpcInvocation invocation = new RpcInvocation();
+ String origin = DubboAdapterGlobalConfig.getOriginParser().parse(null, invocation);
+ Assert.assertEquals("default_null", origin);
+
+ String dubboName = "sentinel";
+ invocation.setAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, dubboName);
+ origin = DubboAdapterGlobalConfig.getOriginParser().parse(null, invocation);
+ Assert.assertEquals(dubboName + "_null", origin);
+
+ invocation.setMethodName("hello");
+ origin = DubboAdapterGlobalConfig.getOriginParser().parse(null, invocation);
+ Assert.assertEquals(dubboName + "_hello", origin);
+ }
+
+}
diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/README.md b/sentinel-adapter/sentinel-apache-httpclient-adapter/README.md
new file mode 100755
index 0000000000..3ec548d2f6
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/README.md
@@ -0,0 +1,75 @@
+# Sentinel Apache Httpclient Adapter
+
+## Introduction
+
+Sentinel provides integration for OkHttp client to enable flow control for web requests.
+
+Add the following dependency in `pom.xml` (if you are using Maven):
+
+```xml
+
+ com.alibaba.csp
+ sentinel-apache-httpclient-adapter
+ x.y.z
+
+```
+
+We can use the `SentinelApacheHttpClientBuilder` when `CloseableHttpClient` at initialization, for example:
+
+```java
+CloseableHttpClient httpclient = new SentinelApacheHttpClientBuilder().build();
+```
+
+If we want to add some additional configurations, we can refer to the following code
+
+```java
+HttpClientBuilder builder = new SentinelApacheHttpClientBuilder();
+//builder Other Definitions
+CloseableHttpClient httpclient = builder.build();
+```
+
+## Configuration
+
+- `SentinelApacheHttpClientConfig` configuration:
+
+| name | description | type | default value |
+|------|------------|------|-------|
+| prefix | customize resource prefix | `String` | `httpclient:` |
+| extractor | customize resource extractor | `ApacheHttpClientResourceExtractor` | `DefaultApacheHttpClientResourceExtractor` |
+| fallback | handle request when it is blocked | `ApacheHttpClientFallback` | `DefaultApacheHttpClientFallback` |
+
+### extractor (resource extractor)
+
+We can define `ApacheHttpClientResourceExtractor` to customize resource extractor replace `DefaultApacheHttpClientResourceExtractor` at `SentinelApacheHttpClientBuilder` default config, for example: httpclient:GET:/httpclient/back/1 ==> httpclient:GET:/httpclient/back/{id}
+
+```java
+SentinelApacheHttpClientConfig config = new SentinelApacheHttpClientConfig();
+config.setExtractor(new ApacheHttpClientResourceExtractor() {
+
+ @Override
+ public String extractor(HttpRequestWrapper request) {
+ String contains = "/httpclient/back/";
+ String uri = request.getRequestLine().getUri();
+ if (uri.startsWith(contains)) {
+ uri = uri.substring(0, uri.indexOf(contains) + contains.length()) + "{id}";
+ }
+ return request.getMethod() + ":" + uri;
+ }
+});
+CloseableHttpClient httpclient = new SentinelApacheHttpClientBuilder(config).build();
+```
+
+### fallback (Block handling)
+
+We can define `ApacheHttpClientFallback` at `SentinelApacheHttpClientBuilder` default config, to handle request is blocked according to the actual scenario, for example:
+
+```java
+public class DefaultApacheHttpClientFallback implements ApacheHttpClientFallback {
+
+ @Override
+ public CloseableHttpResponse handle(HttpRequestWrapper request, BlockException e) {
+ // Just wrap and throw the exception.
+ throw new SentinelRpcException(e);
+ }
+}
+```
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/pom.xml b/sentinel-adapter/sentinel-apache-httpclient-adapter/pom.xml
new file mode 100644
index 0000000000..ae7864d4d9
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/pom.xml
@@ -0,0 +1,69 @@
+
+
+
+ sentinel-adapter
+ com.alibaba.csp
+ 1.8.1-SNAPSHOT
+
+ 4.0.0
+
+ sentinel-apache-httpclient-adapter
+ jar
+
+
+ 4.5.6
+ 2.1.3.RELEASE
+ 5.1.5.RELEASE
+
+
+
+
+ com.alibaba.csp
+ sentinel-core
+
+
+ org.apache.httpcomponents
+ httpclient
+ ${apache.httpclient.version}
+ provided
+
+
+
+ junit
+ junit
+ test
+
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ com.alibaba
+ fastjson
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring.boot.version}
+ test
+
+
+ org.springframework.boot
+ spring-boot-test
+ ${spring.boot.version}
+ test
+
+
+ org.springframework
+ spring-test
+ ${spring-test.version}
+ test
+
+
+
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/SentinelApacheHttpClientBuilder.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/SentinelApacheHttpClientBuilder.java
new file mode 100644
index 0000000000..4b6aad3789
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/SentinelApacheHttpClientBuilder.java
@@ -0,0 +1,76 @@
+/*
+ * 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
+ *
+ * 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.apache.httpclient;
+
+import com.alibaba.csp.sentinel.*;
+import com.alibaba.csp.sentinel.adapter.apache.httpclient.config.SentinelApacheHttpClientConfig;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.util.StringUtil;
+import org.apache.http.HttpException;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpExecutionAware;
+import org.apache.http.client.methods.HttpRequestWrapper;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.execchain.ClientExecChain;
+
+import java.io.IOException;
+
+/**
+ * @author zhaoyuguang
+ */
+public class SentinelApacheHttpClientBuilder extends HttpClientBuilder {
+
+ private final SentinelApacheHttpClientConfig config;
+
+ public SentinelApacheHttpClientBuilder(){
+ this.config = new SentinelApacheHttpClientConfig();
+ }
+
+ public SentinelApacheHttpClientBuilder(SentinelApacheHttpClientConfig config){
+ this.config = config;
+ }
+
+ @Override
+ protected ClientExecChain decorateMainExec(final ClientExecChain mainExec) {
+ return new ClientExecChain() {
+ @Override
+ public CloseableHttpResponse execute(HttpRoute route, HttpRequestWrapper request,
+ HttpClientContext clientContext, HttpExecutionAware execAware)
+ throws IOException, HttpException {
+ Entry entry = null;
+ try {
+ String name = config.getExtractor().extractor(request);
+ if (!StringUtil.isEmpty(config.getPrefix())) {
+ name = config.getPrefix() + name;
+ }
+ entry = SphU.entry(name, ResourceTypeConstants.COMMON_WEB, EntryType.OUT);
+ return mainExec.execute(route, request, clientContext, execAware);
+ } catch (BlockException e) {
+ return config.getFallback().handle(request, e);
+ } catch (Throwable t) {
+ Tracer.traceEntry(t, entry);
+ throw t;
+ } finally {
+ if (entry != null) {
+ entry.exit();
+ }
+ }
+ }
+ };
+ }
+}
diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/config/SentinelApacheHttpClientConfig.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/config/SentinelApacheHttpClientConfig.java
new file mode 100644
index 0000000000..9dbc1520ff
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/config/SentinelApacheHttpClientConfig.java
@@ -0,0 +1,59 @@
+/*
+ * 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
+ *
+ * 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.apache.httpclient.config;
+
+import com.alibaba.csp.sentinel.adapter.apache.httpclient.extractor.ApacheHttpClientResourceExtractor;
+import com.alibaba.csp.sentinel.adapter.apache.httpclient.extractor.DefaultApacheHttpClientResourceExtractor;
+import com.alibaba.csp.sentinel.adapter.apache.httpclient.fallback.ApacheHttpClientFallback;
+import com.alibaba.csp.sentinel.adapter.apache.httpclient.fallback.DefaultApacheHttpClientFallback;
+import com.alibaba.csp.sentinel.util.AssertUtil;
+
+/**
+ * @author zhaoyuguang
+ */
+public class SentinelApacheHttpClientConfig {
+
+ private String prefix = "httpclient:";
+ private ApacheHttpClientResourceExtractor extractor = new DefaultApacheHttpClientResourceExtractor();
+ private ApacheHttpClientFallback fallback = new DefaultApacheHttpClientFallback();
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public void setPrefix(String prefix) {
+ AssertUtil.notNull(prefix, "prefix cannot be null");
+ this.prefix = prefix;
+ }
+
+ public ApacheHttpClientResourceExtractor getExtractor() {
+ return extractor;
+ }
+
+ public void setExtractor(ApacheHttpClientResourceExtractor extractor) {
+ AssertUtil.notNull(extractor, "extractor cannot be null");
+ this.extractor = extractor;
+ }
+
+ public ApacheHttpClientFallback getFallback() {
+ return fallback;
+ }
+
+ public void setFallback(ApacheHttpClientFallback fallback) {
+ AssertUtil.notNull(fallback, "fallback cannot be null");
+ this.fallback = fallback;
+ }
+}
diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/extractor/ApacheHttpClientResourceExtractor.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/extractor/ApacheHttpClientResourceExtractor.java
new file mode 100644
index 0000000000..ab494d67d3
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/extractor/ApacheHttpClientResourceExtractor.java
@@ -0,0 +1,26 @@
+/*
+ * 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
+ *
+ * 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.apache.httpclient.extractor;
+
+import org.apache.http.client.methods.HttpRequestWrapper;
+
+/**
+ * @author zhaoyuguang
+ */
+public interface ApacheHttpClientResourceExtractor {
+
+ String extractor(HttpRequestWrapper request);
+}
diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/extractor/DefaultApacheHttpClientResourceExtractor.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/extractor/DefaultApacheHttpClientResourceExtractor.java
new file mode 100644
index 0000000000..02ccf697a8
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/extractor/DefaultApacheHttpClientResourceExtractor.java
@@ -0,0 +1,29 @@
+/*
+ * 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
+ *
+ * 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.apache.httpclient.extractor;
+
+import org.apache.http.client.methods.HttpRequestWrapper;
+
+/**
+ * @author zhaoyuguang
+ */
+public class DefaultApacheHttpClientResourceExtractor implements ApacheHttpClientResourceExtractor {
+
+ @Override
+ public String extractor(HttpRequestWrapper request) {
+ return request.getRequestLine().getUri();
+ }
+}
diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/ApacheHttpClientFallback.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/ApacheHttpClientFallback.java
new file mode 100644
index 0000000000..f670eca8a5
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/ApacheHttpClientFallback.java
@@ -0,0 +1,33 @@
+/*
+ * 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
+ *
+ * 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.apache.httpclient.fallback;
+
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpRequestWrapper;
+import org.apache.http.protocol.HttpContext;
+
+import java.io.IOException;
+
+/**
+ * @author zhaoyuguang
+ */
+public interface ApacheHttpClientFallback {
+
+ CloseableHttpResponse handle(HttpRequestWrapper request, BlockException e);
+}
diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/DefaultApacheHttpClientFallback.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/DefaultApacheHttpClientFallback.java
new file mode 100644
index 0000000000..81994bfc00
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/DefaultApacheHttpClientFallback.java
@@ -0,0 +1,38 @@
+/*
+ * 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
+ *
+ * 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.apache.httpclient.fallback;
+
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpRequestWrapper;
+import org.apache.http.protocol.HttpContext;
+
+import java.io.IOException;
+
+/**
+ * @author zhaoyuguang
+ */
+public class DefaultApacheHttpClientFallback implements ApacheHttpClientFallback {
+
+ @Override
+ public CloseableHttpResponse handle(HttpRequestWrapper request, BlockException e) {
+ // Just wrap and throw the exception.
+ throw new SentinelRpcException(e);
+ }
+}
diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/SentinelApacheHttpClientTest.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/SentinelApacheHttpClientTest.java
new file mode 100644
index 0000000000..882edafe0c
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/SentinelApacheHttpClientTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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
+ *
+ * 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.apache.httpclient;
+
+import com.alibaba.csp.sentinel.Constants;
+import com.alibaba.csp.sentinel.adapter.apache.httpclient.app.TestApplication;
+import com.alibaba.csp.sentinel.adapter.apache.httpclient.config.SentinelApacheHttpClientConfig;
+import com.alibaba.csp.sentinel.adapter.apache.httpclient.extractor.ApacheHttpClientResourceExtractor;
+import com.alibaba.csp.sentinel.node.ClusterNode;
+import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpRequestWrapper;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.EntityUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.io.IOException;
+
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * @author zhaoyuguang
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = TestApplication.class,
+ webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
+ properties = {
+ "server.port=8084"
+ })
+public class SentinelApacheHttpClientTest {
+
+ @Value("${server.port}")
+ private Integer port;
+
+ @Test
+ public void testSentinelOkHttpInterceptor0() throws Exception {
+
+ CloseableHttpClient httpclient = new SentinelApacheHttpClientBuilder().build();
+
+ HttpGet httpGet = new HttpGet("http://localhost:" + port + "/httpclient/back");
+ System.out.println(getRemoteString(httpclient, httpGet));
+ ClusterNode cn = ClusterBuilderSlot.getClusterNode("httpclient:/httpclient/back");
+ assertNotNull(cn);
+ Constants.ROOT.removeChildList();
+ ClusterBuilderSlot.getClusterNodeMap().clear();
+ }
+
+ @Test
+ public void testSentinelOkHttpInterceptor1() throws Exception {
+ SentinelApacheHttpClientConfig config = new SentinelApacheHttpClientConfig();
+ config.setExtractor(new ApacheHttpClientResourceExtractor() {
+
+ @Override
+ public String extractor(HttpRequestWrapper request) {
+ String contains = "/httpclient/back/";
+ String uri = request.getRequestLine().getUri();
+ if (uri.startsWith(contains)) {
+ uri = uri.substring(0, uri.indexOf(contains) + contains.length()) + "{id}";
+ }
+ return request.getMethod() + ":" + uri;
+ }
+ });
+ CloseableHttpClient httpclient = new SentinelApacheHttpClientBuilder(config).build();
+
+ HttpGet httpGet = new HttpGet("http://localhost:" + port + "/httpclient/back/1");
+ System.out.println(getRemoteString(httpclient, httpGet));
+ ClusterNode cn = ClusterBuilderSlot.getClusterNode("httpclient:GET:/httpclient/back/{id}");
+ assertNotNull(cn);
+ Constants.ROOT.removeChildList();
+ ClusterBuilderSlot.getClusterNodeMap().clear();
+ }
+
+ private String getRemoteString(CloseableHttpClient httpclient, HttpGet httpGet) throws IOException {
+ String result;
+ HttpContext context = new BasicHttpContext();
+ CloseableHttpResponse response;
+ response = httpclient.execute(httpGet, context);
+ try {
+ HttpEntity entity = response.getEntity();
+ result = EntityUtils.toString(entity, "utf-8");
+ EntityUtils.consume(entity);
+ } finally {
+ response.close();
+ }
+ httpclient.close();
+ return result;
+ }
+}
diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/app/TestApplication.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/app/TestApplication.java
new file mode 100644
index 0000000000..e073dda5ed
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/app/TestApplication.java
@@ -0,0 +1,30 @@
+/*
+ * 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
+ *
+ * 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.apache.httpclient.app;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * @author zhaoyuguang
+ */
+@SpringBootApplication
+public class TestApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(TestApplication.class);
+ }
+}
diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/app/controller/TestController.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/app/controller/TestController.java
new file mode 100644
index 0000000000..2ae4275f05
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/app/controller/TestController.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ * 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.apache.httpclient.app.controller;
+
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author zhaoyuguang
+ */
+@RestController
+public class TestController {
+
+ @RequestMapping("/httpclient/back")
+ public String back() {
+ return "Welcome Back!";
+ }
+
+ @RequestMapping("/httpclient/back/{id}")
+ public String back(@PathVariable String id) {
+ return "Welcome Back! " + id;
+ }
+}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/config/SentinelApacheHttpClientConfigTest.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/config/SentinelApacheHttpClientConfigTest.java
new file mode 100644
index 0000000000..414322b79f
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/config/SentinelApacheHttpClientConfigTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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
+ *
+ * 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.apache.httpclient.config;
+
+import org.junit.Test;
+
+/**
+ * @author zhaoyuguang
+ */
+public class SentinelApacheHttpClientConfigTest {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConfigSetPrefix() {
+ SentinelApacheHttpClientConfig config = new SentinelApacheHttpClientConfig();
+ config.setPrefix(null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConfigSetCleaner() {
+ SentinelApacheHttpClientConfig config = new SentinelApacheHttpClientConfig();
+ config.setExtractor(null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConfigSetFallback() {
+ SentinelApacheHttpClientConfig config = new SentinelApacheHttpClientConfig();
+ config.setFallback(null);
+ }
+}
diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/ApacheHttpClientFallbackTest.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/ApacheHttpClientFallbackTest.java
new file mode 100644
index 0000000000..56865918c2
--- /dev/null
+++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/ApacheHttpClientFallbackTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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
+ *
+ * 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.apache.httpclient.fallback;
+
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
+import org.junit.Test;
+
+/**
+ * @author zhaoyuguang
+ */
+public class ApacheHttpClientFallbackTest {
+
+ @Test(expected = SentinelRpcException.class)
+ public void testDefaultOkHttpFallback() {
+ BlockException e = new FlowException("xxx");
+ ApacheHttpClientFallback fallback = new DefaultApacheHttpClientFallback();
+ fallback.handle(null, e);
+ }
+}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/pom.xml b/sentinel-adapter/sentinel-api-gateway-adapter-common/pom.xml
index 6b2d57dcd3..19b37b91e6 100644
--- a/sentinel-adapter/sentinel-api-gateway-adapter-common/pom.xml
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/pom.xml
@@ -5,7 +5,7 @@
sentinel-adaptercom.alibaba.csp
- 1.7.2-SNAPSHOT
+ 1.8.1-SNAPSHOT4.0.0
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManager.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManager.java
index 3985e7d970..8f24803c69 100644
--- a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManager.java
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManager.java
@@ -62,8 +62,8 @@ private static void initializeApiChangeObserverSpi() {
List listeners = SpiLoader.loadInstanceList(ApiDefinitionChangeObserver.class);
for (ApiDefinitionChangeObserver e : listeners) {
API_CHANGE_OBSERVERS.put(e.getClass().getCanonicalName(), e);
- RecordLog.info("[GatewayApiDefinitionManager] ApiDefinitionChangeObserver added: {0}",
- e.getClass().getCanonicalName());
+ RecordLog.info("[GatewayApiDefinitionManager] ApiDefinitionChangeObserver added: {}"
+ , e.getClass().getCanonicalName());
}
}
@@ -103,13 +103,13 @@ private static final class ApiDefinitionPropertyListener implements PropertyList
@Override
public void configUpdate(Set set) {
applyApiUpdateInternal(set);
- RecordLog.info("[GatewayApiDefinitionManager] Api definition updated: " + API_MAP);
+ RecordLog.info("[GatewayApiDefinitionManager] Api definition updated: {}", API_MAP);
}
@Override
public void configLoad(Set set) {
applyApiUpdateInternal(set);
- RecordLog.info("[GatewayApiDefinitionManager] Api definition loaded: " + API_MAP);
+ RecordLog.info("[GatewayApiDefinitionManager] Api definition loaded: {}", API_MAP);
}
private static synchronized void applyApiUpdateInternal(Set set) {
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayApiDefinitionGroupCommandHandler.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayApiDefinitionGroupCommandHandler.java
index d480512c28..96b8a454ce 100644
--- a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayApiDefinitionGroupCommandHandler.java
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayApiDefinitionGroupCommandHandler.java
@@ -56,7 +56,7 @@ public CommandResponse handle(CommandRequest request) {
return CommandResponse.ofFailure(e, "decode gateway API definition data error");
}
- RecordLog.info("[API Server] Receiving data change (type: gateway API definition): {0}", data);
+ RecordLog.info("[API Server] Receiving data change (type: gateway API definition): {}", data);
String result = SUCCESS_MSG;
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayRuleCommandHandler.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayRuleCommandHandler.java
index b79b78f9cf..a5e6d0f830 100644
--- a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayRuleCommandHandler.java
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayRuleCommandHandler.java
@@ -51,7 +51,7 @@ public CommandResponse handle(CommandRequest request) {
return CommandResponse.ofFailure(e, "decode gateway rule data error");
}
- RecordLog.info(String.format("[API Server] Receiving rule change (type: gateway rule): %s", data));
+ RecordLog.info("[API Server] Receiving rule change (type: gateway rule): {}", data);
String result = SUCCESS_MSG;
Set flowRules = JSON.parseObject(data, new TypeReference>() {
@@ -93,4 +93,4 @@ public synchronized static void setWritableDataSource(WritableDataSource> CONVERTED_PARAM_RULE_MAP = new ConcurrentHashMap<>();
private static final GatewayRulePropertyListener LISTENER = new GatewayRulePropertyListener();
+ private static final Set FIELD_REQUIRED_SET = new HashSet<>(
+ Arrays.asList(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM,
+ SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER,
+ SentinelGatewayConstants.PARAM_PARSE_STRATEGY_COOKIE)
+ );
private static SentinelProperty> currentProperty = new DynamicSentinelProperty<>();
static {
currentProperty.addListener(LISTENER);
}
+ private GatewayRuleManager() {
+ }
+
public static void register2Property(SentinelProperty> property) {
AssertUtil.notNull(property, "property cannot be null");
synchronized (LISTENER) {
@@ -111,18 +114,48 @@ public static List getConvertedParamRules(String resourceName) {
return CONVERTED_PARAM_RULE_MAP.get(resourceName);
}
+ public static boolean isValidRule(GatewayFlowRule rule) {
+ if (rule == null || StringUtil.isBlank(rule.getResource()) || rule.getResourceMode() < 0
+ || rule.getGrade() < 0 || rule.getCount() < 0 || rule.getBurst() < 0 || rule.getControlBehavior() < 0) {
+ return false;
+ }
+ if (rule.getGrade() == RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER
+ && rule.getMaxQueueingTimeoutMs() < 0) {
+ return false;
+ }
+ if (rule.getIntervalSec() <= 0) {
+ return false;
+ }
+ GatewayParamFlowItem item = rule.getParamItem();
+ if (item != null) {
+ return isValidParamItem(item);
+ }
+ return true;
+ }
+
+ static boolean isValidParamItem(/*@NonNull*/ GatewayParamFlowItem item) {
+ if (item.getParseStrategy() < 0) {
+ return false;
+ }
+ // Check required field name for item types.
+ if (FIELD_REQUIRED_SET.contains(item.getParseStrategy()) && StringUtil.isBlank(item.getFieldName())) {
+ return false;
+ }
+ return StringUtil.isEmpty(item.getPattern()) || item.getMatchStrategy() >= 0;
+ }
+
private static final class GatewayRulePropertyListener implements PropertyListener> {
@Override
public void configUpdate(Set conf) {
applyGatewayRuleInternal(conf);
- RecordLog.info("[GatewayRuleManager] Gateway flow rules received: " + GATEWAY_RULE_MAP);
+ RecordLog.info("[GatewayRuleManager] Gateway flow rules received: {}", GATEWAY_RULE_MAP);
}
@Override
public void configLoad(Set conf) {
applyGatewayRuleInternal(conf);
- RecordLog.info("[GatewayRuleManager] Gateway flow rules loaded: " + GATEWAY_RULE_MAP);
+ RecordLog.info("[GatewayRuleManager] Gateway flow rules loaded: {}", GATEWAY_RULE_MAP);
}
private int getIdxInternal(Map idxMap, String resourceName) {
@@ -136,7 +169,7 @@ private int getIdxInternal(Map idxMap, String resourceName) {
private void cacheRegexPattern(/*@NonNull*/ GatewayParamFlowItem item) {
String pattern = item.getPattern();
if (StringUtil.isNotEmpty(pattern) &&
- item.getMatchStrategy() == SentinelGatewayConstants.PARAM_MATCH_STRATEGY_REGEX) {
+ item.getMatchStrategy() == SentinelGatewayConstants.PARAM_MATCH_STRATEGY_REGEX) {
if (GatewayRegexCache.getRegexPattern(pattern) == null) {
GatewayRegexCache.addRegexPattern(pattern);
}
@@ -205,7 +238,7 @@ private synchronized void applyGatewayRuleInternal(Set conf) {
private void applyToConvertedParamMap(Set paramFlowRules) {
Map> newRuleMap = ParamFlowRuleUtil.buildParamRuleMap(
- new ArrayList<>(paramFlowRules));
+ new ArrayList<>(paramFlowRules));
if (newRuleMap == null || newRuleMap.isEmpty()) {
// No parameter flow rules, so clear all the metrics.
for (String resource : CONVERTED_PARAM_RULE_MAP.keySet()) {
@@ -217,10 +250,20 @@ private void applyToConvertedParamMap(Set paramFlowRules) {
}
// Clear unused parameter metrics.
- Set previousResources = CONVERTED_PARAM_RULE_MAP.keySet();
- for (String resource : previousResources) {
+ for (Map.Entry> entry : CONVERTED_PARAM_RULE_MAP.entrySet()) {
+ String resource = entry.getKey();
if (!newRuleMap.containsKey(resource)) {
ParameterMetricStorage.clearParamMetricForResource(resource);
+ continue;
+ }
+ List newRuleList = newRuleMap.get(resource);
+ List oldRuleList = new ArrayList<>(entry.getValue());
+ oldRuleList.removeAll(newRuleList);
+ for (ParamFlowRule rule : oldRuleList) {
+ ParameterMetric metric = ParameterMetricStorage.getParamMetricForResource(resource);
+ if (null != metric) {
+ metric.clearForRule(rule);
+ }
}
}
@@ -228,45 +271,7 @@ private void applyToConvertedParamMap(Set paramFlowRules) {
CONVERTED_PARAM_RULE_MAP.clear();
CONVERTED_PARAM_RULE_MAP.putAll(newRuleMap);
- RecordLog.info("[GatewayRuleManager] Converted internal param rules: " + CONVERTED_PARAM_RULE_MAP);
+ RecordLog.info("[GatewayRuleManager] Converted internal param rules: {}", CONVERTED_PARAM_RULE_MAP);
}
}
-
- public static boolean isValidRule(GatewayFlowRule rule) {
- if (rule == null || StringUtil.isBlank(rule.getResource()) || rule.getResourceMode() < 0
- || rule.getGrade() < 0 || rule.getCount() < 0 || rule.getBurst() < 0 || rule.getControlBehavior() < 0) {
- return false;
- }
- if (rule.getGrade() == RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER
- && rule.getMaxQueueingTimeoutMs() < 0) {
- return false;
- }
- if (rule.getIntervalSec() <= 0) {
- return false;
- }
- GatewayParamFlowItem item = rule.getParamItem();
- if (item != null) {
- return isValidParamItem(item);
- }
- return true;
- }
-
- static boolean isValidParamItem(/*@NonNull*/ GatewayParamFlowItem item) {
- if (item.getParseStrategy() < 0) {
- return false;
- }
- // Check required field name for item types.
- if (FIELD_REQUIRED_SET.contains(item.getParseStrategy()) && StringUtil.isBlank(item.getFieldName())) {
- return false;
- }
- return StringUtil.isEmpty(item.getPattern()) || item.getMatchStrategy() >= 0;
- }
-
- private static final Set FIELD_REQUIRED_SET = new HashSet<>(
- Arrays.asList(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM,
- SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER,
- SentinelGatewayConstants.PARAM_PARSE_STRATEGY_COOKIE)
- );
-
- private GatewayRuleManager() {}
}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewayFlowSlot.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewayFlowSlot.java
index 15151729d6..b0932b7c99 100644
--- a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewayFlowSlot.java
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewayFlowSlot.java
@@ -27,11 +27,13 @@
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetricStorage;
+import com.alibaba.csp.sentinel.spi.SpiOrder;
/**
* @author Eric Zhao
* @since 1.6.1
*/
+@SpiOrder(-4000)
public class GatewayFlowSlot extends AbstractLinkedProcessorSlot {
@Override
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewaySlotChainBuilder.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewaySlotChainBuilder.java
index 383e465326..204b46198f 100644
--- a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewaySlotChainBuilder.java
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewaySlotChainBuilder.java
@@ -15,43 +15,18 @@
*/
package com.alibaba.csp.sentinel.adapter.gateway.common.slot;
-import com.alibaba.csp.sentinel.slotchain.DefaultProcessorSlotChain;
-import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain;
-import com.alibaba.csp.sentinel.slotchain.SlotChainBuilder;
-import com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot;
-import com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot;
-import com.alibaba.csp.sentinel.slots.block.flow.FlowSlot;
-import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowSlot;
-import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
-import com.alibaba.csp.sentinel.slots.logger.LogSlot;
-import com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot;
-import com.alibaba.csp.sentinel.slots.statistic.StatisticSlot;
-import com.alibaba.csp.sentinel.slots.system.SystemSlot;
+import com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder;
/**
* @author Eric Zhao
* @since 1.6.1
+ *
+ * @deprecated since 1.7.2, we can use @SpiOrder(-4000) to adjust the order of {@link GatewayFlowSlot},
+ * this class is reserved for compatibility with older versions.
+ * @see GatewayFlowSlot
+ * @see DefaultSlotChainBuilder
*/
-public class GatewaySlotChainBuilder implements SlotChainBuilder {
-
- @Override
- public ProcessorSlotChain build() {
- ProcessorSlotChain chain = new DefaultProcessorSlotChain();
- // Prepare slot
- chain.addLast(new NodeSelectorSlot());
- chain.addLast(new ClusterBuilderSlot());
- // Stat slot
- chain.addLast(new LogSlot());
- chain.addLast(new StatisticSlot());
- // Rule checking slot
- chain.addLast(new AuthoritySlot());
- chain.addLast(new SystemSlot());
- chain.addLast(new GatewayFlowSlot());
-
- chain.addLast(new ParamFlowSlot());
- chain.addLast(new FlowSlot());
- chain.addLast(new DegradeSlot());
+@Deprecated
+public class GatewaySlotChainBuilder extends DefaultSlotChainBuilder {
- return chain;
- }
}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.slotchain.ProcessorSlot b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.slotchain.ProcessorSlot
new file mode 100644
index 0000000000..190da2e814
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.slotchain.ProcessorSlot
@@ -0,0 +1 @@
+com.alibaba.csp.sentinel.adapter.gateway.common.slot.GatewayFlowSlot
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.slotchain.SlotChainBuilder b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.slotchain.SlotChainBuilder
deleted file mode 100644
index 5f9fde567e..0000000000
--- a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.slotchain.SlotChainBuilder
+++ /dev/null
@@ -1 +0,0 @@
-com.alibaba.csp.sentinel.adapter.gateway.common.slot.GatewaySlotChainBuilder
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/README.md b/sentinel-adapter/sentinel-dubbo-adapter/README.md
index cbeb0e2e98..29dc61b79a 100755
--- a/sentinel-adapter/sentinel-dubbo-adapter/README.md
+++ b/sentinel-adapter/sentinel-dubbo-adapter/README.md
@@ -1,6 +1,6 @@
# Sentinel Dubbo Adapter
-> Note: 中文文档请见[此处](https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E6%B5%81%E6%A1%86%E6%9E%B6%E7%9A%84%E9%80%82%E9%85%8D#dubbo)。
+> Note: 中文文档请见[此处](https://github.com/alibaba/Sentinel/wiki/主流框架的适配#dubbo)。
Sentinel Dubbo Adapter provides service consumer filter and provider filter
for [Dubbo](https://dubbo.apache.org/en-us/) services.
@@ -52,17 +52,21 @@ If `limitApp` of flow rules is not configured (`default`), flow control will tak
If `limitApp` of a flow rule is configured with a caller, then the corresponding flow rule will only take effect on the specific caller.
> Note: Dubbo consumer does not provide its Dubbo application name when doing RPC,
-so developers should manually put the application name into *attachment* at consumer side,
-then extract it at provider side. Sentinel Dubbo Adapter has implemented a filter (`DubboAppContextFilter`)
-where consumer can carry application name information to provider automatically.
-If the consumer does not use Sentinel Dubbo Adapter but requires flow control based on caller, developers can manually put the application name into attachment with the key `dubboApplication`.
+> so developers should manually put the application name into *attachment* at consumer side,
+> then extract it at provider side. Sentinel Dubbo Adapter has implemented a filter (`DubboAppContextFilter`)
+> where consumer can carry application name information to provider automatically.
+> If the consumer does not use Sentinel Dubbo Adapter but requires flow control based on caller,
+> developers can manually put the application name into attachment with the key `dubboApplication`.
+>
+> Since 1.8.0, the adapter provides support for customizing origin parsing logic. You may register your own `DubboOriginParser`
+> implementation to `DubboAdapterGlobalConfig`.
## Global fallback
Sentinel Dubbo 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 `DubboFallback` interface
-and then register to `DubboFallbackRegistry`. If no fallback is configured, Sentinel will wrap the `BlockException`
-then directly throw it out.
+and then register to `DubboAdapterGlobalConfig`.
+If no fallback is configured, Sentinel will wrap the `BlockException` as the fallback result.
Besides, we can also leverage [Dubbo mock mechanism](http://dubbo.apache.org/en-us/docs/user/demos/local-mock.html) to provide fallback implementation of degraded Dubbo services.
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/pom.xml b/sentinel-adapter/sentinel-dubbo-adapter/pom.xml
index 732c8c6caa..eba2c16ad9 100755
--- a/sentinel-adapter/sentinel-dubbo-adapter/pom.xml
+++ b/sentinel-adapter/sentinel-dubbo-adapter/pom.xml
@@ -6,7 +6,7 @@
com.alibaba.cspsentinel-adapter
- 1.7.2-SNAPSHOT
+ 1.8.1-SNAPSHOT4.0.0sentinel-dubbo-adapter
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/AbstractDubboFilter.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/AbstractDubboFilter.java
index aa8248a524..8939e4a39e 100755
--- a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/AbstractDubboFilter.java
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/AbstractDubboFilter.java
@@ -25,12 +25,12 @@
*/
abstract class AbstractDubboFilter implements Filter {
- protected String getResourceName(Invoker> invoker, Invocation invocation) {
+ protected String getMethodResourceName(Invoker> invoker, Invocation invocation) {
StringBuilder buf = new StringBuilder(64);
buf.append(invoker.getInterface().getName())
- .append(":")
- .append(invocation.getMethodName())
- .append("(");
+ .append(":")
+ .append(invocation.getMethodName())
+ .append("(");
boolean isFirst = true;
for (Class> clazz : invocation.getParameterTypes()) {
if (!isFirst) {
@@ -43,15 +43,24 @@ protected String getResourceName(Invoker> invoker, Invocation invocation) {
return buf.toString();
}
- protected String getResourceName(Invoker> invoker, Invocation invocation, String prefix) {
+ protected String getMethodResourceName(Invoker> invoker, Invocation invocation, String prefix) {
if (StringUtil.isBlank(prefix)) {
- return getResourceName(invoker, invocation);
+ return getMethodResourceName(invoker, invocation);
}
StringBuilder buf = new StringBuilder(64);
return buf.append(prefix)
- .append(getResourceName(invoker, invocation))
- .toString();
+ .append(getMethodResourceName(invoker, invocation))
+ .toString();
+ }
+ protected String getInterfaceName(Invoker> invoker) {
+ return invoker.getInterface().getName();
+ }
+ protected String getInterfaceName(Invoker> invoker, String prefix) {
+ if (StringUtil.isBlank(prefix)) {
+ return getInterfaceName(invoker);
+ }
+ return prefix + getInterfaceName(invoker);
}
}
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAdapterGlobalConfig.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAdapterGlobalConfig.java
new file mode 100644
index 0000000000..e7ec688bca
--- /dev/null
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAdapterGlobalConfig.java
@@ -0,0 +1,108 @@
+/*
+ * 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.dubbo;
+
+import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DefaultDubboFallback;
+import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallback;
+import com.alibaba.csp.sentinel.adapter.dubbo.origin.DefaultDubboOriginParser;
+import com.alibaba.csp.sentinel.adapter.dubbo.origin.DubboOriginParser;
+import com.alibaba.csp.sentinel.config.SentinelConfig;
+import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.util.StringUtil;
+
+/**
+ *
Global config and callback registry of Dubbo legacy adapter.
+ *
+ * @author lianglin
+ * @author Eric Zhao
+ * @since 1.7.0
+ */
+public final class DubboAdapterGlobalConfig {
+
+ private static final String TRUE_STR = "true";
+
+ public static final String DUBBO_RES_NAME_WITH_PREFIX_KEY = "csp.sentinel.dubbo.resource.use.prefix";
+ public static final String DUBBO_PROVIDER_RES_NAME_PREFIX_KEY = "csp.sentinel.dubbo.resource.provider.prefix";
+ public static final String DUBBO_CONSUMER_RES_NAME_PREFIX_KEY = "csp.sentinel.dubbo.resource.consumer.prefix";
+
+ private static final String DEFAULT_DUBBO_PROVIDER_PREFIX = "dubbo:provider:";
+ private static final String DEFAULT_DUBBO_CONSUMER_PREFIX = "dubbo:consumer:";
+
+ private static volatile DubboFallback consumerFallback = new DefaultDubboFallback();
+ private static volatile DubboFallback providerFallback = new DefaultDubboFallback();
+ private static volatile DubboOriginParser originParser = new DefaultDubboOriginParser();
+
+ public static boolean isUsePrefix() {
+ return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_RES_NAME_WITH_PREFIX_KEY));
+ }
+
+ public static String getDubboProviderPrefix() {
+ if (isUsePrefix()) {
+ String config = SentinelConfig.getConfig(DUBBO_PROVIDER_RES_NAME_PREFIX_KEY);
+ return StringUtil.isNotBlank(config) ? config : DEFAULT_DUBBO_PROVIDER_PREFIX;
+ }
+ return null;
+ }
+
+ public static String getDubboConsumerPrefix() {
+ if (isUsePrefix()) {
+ String config = SentinelConfig.getConfig(DUBBO_CONSUMER_RES_NAME_PREFIX_KEY);
+ return StringUtil.isNotBlank(config) ? config : DEFAULT_DUBBO_CONSUMER_PREFIX;
+ }
+ return null;
+ }
+
+ public static DubboFallback getConsumerFallback() {
+ return consumerFallback;
+ }
+
+ public static void setConsumerFallback(DubboFallback consumerFallback) {
+ AssertUtil.notNull(consumerFallback, "consumerFallback cannot be null");
+ DubboAdapterGlobalConfig.consumerFallback = consumerFallback;
+ }
+
+ public static DubboFallback getProviderFallback() {
+ return providerFallback;
+ }
+
+ public static void setProviderFallback(DubboFallback providerFallback) {
+ AssertUtil.notNull(providerFallback, "providerFallback cannot be null");
+ DubboAdapterGlobalConfig.providerFallback = providerFallback;
+ }
+
+ /**
+ * Get the origin parser of Dubbo adapter.
+ *
+ * @return the origin parser
+ * @since 1.8.0
+ */
+ public static DubboOriginParser getOriginParser() {
+ return originParser;
+ }
+
+ /**
+ * Set the origin parser of Dubbo adapter.
+ *
+ * @param originParser the origin parser
+ * @since 1.8.0
+ */
+ public static void setOriginParser(DubboOriginParser originParser) {
+ AssertUtil.notNull(originParser, "originParser cannot be null");
+ DubboAdapterGlobalConfig.originParser = originParser;
+ }
+
+ private DubboAdapterGlobalConfig() {}
+}
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java
index e7c36af92e..72dfd281e4 100755
--- a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java
@@ -20,8 +20,6 @@
import com.alibaba.csp.sentinel.ResourceTypeConstants;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
-import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
-import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.dubbo.common.extension.Activate;
@@ -56,10 +54,12 @@ public Result invoke(Invoker> invoker, Invocation invocation) throws RpcExcept
Entry interfaceEntry = null;
Entry methodEntry = null;
try {
- String resourceName = getResourceName(invoker, invocation, DubboConfig.getDubboConsumerPrefix());
- interfaceEntry = SphU.entry(invoker.getInterface().getName(), ResourceTypeConstants.COMMON_RPC,
- EntryType.OUT);
- methodEntry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
+ String prefix = DubboAdapterGlobalConfig.getDubboConsumerPrefix();
+ String interfaceResourceName = getInterfaceName(invoker, prefix);
+ String methodResourceName = getMethodResourceName(invoker, invocation, prefix);
+ interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
+ methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC,
+ EntryType.OUT, invocation.getArguments());
Result result = invoker.invoke(invocation);
if (result.hasException()) {
@@ -70,14 +70,14 @@ public Result invoke(Invoker> invoker, Invocation invocation) throws RpcExcept
}
return result;
} catch (BlockException e) {
- return DubboFallbackRegistry.getConsumerFallback().handle(invoker, invocation, e);
+ return DubboAdapterGlobalConfig.getConsumerFallback().handle(invoker, invocation, e);
} catch (RpcException e) {
Tracer.traceEntry(e, interfaceEntry);
Tracer.traceEntry(e, methodEntry);
throw e;
} finally {
if (methodEntry != null) {
- methodEntry.exit();
+ methodEntry.exit(1, invocation.getArguments());
}
if (interfaceEntry != null) {
interfaceEntry.exit();
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java
index 85d566ad99..405706e0a2 100755
--- a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java
@@ -20,8 +20,6 @@
import com.alibaba.csp.sentinel.ResourceTypeConstants;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
-import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
-import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.slots.block.BlockException;
@@ -55,16 +53,20 @@ public SentinelDubboProviderFilter() {
@Override
public Result invoke(Invoker> invoker, Invocation invocation) throws RpcException {
// Get origin caller.
- String application = DubboUtils.getApplication(invocation, "");
+ String origin = DubboAdapterGlobalConfig.getOriginParser().parse(invoker, invocation);
+ if (null == origin) {
+ origin = "";
+ }
Entry interfaceEntry = null;
Entry methodEntry = null;
try {
- String resourceName = getResourceName(invoker, invocation, DubboConfig.getDubboProviderPrefix());
- String interfaceName = invoker.getInterface().getName();
- ContextUtil.enter(resourceName, application);
+ String prefix = DubboAdapterGlobalConfig.getDubboProviderPrefix();
+ String methodResourceName = getMethodResourceName(invoker, invocation, prefix);
+ String interfaceName = getInterfaceName(invoker, prefix);
+ ContextUtil.enter(methodResourceName, origin);
interfaceEntry = SphU.entry(interfaceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN);
- methodEntry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_RPC,
+ methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC,
EntryType.IN, invocation.getArguments());
Result result = invoker.invoke(invocation);
@@ -76,7 +78,7 @@ public Result invoke(Invoker> invoker, Invocation invocation) throws RpcExcept
}
return result;
} catch (BlockException e) {
- return DubboFallbackRegistry.getProviderFallback().handle(invoker, invocation, e);
+ return DubboAdapterGlobalConfig.getProviderFallback().handle(invoker, invocation, e);
} catch (RpcException e) {
Tracer.traceEntry(e, interfaceEntry);
Tracer.traceEntry(e, methodEntry);
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/config/DubboConfig.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/config/DubboConfig.java
deleted file mode 100644
index b7658d29a4..0000000000
--- a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/config/DubboConfig.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.dubbo.config;
-
-import com.alibaba.csp.sentinel.config.SentinelConfig;
-import com.alibaba.csp.sentinel.util.StringUtil;
-
-/**
- *
- * Responsible for dubbo service provider, consumer attribute configuration
- *
- *
- * @author lianglin
- * @since 1.7.0
- */
-public final class DubboConfig {
-
- public static final String DUBBO_USE_PREFIX = "csp.sentinel.dubbo.resource.use.prefix";
- private static final String TRUE_STR = "true";
-
- public static final String DUBBO_PROVIDER_PREFIX = "csp.sentinel.dubbo.resource.provider.prefix";
- public static final String DUBBO_CONSUMER_PREFIX = "csp.sentinel.dubbo.resource.consumer.prefix";
-
- private static final String DEFAULT_DUBBO_PROVIDER_PREFIX = "dubbo:provider:";
- private static final String DEFAULT_DUBBO_CONSUMER_PREFIX = "dubbo:consumer:";
-
- public static boolean isUsePrefix() {
- return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_USE_PREFIX));
- }
-
-
- public static String getDubboProviderPrefix() {
- if (isUsePrefix()) {
- String config = SentinelConfig.getConfig(DUBBO_PROVIDER_PREFIX);
- return StringUtil.isNotBlank(config) ? config : DEFAULT_DUBBO_PROVIDER_PREFIX;
- }
- return null;
- }
-
- public static String getDubboConsumerPrefix() {
- if (isUsePrefix()) {
- String config = SentinelConfig.getConfig(DUBBO_CONSUMER_PREFIX);
- return StringUtil.isNotBlank(config) ? config : DEFAULT_DUBBO_CONSUMER_PREFIX;
- }
- return null;
- }
-
-
-}
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DefaultDubboFallback.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DefaultDubboFallback.java
index b97f703036..b893a55851 100644
--- a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DefaultDubboFallback.java
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DefaultDubboFallback.java
@@ -20,6 +20,7 @@
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcResult;
/**
* @author Eric Zhao
@@ -28,7 +29,9 @@ public class DefaultDubboFallback implements DubboFallback {
@Override
public Result handle(Invoker> invoker, Invocation invocation, BlockException ex) {
- // Just wrap and throw the exception.
- throw new SentinelRpcException(ex);
+ // Just wrap the exception. edit by wzg923 2020/9/23
+ RpcResult result = new RpcResult();
+ result.setException(new SentinelRpcException(ex.toRuntimeException()));
+ return result;
}
}
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistry.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistry.java
index fe06741bbd..2fb80a3cd2 100644
--- a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistry.java
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistry.java
@@ -15,33 +15,31 @@
*/
package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
+import com.alibaba.csp.sentinel.adapter.dubbo.DubboAdapterGlobalConfig;
+
/**
- * Global fallback registry for Dubbo.
- *
- * Note: Degrading is mainly designed for consumer. The provider should not
- * give fallback result in most circumstances.
+ *
Global fallback registry for Dubbo.
*
* @author Eric Zhao
+ * @deprecated use {@link DubboAdapterGlobalConfig} instead.
*/
+@Deprecated
public final class DubboFallbackRegistry {
- private static volatile DubboFallback consumerFallback = new DefaultDubboFallback();
- private static volatile DubboFallback providerFallback = new DefaultDubboFallback();
-
public static DubboFallback getConsumerFallback() {
- return consumerFallback;
+ return DubboAdapterGlobalConfig.getConsumerFallback();
}
public static void setConsumerFallback(DubboFallback consumerFallback) {
- DubboFallbackRegistry.consumerFallback = consumerFallback;
+ DubboAdapterGlobalConfig.setConsumerFallback(consumerFallback);
}
public static DubboFallback getProviderFallback() {
- return providerFallback;
+ return DubboAdapterGlobalConfig.getProviderFallback();
}
public static void setProviderFallback(DubboFallback providerFallback) {
- DubboFallbackRegistry.providerFallback = providerFallback;
+ DubboAdapterGlobalConfig.setProviderFallback(providerFallback);
}
private DubboFallbackRegistry() {}
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DefaultDubboOriginParser.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DefaultDubboOriginParser.java
new file mode 100644
index 0000000000..8276079343
--- /dev/null
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DefaultDubboOriginParser.java
@@ -0,0 +1,35 @@
+/*
+ * 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.dubbo.origin;
+
+import com.alibaba.csp.sentinel.adapter.dubbo.DubboUtils;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+
+/**
+ * Default Dubbo origin parser.
+ *
+ * @author tiecheng
+ * @since 1.8.0
+ */
+public class DefaultDubboOriginParser implements DubboOriginParser {
+
+ @Override
+ public String parse(Invoker> invoker, Invocation invocation) {
+ return DubboUtils.getApplication(invocation, "");
+ }
+
+}
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DubboOriginParser.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DubboOriginParser.java
new file mode 100644
index 0000000000..13e476a1f7
--- /dev/null
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DubboOriginParser.java
@@ -0,0 +1,38 @@
+/*
+ * 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.dubbo.origin;
+
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+
+/**
+ * Customized origin parser for Dubbo provider filter.
+ *
+ * @author tiecheng
+ * @since 1.8.0
+ */
+public interface DubboOriginParser {
+
+ /**
+ * Parses the origin (caller) from Dubbo invocation.
+ *
+ * @param invoker Dubbo invoker
+ * @param invocation Dubbo invocation
+ * @return the parsed origin
+ */
+ String parse(Invoker> invoker, Invocation invocation);
+
+}
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/AbstractDubboFilterTest.java b/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/AbstractDubboFilterTest.java
index cb9da8c413..d1003fd27a 100644
--- a/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/AbstractDubboFilterTest.java
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/AbstractDubboFilterTest.java
@@ -1,6 +1,5 @@
package com.alibaba.csp.sentinel.adapter.dubbo;
-import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboConfig;
import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.dubbo.rpc.Invocation;
@@ -26,15 +25,15 @@ public class AbstractDubboFilterTest {
@Before
public void setUp() {
SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "true");
- SentinelConfig.setConfig(DubboConfig.DUBBO_PROVIDER_PREFIX, "");
- SentinelConfig.setConfig(DubboConfig.DUBBO_CONSUMER_PREFIX, "");
+ SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "");
+ SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "");
}
@After
public void tearDown() {
SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "false");
- SentinelConfig.setConfig(DubboConfig.DUBBO_PROVIDER_PREFIX, "");
- SentinelConfig.setConfig(DubboConfig.DUBBO_CONSUMER_PREFIX, "");
+ SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "");
+ SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "");
}
private AbstractDubboFilter filter = new AbstractDubboFilter() {
@@ -55,7 +54,7 @@ public void testGetResourceName() {
when(invocation.getMethodName()).thenReturn(method.getName());
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
- String resourceName = filter.getResourceName(invoker, invocation);
+ String resourceName = filter.getMethodResourceName(invoker, invocation);
assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
}
@@ -71,19 +70,19 @@ public void testGetResourceNameWithPrefix() {
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
//test with default prefix
- String resourceName = filter.getResourceName(invoker, invocation, DubboConfig.getDubboProviderPrefix());
+ String resourceName = filter.getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboProviderPrefix());
System.out.println("resourceName = " + resourceName);
assertEquals("dubbo:provider:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
- resourceName = filter.getResourceName(invoker, invocation, DubboConfig.getDubboConsumerPrefix());
+ resourceName = filter.getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboConsumerPrefix());
assertEquals("dubbo:consumer:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
//test with custom prefix
- SentinelConfig.setConfig(DubboConfig.DUBBO_PROVIDER_PREFIX, "my:dubbo:provider:");
- SentinelConfig.setConfig(DubboConfig.DUBBO_CONSUMER_PREFIX, "my:dubbo:consumer:");
- resourceName = filter.getResourceName(invoker, invocation, DubboConfig.getDubboProviderPrefix());
+ SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "my:dubbo:provider:");
+ SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "my:dubbo:consumer:");
+ resourceName = filter.getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboProviderPrefix());
assertEquals("my:dubbo:provider:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
- resourceName = filter.getResourceName(invoker, invocation, DubboConfig.getDubboConsumerPrefix());
+ resourceName = filter.getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboConsumerPrefix());
assertEquals("my:dubbo:consumer:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
}
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java b/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java
index 58de29d64f..ee7bfccc91 100644
--- a/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java
@@ -85,7 +85,7 @@ private void verifyInvocationStructure(Invoker invoker, Invocation invocation) {
// As not call ContextUtil.enter(resourceName, application) in SentinelDubboConsumerFilter, use default context
// In actual project, a consumer is usually also a provider, the context will be created by SentinelDubboProviderFilter
// If consumer is on the top of Dubbo RPC invocation chain, use default context
- String resourceName = filter.getResourceName(invoker, invocation);
+ String resourceName = filter.getMethodResourceName(invoker, invocation);
assertEquals(Constants.CONTEXT_DEFAULT_NAME, context.getName());
assertEquals("", context.getOrigin());
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilterTest.java b/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilterTest.java
index 8f1f850f2f..bcd986e31d 100644
--- a/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilterTest.java
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilterTest.java
@@ -85,7 +85,7 @@ private void verifyInvocationStructure(String originApplication, Invoker invoker
assertNotNull(context);
// As ContextUtil.enter(resourceName, application) in SentinelDubboProviderFilter
- String resourceName = filter.getResourceName(invoker, invocation);
+ String resourceName = filter.getMethodResourceName(invoker, invocation);
assertEquals(resourceName, context.getName());
assertEquals(originApplication, context.getOrigin());
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java b/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java
index f75e0c34e8..edcd2a017b 100644
--- a/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java
@@ -15,6 +15,7 @@
*/
package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
+import com.alibaba.csp.sentinel.adapter.dubbo.DubboAdapterGlobalConfig;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
@@ -22,7 +23,6 @@
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcResult;
-
import org.junit.Assert;
import org.junit.Test;
@@ -31,24 +31,25 @@
*/
public class DubboFallbackRegistryTest {
- @Test(expected = SentinelRpcException.class)
+ @Test
public void testDefaultFallback() {
- // Test for default.
+ // Test for default fallback.
BlockException ex = new FlowException("xxx");
- DubboFallbackRegistry.getConsumerFallback()
- .handle(null, null, ex);
+ Result result = new DefaultDubboFallback().handle(null, null, ex);
+ Assert.assertTrue(result.hasException());
+ Assert.assertEquals(SentinelRpcException.class, result.getException().getClass());
}
@Test
public void testCustomFallback() {
BlockException ex = new FlowException("xxx");
- DubboFallbackRegistry.setConsumerFallback(new DubboFallback() {
+ DubboAdapterGlobalConfig.setConsumerFallback(new DubboFallback() {
@Override
public Result handle(Invoker> invoker, Invocation invocation, BlockException e) {
return new RpcResult("Error: " + e.getClass().getName());
}
});
- Result result = DubboFallbackRegistry.getConsumerFallback()
+ Result result = DubboAdapterGlobalConfig.getConsumerFallback()
.handle(null, null, ex);
Assert.assertFalse("The invocation should not fail", result.hasException());
Assert.assertEquals("Error: " + ex.getClass().getName(), result.getValue());
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DubboOriginRegistryTest.java b/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DubboOriginRegistryTest.java
new file mode 100644
index 0000000000..ab253c267f
--- /dev/null
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/origin/DubboOriginRegistryTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.dubbo.origin;
+
+import com.alibaba.csp.sentinel.adapter.dubbo.DubboAdapterGlobalConfig;
+import com.alibaba.csp.sentinel.adapter.dubbo.DubboUtils;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author tiecheng
+ */
+public class DubboOriginRegistryTest {
+
+ @After
+ public void cleanUp() {
+ DubboAdapterGlobalConfig.setOriginParser(new DefaultDubboOriginParser());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDefaultOriginParserFail() {
+ DubboAdapterGlobalConfig.getOriginParser().parse(null, null);
+ }
+
+ @Test
+ public void testDefaultOriginParserSuccess() {
+ RpcInvocation invocation = new RpcInvocation();
+ String dubboName = "sentinel";
+ invocation.setAttachment(DubboUtils.DUBBO_APPLICATION_KEY, dubboName);
+ String origin = DubboAdapterGlobalConfig.getOriginParser().parse(null, invocation);
+ Assert.assertEquals(dubboName, origin);
+ }
+
+ @Test
+ public void testCustomOriginParser() {
+ DubboAdapterGlobalConfig.setOriginParser(new DubboOriginParser() {
+ @Override
+ public String parse(Invoker> invoker, Invocation invocation) {
+ return invocation.getAttachment(DubboUtils.DUBBO_APPLICATION_KEY, "default") + "_" + invocation
+ .getMethodName();
+ }
+ });
+
+ RpcInvocation invocation = new RpcInvocation();
+ String origin = DubboAdapterGlobalConfig.getOriginParser().parse(null, invocation);
+ Assert.assertEquals("default_null", origin);
+
+ String dubboName = "sentinel";
+ invocation.setAttachment(DubboUtils.DUBBO_APPLICATION_KEY, dubboName);
+ origin = DubboAdapterGlobalConfig.getOriginParser().parse(null, invocation);
+ Assert.assertEquals(dubboName + "_null", origin);
+
+ invocation.setMethodName("hello");
+ origin = DubboAdapterGlobalConfig.getOriginParser().parse(null, invocation);
+ Assert.assertEquals(dubboName + "_hello", origin);
+ }
+
+}
diff --git a/sentinel-adapter/sentinel-grpc-adapter/pom.xml b/sentinel-adapter/sentinel-grpc-adapter/pom.xml
index 7f4f9f1378..909e397a2f 100755
--- a/sentinel-adapter/sentinel-grpc-adapter/pom.xml
+++ b/sentinel-adapter/sentinel-grpc-adapter/pom.xml
@@ -5,14 +5,14 @@
sentinel-adaptercom.alibaba.csp
- 1.7.2-SNAPSHOT
+ 1.8.1-SNAPSHOT4.0.0sentinel-grpc-adapterjar
- 1.13.1
+ 1.30.2
@@ -43,7 +43,7 @@
javax.annotationjavax.annotation-api
- 1.2
+ ${javax.annotation-api.version}
@@ -95,4 +95,4 @@
-
\ No newline at end of file
+
diff --git a/sentinel-adapter/sentinel-jax-rs-adapter/README.md b/sentinel-adapter/sentinel-jax-rs-adapter/README.md
new file mode 100755
index 0000000000..50ac623d01
--- /dev/null
+++ b/sentinel-adapter/sentinel-jax-rs-adapter/README.md
@@ -0,0 +1,83 @@
+# Sentinel adapter for JAX-RS
+
+Sentinel provides integration to enable fault-tolerance and flow control for JAX-RS web requests.
+Add the following dependency in `pom.xml` (if you are using Maven):
+
+```xml
+
+ com.alibaba.csp
+ sentinel-jax-rs-adapter
+ x.y.z
+
+```
+
+## SentinelJaxRsProviderFilter
+
+The `SentinelJaxRsProviderFilter` is auto activated in pure JAX-RS application.
+
+For Spring web applications you can configure with Spring bean:
+
+```java
+@Configuration
+public class FilterConfig {
+
+ @Bean
+ public SentinelJaxRsProviderFilter sentinelJaxRsProviderFilter() {
+ return new SentinelJaxRsProviderFilter();
+ }
+}
+```
+
+## DefaultExceptionMapper
+
+Sentinel provides DefaultExceptionMapper to map Throwable to Response (with Status.INTERNAL_SERVER_ERROR),
+in order to let SentinelJaxRsProviderFilter to be called and exit the Sentinel entry.
+
+According to `3.3.4 Exceptions` of [jaxrs-2_1-final-spec](https://download.oracle.com/otn-pub/jcp/jaxrs-2_1-final-eval-spec/jaxrs-2_1-final-spec.pdf):
+
+> Checked exceptions and throwables that have not been mapped and cannot be thrown directly MUST be wrapped in a container-specific exception that is then thrown and allowed to propagate to the underlying container.
+
+If WebApplicationException or its subclasses are thrown, they'll be automatically converted to `Response` and can enter response filter.
+If other kind of exceptions were thrown, and not matched by custom exception mapper, then the response filter cannot be called.
+For this case, a default exception mapper maybe introduced.
+
+According to `4.4 Exception Mapping Providers` of [jaxrs-2_1-final-spec](https://download.oracle.com/otn-pub/jcp/jaxrs-2_1-final-eval-spec/jaxrs-2_1-final-spec.pdf):
+
+> When choosing an exception mapping provider to map an exception, an implementation MUST use the provider whose generic type is the nearest superclass of the exception. If two or more exception providers are applicable, the one with the highest priority MUST be chosen as described in Section 4.1.3.
+
+If user also provides customized exception mapper of `Throwable`, then user has the responsibility to convert it to response and then the response filter can be called.
+
+As describe in `6.7.1 exceptions` of [jaxrs-2_1-final-spec](https://download.oracle.com/otn-pub/jcp/jaxrs-2_1-final-eval-spec/jaxrs-2_1-final-spec.pdf):
+
+> A response mapped from an exception MUST be processed using the ContainerResponse filter chain and the WriteTo interceptor chain (if an entity is present in the mapped response).
+
+## SentinelJaxRsClientTemplate
+
+For jax-rs client, we provide `SentinelJaxRsClientTemplate` you can use it like this:
+
+```
+Response response = SentinelJaxRsClientTemplate.execute(resourceName, new Supplier() {
+ @Override
+ public Response get() {
+ return client.target(host).path(url).request()
+ .get();
+ }
+});
+```
+
+or executeAsync like this:
+
+```
+Future future = SentinelJaxRsClientTemplate.executeAsync(resourceName, new Supplier>() {
+ @Override
+ public Future get() {
+ return client.target(host).path(url).request()
+ .async()
+ .get();
+ }
+});
+```
+
+When a request is blocked, Sentinel JAX-RS filter will return Response with status of `TOO_MANY_REQUESTS` indicating the request is rejected.
+
+You can customize it by implement your own `SentinelJaxRsFallback` and register to `SentinelJaxRsConfig`.
diff --git a/sentinel-adapter/sentinel-jax-rs-adapter/pom.xml b/sentinel-adapter/sentinel-jax-rs-adapter/pom.xml
new file mode 100755
index 0000000000..7b1a9b6074
--- /dev/null
+++ b/sentinel-adapter/sentinel-jax-rs-adapter/pom.xml
@@ -0,0 +1,74 @@
+
+
+ 4.0.0
+
+
+ com.alibaba.csp
+ sentinel-adapter
+ 1.8.1-SNAPSHOT
+
+
+ sentinel-jax-rs-adapter
+ jar
+
+
+ 2.1.1
+
+
+
+
+ com.alibaba.csp
+ sentinel-core
+
+
+
+ javax.ws.rs
+ javax.ws.rs-api
+ ${javax.ws.rs-api.version}
+ provided
+
+
+
+ junit
+ junit
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ 2.2.6.RELEASE
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ 2.2.6.RELEASE
+ test
+
+
+ io.rest-assured
+ rest-assured
+ 4.3.0
+ test
+
+
+ org.jboss.resteasy
+ resteasy-spring-boot-starter
+ 4.5.1.Final
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ always
+
+
+
+
+
diff --git a/sentinel-adapter/sentinel-jax-rs-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/jaxrs/SentinelJaxRsClientTemplate.java b/sentinel-adapter/sentinel-jax-rs-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/jaxrs/SentinelJaxRsClientTemplate.java
new file mode 100644
index 0000000000..07abea3d77
--- /dev/null
+++ b/sentinel-adapter/sentinel-jax-rs-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/jaxrs/SentinelJaxRsClientTemplate.java
@@ -0,0 +1,79 @@
+/*
+ * 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.jaxrs;
+
+import com.alibaba.csp.sentinel.*;
+import com.alibaba.csp.sentinel.adapter.jaxrs.config.SentinelJaxRsConfig;
+import com.alibaba.csp.sentinel.adapter.jaxrs.future.FutureWrapper;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.util.function.Supplier;
+
+import javax.ws.rs.core.Response;
+import java.util.concurrent.Future;
+
+
+/**
+ * wrap jax-rs client execution with sentinel
+ *
+ *
* @author kaizi2009
* @since 1.7.1
*/
@@ -49,24 +64,53 @@ public AbstractSentinelInterceptor(BaseWebMvcConfig config) {
AssertUtil.assertNotBlank(config.getRequestAttributeName(), "requestAttributeName should not be blank");
this.baseWebMvcConfig = config;
}
-
+
+ /**
+ * @param request
+ * @param rcKey
+ * @param step
+ * @return reference count after increasing (initial value as zero to be increased)
+ */
+ private Integer increaseReferece(HttpServletRequest request, String rcKey, int step) {
+ Object obj = request.getAttribute(rcKey);
+
+ if (obj == null) {
+ // initial
+ obj = Integer.valueOf(0);
+ }
+
+ Integer newRc = (Integer)obj + step;
+ request.setAttribute(rcKey, newRc);
+ return newRc;
+ }
+
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
try {
String resourceName = getResourceName(request);
- if (StringUtil.isNotEmpty(resourceName)) {
- // Parse the request origin using registered origin parser.
- String origin = parseOrigin(request);
- ContextUtil.enter(SENTINEL_SPRING_WEB_CONTEXT_NAME, origin);
- Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
-
- setEntryInRequest(request, baseWebMvcConfig.getRequestAttributeName(), entry);
+ if (StringUtil.isEmpty(resourceName)) {
+ return true;
+ }
+
+ if (increaseReferece(request, this.baseWebMvcConfig.getRequestRefName(), 1) != 1) {
+ return true;
}
+
+ // Parse the request origin using registered origin parser.
+ String origin = parseOrigin(request);
+ String contextName = getContextName(request);
+ ContextUtil.enter(contextName, origin);
+ Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
+ request.setAttribute(baseWebMvcConfig.getRequestAttributeName(), entry);
return true;
} catch (BlockException e) {
- handleBlockException(request, response, e);
+ try {
+ handleBlockException(request, response, e);
+ } finally {
+ ContextUtil.exit();
+ }
return false;
}
}
@@ -79,14 +123,33 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
*/
protected abstract String getResourceName(HttpServletRequest request);
+ /**
+ * Return the context name of the target web resource.
+ *
+ * @param request web request
+ * @return the context name of the target web resource.
+ */
+ protected String getContextName(HttpServletRequest request) {
+ return SENTINEL_SPRING_WEB_CONTEXT_NAME;
+ }
+
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
+ if (increaseReferece(request, this.baseWebMvcConfig.getRequestRefName(), -1) != 0) {
+ return;
+ }
+
Entry entry = getEntryInRequest(request, baseWebMvcConfig.getRequestAttributeName());
- if (entry != null) {
- traceExceptionAndExit(entry, ex);
- removeEntryInRequest(request);
+ if (entry == null) {
+ // should not happen
+ RecordLog.warn("[{}] No entry found in request, key: {}",
+ getClass().getSimpleName(), baseWebMvcConfig.getRequestAttributeName());
+ return;
}
+
+ traceExceptionAndExit(entry, ex);
+ removeEntryInRequest(request);
ContextUtil.exit();
}
@@ -95,16 +158,6 @@ public void postHandle(HttpServletRequest request, HttpServletResponse response,
ModelAndView modelAndView) throws Exception {
}
- protected void setEntryInRequest(HttpServletRequest request, String name, Entry entry) {
- Object attrVal = request.getAttribute(name);
- if (attrVal != null) {
- RecordLog.warn("[{}] The attribute key '{0}' already exists in request, please set `requestAttributeName`",
- getClass().getSimpleName(), name);
- } else {
- request.setAttribute(name, entry);
- }
- }
-
protected Entry getEntryInRequest(HttpServletRequest request, String attrKey) {
Object entryObject = request.getAttribute(attrKey);
return entryObject == null ? null : (Entry)entryObject;
diff --git a/sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebInterceptor.java b/sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebInterceptor.java
index b01b2c358a..0c244d680a 100644
--- a/sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebInterceptor.java
+++ b/sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebInterceptor.java
@@ -66,4 +66,12 @@ protected String getResourceName(HttpServletRequest request) {
return resourceName;
}
+ @Override
+ protected String getContextName(HttpServletRequest request) {
+ if (config.isWebContextUnify()) {
+ return super.getContextName(request);
+ }
+
+ return getResourceName(request);
+ }
}
diff --git a/sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/BaseWebMvcConfig.java b/sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/BaseWebMvcConfig.java
index 03ff0ebc7b..e1bd1542fd 100644
--- a/sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/BaseWebMvcConfig.java
+++ b/sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/BaseWebMvcConfig.java
@@ -17,7 +17,6 @@
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
-import com.alibaba.csp.sentinel.util.AssertUtil;
/**
* Common base configuration for Spring Web MVC adapter.
@@ -28,6 +27,7 @@
public abstract class BaseWebMvcConfig {
protected String requestAttributeName;
+ protected String requestRefName;
protected BlockExceptionHandler blockExceptionHandler;
protected RequestOriginParser originParser;
@@ -37,6 +37,16 @@ public String getRequestAttributeName() {
public void setRequestAttributeName(String requestAttributeName) {
this.requestAttributeName = requestAttributeName;
+ this.requestRefName = this.requestAttributeName + "-rc";
+ }
+
+ /**
+ * Paired with attr name used to track reference count.
+ *
+ * @return
+ */
+ public String getRequestRefName() {
+ return requestRefName;
}
public BlockExceptionHandler getBlockExceptionHandler() {
diff --git a/sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/SentinelWebMvcConfig.java b/sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/SentinelWebMvcConfig.java
index 29a2891ca6..c94ff8c20f 100755
--- a/sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/SentinelWebMvcConfig.java
+++ b/sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/SentinelWebMvcConfig.java
@@ -29,11 +29,19 @@ public class SentinelWebMvcConfig extends BaseWebMvcConfig {
* Specify the URL cleaner that unifies the URL resources.
*/
private UrlCleaner urlCleaner;
+
/**
* Specify whether the URL resource name should contain the HTTP method prefix (e.g. {@code POST:}).
*/
private boolean httpMethodSpecify;
+ /**
+ * Specify whether unify web context(i.e. use the default context name), and is true by default.
+ *
+ * @since 1.7.2
+ */
+ private boolean webContextUnify = true;
+
public SentinelWebMvcConfig() {
super();
setRequestAttributeName(DEFAULT_REQUEST_ATTRIBUTE_NAME);
@@ -57,11 +65,21 @@ public SentinelWebMvcConfig setHttpMethodSpecify(boolean httpMethodSpecify) {
return this;
}
+ public boolean isWebContextUnify() {
+ return webContextUnify;
+ }
+
+ public SentinelWebMvcConfig setWebContextUnify(boolean webContextUnify) {
+ this.webContextUnify = webContextUnify;
+ return this;
+ }
+
@Override
public String toString() {
return "SentinelWebMvcConfig{" +
"urlCleaner=" + urlCleaner +
", httpMethodSpecify=" + httpMethodSpecify +
+ ", webContextUnify=" + webContextUnify +
", requestAttributeName='" + requestAttributeName + '\'' +
", blockExceptionHandler=" + blockExceptionHandler +
", originParser=" + originParser +
diff --git a/sentinel-adapter/sentinel-spring-webmvc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelSpringMvcIntegrationTest.java b/sentinel-adapter/sentinel-spring-webmvc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelSpringMvcIntegrationTest.java
index f6a4b30fba..f1ddb937ce 100644
--- a/sentinel-adapter/sentinel-spring-webmvc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelSpringMvcIntegrationTest.java
+++ b/sentinel-adapter/sentinel-spring-webmvc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelSpringMvcIntegrationTest.java
@@ -71,18 +71,21 @@ public void testOriginParser() throws Exception {
final String headerName = "S-User";
configureRulesFor(springMvcPathVariableUrl, 0, limitOrigin);
+ // This will be passed since the caller is different: userB
this.mvc.perform(get("/foo/1").accept(MediaType.TEXT_PLAIN).header(headerName, "userB"))
.andExpect(status().isOk())
.andExpect(content().string("foo 1"));
- // This will be blocked and reponse json.
+ // This will be blocked since the caller is same: userA
this.mvc.perform(
get("/foo/2").accept(MediaType.APPLICATION_JSON).header(headerName, limitOrigin))
.andExpect(status().isOk())
.andExpect(content().json(ResultWrapper.blocked().toJsonString()));
+
+ // This will be passed since the caller is different: ""
this.mvc.perform(get("/foo/3").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
- .andExpect(content().string(ResultWrapper.blocked().toJsonString()));
+ .andExpect(content().string("foo 3"));
FlowRuleManager.loadRules(null);
}
@@ -115,7 +118,7 @@ public void testRuntimeException() throws Exception {
assertEquals(i + 1, cn.passQps(), 0.01);
}
- // This will be blocked and reponse json.
+ // This will be blocked and response json.
this.mvc.perform(get(url))
.andExpect(status().isOk())
.andExpect(content().string(ResultWrapper.blocked().toJsonString()));
diff --git a/sentinel-adapter/sentinel-spring-webmvc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/InterceptorConfig.java b/sentinel-adapter/sentinel-spring-webmvc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/InterceptorConfig.java
index 1ff24fd2cf..5b167a0c1d 100644
--- a/sentinel-adapter/sentinel-spring-webmvc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/InterceptorConfig.java
+++ b/sentinel-adapter/sentinel-spring-webmvc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/InterceptorConfig.java
@@ -66,6 +66,7 @@ public void handle(HttpServletRequest request, HttpServletResponse response, Blo
//Custom configuration if necessary
config.setHttpMethodSpecify(false);
+ config.setWebContextUnify(true);
config.setOriginParser(new RequestOriginParser() {
@Override
public String parseOrigin(HttpServletRequest request) {
diff --git a/sentinel-adapter/sentinel-web-servlet/pom.xml b/sentinel-adapter/sentinel-web-servlet/pom.xml
index 32c6b0d3fa..618f71f6ef 100755
--- a/sentinel-adapter/sentinel-web-servlet/pom.xml
+++ b/sentinel-adapter/sentinel-web-servlet/pom.xml
@@ -6,7 +6,7 @@
com.alibaba.cspsentinel-adapter
- 1.7.2-SNAPSHOT
+ 1.8.1-SNAPSHOTsentinel-web-servlet
diff --git a/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/CommonFilter.java b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/CommonFilter.java
index e246e06c65..f7bf3d6148 100755
--- a/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/CommonFilter.java
+++ b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/CommonFilter.java
@@ -54,7 +54,7 @@ public class CommonFilter implements Filter {
*/
public static final String HTTP_METHOD_SPECIFY = "HTTP_METHOD_SPECIFY";
/**
- * If enabled, use the URL path as the context name, or else use the default
+ * If enabled, use the default context name, or else use the URL path as the context name,
* {@link WebServletConfig#WEB_SERVLET_CONTEXT_NAME}. Please pay attention to the number of context (EntranceNode),
* which may affect the memory footprint.
*
diff --git a/sentinel-adapter/sentinel-zuul-adapter/README.md b/sentinel-adapter/sentinel-zuul-adapter/README.md
index 9814a948bc..dcfd8de6af 100755
--- a/sentinel-adapter/sentinel-zuul-adapter/README.md
+++ b/sentinel-adapter/sentinel-zuul-adapter/README.md
@@ -64,7 +64,7 @@ As Zuul run as per thread per connection block model, we add filters around rout
- `SentinelZuulPreFilter`: This pre-filter will regard all proxy ID (`proxy` in `RequestContext`) and all customized API as resources. When a `BlockException` caught, the filter will try to find a fallback to execute.
- `SentinelZuulPostFilter`: When the response has no exception caught, the post filter will complete the entries.
-- `SentinelZuulPreFilter`: When an exception is caught, the filter will trace the exception and complete the entries.
+- `SentinelZuulErrorFilter`: When an exception is caught, the filter will trace the exception and complete the entries.
diff --git a/sentinel-adapter/sentinel-zuul-adapter/pom.xml b/sentinel-adapter/sentinel-zuul-adapter/pom.xml
index 6857cb8e8e..7aa5181d2e 100755
--- a/sentinel-adapter/sentinel-zuul-adapter/pom.xml
+++ b/sentinel-adapter/sentinel-zuul-adapter/pom.xml
@@ -5,7 +5,7 @@
sentinel-adaptercom.alibaba.csp
- 1.7.2-SNAPSHOT
+ 1.8.1-SNAPSHOT4.0.0sentinel-zuul-adapter
@@ -69,6 +69,12 @@
mockito-coretest
+
+ org.springframework
+ spring-test
+ 4.3.20.RELEASE
+ test
+
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/PrefixRoutePathMatcher.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/PrefixRoutePathMatcher.java
index 21e636e589..b592a9cf03 100644
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/PrefixRoutePathMatcher.java
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/PrefixRoutePathMatcher.java
@@ -17,11 +17,12 @@
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.csp.sentinel.util.function.Predicate;
-
import com.netflix.zuul.context.RequestContext;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
+import javax.servlet.http.HttpServletRequest;
+
/**
* @author Eric Zhao
* @since 1.6.0
@@ -42,7 +43,12 @@ public PrefixRoutePathMatcher(String pattern) {
@Override
public boolean test(RequestContext context) {
- String path = context.getRequest().getServletPath();
+ //Solve the problem of prefix matching
+ HttpServletRequest request = context.getRequest();
+ String path = request.getRequestURI();
+ if (path == null) {
+ AssertUtil.assertNotBlank(pattern, "requesturi cannot be blank");
+ }
if (canMatch) {
return pathMatcher.match(pattern, path);
}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/RegexRoutePathMatcher.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/RegexRoutePathMatcher.java
index daf1310ec3..4363e4e795 100644
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/RegexRoutePathMatcher.java
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/RegexRoutePathMatcher.java
@@ -15,13 +15,13 @@
*/
package com.alibaba.csp.sentinel.adapter.gateway.zuul.api.route;
-import java.util.regex.Pattern;
-
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.csp.sentinel.util.function.Predicate;
-
import com.netflix.zuul.context.RequestContext;
+import javax.servlet.http.HttpServletRequest;
+import java.util.regex.Pattern;
+
/**
* @author Eric Zhao
* @since 1.6.0
@@ -39,7 +39,12 @@ public RegexRoutePathMatcher(String pattern) {
@Override
public boolean test(RequestContext context) {
- String path = context.getRequest().getServletPath();
+ //Solve the problem of route matching
+ HttpServletRequest request = context.getRequest();
+ String path = request.getRequestURI();
+ if (path == null) {
+ AssertUtil.assertNotBlank(pattern, "requesturi cannot be blank");
+ }
return regex.matcher(path).matches();
}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/route/SentinelZuulRouteTest.java b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/route/SentinelZuulRouteTest.java
new file mode 100644
index 0000000000..eb67921a77
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/route/SentinelZuulRouteTest.java
@@ -0,0 +1,56 @@
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.route;
+
+import com.alibaba.csp.sentinel.adapter.gateway.zuul.api.route.PrefixRoutePathMatcher;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul.api.route.RegexRoutePathMatcher;
+import com.netflix.zuul.context.RequestContext;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.mock.web.MockHttpServletRequest;
+
+import static com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant.SERVICE_ID_KEY;
+
+/**
+ * @author: jiangzian
+ **/
+public class SentinelZuulRouteTest {
+
+ private final String SERVICE_ID = "servicea";
+
+ private final String SERVER_NAME = "www.example.com";
+ private final String REQUEST_URI = "/servicea/test.jsp";
+ private final String QUERY_STRING = "param1=value1¶m";
+
+ private RequestContext requestContext = new RequestContext();
+
+
+ @Before
+ public void setUp() {
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.setServerName(SERVER_NAME);
+ request.setRequestURI(REQUEST_URI);
+ request.setQueryString(QUERY_STRING);
+ requestContext.set(SERVICE_ID_KEY, SERVICE_ID);
+ requestContext.setRequest(request);
+ RequestContext.testSetCurrentContext(requestContext);
+ }
+
+ @Test
+ public void testPrefixRoutePathMatche() {
+ PrefixRoutePathMatcher prefixRoutePathMatcher = new PrefixRoutePathMatcher("/servicea/????.jsp");
+ Assert.assertTrue(prefixRoutePathMatcher.test(requestContext));
+
+ prefixRoutePathMatcher = new PrefixRoutePathMatcher("/servicea/????.do");
+ Assert.assertTrue(!prefixRoutePathMatcher.test(requestContext));
+ }
+
+ @Test
+ public void testRegexRoutePathMatcher() {
+ RegexRoutePathMatcher regexRoutePathMatcher = new RegexRoutePathMatcher("/servicea/[a-zA-z]+(\\.jsp)");
+ Assert.assertTrue(regexRoutePathMatcher.test(requestContext));
+
+ regexRoutePathMatcher = new RegexRoutePathMatcher("/serviceb/[a-zA-z]+(\\.jsp)");
+ Assert.assertTrue(!regexRoutePathMatcher.test(requestContext));
+ }
+
+}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/README.md b/sentinel-adapter/sentinel-zuul2-adapter/README.md
new file mode 100755
index 0000000000..e0387ee516
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/README.md
@@ -0,0 +1,90 @@
+# Sentinel Zuul 2.x Adapter
+
+This adapter provides **route level** and **customized API level**
+flow control for Zuul 2.x API Gateway.
+
+> *Note*: this adapter only supports Zuul 2.x.
+
+## How to use
+
+> You can refer to demo [`sentinel-demo-zuul2-gateway`](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-zuul2-gateway).
+
+1. Add Maven dependency to your `pom.xml`:
+
+```xml
+
+ com.alibaba.csp
+ sentinel-zuul2-adapter
+ x.y.z
+
+```
+
+2. Register filters
+
+```java
+filterMultibinder.addBinding().toInstance(new SentinelZuulInboundFilter(500));
+filterMultibinder.addBinding().toInstance(new SentinelZuulOutboundFilter(500));
+filterMultibinder.addBinding().toInstance(new SentinelZuulEndpoint());
+```
+
+## How it works
+
+As Zuul 2.x is based on Netty, an event-driven asynchronous model, so we use `AsyncEntry`.
+
+- `SentinelZuulInboundFilter`: This inbound filter will regard all routes (`routeVIP` in `SessionContext` by default) and all customized API as resources. When a `BlockException` caught, the filter will set endpoint to find a fallback to execute.
+- `SentinelZuulOutboundFilter`: When the response has no exception caught, the post filter will trace the exception and complete the entries.
+- `SentinelZuulEndpoint`: When an exception is caught, the filter will find a fallback to execute.
+
+## Integration with Sentinel Dashboard
+
+1. Start [Sentinel Dashboard](https://github.com/alibaba/Sentinel/wiki/Dashboard).
+2. You can configure the rules in Sentinel dashboard or via dynamic rule configuration.
+
+> You may need to add `-Dcsp.sentinel.app.type=1` property to mark this application as API gateway.
+
+## Fallbacks
+
+You can implement `ZuulBlockFallbackProvider` to define your own fallback provider when Sentinel `BlockException` is thrown.
+The default fallback provider is `DefaultBlockFallbackProvider`.
+
+By default fallback route is proxy ID (or customized API name).
+
+Here is an example:
+
+```java
+
+// custom provider
+public class MyBlockFallbackProvider implements ZuulBlockFallbackProvider {
+
+ private Logger logger = LoggerFactory.getLogger(DefaultBlockFallbackProvider.class);
+
+ // you can define root as service level
+ @Override
+ public String getRoute() {
+ return "my-route";
+ }
+
+ @Override
+ public BlockResponse fallbackResponse(String route, Throwable cause) {
+ RecordLog.info(String.format("[Sentinel DefaultBlockFallbackProvider] Run fallback route: %s", route));
+ if (cause instanceof BlockException) {
+ return new BlockResponse(429, "Sentinel block exception", route);
+ } else {
+ return new BlockResponse(500, "System Error", route);
+ }
+ }
+ }
+
+ // register fallback
+ ZuulBlockFallbackManager.registerProvider(new MyBlockFallbackProvider());
+```
+
+Default block response:
+
+```json
+{
+ "code":429,
+ "message":"Sentinel block exception",
+ "route":"/"
+}
+```
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/pom.xml b/sentinel-adapter/sentinel-zuul2-adapter/pom.xml
new file mode 100644
index 0000000000..c841a53588
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/pom.xml
@@ -0,0 +1,63 @@
+
+
+
+ sentinel-adapter
+ com.alibaba.csp
+ 1.8.1-SNAPSHOT
+
+ 4.0.0
+
+ sentinel-zuul2-adapter
+ jar
+
+
+ 1.8
+ 1.8
+ 2.1.5
+
+
+
+
+ com.alibaba.csp
+ sentinel-core
+
+
+ com.alibaba.csp
+ sentinel-api-gateway-adapter-common
+
+
+
+ com.netflix.zuul
+ zuul-core
+ ${zuul.version}
+ provided
+
+
+ org.mockito
+ mockito-core
+
+
+
+
+
+
+ org.springframework
+ spring-core
+ 5.1.9.RELEASE
+
+
+
+ junit
+ junit
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/HttpRequestMessageItemParser.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/HttpRequestMessageItemParser.java
new file mode 100644
index 0000000000..8c3f3cff35
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/HttpRequestMessageItemParser.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1999-2019 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.gateway.zuul2;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.param.RequestItemParser;
+import com.netflix.zuul.message.http.HttpRequestMessage;
+
+/**
+ * @author wavesZh
+ * @since 1.7.2
+ */
+public class HttpRequestMessageItemParser implements RequestItemParser {
+
+ @Override
+ public String getPath(HttpRequestMessage request) {
+ return request.getInboundRequest().getPath();
+ }
+
+ @Override
+ public String getRemoteAddress(HttpRequestMessage request) {
+ return request.getOriginalHost();
+ }
+
+ @Override
+ public String getHeader(HttpRequestMessage request, String key) {
+ return String.valueOf(request.getInboundRequest().getHeaders().get(key));
+ }
+
+ @Override
+ public String getUrlParam(HttpRequestMessage request, String paramName) {
+ return String.valueOf(request.getInboundRequest().getQueryParams().get(paramName));
+ }
+
+ @Override
+ public String getCookieValue(HttpRequestMessage request, String cookieName) {
+ return String.valueOf(request.getInboundRequest().parseCookies().get(cookieName));
+ }
+}
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/ZuulApiDefinitionChangeObserver.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/ZuulApiDefinitionChangeObserver.java
new file mode 100644
index 0000000000..0cbaa4e4b3
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/ZuulApiDefinitionChangeObserver.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2019 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.gateway.zuul2.api;
+
+import java.util.Set;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinitionChangeObserver;
+
+/**
+ * @author Eric Zhao
+ * @since 1.7.2
+ */
+public class ZuulApiDefinitionChangeObserver implements ApiDefinitionChangeObserver {
+
+ @Override
+ public void onChange(Set apiDefinitions) {
+ ZuulGatewayApiMatcherManager.loadApiDefinitions(apiDefinitions);
+ }
+}
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/ZuulGatewayApiMatcherManager.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/ZuulGatewayApiMatcherManager.java
new file mode 100644
index 0000000000..7c71724c89
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/ZuulGatewayApiMatcherManager.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1999-2019 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.gateway.zuul2.api;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul2.api.matcher.HttpRequestMessageApiMatcher;
+
+/**
+ * @author wavesZh
+ * @since 1.7.2
+ */
+public final class ZuulGatewayApiMatcherManager {
+
+ private static final Map API_MATCHER_MAP = new ConcurrentHashMap<>();
+
+ public static Map getApiMatcherMap() {
+ return Collections.unmodifiableMap(API_MATCHER_MAP);
+ }
+
+ public static HttpRequestMessageApiMatcher getMatcher(final String apiName) {
+ if (apiName == null) {
+ return null;
+ }
+ return API_MATCHER_MAP.get(apiName);
+ }
+
+ public static Set getApiDefinitionSet() {
+ Set set = new HashSet<>();
+ for (HttpRequestMessageApiMatcher matcher : API_MATCHER_MAP.values()) {
+ set.add(matcher.getApiDefinition());
+ }
+ return set;
+ }
+
+ static synchronized void loadApiDefinitions(/*@Valid*/ Set definitions) {
+ if (definitions == null || definitions.isEmpty()) {
+ API_MATCHER_MAP.clear();
+ return;
+ }
+ for (ApiDefinition definition : definitions) {
+ addApiDefinition(definition);
+ }
+ }
+
+ static void addApiDefinition(ApiDefinition definition) {
+ API_MATCHER_MAP.put(definition.getApiName(), new HttpRequestMessageApiMatcher(definition));
+ }
+
+ private ZuulGatewayApiMatcherManager() {}
+}
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/matcher/HttpRequestMessageApiMatcher.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/matcher/HttpRequestMessageApiMatcher.java
new file mode 100644
index 0000000000..12924da7ab
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/matcher/HttpRequestMessageApiMatcher.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 1999-2019 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.gateway.zuul2.api.matcher;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.matcher.AbstractApiMatcher;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul2.api.route.ZuulRouteMatchers;
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.csp.sentinel.util.function.Predicate;
+import com.netflix.zuul.message.http.HttpRequestMessage;
+
+/**
+ * @author wavesZh
+ */
+public class HttpRequestMessageApiMatcher extends AbstractApiMatcher {
+
+ public HttpRequestMessageApiMatcher(ApiDefinition apiDefinition) {
+ super(apiDefinition);
+ }
+
+ @Override
+ protected void initializeMatchers() {
+ if (apiDefinition.getPredicateItems() != null) {
+ for (ApiPredicateItem item : apiDefinition.getPredicateItems()) {
+ Predicate predicate = fromApiPredicate(item);
+ if (predicate != null) {
+ matchers.add(predicate);
+ }
+ }
+ }
+ }
+
+ private Predicate fromApiPredicate(/*@NonNull*/ ApiPredicateItem item) {
+ if (item instanceof ApiPathPredicateItem) {
+ return fromApiPathPredicate((ApiPathPredicateItem)item);
+ }
+ return null;
+ }
+
+ private Predicate fromApiPathPredicate(/*@Valid*/ ApiPathPredicateItem item) {
+ String pattern = item.getPattern();
+ if (StringUtil.isBlank(pattern)) {
+ return null;
+ }
+ switch (item.getMatchStrategy()) {
+ case SentinelGatewayConstants.URL_MATCH_STRATEGY_REGEX:
+ return ZuulRouteMatchers.regexPath(pattern);
+ case SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX:
+ return ZuulRouteMatchers.antPath(pattern);
+ default:
+ return ZuulRouteMatchers.exactPath(pattern);
+ }
+ }
+}
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/route/PrefixRoutePathMatcher.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/route/PrefixRoutePathMatcher.java
new file mode 100644
index 0000000000..2b979bc8a0
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/route/PrefixRoutePathMatcher.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1999-2019 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.gateway.zuul2.api.route;
+
+import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.util.function.Predicate;
+import com.netflix.zuul.message.http.HttpRequestMessage;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.PathMatcher;
+
+/**
+ * @author wavesZh
+ */
+public class PrefixRoutePathMatcher implements Predicate {
+
+ private final String pattern;
+
+ private final PathMatcher pathMatcher;
+ private final boolean canMatch;
+
+ public PrefixRoutePathMatcher(String pattern) {
+ AssertUtil.assertNotBlank(pattern, "pattern cannot be blank");
+ this.pattern = pattern;
+ this.pathMatcher = new AntPathMatcher();
+ this.canMatch = pathMatcher.isPattern(pattern);
+ }
+
+ @Override
+ public boolean test(HttpRequestMessage context) {
+ String path = context.getPath();
+ if (canMatch) {
+ return pathMatcher.match(pattern, path);
+ }
+ return false;
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+}
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/route/RegexRoutePathMatcher.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/route/RegexRoutePathMatcher.java
new file mode 100644
index 0000000000..fd88e6489b
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/route/RegexRoutePathMatcher.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2019 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.gateway.zuul2.api.route;
+
+import java.util.regex.Pattern;
+
+import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.util.function.Predicate;
+import com.netflix.zuul.message.http.HttpRequestMessage;
+
+/**
+ * @author wavesZh
+ */
+public class RegexRoutePathMatcher implements Predicate {
+
+ private final String pattern;
+ private final Pattern regex;
+
+ public RegexRoutePathMatcher(String pattern) {
+ AssertUtil.assertNotBlank(pattern, "pattern cannot be blank");
+ this.pattern = pattern;
+ this.regex = Pattern.compile(pattern);
+ }
+
+ @Override
+ public boolean test(HttpRequestMessage input) {
+ String path = input.getInboundRequest().getPath();
+ return regex.matcher(path).matches();
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/route/ZuulRouteMatchers.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/route/ZuulRouteMatchers.java
new file mode 100644
index 0000000000..7d62c63e45
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/route/ZuulRouteMatchers.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999-2019 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.gateway.zuul2.api.route;
+
+import com.alibaba.csp.sentinel.util.function.Predicate;
+import com.netflix.zuul.message.http.HttpRequestMessage;
+
+/**
+ * @author wavesZh
+ */
+public final class ZuulRouteMatchers {
+
+ public static Predicate all() {
+ return requestContext -> true;
+ }
+
+ public static Predicate antPath(String pathPattern) {
+ return new PrefixRoutePathMatcher(pathPattern);
+ }
+
+ public static Predicate exactPath(final String path) {
+ return exchange -> exchange.getPath().equals(path);
+ }
+
+ public static Predicate regexPath(String pathPattern) {
+ return new RegexRoutePathMatcher(pathPattern);
+ }
+
+ private ZuulRouteMatchers() {}
+}
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/constants/SentinelZuul2Constants.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/constants/SentinelZuul2Constants.java
new file mode 100644
index 0000000000..0e01d490f1
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/constants/SentinelZuul2Constants.java
@@ -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.gateway.zuul2.constants;
+
+/**
+ * @author wavesZh
+ */
+public class SentinelZuul2Constants {
+ /**
+ * The default entrance (context) name when the routeId is empty.
+ */
+ public static final String ZUUL_DEFAULT_CONTEXT = "zuul2_default_context";
+ /**
+ * Zuul context key for keeping Sentinel entries.
+ */
+ public static final String ZUUL_CTX_SENTINEL_ENTRIES_KEY = "_sentinel_entries";
+
+ public static final String ZUUL_CTX_SENTINEL_FALLBACK_ROUTE = "_sentinel_fallback_route";
+ /**
+ * Indicate if request is blocked .
+ */
+ public static final String ZUUL_CTX_SENTINEL_BLOCKED_FLAG = "_sentinel_blocked_flag";
+
+ private SentinelZuul2Constants() {}
+}
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/BlockResponse.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/BlockResponse.java
new file mode 100644
index 0000000000..5209f2329a
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/BlockResponse.java
@@ -0,0 +1,72 @@
+/*
+ * 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.gateway.zuul2.fallback;
+
+/**
+ * Fall back response for {@link com.alibaba.csp.sentinel.slots.block.BlockException}
+ *
+ * @author tiger
+ */
+public class BlockResponse {
+
+ /**
+ * HTTP status code.
+ */
+ private int code;
+
+ private String message;
+ private String route;
+
+ public BlockResponse(int code, String message, String route) {
+ this.code = code;
+ this.message = message;
+ this.route = route;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public String getRoute() {
+ return route;
+ }
+
+ public void setRoute(String route) {
+ this.route = route;
+ }
+
+ @Override
+ public String toString() {
+ return "{" +
+ "\"code\":" + code +
+ ", \"message\":" + "\"" + message + "\"" +
+ ", \"route\":" + "\"" + route + "\"" +
+ '}';
+ }
+}
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/DefaultBlockFallbackProvider.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/DefaultBlockFallbackProvider.java
new file mode 100644
index 0000000000..222630d539
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/DefaultBlockFallbackProvider.java
@@ -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.adapter.gateway.zuul2.fallback;
+
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/**
+ * Default fallback provider for Sentinel {@link BlockException}, {@literal *} meant for all routes.
+ *
+ * @author tiger
+ */
+public class DefaultBlockFallbackProvider implements ZuulBlockFallbackProvider {
+
+ @Override
+ public String getRoute() {
+ return "*";
+ }
+
+ @Override
+ public BlockResponse fallbackResponse(String route, Throwable cause) {
+ if (cause instanceof BlockException) {
+ return new BlockResponse(429, "SentinelBlockException", route);
+ } else {
+ return new BlockResponse(500, "System Error", route);
+ }
+ }
+}
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/ZuulBlockFallbackManager.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/ZuulBlockFallbackManager.java
new file mode 100644
index 0000000000..4e94e03aea
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/ZuulBlockFallbackManager.java
@@ -0,0 +1,60 @@
+/*
+ * 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.gateway.zuul2.fallback;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.csp.sentinel.util.AssertUtil;
+
+/**
+ * This provide fall back class manager.
+ *
+ * @author tiger
+ */
+public class ZuulBlockFallbackManager {
+
+ private static Map fallbackProviderCache = new HashMap<>();
+
+ private static ZuulBlockFallbackProvider defaultFallbackProvider = new DefaultBlockFallbackProvider();
+
+ /**
+ * Register special provider for different route.
+ */
+ public static synchronized void registerProvider(ZuulBlockFallbackProvider provider) {
+ AssertUtil.notNull(provider, "fallback provider cannot be null");
+ String route = provider.getRoute();
+ if ("*".equals(route) || route == null) {
+ defaultFallbackProvider = provider;
+ } else {
+ fallbackProviderCache.put(route, provider);
+ }
+ }
+
+ public static ZuulBlockFallbackProvider getFallbackProvider(String route) {
+ ZuulBlockFallbackProvider provider = fallbackProviderCache.get(route);
+ if (provider == null) {
+ provider = defaultFallbackProvider;
+ }
+ return provider;
+ }
+
+ public synchronized static void clear(){
+ fallbackProviderCache.clear();
+ }
+
+}
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/ZuulBlockFallbackProvider.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/ZuulBlockFallbackProvider.java
new file mode 100644
index 0000000000..ee6f51f391
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/ZuulBlockFallbackProvider.java
@@ -0,0 +1,40 @@
+/*
+ * 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.gateway.zuul2.fallback;
+
+/**
+ * This interface is compatible for different spring cloud version.
+ *
+ * @author tiger
+ */
+public interface ZuulBlockFallbackProvider {
+
+ /**
+ * The route this fallback will be used for.
+ * @return The route the fallback will be used for.
+ */
+ String getRoute();
+
+ /**
+ * Provides a fallback response based on the cause of the failed execution.
+ *
+ * @param route The route the fallback is for
+ * @param cause cause of the main method failure, may be null
+ * @return the fallback response
+ */
+ BlockResponse fallbackResponse(String route, Throwable cause);
+}
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/EntryHolder.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/EntryHolder.java
new file mode 100644
index 0000000000..59fb2ef987
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/EntryHolder.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2019 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.gateway.zuul2.filters;
+
+import com.alibaba.csp.sentinel.Entry;
+
+/**
+ * @author wavesZh
+ */
+public class EntryHolder {
+
+ final private Entry entry;
+
+ final private Object[] params;
+
+ public EntryHolder(Entry entry, Object[] params) {
+ this.entry = entry;
+ this.params = params;
+ }
+
+ public Entry getEntry() {
+ return entry;
+ }
+
+ public Object[] getParams() {
+ return params;
+ }
+}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/endpoint/SentinelZuulEndpoint.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/endpoint/SentinelZuulEndpoint.java
new file mode 100644
index 0000000000..6713127415
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/endpoint/SentinelZuulEndpoint.java
@@ -0,0 +1,49 @@
+/*
+ * 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.gateway.zuul2.filters.endpoint;
+
+import com.alibaba.csp.sentinel.adapter.gateway.zuul2.constants.SentinelZuul2Constants;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul2.fallback.BlockResponse;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul2.fallback.ZuulBlockFallbackManager;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul2.fallback.ZuulBlockFallbackProvider;
+
+import com.netflix.zuul.context.SessionContext;
+import com.netflix.zuul.filters.http.HttpSyncEndpoint;
+import com.netflix.zuul.message.http.HttpRequestMessage;
+import com.netflix.zuul.message.http.HttpResponseMessage;
+import com.netflix.zuul.message.http.HttpResponseMessageImpl;
+
+/**
+ * Default Endpoint for handling exception.
+ *
+ * @author wavesZh
+ */
+public class SentinelZuulEndpoint extends HttpSyncEndpoint {
+
+ @Override
+ public HttpResponseMessage apply(HttpRequestMessage request) {
+ SessionContext context = request.getContext();
+ Throwable throwable = context.getError();
+ String fallBackRoute = (String) context.get(SentinelZuul2Constants.ZUUL_CTX_SENTINEL_FALLBACK_ROUTE);
+ ZuulBlockFallbackProvider zuulBlockFallbackProvider = ZuulBlockFallbackManager
+ .getFallbackProvider(fallBackRoute);
+ BlockResponse response = zuulBlockFallbackProvider.fallbackResponse(fallBackRoute, throwable);
+ HttpResponseMessage resp = new HttpResponseMessageImpl(context, request, response.getCode());
+ resp.setBodyAsText(response.toString());
+ return resp;
+ }
+}
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/inbound/SentinelZuulInboundFilter.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/inbound/SentinelZuulInboundFilter.java
new file mode 100644
index 0000000000..ff17453889
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/inbound/SentinelZuulInboundFilter.java
@@ -0,0 +1,182 @@
+/*
+ * 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.gateway.zuul2.filters.inbound;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.Function;
+
+import com.alibaba.csp.sentinel.AsyncEntry;
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.ResourceTypeConstants;
+import com.alibaba.csp.sentinel.SphU;
+import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul2.HttpRequestMessageItemParser;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul2.api.ZuulGatewayApiMatcherManager;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul2.api.matcher.HttpRequestMessageApiMatcher;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul2.constants.SentinelZuul2Constants;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul2.filters.EntryHolder;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul2.filters.endpoint.SentinelZuulEndpoint;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.netflix.zuul.context.SessionContext;
+import com.netflix.zuul.filters.http.HttpInboundFilter;
+import com.netflix.zuul.message.http.HttpRequestMessage;
+import rx.Observable;
+import rx.schedulers.Schedulers;
+
+import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*;
+
+/**
+ * The Zuul inbound filter wrapped with Sentinel route and customized API group entries.
+ *
+ * @author wavesZh
+ */
+public class SentinelZuulInboundFilter extends HttpInboundFilter {
+
+ private static final String DEFAULT_BLOCK_ENDPOINT_NAME = SentinelZuulEndpoint.class.getCanonicalName();
+
+ private final int order;
+
+ private final String blockedEndpointName;
+ /**
+ * If the executor is null, flow control action will be performed on I/O thread
+ */
+ private final Executor executor;
+ /**
+ * If true, the rest of inbound filters will be skipped when the request is blocked.
+ */
+ private final boolean fastError;
+ private final Function routeExtractor;
+
+ private final GatewayParamParser paramParser = new GatewayParamParser<>(
+ new HttpRequestMessageItemParser());
+
+ /**
+ * Constructor of the inbound filter, which extracts the route from the context route VIP attribute by default.
+ *
+ * @param order the order of the filter
+ */
+ public SentinelZuulInboundFilter(int order) {
+ this(order, m -> m.getContext().getRouteVIP());
+ }
+
+ public SentinelZuulInboundFilter(int order, Function routeExtractor) {
+ this(order, null, routeExtractor);
+ }
+
+ public SentinelZuulInboundFilter(int order, Executor executor, Function routeExtractor) {
+ this(order, DEFAULT_BLOCK_ENDPOINT_NAME, executor, true, routeExtractor);
+ }
+
+ /**
+ * Constructor of the inbound filter.
+ *
+ * @param order the order of the filter
+ * @param blockedEndpointName the endpoint to go when the request is blocked
+ * @param executor the executor where Sentinel do flow checking. If null, it will be executed in current thread.
+ * @param fastError whether the rest of the filters will be skipped if the request is blocked
+ * @param routeExtractor the route ID extractor
+ */
+ public SentinelZuulInboundFilter(int order, String blockedEndpointName, Executor executor, boolean fastError,
+ Function routeExtractor) {
+ AssertUtil.notEmpty(blockedEndpointName, "blockedEndpointName cannot be empty");
+ AssertUtil.notNull(routeExtractor, "routeExtractor cannot be null");
+ this.order = order;
+ this.blockedEndpointName = blockedEndpointName;
+ this.executor = executor;
+ this.fastError = fastError;
+ this.routeExtractor = routeExtractor;
+ }
+
+ @Override
+ public int filterOrder() {
+ return order;
+ }
+
+ @Override
+ public Observable applyAsync(HttpRequestMessage request) {
+ if (executor != null) {
+ return Observable.just(request).subscribeOn(Schedulers.from(executor)).flatMap(this::apply);
+ } else {
+ return Observable.just(request).flatMap(this::apply);
+ }
+ }
+
+ private Observable apply(HttpRequestMessage request) {
+ SessionContext context = request.getContext();
+ Deque holders = new ArrayDeque<>();
+ String routeId = routeExtractor.apply(request);
+ String fallBackRoute = routeId;
+ try {
+ if (StringUtil.isNotBlank(routeId)) {
+ ContextUtil.enter(GATEWAY_CONTEXT_ROUTE_PREFIX + routeId);
+ doSentinelEntry(routeId, RESOURCE_MODE_ROUTE_ID, request, holders);
+ }
+ Set matchingApis = pickMatchingApiDefinitions(request);
+ if (!matchingApis.isEmpty() && ContextUtil.getContext() == null) {
+ ContextUtil.enter(SentinelZuul2Constants.ZUUL_DEFAULT_CONTEXT);
+ }
+ for (String apiName : matchingApis) {
+ fallBackRoute = apiName;
+ doSentinelEntry(apiName, RESOURCE_MODE_CUSTOM_API_NAME, request, holders);
+ }
+ return Observable.just(request);
+ } catch (BlockException t) {
+ context.put(SentinelZuul2Constants.ZUUL_CTX_SENTINEL_BLOCKED_FLAG, Boolean.TRUE);
+ context.put(SentinelZuul2Constants.ZUUL_CTX_SENTINEL_FALLBACK_ROUTE, fallBackRoute);
+ if (fastError) {
+ context.setShouldSendErrorResponse(true);
+ context.setErrorEndpoint(blockedEndpointName);
+ } else {
+ context.setEndpoint(blockedEndpointName);
+ }
+ return Observable.error(t);
+ } finally {
+ if (!holders.isEmpty()) {
+ context.put(SentinelZuul2Constants.ZUUL_CTX_SENTINEL_ENTRIES_KEY, holders);
+ }
+ // clear context to avoid another request use incorrect context
+ ContextUtil.exit();
+ }
+ }
+
+ private void doSentinelEntry(String resourceName, final int resType, HttpRequestMessage input, Deque holders) throws BlockException {
+ Object[] params = paramParser.parseParameterFor(resourceName, input, r -> r.getResourceMode() == resType);
+ AsyncEntry entry = SphU.asyncEntry(resourceName, ResourceTypeConstants.COMMON_API_GATEWAY, EntryType.IN, params);
+ holders.push(new EntryHolder(entry, params));
+ }
+
+ private Set pickMatchingApiDefinitions(HttpRequestMessage message) {
+ Set apis = new HashSet<>();
+ for (HttpRequestMessageApiMatcher matcher : ZuulGatewayApiMatcherManager.getApiMatcherMap().values()) {
+ if (matcher.test(message)) {
+ apis.add(matcher.getApiName());
+ }
+ }
+ return apis;
+ }
+
+ @Override
+ public boolean shouldFilter(HttpRequestMessage msg) {
+ return true;
+ }
+}
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/outbound/SentinelZuulOutboundFilter.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/outbound/SentinelZuulOutboundFilter.java
new file mode 100644
index 0000000000..9946fc256e
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/outbound/SentinelZuulOutboundFilter.java
@@ -0,0 +1,78 @@
+/*
+ * 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.gateway.zuul2.filters.outbound;
+
+import java.util.Deque;
+
+import com.alibaba.csp.sentinel.Tracer;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul2.constants.SentinelZuul2Constants;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul2.filters.EntryHolder;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.netflix.zuul.context.SessionContext;
+import com.netflix.zuul.filters.http.HttpOutboundFilter;
+import com.netflix.zuul.message.http.HttpResponseMessage;
+import rx.Observable;
+
+/**
+ * The Zuul outbound filter which will complete the Sentinel entries and
+ * trace the exception that happened in previous filters.
+ *
+ * @author wavesZh
+ */
+public class SentinelZuulOutboundFilter extends HttpOutboundFilter {
+
+ private final int order;
+
+ public SentinelZuulOutboundFilter(int order) {
+ this.order = order;
+ }
+
+ @Override
+ public int filterOrder() {
+ return order;
+ }
+
+ @Override
+ public Observable applyAsync(HttpResponseMessage input) {
+ return Observable.just(apply(input));
+ }
+
+ public HttpResponseMessage apply(HttpResponseMessage response) {
+ SessionContext context = response.getContext();
+
+ if (context.get(SentinelZuul2Constants.ZUUL_CTX_SENTINEL_ENTRIES_KEY) == null) {
+ return response;
+ }
+ boolean previousBlocked = context.getFilterErrors().stream()
+ .anyMatch(e -> BlockException.isBlockException(e.getException()));
+ Deque holders = (Deque) context.get(SentinelZuul2Constants.ZUUL_CTX_SENTINEL_ENTRIES_KEY);
+ while (!holders.isEmpty()) {
+ EntryHolder holder = holders.pop();
+ if (!previousBlocked) {
+ Tracer.traceEntry(context.getError(), holder.getEntry());
+ holder.getEntry().exit(1, holder.getParams());
+ }
+ }
+ return response;
+ }
+
+
+ @Override
+ public boolean shouldFilter(HttpResponseMessage msg) {
+ return true;
+ }
+}
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinitionChangeObserver b/sentinel-adapter/sentinel-zuul2-adapter/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinitionChangeObserver
new file mode 100644
index 0000000000..6c27b158ed
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinitionChangeObserver
@@ -0,0 +1 @@
+com.alibaba.csp.sentinel.adapter.gateway.zuul2.api.ZuulApiDefinitionChangeObserver
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/ZuulBlockFallbackManagerTest.java b/sentinel-adapter/sentinel-zuul2-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/ZuulBlockFallbackManagerTest.java
new file mode 100644
index 0000000000..1b0416d5a2
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/ZuulBlockFallbackManagerTest.java
@@ -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.gateway.zuul2.fallback;
+
+import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author tiger
+ */
+public class ZuulBlockFallbackManagerTest {
+
+ private String ROUTE = "/test";
+
+ private String DEFAULT_ROUTE = "*";
+
+ class MyNullResponseFallBackProvider implements ZuulBlockFallbackProvider {
+ @Override
+ public String getRoute() {
+ return ROUTE;
+ }
+
+ @Override
+ public BlockResponse fallbackResponse(String route, Throwable cause) {
+ return null;
+ }
+ }
+
+ @Test
+ public void testRegisterProvider() throws Exception {
+ MyNullResponseFallBackProvider myNullResponseFallBackProvider = new MyNullResponseFallBackProvider();
+ ZuulBlockFallbackManager.registerProvider(myNullResponseFallBackProvider);
+ Assert.assertEquals(myNullResponseFallBackProvider.getRoute(), ROUTE);
+ Assert.assertNull(myNullResponseFallBackProvider.fallbackResponse(ROUTE, new FlowException("flow ex")));
+ }
+
+ @Test
+ public void clear() {
+ MyNullResponseFallBackProvider myNullResponseFallBackProvider = new MyNullResponseFallBackProvider();
+ ZuulBlockFallbackManager.registerProvider(myNullResponseFallBackProvider);
+ Assert.assertEquals(myNullResponseFallBackProvider.getRoute(), ROUTE);
+ ZuulBlockFallbackManager.clear();
+ Assert.assertEquals(ZuulBlockFallbackManager.getFallbackProvider(ROUTE).getRoute(), DEFAULT_ROUTE);
+ }
+
+}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/ZuulBlockFallbackProviderTest.java b/sentinel-adapter/sentinel-zuul2-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/ZuulBlockFallbackProviderTest.java
new file mode 100644
index 0000000000..59f6d74a19
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul2-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/ZuulBlockFallbackProviderTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.gateway.zuul2.fallback;
+
+import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author tiger
+ */
+public class ZuulBlockFallbackProviderTest {
+
+ private String ALL_ROUTE = "*";
+
+ @Test
+ public void testGetNullRoute() throws Exception {
+ ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(null);
+ Assert.assertEquals(fallbackProvider.getRoute(), ALL_ROUTE);
+ }
+
+ @Test
+ public void testGetDefaultRoute() throws Exception {
+ ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(ALL_ROUTE);
+ Assert.assertEquals(fallbackProvider.getRoute(), ALL_ROUTE);
+ }
+
+ @Test
+ public void testGetNotInCacheRoute() throws Exception {
+ ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider("/not/in");
+ Assert.assertEquals(fallbackProvider.getRoute(), ALL_ROUTE);
+ }
+
+ @Test
+ public void testFlowControlFallbackResponse() throws Exception {
+ ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(ALL_ROUTE);
+ BlockResponse clientHttpResponse = fallbackProvider.fallbackResponse(ALL_ROUTE,
+ new FlowException("flow exception"));
+ Assert.assertEquals(clientHttpResponse.getCode(), 429);
+ }
+
+ @Test
+ public void testRuntimeExceptionFallbackResponse() throws Exception {
+ ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(ALL_ROUTE);
+ BlockResponse clientHttpResponse = fallbackProvider.fallbackResponse(ALL_ROUTE, new RuntimeException());
+ Assert.assertEquals(clientHttpResponse.getCode(), 500);
+ }
+}
\ No newline at end of file
diff --git a/sentinel-benchmark/pom.xml b/sentinel-benchmark/pom.xml
index 50410f2dbd..e0c14884ab 100644
--- a/sentinel-benchmark/pom.xml
+++ b/sentinel-benchmark/pom.xml
@@ -5,7 +5,7 @@
sentinel-parentcom.alibaba.csp
- 1.7.2-SNAPSHOT
+ 1.8.1-SNAPSHOT4.0.0
diff --git a/sentinel-cluster/pom.xml b/sentinel-cluster/pom.xml
index 52170486a0..5bde8cc759 100644
--- a/sentinel-cluster/pom.xml
+++ b/sentinel-cluster/pom.xml
@@ -5,7 +5,7 @@
sentinel-parentcom.alibaba.csp
- 1.7.2-SNAPSHOT
+ 1.8.1-SNAPSHOT4.0.0pom
diff --git a/sentinel-cluster/sentinel-cluster-client-default/pom.xml b/sentinel-cluster/sentinel-cluster-client-default/pom.xml
index 0775cb3196..630c1ff6df 100644
--- a/sentinel-cluster/sentinel-cluster-client-default/pom.xml
+++ b/sentinel-cluster/sentinel-cluster-client-default/pom.xml
@@ -5,7 +5,7 @@
sentinel-clustercom.alibaba.csp
- 1.7.2-SNAPSHOT
+ 1.8.1-SNAPSHOT4.0.0
diff --git a/sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/DefaultClusterTokenClient.java b/sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/DefaultClusterTokenClient.java
index ecc727a5b2..1a0acf6c66 100644
--- a/sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/DefaultClusterTokenClient.java
+++ b/sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/DefaultClusterTokenClient.java
@@ -79,7 +79,7 @@ private void initNewConnection() {
try {
this.transportClient = new NettyTransportClient(host, port);
this.serverDescriptor = new TokenServerDescriptor(host, port);
- RecordLog.info("[DefaultClusterTokenClient] New client created: " + serverDescriptor);
+ RecordLog.info("[DefaultClusterTokenClient] New client created: {}", serverDescriptor);
} catch (Exception ex) {
RecordLog.warn("[DefaultClusterTokenClient] Failed to initialize new token client", ex);
}
@@ -97,7 +97,7 @@ private void changeServer(/*@Valid*/ ClusterClientAssignConfig config) {
this.transportClient = new NettyTransportClient(config.getServerHost(), config.getServerPort());
this.serverDescriptor = new TokenServerDescriptor(config.getServerHost(), config.getServerPort());
startClientIfScheduled();
- RecordLog.info("[DefaultClusterTokenClient] New client created: " + serverDescriptor);
+ RecordLog.info("[DefaultClusterTokenClient] New client created: {}", serverDescriptor);
} catch (Exception ex) {
RecordLog.warn("[DefaultClusterTokenClient] Failed to change remote token server", ex);
}
@@ -182,6 +182,15 @@ public TokenResult requestParamToken(Long flowId, int acquireCount, Collection");
+ RecordLog.info("[NettyTransportClient] Successfully connect to server <{}:{}>", host, port);
}
}
});
@@ -144,7 +143,7 @@ public void run() {
@Override
public void run() {
if (shouldRetry.get()) {
- RecordLog.info("[NettyTransportClient] Reconnecting to server <" + host + ":" + port + ">");
+ RecordLog.info("[NettyTransportClient] Reconnecting to server <{}:{}>", host, port);
try {
startInternal();
} catch (Exception e) {
@@ -238,10 +237,12 @@ public ClusterResponse sendRequest(ClusterRequest request) throws Exception {
}
private int getCurrentId() {
- if (idGenerator.get() > MAX_ID) {
- idGenerator.set(0);
- }
- return idGenerator.incrementAndGet();
+ int pre, next;
+ do {
+ pre = idGenerator.get();
+ next = pre >= MAX_ID ? MIN_ID : pre + 1;
+ } while (!idGenerator.compareAndSet(pre, next));
+ return next;
}
/*public CompletableFuture sendRequestAsync(ClusterRequest request) {
@@ -266,5 +267,6 @@ private int getCurrentId() {
return future;
}*/
+ private static final int MIN_ID = 1;
private static final int MAX_ID = 999_999_999;
}
diff --git a/sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/ClientEntityCodecProvider.java b/sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/ClientEntityCodecProvider.java
index 6cb0ebda09..e27ce68223 100644
--- a/sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/ClientEntityCodecProvider.java
+++ b/sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/ClientEntityCodecProvider.java
@@ -39,14 +39,16 @@ private static void resolveInstance() {
RecordLog.warn("[ClientEntityCodecProvider] No existing request entity writer, resolve failed");
} else {
requestEntityWriter = writer;
- RecordLog.info("[ClientEntityCodecProvider] Request entity writer resolved: " + requestEntityWriter.getClass().getCanonicalName());
+ RecordLog.info("[ClientEntityCodecProvider] Request entity writer resolved: {}",
+ requestEntityWriter.getClass().getCanonicalName());
}
ResponseEntityDecoder decoder = SpiLoader.loadFirstInstance(ResponseEntityDecoder.class);
if (decoder == null) {
RecordLog.warn("[ClientEntityCodecProvider] No existing response entity decoder, resolve failed");
} else {
responseEntityDecoder = decoder;
- RecordLog.info("[ClientEntityCodecProvider] Response entity decoder resolved: " + responseEntityDecoder.getClass().getCanonicalName());
+ RecordLog.info("[ClientEntityCodecProvider] Response entity decoder resolved: {}",
+ responseEntityDecoder.getClass().getCanonicalName());
}
}
diff --git a/sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/DefaultRequestEntityWriter.java b/sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/DefaultRequestEntityWriter.java
index 0ba9680f69..62572f311b 100644
--- a/sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/DefaultRequestEntityWriter.java
+++ b/sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/DefaultRequestEntityWriter.java
@@ -36,7 +36,7 @@ public void writeTo(ClusterRequest request, ByteBuf target) {
EntityWriter
*
- * | flow ID (4) | count (4) | priority flag (1) |
+ * | flow ID (8) | count (4) | priority flag (1) |
*
*
* @author Eric Zhao
diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/ModifyClusterFlowRulesCommandHandler.java b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/ModifyClusterFlowRulesCommandHandler.java
index 66833cb5d1..135a3d89cf 100644
--- a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/ModifyClusterFlowRulesCommandHandler.java
+++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/ModifyClusterFlowRulesCommandHandler.java
@@ -47,7 +47,7 @@ public CommandResponse handle(CommandRequest request) {
}
try {
data = URLDecoder.decode(data, "UTF-8");
- RecordLog.info("[ModifyClusterFlowRulesCommandHandler] Receiving cluster flow rules for namespace <{0}>: {1}", namespace, data);
+ RecordLog.info("[ModifyClusterFlowRulesCommandHandler] Receiving cluster flow rules for namespace <{}>: {}", namespace, data);
List flowRules = JSONArray.parseArray(data, FlowRule.class);
ClusterFlowRuleManager.loadRules(namespace, flowRules);
diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/ModifyClusterParamFlowRulesCommandHandler.java b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/ModifyClusterParamFlowRulesCommandHandler.java
index 393950f786..1a46cd6e48 100644
--- a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/ModifyClusterParamFlowRulesCommandHandler.java
+++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/ModifyClusterParamFlowRulesCommandHandler.java
@@ -47,7 +47,7 @@ public CommandResponse handle(CommandRequest request) {
}
try {
data = URLDecoder.decode(data, "UTF-8");
- RecordLog.info("[ModifyClusterParamFlowRulesCommandHandler] Receiving cluster param rules for namespace <{0}>: {1}", namespace, data);
+ RecordLog.info("Receiving cluster param rules for namespace <{}> from command handler: {}", namespace, data);
List flowRules = JSONArray.parseArray(data, ParamFlowRule.class);
ClusterParamFlowRuleManager.loadRules(namespace, flowRules);
diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/ModifyClusterServerFlowConfigHandler.java b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/ModifyClusterServerFlowConfigHandler.java
index 228851051f..c5802421e9 100644
--- a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/ModifyClusterServerFlowConfigHandler.java
+++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/ModifyClusterServerFlowConfigHandler.java
@@ -46,14 +46,14 @@ public CommandResponse handle(CommandRequest request) {
data = URLDecoder.decode(data, "utf-8");
if (StringUtil.isEmpty(namespace)) {
- RecordLog.info("[ModifyClusterServerFlowConfigHandler] Receiving cluster server global flow config: " + data);
+ RecordLog.info("[ModifyClusterServerFlowConfigHandler] Receiving cluster server global flow config: {}", data);
ServerFlowConfig config = JSON.parseObject(data, ServerFlowConfig.class);
if (!ClusterServerConfigManager.isValidFlowConfig(config)) {
CommandResponse.ofFailure(new IllegalArgumentException("Bad flow config"));
}
ClusterServerConfigManager.loadGlobalFlowConfig(config);
} else {
- RecordLog.info("[ModifyClusterServerFlowConfigHandler] Receiving cluster server flow config for namespace <{0}>: {1}", namespace, data);
+ RecordLog.info("[ModifyClusterServerFlowConfigHandler] Receiving cluster server flow config for namespace <{}>: {}", namespace, data);
ServerFlowConfig config = JSON.parseObject(data, ServerFlowConfig.class);
if (!ClusterServerConfigManager.isValidFlowConfig(config)) {
CommandResponse.ofFailure(new IllegalArgumentException("Bad flow config"));
diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/ModifyServerNamespaceSetHandler.java b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/ModifyServerNamespaceSetHandler.java
index 2891623c70..4e6da032d6 100644
--- a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/ModifyServerNamespaceSetHandler.java
+++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/ModifyServerNamespaceSetHandler.java
@@ -44,7 +44,7 @@ public CommandResponse handle(CommandRequest request) {
}
try {
data = URLDecoder.decode(data, "utf-8");
- RecordLog.info("[ModifyServerNamespaceSetHandler] Receiving cluster server namespace set: " + data);
+ RecordLog.info("[ModifyServerNamespaceSetHandler] Receiving cluster server namespace set: {}", data);
Set set = JSON.parseObject(data, new TypeReference>() {});
ClusterServerConfigManager.loadServerNamespaceSet(set);
return CommandResponse.ofSuccess("success");
diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/config/ClusterServerConfigManager.java b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/config/ClusterServerConfigManager.java
index b3d3fcd651..5da3b78223 100644
--- a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/config/ClusterServerConfigManager.java
+++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/config/ClusterServerConfigManager.java
@@ -219,7 +219,7 @@ private static void applyNamespaceSetChange(Set newSet) {
if (newSet == null) {
return;
}
- RecordLog.info("[ClusterServerConfigManager] Server namespace set will be update to: " + newSet);
+ RecordLog.info("[ClusterServerConfigManager] Server namespace set will be update to: {}", newSet);
if (newSet.isEmpty()) {
ClusterServerConfigManager.namespaceSet = Collections.singleton(ServerConstants.DEFAULT_NAMESPACE);
return;
@@ -276,10 +276,10 @@ public void configUpdate(ServerTransportConfig config) {
private synchronized void applyConfig(ServerTransportConfig config) {
if (!isValidTransportConfig(config)) {
RecordLog.warn(
- "[ClusterServerConfigManager] Invalid cluster server transport config, ignoring: " + config);
+ "[ClusterServerConfigManager] Invalid cluster server transport config, ignoring: {}", config);
return;
}
- RecordLog.info("[ClusterServerConfigManager] Updating new server transport config: " + config);
+ RecordLog.info("[ClusterServerConfigManager] Updating new server transport config: {}", config);
if (config.getIdleSeconds() != idleSeconds) {
idleSeconds = config.getIdleSeconds();
}
@@ -315,10 +315,10 @@ public void configLoad(ServerFlowConfig config) {
private synchronized void applyGlobalFlowConfig(ServerFlowConfig config) {
if (!isValidFlowConfig(config)) {
RecordLog.warn(
- "[ClusterServerConfigManager] Invalid cluster server global flow config, ignoring: " + config);
+ "[ClusterServerConfigManager] Invalid cluster server global flow config, ignoring: {}", config);
return;
}
- RecordLog.info("[ClusterServerConfigManager] Updating new server global flow config: " + config);
+ RecordLog.info("[ClusterServerConfigManager] Updating new server global flow config: {}", config);
if (config.getExceedCount() != exceedCount) {
exceedCount = config.getExceedCount();
}
diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/connection/ConnectionManager.java b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/connection/ConnectionManager.java
index 37f88cedd7..aa3a87be00 100644
--- a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/connection/ConnectionManager.java
+++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/connection/ConnectionManager.java
@@ -73,7 +73,7 @@ public static void removeConnection(String address) {
return;
}
group.removeConnection(address);
- RecordLog.info("[ConnectionManager] Client <{0}> disconnected and removed from namespace <{1}>", address, namespace);
+ RecordLog.info("[ConnectionManager] Client <{}> disconnected and removed from namespace <{}>", address, namespace);
}
NAMESPACE_MAP.remove(address);
}
@@ -87,7 +87,7 @@ public static void removeConnection(String namespace, String address) {
}
group.removeConnection(address);
NAMESPACE_MAP.remove(address);
- RecordLog.info("[ConnectionManager] Client <{0}> disconnected and removed from namespace <{1}>", address, namespace);
+ RecordLog.info("[ConnectionManager] Client <{}> disconnected and removed from namespace <{}>", address, namespace);
}
public static ConnectionGroup addConnection(String namespace, String address) {
@@ -96,7 +96,7 @@ public static ConnectionGroup addConnection(String namespace, String address) {
ConnectionGroup group = getOrCreateGroup(namespace);
group.addConnection(address);
NAMESPACE_MAP.put(address, namespace);
- RecordLog.info("[ConnectionManager] Client <{0}> registered with namespace <{1}>", address, namespace);
+ RecordLog.info("[ConnectionManager] Client <{}> registered with namespace <{}>", address, namespace);
return group;
}
@@ -112,6 +112,9 @@ public static ConnectionGroup getConnectionGroup(String namespace) {
return group;
}
+ public static boolean isClientOnline(String address){
+ return NAMESPACE_MAP.containsKey(address);
+ }
static void clear() {
CONN_MAP.clear();
NAMESPACE_MAP.clear();
diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/connection/ScanIdleConnectionTask.java b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/connection/ScanIdleConnectionTask.java
index a559aeeb06..b3ed118072 100644
--- a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/connection/ScanIdleConnectionTask.java
+++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/connection/ScanIdleConnectionTask.java
@@ -31,10 +31,8 @@ public void run() {
List connections = connectionPool.listAllConnection();
for (Connection conn : connections) {
if ((now - conn.getLastReadTime()) > idleTimeMillis) {
- RecordLog.info(
- String.format("[ScanIdleConnectionTask] The connection <%s:%d> has been idle for <%d>s. "
- + "It will be closed now.", conn.getRemoteIP(), conn.getRemotePort(), idleSeconds)
- );
+ RecordLog.info("[ScanIdleConnectionTask] The connection <{}:{}> has been idle for <{}>s. It will be closed now.",
+ conn.getRemoteIP(), conn.getRemotePort(), idleSeconds);
conn.close();
}
}
diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/handler/TokenServerHandler.java b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/handler/TokenServerHandler.java
index da5375d5ed..44f5d6691c 100644
--- a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/handler/TokenServerHandler.java
+++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/handler/TokenServerHandler.java
@@ -47,7 +47,6 @@ public TokenServerHandler(ConnectionPool globalConnectionPool) {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
globalConnectionPool.createConnection(ctx.channel());
- String remoteAddress = getRemoteAddress(ctx);
}
@Override
diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/ConcurrentClusterFlowCheckerTest.java b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/ConcurrentClusterFlowCheckerTest.java
new file mode 100644
index 0000000000..3a7e2e9618
--- /dev/null
+++ b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/ConcurrentClusterFlowCheckerTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.cluster.flow;
+
+import com.alibaba.csp.sentinel.cluster.TokenResult;
+import com.alibaba.csp.sentinel.cluster.TokenResultStatus;
+import com.alibaba.csp.sentinel.cluster.flow.rule.ClusterFlowRuleManager;
+import com.alibaba.csp.sentinel.cluster.flow.statistic.concurrent.CurrentConcurrencyManager;
+import com.alibaba.csp.sentinel.cluster.flow.statistic.concurrent.TokenCacheNodeManager;
+import com.alibaba.csp.sentinel.cluster.server.connection.ConnectionManager;
+import com.alibaba.csp.sentinel.slots.block.ClusterRuleConstant;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.block.flow.ClusterFlowConfig;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+import com.alibaba.csp.sentinel.test.AbstractTimeBasedTest;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * @author yunfeiyanggzq
+ */
+public class ConcurrentClusterFlowCheckerTest extends AbstractTimeBasedTest {
+ @Before
+ public void setUp() {
+ FlowRule rule = new FlowRule();
+ ClusterFlowConfig config = new ClusterFlowConfig();
+ config.setResourceTimeout(500);
+ config.setClientOfflineTime(1000);
+ config.setFlowId(111L);
+ config.setThresholdType(ClusterRuleConstant.FLOW_THRESHOLD_GLOBAL);
+ rule.setClusterConfig(config);
+ rule.setClusterMode(true);
+ rule.setCount(10);
+ rule.setResource("test");
+ rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);
+ ArrayList rules = new ArrayList<>();
+ rules.add(rule);
+ ClusterFlowRuleManager.registerPropertyIfAbsent("1-name");
+ ClusterFlowRuleManager.loadRules("1-name", rules);
+ }
+
+ @Test
+ public void testEasyAcquireAndRelease() throws InterruptedException {
+ setCurrentMillis(System.currentTimeMillis());
+ FlowRule rule = ClusterFlowRuleManager.getFlowRuleById(111L);
+ ArrayList list = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ TokenResult result = ConcurrentClusterFlowChecker.acquireConcurrentToken("127.0.0.1", rule, 1);
+ Assert.assertTrue("fail to acquire token",
+ result.getStatus() == TokenResultStatus.OK && result.getTokenId() != 0);
+ list.add(result.getTokenId());
+ }
+ for (int i = 0; i < 10; i++) {
+ TokenResult result = ConcurrentClusterFlowChecker.acquireConcurrentToken("127.0.0.1", rule, 1);
+ Assert.assertTrue("fail to acquire block token",
+ result.getStatus() == TokenResultStatus.BLOCKED);
+ }
+ for (int i = 0; i < 10; i++) {
+ TokenResult result = ConcurrentClusterFlowChecker.releaseConcurrentToken(list.get(i));
+ Assert.assertTrue("fail to release token",
+ result.getStatus() == TokenResultStatus.RELEASE_OK);
+ }
+ Assert.assertTrue("fail to release token",
+ CurrentConcurrencyManager.get(111L).get() == 0 && TokenCacheNodeManager.getSize() == 0);
+ }
+
+ @Test
+ public void testConcurrentAcquireAndRelease() throws InterruptedException {
+ setCurrentMillis(System.currentTimeMillis());
+ final FlowRule rule = ClusterFlowRuleManager.getFlowRuleById(111L);
+ final CountDownLatch countDownLatch = new CountDownLatch(1000);
+ ExecutorService pool = Executors.newFixedThreadPool(100);
+
+ for (long i = 0; i < 1000; i++) {
+ Runnable task = new Runnable() {
+ @Override
+ public void run() {
+ assert rule != null;
+ TokenResult result = ConcurrentClusterFlowChecker.acquireConcurrentToken("127.0.0.1", rule, 1);
+ Assert.assertTrue("concurrent control fail", CurrentConcurrencyManager.get(111L).get() <= rule.getCount());
+ if (result.getStatus() == TokenResultStatus.OK) {
+ ConcurrentClusterFlowChecker.releaseConcurrentToken(result.getTokenId());
+ }
+ countDownLatch.countDown();
+ }
+ };
+ pool.execute(task);
+ }
+ countDownLatch.await();
+ pool.shutdown();
+ assert rule != null;
+ Assert.assertTrue("fail to acquire and release token",
+ CurrentConcurrencyManager.get(rule.getClusterConfig().getFlowId()).get() == 0 && TokenCacheNodeManager.getSize() == 0);
+ }
+
+ @Test
+ public void testReleaseExpiredToken() throws InterruptedException {
+ ConnectionManager.addConnection("test", "127.0.0.1");
+ FlowRule rule = ClusterFlowRuleManager.getFlowRuleById(111L);
+ for (int i = 0; i < 10; i++) {
+ ConcurrentClusterFlowChecker.acquireConcurrentToken("127.0.0.1", rule, 1);
+ }
+ Thread.sleep(3000);
+ Assert.assertTrue("fail to acquire and release token", CurrentConcurrencyManager.get(rule.getClusterConfig().getFlowId()).get() == 0 && TokenCacheNodeManager.getSize() == 0);
+ }
+}
diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/concurrent/CurrentConcurrencyManagerTest.java b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/concurrent/CurrentConcurrencyManagerTest.java
new file mode 100644
index 0000000000..dff3ca949d
--- /dev/null
+++ b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/concurrent/CurrentConcurrencyManagerTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.cluster.flow.statistic.concurrent;
+
+import com.alibaba.csp.sentinel.cluster.flow.statistic.concurrent.CurrentConcurrencyManager;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class CurrentConcurrencyManagerTest {
+ @Test
+ public void updateTest() throws InterruptedException {
+ CurrentConcurrencyManager.put(111L, 0);
+ CurrentConcurrencyManager.put(222L, 0);
+ final CountDownLatch countDownLatch = new CountDownLatch(1000);
+ ExecutorService pool = Executors.newFixedThreadPool(100);
+ for (int i = 0; i < 1000; i++) {
+ Runnable task = new Runnable() {
+ @Override
+ public void run() {
+ CurrentConcurrencyManager.addConcurrency(111L, 1);
+ CurrentConcurrencyManager.addConcurrency(222L, 2);
+ countDownLatch.countDown();
+ }
+ };
+ pool.execute(task);
+ }
+ countDownLatch.await();
+ pool.shutdown();
+ Assert.assertEquals(1000, CurrentConcurrencyManager.get(111L).get());
+ Assert.assertEquals(2000, CurrentConcurrencyManager.get(222L).get());
+ }
+}
diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/concurrent/TokenCacheNodeManagerTest.java b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/concurrent/TokenCacheNodeManagerTest.java
new file mode 100644
index 0000000000..8ca4f97a40
--- /dev/null
+++ b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/concurrent/TokenCacheNodeManagerTest.java
@@ -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.cluster.flow.statistic.concurrent;
+
+import com.alibaba.csp.sentinel.cluster.flow.rule.ClusterFlowRuleManager;
+import com.alibaba.csp.sentinel.cluster.flow.statistic.concurrent.TokenCacheNode;
+import com.alibaba.csp.sentinel.cluster.flow.statistic.concurrent.TokenCacheNodeManager;
+import com.alibaba.csp.sentinel.slots.block.ClusterRuleConstant;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.block.flow.ClusterFlowConfig;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+import com.alibaba.csp.sentinel.test.AbstractTimeBasedTest;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TokenCacheNodeManagerTest extends AbstractTimeBasedTest {
+ @Before
+ public void setUp() {
+ FlowRule rule = new FlowRule();
+ ClusterFlowConfig config = new ClusterFlowConfig();
+ config.setResourceTimeout(500);
+ config.setClientOfflineTime(1000);
+ config.setFlowId(111L);
+ config.setThresholdType(ClusterRuleConstant.FLOW_THRESHOLD_GLOBAL);
+ rule.setClusterConfig(config);
+ rule.setClusterMode(true);
+ rule.setCount(10);
+ rule.setResource("test");
+ rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);
+ ArrayList rules = new ArrayList<>();
+ rules.add(rule);
+ ClusterFlowRuleManager.registerPropertyIfAbsent("1-name");
+ ClusterFlowRuleManager.loadRules("1-name", rules);
+ }
+
+ @Test
+ public void testPutTokenCacheNode() throws InterruptedException {
+ setCurrentMillis(System.currentTimeMillis());
+
+ for (long i = 0; i < 100; i++) {
+ final TokenCacheNode node = new TokenCacheNode();
+ node.setTokenId(i);
+ node.setFlowId(111L);
+ node.setResourceTimeout(10000L);
+ node.setClientTimeout(10000L);
+ node.setClientAddress("localhost");
+ if (TokenCacheNodeManager.validToken(node)) {
+ TokenCacheNodeManager.putTokenCacheNode(node.getTokenId(), node);
+
+ }
+ }
+ Assert.assertEquals(100, TokenCacheNodeManager.getSize());
+ for (int i = 0; i < 100; i++) {
+ TokenCacheNodeManager.getTokenCacheNode((long) (Math.random() * 100));
+ }
+ List keyList = new ArrayList<>(TokenCacheNodeManager.getCacheKeySet());
+ for (int i = 0; i < 100; i++) {
+ Assert.assertEquals(i, (long) keyList.get(i));
+ TokenCacheNodeManager.removeTokenCacheNode(i);
+ }
+ }
+}
diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/limit/GlobalRequestLimiterTest.java b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/limit/GlobalRequestLimiterTest.java
new file mode 100644
index 0000000000..e618d7dec5
--- /dev/null
+++ b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/limit/GlobalRequestLimiterTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.cluster.flow.statistic.limit;
+
+import com.alibaba.csp.sentinel.cluster.server.config.ClusterServerConfigManager;
+import com.alibaba.csp.sentinel.cluster.test.AbstractTimeBasedTest;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class GlobalRequestLimiterTest extends AbstractTimeBasedTest {
+ @Before
+ public void preTest() {
+ ClusterServerConfigManager.setMaxAllowedQps(3);
+ }
+
+ @Test
+ public void testPass() throws InterruptedException {
+ setCurrentMillis(System.currentTimeMillis());
+ GlobalRequestLimiter.initIfAbsent("user");
+ Assert.assertNotNull(GlobalRequestLimiter.getRequestLimiter("user"));
+ Assert.assertEquals(3, GlobalRequestLimiter.getMaxAllowedQps("user"), 0.01);
+ Assert.assertTrue(GlobalRequestLimiter.tryPass("user"));
+ Assert.assertTrue(GlobalRequestLimiter.tryPass("user"));
+ Assert.assertTrue(GlobalRequestLimiter.tryPass("user"));
+ Assert.assertFalse(GlobalRequestLimiter.tryPass("user"));
+ Assert.assertEquals(3, GlobalRequestLimiter.getCurrentQps("user"), 0.01);
+
+ // wait a second to refresh the window
+ sleep(1000);
+ Assert.assertTrue(GlobalRequestLimiter.tryPass("user"));
+ Assert.assertTrue(GlobalRequestLimiter.tryPass("user"));
+ Assert.assertEquals(2, GlobalRequestLimiter.getCurrentQps("user"), 0.01);
+ }
+
+ @Test
+ public void testChangeMaxAllowedQps() {
+ GlobalRequestLimiter.initIfAbsent("foo");
+ Assert.assertEquals(3, GlobalRequestLimiter.getMaxAllowedQps("foo"), 0.01);
+ GlobalRequestLimiter.applyMaxQpsChange(10);
+ Assert.assertEquals(10, GlobalRequestLimiter.getMaxAllowedQps("foo"), 0.01);
+ }
+}
diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/limit/RequestLimiterTest.java b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/limit/RequestLimiterTest.java
new file mode 100644
index 0000000000..0d3f95e6af
--- /dev/null
+++ b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/limit/RequestLimiterTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.cluster.flow.statistic.limit;
+
+import com.alibaba.csp.sentinel.cluster.test.AbstractTimeBasedTest;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class RequestLimiterTest extends AbstractTimeBasedTest {
+
+ @Test
+ public void testRequestLimiter() {
+ setCurrentMillis(System.currentTimeMillis());
+ RequestLimiter limiter = new RequestLimiter(10);
+ limiter.add(3);
+ limiter.add(3);
+ limiter.add(3);
+ assertTrue(limiter.canPass());
+ assertEquals(9, limiter.getSum());
+ limiter.add(3);
+ assertFalse(limiter.canPass());
+
+ // wait a second to refresh the window
+ sleep(1000);
+ limiter.add(3);
+ assertTrue(limiter.tryPass());
+ assertTrue(limiter.canPass());
+ assertEquals(4, limiter.getSum());
+ }
+}
diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterMetricTest.java b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterMetricTest.java
new file mode 100644
index 0000000000..e8c286bc7c
--- /dev/null
+++ b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterMetricTest.java
@@ -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.cluster.flow.statistic.metric;
+
+import com.alibaba.csp.sentinel.cluster.flow.statistic.data.ClusterFlowEvent;
+import com.alibaba.csp.sentinel.cluster.test.AbstractTimeBasedTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ClusterMetricTest extends AbstractTimeBasedTest {
+
+ @Test
+ public void testTryOccupyNext() {
+ setCurrentMillis(System.currentTimeMillis());
+ ClusterMetric metric = new ClusterMetric(5, 25);
+ metric.add(ClusterFlowEvent.PASS, 1);
+ metric.add(ClusterFlowEvent.PASS, 2);
+ metric.add(ClusterFlowEvent.PASS, 1);
+ metric.add(ClusterFlowEvent.BLOCK, 1);
+ Assert.assertEquals(4, metric.getSum(ClusterFlowEvent.PASS));
+ Assert.assertEquals(1, metric.getSum(ClusterFlowEvent.BLOCK));
+ Assert.assertEquals(160, metric.getAvg(ClusterFlowEvent.PASS), 0.01);
+ Assert.assertEquals(200, metric.tryOccupyNext(ClusterFlowEvent.PASS, 111, 900));
+ metric.add(ClusterFlowEvent.PASS, 1);
+ metric.add(ClusterFlowEvent.PASS, 2);
+ metric.add(ClusterFlowEvent.PASS, 1);
+ Assert.assertEquals(200, metric.tryOccupyNext(ClusterFlowEvent.PASS, 222, 900));
+ metric.add(ClusterFlowEvent.PASS, 1);
+ metric.add(ClusterFlowEvent.PASS, 2);
+ metric.add(ClusterFlowEvent.PASS, 1);
+ Assert.assertEquals(0, metric.tryOccupyNext(ClusterFlowEvent.PASS, 333, 900));
+ }
+}
diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterParamMetricTest.java b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterParamMetricTest.java
new file mode 100644
index 0000000000..82645877ad
--- /dev/null
+++ b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterParamMetricTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.cluster.flow.statistic.metric;
+
+import com.alibaba.csp.sentinel.cluster.test.AbstractTimeBasedTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ClusterParamMetricTest extends AbstractTimeBasedTest {
+
+ @Test
+ public void testClusterParamMetric() {
+ setCurrentMillis(System.currentTimeMillis());
+ Map topMap = new HashMap();
+ ClusterParamMetric metric = new ClusterParamMetric(5, 25, 100);
+ metric.addValue("e1", -1);
+ metric.addValue("e1", -2);
+ metric.addValue("e2", 100);
+ metric.addValue("e2", 23);
+ metric.addValue("e3", 100);
+ metric.addValue("e3", 230);
+ Assert.assertEquals(-3, metric.getSum("e1"));
+ Assert.assertEquals(-120, metric.getAvg("e1"), 0.01);
+ topMap.put("e3", (double) 13200);
+ Assert.assertEquals(topMap, metric.getTopValues(1));
+ topMap.put("e2", (double) 4920);
+ topMap.put("e1", (double) -120);
+ Assert.assertEquals(topMap, metric.getTopValues(5));
+ metric.addValue("e2", 100);
+ metric.addValue("e2", 23);
+ Assert.assertEquals(246, metric.getSum("e2"));
+ Assert.assertEquals(9840, metric.getAvg("e2"), 0.01);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testIllegalArgument() {
+ ClusterParamMetric metric = new ClusterParamMetric(5, 25, 100);
+ metric.getTopValues(-1);
+ }
+}
diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/test/AbstractTimeBasedTest.java b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/test/AbstractTimeBasedTest.java
new file mode 100644
index 0000000000..d4458a0d21
--- /dev/null
+++ b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/test/AbstractTimeBasedTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.cluster.test;
+
+import com.alibaba.csp.sentinel.util.TimeUtil;
+import org.junit.runner.RunWith;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+/**
+ * Mock support for {@link TimeUtil}.
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({TimeUtil.class})
+public abstract class AbstractTimeBasedTest {
+
+ private long currentMillis = 0;
+
+ {
+ PowerMockito.mockStatic(TimeUtil.class);
+ PowerMockito.when(TimeUtil.currentTimeMillis()).thenReturn(currentMillis);
+ }
+
+ protected final void useActualTime() {
+ PowerMockito.when(TimeUtil.currentTimeMillis()).thenCallRealMethod();
+ }
+
+ protected final void setCurrentMillis(long cur) {
+ currentMillis = cur;
+ PowerMockito.when(TimeUtil.currentTimeMillis()).thenReturn(currentMillis);
+ }
+
+ protected final void sleep(int t) {
+ currentMillis += t;
+ PowerMockito.when(TimeUtil.currentTimeMillis()).thenReturn(currentMillis);
+ }
+
+ protected final void sleepSecond(int timeSec) {
+ sleep(timeSec * 1000);
+ }
+}
diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/test/AbstractTimeBasedTest.java b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/test/AbstractTimeBasedTest.java
new file mode 100644
index 0000000000..6e8bab4d1e
--- /dev/null
+++ b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/test/AbstractTimeBasedTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.test;
+
+import com.alibaba.csp.sentinel.util.TimeUtil;
+import org.junit.runner.RunWith;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+/**
+ * Mock support for {@link TimeUtil}
+ *
+ * @author jason
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({TimeUtil.class})
+public abstract class AbstractTimeBasedTest {
+
+ private long currentMillis = 0;
+
+ {
+ PowerMockito.mockStatic(TimeUtil.class);
+ PowerMockito.when(TimeUtil.currentTimeMillis()).thenReturn(currentMillis);
+ }
+
+ protected final void useActualTime() {
+ PowerMockito.when(TimeUtil.currentTimeMillis()).thenCallRealMethod();
+ }
+
+ protected final void setCurrentMillis(long cur) {
+ currentMillis = cur;
+ PowerMockito.when(TimeUtil.currentTimeMillis()).thenReturn(currentMillis);
+ }
+
+ protected final void sleep(int t) {
+ currentMillis += t;
+ PowerMockito.when(TimeUtil.currentTimeMillis()).thenReturn(currentMillis);
+ }
+
+ protected final void sleepSecond(int timeSec) {
+ sleep(timeSec * 1000);
+ }
+}
diff --git a/sentinel-cluster/sentinel-cluster-server-envoy-rls/README.md b/sentinel-cluster/sentinel-cluster-server-envoy-rls/README.md
index e610043686..253a9b67d9 100644
--- a/sentinel-cluster/sentinel-cluster-server-envoy-rls/README.md
+++ b/sentinel-cluster/sentinel-cluster-server-envoy-rls/README.md
@@ -16,10 +16,10 @@ mvn clean package -P prod
## Rule configuration
-Currently Sentinel RLS token server supports dynamic rule configuration via the yaml file.
+Sentinel RLS token server supports dynamic rule configuration via the yaml file.
The file may provide rules for one *domain* (defined in Envoy's conf file).
In Envoy, one rate limit request might carry multiple *rate limit descriptors*
-(which will be generated from [Envoy rate limit actions](https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/route/route.proto#envoy-api-msg-route-ratelimit)).
+(which will be generated from [Envoy rate limit actions](https://www.envoyproxy.io/docs/envoy/v1.12.1/api-v2/api/v2/route/route.proto#envoy-api-msg-route-ratelimit)).
One rate limit descriptor may have multiple entries (key-value pair).
We may set different threshold for each rate limit descriptors.
diff --git a/sentinel-cluster/sentinel-cluster-server-envoy-rls/pom.xml b/sentinel-cluster/sentinel-cluster-server-envoy-rls/pom.xml
index 3c84641c79..a52247b44d 100644
--- a/sentinel-cluster/sentinel-cluster-server-envoy-rls/pom.xml
+++ b/sentinel-cluster/sentinel-cluster-server-envoy-rls/pom.xml
@@ -5,7 +5,7 @@
sentinel-clustercom.alibaba.csp
- 1.7.2-SNAPSHOT
+ 1.8.1-SNAPSHOT4.0.0
@@ -16,7 +16,7 @@
1.83.10.0
- 1.24.0
+ 1.30.23.2.1
@@ -35,6 +35,11 @@
com.alibaba.cspsentinel-transport-simple-http
+
+ javax.annotation
+ javax.annotation-api
+ ${javax.annotation-api.version}
+ io.grpc
@@ -153,4 +158,4 @@
-
\ No newline at end of file
+
diff --git a/sentinel-cluster/sentinel-cluster-server-envoy-rls/src/main/java/com/alibaba/csp/sentinel/cluster/server/envoy/rls/rule/EnvoyRlsRuleManager.java b/sentinel-cluster/sentinel-cluster-server-envoy-rls/src/main/java/com/alibaba/csp/sentinel/cluster/server/envoy/rls/rule/EnvoyRlsRuleManager.java
index 1acca60040..029fc148ff 100644
--- a/sentinel-cluster/sentinel-cluster-server-envoy-rls/src/main/java/com/alibaba/csp/sentinel/cluster/server/envoy/rls/rule/EnvoyRlsRuleManager.java
+++ b/sentinel-cluster/sentinel-cluster-server-envoy-rls/src/main/java/com/alibaba/csp/sentinel/cluster/server/envoy/rls/rule/EnvoyRlsRuleManager.java
@@ -91,7 +91,7 @@ public synchronized void configUpdate(List conf) {
RULE_MAP.clear();
RULE_MAP.putAll(ruleMap);
- RecordLog.info("[EnvoyRlsRuleManager] Envoy RLS rules loaded: " + flowRules);
+ RecordLog.info("[EnvoyRlsRuleManager] Envoy RLS rules loaded: {}", flowRules);
// Use the "default" namespace.
ClusterFlowRuleManager.loadRules(ServerConstants.DEFAULT_NAMESPACE, flowRules);
diff --git a/sentinel-core/pom.xml b/sentinel-core/pom.xml
index 1cb08f7d44..06c50f4aa4 100755
--- a/sentinel-core/pom.xml
+++ b/sentinel-core/pom.xml
@@ -6,7 +6,7 @@
com.alibaba.cspsentinel-parent
- 1.7.2-SNAPSHOT
+ 1.8.1-SNAPSHOTsentinel-corejar
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java
index c3bbadf091..9c50ddf832 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java
@@ -31,7 +31,7 @@
*/
public final class Constants {
- public static final String SENTINEL_VERSION = VersionUtil.getVersion("1.7.2");
+ public static final String SENTINEL_VERSION = VersionUtil.getVersion("1.8.1");
public final static int MAX_CONTEXT_NAME_SIZE = 2000;
public final static int MAX_SLOT_CHAIN_SIZE = 6000;
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/CtEntry.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/CtEntry.java
index d59e52a5ff..6c62abe810 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/CtEntry.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/CtEntry.java
@@ -15,12 +15,16 @@
*/
package com.alibaba.csp.sentinel;
+import java.util.LinkedList;
+
import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.context.NullContext;
+import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.node.Node;
import com.alibaba.csp.sentinel.slotchain.ProcessorSlot;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
+import com.alibaba.csp.sentinel.util.function.BiConsumer;
/**
* Linked entry within current context.
@@ -35,6 +39,7 @@ class CtEntry extends Entry {
protected ProcessorSlot chain;
protected Context context;
+ protected LinkedList> exitHandlers;
CtEntry(ResourceWrapper resourceWrapper, ProcessorSlot chain, Context context) {
super(resourceWrapper);
@@ -51,7 +56,7 @@ private void setUpEntryFor(Context context) {
}
this.parent = context.getCurEntry();
if (parent != null) {
- ((CtEntry)parent).child = this;
+ ((CtEntry) parent).child = this;
}
context.setCurEntry(this);
}
@@ -61,31 +66,55 @@ public void exit(int count, Object... args) throws ErrorEntryFreeException {
trueExit(count, args);
}
+ /**
+ * Note: the exit handlers will be called AFTER onExit of slot chain.
+ */
+ private void callExitHandlersAndCleanUp(Context ctx) {
+ if (exitHandlers != null && !exitHandlers.isEmpty()) {
+ for (BiConsumer handler : this.exitHandlers) {
+ try {
+ handler.accept(ctx, this);
+ } catch (Exception e) {
+ RecordLog.warn("Error occurred when invoking entry exit handler, current entry: "
+ + resourceWrapper.getName(), e);
+ }
+ }
+ exitHandlers = null;
+ }
+ }
+
protected void exitForContext(Context context, int count, Object... args) throws ErrorEntryFreeException {
if (context != null) {
// Null context should exit without clean-up.
if (context instanceof NullContext) {
return;
}
+
if (context.getCurEntry() != this) {
- String curEntryNameInContext = context.getCurEntry() == null ? null : context.getCurEntry().getResourceWrapper().getName();
+ String curEntryNameInContext = context.getCurEntry() == null ? null
+ : context.getCurEntry().getResourceWrapper().getName();
// Clean previous call stack.
- CtEntry e = (CtEntry)context.getCurEntry();
+ CtEntry e = (CtEntry) context.getCurEntry();
while (e != null) {
e.exit(count, args);
- e = (CtEntry)e.parent;
+ e = (CtEntry) e.parent;
}
String errorMessage = String.format("The order of entry exit can't be paired with the order of entry"
- + ", current entry in context: <%s>, but expected: <%s>", curEntryNameInContext, resourceWrapper.getName());
+ + ", current entry in context: <%s>, but expected: <%s>", curEntryNameInContext,
+ resourceWrapper.getName());
throw new ErrorEntryFreeException(errorMessage);
} else {
+ // Go through the onExit hook of all slots.
if (chain != null) {
chain.exit(context, resourceWrapper, count, args);
}
+ // Go through the existing terminate handlers (associated to this invocation).
+ callExitHandlersAndCleanUp(context);
+
// Restore the call stack.
context.setCurEntry(parent);
if (parent != null) {
- ((CtEntry)parent).child = null;
+ ((CtEntry) parent).child = null;
}
if (parent == null) {
// Default context (auto entered) will be exited automatically.
@@ -103,6 +132,14 @@ protected void clearEntryContext() {
this.context = null;
}
+ @Override
+ public void whenTerminate(BiConsumer handler) {
+ if (this.exitHandlers == null) {
+ this.exitHandlers = new LinkedList<>();
+ }
+ this.exitHandlers.add(handler);
+ }
+
@Override
protected Entry trueExit(int count, Object... args) throws ErrorEntryFreeException {
exitForContext(context, count, args);
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/CtSph.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/CtSph.java
index 5c5b137b1d..84ebf17dcb 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/CtSph.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/CtSph.java
@@ -181,7 +181,7 @@ public Entry entry(ResourceWrapper resourceWrapper, int count, Object... args) t
* be created if the resource doesn't relate one.
*
*
Same resource({@link ResourceWrapper#equals(Object)}) will share the same
- * {@link ProcessorSlotChain} globally, no matter in witch {@link Context}.
+ * {@link ProcessorSlotChain} globally, no matter in which {@link Context}.
*
*
* Note that total {@link ProcessorSlot} count must not exceed {@link Constants#MAX_SLOT_CHAIN_SIZE},
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Entry.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Entry.java
index 8740022992..c3114821c3 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Entry.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Entry.java
@@ -15,7 +15,9 @@
*/
package com.alibaba.csp.sentinel;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.util.TimeUtil;
+import com.alibaba.csp.sentinel.util.function.BiConsumer;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.node.Node;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
@@ -44,6 +46,7 @@
* @author qinan.qn
* @author jialiang.linjl
* @author leyou(lihao)
+ * @author Eric Zhao
* @see SphU
* @see Context
* @see ContextUtil
@@ -52,18 +55,23 @@ public abstract class Entry implements AutoCloseable {
private static final Object[] OBJECTS0 = new Object[0];
- private long createTime;
+ private final long createTimestamp;
+ private long completeTimestamp;
+
private Node curNode;
/**
* {@link Node} of the specific origin, Usually the origin is the Service Consumer.
*/
private Node originNode;
+
private Throwable error;
- protected ResourceWrapper resourceWrapper;
+ private BlockException blockError;
+
+ protected final ResourceWrapper resourceWrapper;
public Entry(ResourceWrapper resourceWrapper) {
this.resourceWrapper = resourceWrapper;
- this.createTime = TimeUtil.currentTimeMillis();
+ this.createTimestamp = TimeUtil.currentTimeMillis();
}
public ResourceWrapper getResourceWrapper() {
@@ -119,8 +127,17 @@ public void close() {
*/
public abstract Node getLastNode();
- public long getCreateTime() {
- return createTime;
+ public long getCreateTimestamp() {
+ return createTimestamp;
+ }
+
+ public long getCompleteTimestamp() {
+ return completeTimestamp;
+ }
+
+ public Entry setCompleteTimestamp(long completeTimestamp) {
+ this.completeTimestamp = completeTimestamp;
+ return this;
}
public Node getCurNode() {
@@ -131,6 +148,15 @@ public void setCurNode(Node node) {
this.curNode = node;
}
+ public BlockException getBlockError() {
+ return blockError;
+ }
+
+ public Entry setBlockError(BlockException blockError) {
+ this.blockError = blockError;
+ return this;
+ }
+
public Throwable getError() {
return error;
}
@@ -153,4 +179,14 @@ public void setOriginNode(Node originNode) {
this.originNode = originNode;
}
+ /**
+ * Like {@code CompletableFuture} since JDK 8, it guarantees specified handler
+ * is invoked when this entry terminated (exited), no matter it's blocked or permitted.
+ * Use it when you did some STATEFUL operations on entries.
+ *
+ * @param handler handler function on the invocation terminates
+ * @since 1.8.0
+ */
+ public abstract void whenTerminate(BiConsumer handler);
+
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Sph.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Sph.java
index 32643b4697..ab34176a57 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Sph.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Sph.java
@@ -18,11 +18,10 @@
import java.lang.reflect.Method;
import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.slots.system.SystemRule;
/**
- * Interface to get {@link Entry} for resource protection. If any block criteria is met,
- * a {@link BlockException} or its subclasses will be thrown. Successfully getting a entry
- * indicates permitting the invocation pass.
+ * The basic interface for recording statistics and performing rule checking for resources.
*
* @author qinan.qn
* @author jialiang.linjl
@@ -32,158 +31,166 @@
public interface Sph extends SphResourceTypeSupport {
/**
- * Create a protected resource.
+ * Record statistics and perform rule checking for the given resource.
*
* @param name the unique name of the protected resource
- * @return entry get.
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
* @throws BlockException if the block criteria is met
*/
Entry entry(String name) throws BlockException;
/**
- * Create a protected method.
+ * Record statistics and perform rule checking for the given method.
*
* @param method the protected method
- * @return entry get.
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
* @throws BlockException if the block criteria is met
*/
Entry entry(Method method) throws BlockException;
/**
- * Create a protected method.
+ * Record statistics and perform rule checking for the given method.
*
- * @param method the protected method
- * @param count the count that the resource requires
- * @return entry get.
+ * @param method the protected method
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
* @throws BlockException if the block criteria is met
*/
- Entry entry(Method method, int count) throws BlockException;
+ Entry entry(Method method, int batchCount) throws BlockException;
/**
- * Create a protected resource.
+ * Record statistics and perform rule checking for the given resource.
*
- * @param name the unique string for the resource
- * @param count the count that the resource requires
- * @return entry get.
+ * @param name the unique string for the resource
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
* @throws BlockException if the block criteria is met
*/
- Entry entry(String name, int count) throws BlockException;
+ Entry entry(String name, int batchCount) throws BlockException;
/**
- * Create a protected method.
+ * Record statistics and perform rule checking for the given method.
*
- * @param method the protected method
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable
- * @return entry get.
+ * @param method the protected method
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
* @throws BlockException if the block criteria is met
*/
- Entry entry(Method method, EntryType type) throws BlockException;
+ Entry entry(Method method, EntryType trafficType) throws BlockException;
/**
- * Create a protected resource.
+ * Record statistics and perform rule checking for the given resource.
*
- * @param name the unique name for the protected resource
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable
- * @return entry get.
+ * @param name the unique name for the protected resource
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
* @throws BlockException if the block criteria is met
*/
- Entry entry(String name, EntryType type) throws BlockException;
+ Entry entry(String name, EntryType trafficType) throws BlockException;
/**
- * Create a protected method.
+ * Record statistics and perform rule checking for the given method.
*
- * @param method the protected method
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable
- * @param count the count that the resource requires
- * @return entry get.
+ * @param method the protected method
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
* @throws BlockException if the block criteria is met
*/
- Entry entry(Method method, EntryType type, int count) throws BlockException;
+ Entry entry(Method method, EntryType trafficType, int batchCount) throws BlockException;
/**
- * Create a protected resource.
+ * Record statistics and perform rule checking for the given resource.
*
- * @param name the unique name for the protected resource
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable
- * @param count the count that the resource requires
- * @return entry get.
+ * @param name the unique name for the protected resource
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
* @throws BlockException if the block criteria is met
*/
- Entry entry(String name, EntryType type, int count) throws BlockException;
+ Entry entry(String name, EntryType trafficType, int batchCount) throws BlockException;
/**
- * Create a protected resource.
+ * Record statistics and perform rule checking for the given resource.
*
- * @param method the protected method
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable
- * @param count the count that the resource requires
- * @param args the parameters of the method. It can also be counted by setting
- * hot parameter rule
- * @return entry get.
+ * @param method the protected method
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @param args parameters of the method for flow control or customized slots
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
* @throws BlockException if the block criteria is met
*/
- Entry entry(Method method, EntryType type, int count, Object... args) throws BlockException;
+ Entry entry(Method method, EntryType trafficType, int batchCount, Object... args) throws BlockException;
/**
- * Create a protected resource.
+ * Record statistics and perform rule checking for the given resource.
*
- * @param name the unique name for the protected resource
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable
- * @param count the count that the resource requires
- * @param args the parameters of the method. It can also be counted by setting hot parameter rule
- * @return entry get
+ * @param name the unique name for the protected resource
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @param args args for parameter flow control or customized slots
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
* @throws BlockException if the block criteria is met
*/
- Entry entry(String name, EntryType type, int count, Object... args) throws BlockException;
+ Entry entry(String name, EntryType trafficType, int batchCount, Object... args) throws BlockException;
/**
* Create a protected asynchronous resource.
*
- * @param name the unique name for the protected resource
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable
- * @param count the count that the resource requires
- * @param args the parameters of the method. It can also be counted by setting hot parameter rule
+ * @param name the unique name for the protected resource
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @param args args for parameter flow control or customized slots
* @return created asynchronous entry
* @throws BlockException if the block criteria is met
* @since 0.2.0
*/
- AsyncEntry asyncEntry(String name, EntryType type, int count, Object... args) throws BlockException;
+ AsyncEntry asyncEntry(String name, EntryType trafficType, int batchCount, Object... args) throws BlockException;
/**
* Create a protected resource with priority.
*
* @param name the unique name for the protected resource
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable
- * @param count the count that the resource requires
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
* @param prioritized whether the entry is prioritized
- * @return entry get
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
* @throws BlockException if the block criteria is met
* @since 1.4.0
*/
- Entry entryWithPriority(String name, EntryType type, int count, boolean prioritized) throws BlockException;
+ Entry entryWithPriority(String name, EntryType trafficType, int batchCount, boolean prioritized)
+ throws BlockException;
/**
* Create a protected resource with priority.
*
* @param name the unique name for the protected resource
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable
- * @param count the count that the resource requires
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
* @param prioritized whether the entry is prioritized
- * @param args the parameters of the method. It can also be counted by setting hot parameter
- * rule
- * @return entry get
+ * @param args args for parameter flow control or customized slots
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
* @throws BlockException if the block criteria is met
* @since 1.5.0
*/
- Entry entryWithPriority(String name, EntryType type, int count, boolean prioritized, Object... args)
+ Entry entryWithPriority(String name, EntryType trafficType, int batchCount, boolean prioritized, Object... args)
throws BlockException;
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/SphO.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/SphO.java
index 6f6231da43..7667741269 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/SphO.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/SphO.java
@@ -18,8 +18,8 @@
import java.lang.reflect.Method;
import java.util.List;
-import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.Rule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
@@ -63,6 +63,7 @@
*
* @author jialiang.linjl
* @author leyou
+ * @author Eric Zhao
* @see SphU
*/
public class SphO {
@@ -70,7 +71,7 @@ public class SphO {
private static final Object[] OBJECTS0 = new Object[0];
/**
- * Checking all {@link Rule}s about the resource.
+ * Record statistics and perform rule checking for the given resource.
*
* @param name the unique name of the protected resource
* @return true if no rule's threshold is exceeded, otherwise return false.
@@ -92,23 +93,23 @@ public static boolean entry(Method method) {
/**
* Checking all {@link Rule}s about the protected method.
*
- * @param method the protected method
- * @param count tokens required
+ * @param method the protected method
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
* @return true if no rule's threshold is exceeded, otherwise return false.
*/
- public static boolean entry(Method method, int count) {
- return entry(method, EntryType.OUT, count, OBJECTS0);
+ public static boolean entry(Method method, int batchCount) {
+ return entry(method, EntryType.OUT, batchCount, OBJECTS0);
}
/**
- * Checking all {@link Rule}s about the resource.
+ * Record statistics and perform rule checking for the given resource.
*
- * @param name the unique string for the resource
- * @param count tokens required
+ * @param name the unique string for the resource
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
* @return true if no rule's threshold is exceeded, otherwise return false.
*/
- public static boolean entry(String name, int count) {
- return entry(name, EntryType.OUT, count, OBJECTS0);
+ public static boolean entry(String name, int batchCount) {
+ return entry(name, EntryType.OUT, batchCount, OBJECTS0);
}
/**
@@ -125,7 +126,7 @@ public static boolean entry(Method method, EntryType type) {
}
/**
- * Checking all {@link Rule}s about the resource.
+ * Record statistics and perform rule checking for the given resource.
*
* @param name the unique name for the protected resource
* @param type the resource is an inbound or an outbound method. This is used
@@ -144,7 +145,7 @@ public static boolean entry(String name, EntryType type) {
* @param type the resource is an inbound or an outbound method. This is used
* to mark whether it can be blocked when the system is unstable,
* only inbound traffic could be blocked by {@link SystemRule}
- * @param count tokens required
+ * @param count the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
* @return true if no rule's threshold is exceeded, otherwise return false.
*/
public static boolean entry(Method method, EntryType type, int count) {
@@ -152,13 +153,13 @@ public static boolean entry(Method method, EntryType type, int count) {
}
/**
- * Checking all {@link Rule}s about the resource.
+ * Record statistics and perform rule checking for the given resource.
*
* @param name the unique name for the protected resource
* @param type the resource is an inbound or an outbound method. This is used
* to mark whether it can be blocked when the system is unstable,
* only inbound traffic could be blocked by {@link SystemRule}
- * @param count tokens required
+ * @param count the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
* @return true if no rule's threshold is exceeded, otherwise return false.
*/
public static boolean entry(String name, EntryType type, int count) {
@@ -166,46 +167,46 @@ public static boolean entry(String name, EntryType type, int count) {
}
/**
- * Checking all {@link Rule}s about the resource.
+ * Record statistics and perform rule checking for the given resource.
*
- * @param name the unique name for the protected resource
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable,
- * only inbound traffic could be blocked by {@link SystemRule}
- * @param count tokens required
- * @param args extra parameters.
+ * @param name the unique name for the protected resource
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @param args args for parameter flow control or customized slots
* @return true if no rule's threshold is exceeded, otherwise return false.
*/
- public static boolean entry(String name, EntryType type, int count, Object... args) {
+ public static boolean entry(String name, EntryType trafficType, int batchCount, Object... args) {
try {
- Env.sph.entry(name, type, count, args);
+ Env.sph.entry(name, trafficType, batchCount, args);
} catch (BlockException e) {
return false;
} catch (Throwable e) {
- RecordLog.info("[Sentinel] Fatal error", e);
+ RecordLog.warn("SphO fatal error", e);
return true;
}
return true;
}
/**
- * Checking all {@link Rule}s about the protected method.
+ * Record statistics and perform rule checking for the given method resource.
*
- * @param method the protected method
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable,
- * only inbound traffic could be blocked by {@link SystemRule}
- * @param count tokens required
- * @param args the parameters of the method.
+ * @param method the protected method
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @param args args for parameter flow control or customized slots
* @return true if no rule's threshold is exceeded, otherwise return false.
*/
- public static boolean entry(Method method, EntryType type, int count, Object... args) {
+ public static boolean entry(Method method, EntryType trafficType, int batchCount, Object... args) {
try {
- Env.sph.entry(method, type, count, args);
+ Env.sph.entry(method, trafficType, batchCount, args);
} catch (BlockException e) {
return false;
} catch (Throwable e) {
- RecordLog.info("[Sentinel] Fatal error", e);
+ RecordLog.warn("SphO fatal error", e);
return true;
}
return true;
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/SphResourceTypeSupport.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/SphResourceTypeSupport.java
index 4349473f6c..ab9574e434 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/SphResourceTypeSupport.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/SphResourceTypeSupport.java
@@ -16,6 +16,7 @@
package com.alibaba.csp.sentinel;
import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.slots.system.SystemRule;
/**
* @author Eric Zhao
@@ -24,46 +25,53 @@
public interface SphResourceTypeSupport {
/**
- * Create a protected resource with provided classification.
+ * Record statistics and perform rule checking for the given resource with provided classification.
*
- * @param name the unique name of the protected resource
+ * @param name the unique name of the protected resource
* @param resourceType the classification of the resource
- * @param entryType the traffic entry type (IN/OUT) of the resource
- * @param count tokens required
- * @param args extra parameters
- * @return new entry of the resource
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @param args args for parameter flow control or customized slots
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
* @throws BlockException if the block criteria is met
*/
- Entry entryWithType(String name, int resourceType, EntryType entryType, int count, Object[] args)
+ Entry entryWithType(String name, int resourceType, EntryType trafficType, int batchCount, Object[] args)
throws BlockException;
/**
- * Create a protected resource with provided classification.
+ * Record statistics and perform rule checking for the given resource with the provided classification.
*
- * @param name the unique name of the protected resource
- * @param resourceType the classification of the resource
- * @param entryType the traffic entry type (IN/OUT) of the resource
- * @param count tokens required
- * @param prioritized whether the entry is prioritized
- * @param args extra parameters
- * @return new entry of the resource
+ * @param name the unique name of the protected resource
+ * @param resourceType classification of the resource (e.g. Web or RPC)
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @param prioritized whether the entry is prioritized
+ * @param args args for parameter flow control or customized slots
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
* @throws BlockException if the block criteria is met
*/
- Entry entryWithType(String name, int resourceType, EntryType entryType, int count, boolean prioritized,
+ Entry entryWithType(String name, int resourceType, EntryType trafficType, int batchCount, boolean prioritized,
Object[] args) throws BlockException;
/**
- * Create an asynchronous resource with provided classification.
+ * Record statistics and perform rule checking for the given resource that indicates an async invocation.
*
- * @param name the unique name of the protected resource
- * @param resourceType the classification of the resource
- * @param entryType the traffic entry type (IN/OUT) of the resource
- * @param count tokens required
- * @param prioritized whether the entry is prioritized
- * @param args extra parameters
- * @return new entry of the resource
+ * @param name the unique name for the protected resource
+ * @param resourceType classification of the resource (e.g. Web or RPC)
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @param prioritized whether the entry is prioritized
+ * @param args args for parameter flow control or customized slots
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
* @throws BlockException if the block criteria is met
*/
- AsyncEntry asyncEntryWithType(String name, int resourceType, EntryType entryType, int count, boolean prioritized,
+ AsyncEntry asyncEntryWithType(String name, int resourceType, EntryType trafficType, int batchCount,
+ boolean prioritized,
Object[] args) throws BlockException;
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/SphU.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/SphU.java
index a7ad2aa19d..04feea0ccd 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/SphU.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/SphU.java
@@ -16,25 +16,21 @@
package com.alibaba.csp.sentinel;
import java.lang.reflect.Method;
-import java.util.List;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.Rule;
-import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
-import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
-import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
/**
+ *
The fundamental Sentinel API for recording statistics and performing rule checking for resources.
+ *
* Conceptually, physical or logical resource that need protection should be
* surrounded by an entry. The requests to this resource will be blocked if any
* criteria is met, eg. when any {@link Rule}'s threshold is exceeded. Once blocked,
* a {@link BlockException} will be thrown.
- *
+ *
*
- * To configure the criteria, we can use XXXRuleManager.loadRules() to add rules, eg.
- * {@link FlowRuleManager#loadRules(List)}, {@link DegradeRuleManager#loadRules(List)},
- * {@link SystemRuleManager#loadRules(List)}.
+ * To configure the criteria, we can use XxxRuleManager.loadRules() to load rules.
*
*
*
@@ -79,10 +75,11 @@ public class SphU {
private SphU() {}
/**
- * Checking all {@link Rule}s about the resource.
+ * Record statistics and perform rule checking for the given resource.
*
* @param name the unique name of the protected resource
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded.
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
*/
public static Entry entry(String name) throws BlockException {
return Env.sph.entry(name, EntryType.OUT, 1, OBJECTS0);
@@ -92,7 +89,8 @@ public static Entry entry(String name) throws BlockException {
* Checking all {@link Rule}s about the protected method.
*
* @param method the protected method
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded.
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
*/
public static Entry entry(Method method) throws BlockException {
return Env.sph.entry(method, EntryType.OUT, 1, OBJECTS0);
@@ -101,114 +99,120 @@ public static Entry entry(Method method) throws BlockException {
/**
* Checking all {@link Rule}s about the protected method.
*
- * @param method the protected method
- * @param count tokens required
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded.
+ * @param method the protected method
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
*/
- public static Entry entry(Method method, int count) throws BlockException {
- return Env.sph.entry(method, EntryType.OUT, count, OBJECTS0);
+ public static Entry entry(Method method, int batchCount) throws BlockException {
+ return Env.sph.entry(method, EntryType.OUT, batchCount, OBJECTS0);
}
/**
- * Checking all {@link Rule}s about the resource.
+ * Record statistics and perform rule checking for the given resource.
*
- * @param name the unique string for the resource
- * @param count tokens required
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded.
+ * @param name the unique string for the resource
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
*/
- public static Entry entry(String name, int count) throws BlockException {
- return Env.sph.entry(name, EntryType.OUT, count, OBJECTS0);
+ public static Entry entry(String name, int batchCount) throws BlockException {
+ return Env.sph.entry(name, EntryType.OUT, batchCount, OBJECTS0);
}
/**
* Checking all {@link Rule}s about the protected method.
*
- * @param method the protected method
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable,
- * only inbound traffic could be blocked by {@link SystemRule}
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded.
+ * @param method the protected method
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
*/
- public static Entry entry(Method method, EntryType type) throws BlockException {
- return Env.sph.entry(method, type, 1, OBJECTS0);
+ public static Entry entry(Method method, EntryType trafficType) throws BlockException {
+ return Env.sph.entry(method, trafficType, 1, OBJECTS0);
}
/**
- * Checking all {@link Rule}s about the resource.
+ * Record statistics and perform rule checking for the given resource.
*
- * @param name the unique name for the protected resource
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable,
- * only inbound traffic could be blocked by {@link SystemRule}
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded.
+ * @param name the unique name for the protected resource
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
*/
- public static Entry entry(String name, EntryType type) throws BlockException {
- return Env.sph.entry(name, type, 1, OBJECTS0);
+ public static Entry entry(String name, EntryType trafficType) throws BlockException {
+ return Env.sph.entry(name, trafficType, 1, OBJECTS0);
}
/**
* Checking all {@link Rule}s about the protected method.
*
- * @param method the protected method
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable,
- * only inbound traffic could be blocked by {@link SystemRule}
- * @param count tokens required
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded.
+ * @param method the protected method
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
*/
- public static Entry entry(Method method, EntryType type, int count) throws BlockException {
- return Env.sph.entry(method, type, count, OBJECTS0);
+ public static Entry entry(Method method, EntryType trafficType, int batchCount) throws BlockException {
+ return Env.sph.entry(method, trafficType, batchCount, OBJECTS0);
}
/**
- * Checking all {@link Rule}s about the resource.
+ * Record statistics and perform rule checking for the given resource.
*
- * @param name the unique name for the protected resource
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable,
- * only inbound traffic could be blocked by {@link SystemRule}
- * @param count tokens required
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded.
+ * @param name the unique name for the protected resource
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
*/
- public static Entry entry(String name, EntryType type, int count) throws BlockException {
- return Env.sph.entry(name, type, count, OBJECTS0);
+ public static Entry entry(String name, EntryType trafficType, int batchCount) throws BlockException {
+ return Env.sph.entry(name, trafficType, batchCount, OBJECTS0);
}
/**
* Checking all {@link Rule}s about the protected method.
*
- * @param method the protected method
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable,
- * only inbound traffic could be blocked by {@link SystemRule}
- * @param count tokens required
- * @param args the parameters of the method.
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded.
+ * @param method the protected method
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @param args args for parameter flow control or customized slots
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
*/
- public static Entry entry(Method method, EntryType type, int count, Object... args) throws BlockException {
- return Env.sph.entry(method, type, count, args);
+ public static Entry entry(Method method, EntryType trafficType, int batchCount, Object... args)
+ throws BlockException {
+ return Env.sph.entry(method, trafficType, batchCount, args);
}
/**
- * Checking all {@link Rule}s about the resource.
+ * Record statistics and perform rule checking for the given resource.
*
- * @param name the unique name for the protected resource
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable,
- * only inbound traffic could be blocked by {@link SystemRule}
- * @param count tokens required
- * @param args extra parameters.
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded.
+ * @param name the unique name for the protected resource
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @param args args for parameter flow control
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
*/
- public static Entry entry(String name, EntryType type, int count, Object... args) throws BlockException {
- return Env.sph.entry(name, type, count, args);
+ public static Entry entry(String name, EntryType trafficType, int batchCount, Object... args)
+ throws BlockException {
+ return Env.sph.entry(name, trafficType, batchCount, args);
}
/**
- * Checking all rules about the asynchronous resource.
+ * Record statistics and check all rules of the resource that indicates an async invocation.
*
* @param name the unique name of the protected resource
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
* @since 0.2.0
*/
public static AsyncEntry asyncEntry(String name) throws BlockException {
@@ -216,40 +220,43 @@ public static AsyncEntry asyncEntry(String name) throws BlockException {
}
/**
- * Checking all {@link Rule}s about the asynchronous resource.
+ * Record statistics and check all rules of the resource that indicates an async invocation.
*
- * @param name the unique name for the protected resource
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable,
- * only inbound traffic could be blocked by {@link SystemRule}
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded
+ * @param name the unique name for the protected resource
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
* @since 0.2.0
*/
- public static AsyncEntry asyncEntry(String name, EntryType type) throws BlockException {
- return Env.sph.asyncEntry(name, type, 1, OBJECTS0);
+ public static AsyncEntry asyncEntry(String name, EntryType trafficType) throws BlockException {
+ return Env.sph.asyncEntry(name, trafficType, 1, OBJECTS0);
}
/**
- * Checking all {@link Rule}s about the asynchronous resource.
+ * Record statistics and check all rules of the resource that indicates an async invocation.
*
- * @param name the unique name for the protected resource
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable,
- * only inbound traffic could be blocked by {@link SystemRule}
- * @param count tokens required
- * @param args extra parameters
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded
+ * @param name the unique name for the protected resource
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @param args args for parameter flow control
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
* @since 0.2.0
*/
- public static AsyncEntry asyncEntry(String name, EntryType type, int count, Object... args) throws BlockException {
- return Env.sph.asyncEntry(name, type, count, args);
+ public static AsyncEntry asyncEntry(String name, EntryType trafficType, int batchCount, Object... args)
+ throws BlockException {
+ return Env.sph.asyncEntry(name, trafficType, batchCount, args);
}
/**
- * Checking all {@link Rule}s related the resource. The entry is prioritized.
+ * Record statistics and perform rule checking for the given resource. The entry is prioritized.
*
* @param name the unique name for the protected resource
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded.
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
* @since 1.4.0
*/
public static Entry entryWithPriority(String name) throws BlockException {
@@ -257,99 +264,105 @@ public static Entry entryWithPriority(String name) throws BlockException {
}
/**
- * Checking all {@link Rule}s related the resource. The entry is prioritized.
+ * Record statistics and perform rule checking for the given resource. The entry is prioritized.
*
- * @param name the unique name for the protected resource
- * @param type the resource is an inbound or an outbound method. This is used
- * to mark whether it can be blocked when the system is unstable,
- * only inbound traffic could be blocked by {@link SystemRule}
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded.
+ * @param name the unique name for the protected resource
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
+ * to mark whether it can be blocked when the system is unstable,
+ * only inbound traffic could be blocked by {@link SystemRule}
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
* @since 1.4.0
*/
- public static Entry entryWithPriority(String name, EntryType type) throws BlockException {
- return Env.sph.entryWithPriority(name, type, 1, true);
+ public static Entry entryWithPriority(String name, EntryType trafficType) throws BlockException {
+ return Env.sph.entryWithPriority(name, trafficType, 1, true);
}
/**
- * Record statistics and check all rules of the resource.
+ * Record statistics and perform rule checking for the given resource.
*
* @param name the unique name for the protected resource
* @param resourceType classification of the resource (e.g. Web or RPC)
- * @param type the resource is an inbound or an outbound method. This is used
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
* to mark whether it can be blocked when the system is unstable,
* only inbound traffic could be blocked by {@link SystemRule}
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
* @since 1.7.0
*/
- public static Entry entry(String name, int resourceType, EntryType type) throws BlockException {
- return Env.sph.entryWithType(name, resourceType, type, 1, OBJECTS0);
+ public static Entry entry(String name, int resourceType, EntryType trafficType) throws BlockException {
+ return Env.sph.entryWithType(name, resourceType, trafficType, 1, OBJECTS0);
}
/**
- * Record statistics and check all rules of the resource.
+ * Record statistics and perform rule checking for the given resource.
*
* @param name the unique name for the protected resource
- * @param type the resource is an inbound or an outbound method. This is used
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
* to mark whether it can be blocked when the system is unstable,
* only inbound traffic could be blocked by {@link SystemRule}
* @param resourceType classification of the resource (e.g. Web or RPC)
- * @param args extra parameters.
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded
+ * @param args args for parameter flow control or customized slots
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
* @since 1.7.0
*/
- public static Entry entry(String name, int resourceType, EntryType type, Object[] args)
+ public static Entry entry(String name, int resourceType, EntryType trafficType, Object[] args)
throws BlockException {
- return Env.sph.entryWithType(name, resourceType, type, 1, args);
+ return Env.sph.entryWithType(name, resourceType, trafficType, 1, args);
}
/**
- * Record statistics and check all rules of the resource.
+ * Record statistics and perform rule checking for the given resource that indicates an async invocation.
*
* @param name the unique name for the protected resource
- * @param type the resource is an inbound or an outbound method. This is used
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
* to mark whether it can be blocked when the system is unstable,
* only inbound traffic could be blocked by {@link SystemRule}
* @param resourceType classification of the resource (e.g. Web or RPC)
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
* @since 1.7.0
*/
- public static AsyncEntry asyncEntry(String name, int resourceType, EntryType type)
+ public static AsyncEntry asyncEntry(String name, int resourceType, EntryType trafficType)
throws BlockException {
- return Env.sph.asyncEntryWithType(name, resourceType, type, 1, false, OBJECTS0);
+ return Env.sph.asyncEntryWithType(name, resourceType, trafficType, 1, false, OBJECTS0);
}
/**
- * Record statistics and check all rules of the resource.
+ * Record statistics and perform rule checking for the given resource that indicates an async invocation.
*
* @param name the unique name for the protected resource
- * @param type the resource is an inbound or an outbound method. This is used
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
* to mark whether it can be blocked when the system is unstable,
* only inbound traffic could be blocked by {@link SystemRule}
* @param resourceType classification of the resource (e.g. Web or RPC)
- * @param args extra parameters
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded
+ * @param args args for parameter flow control or customized slots
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
* @since 1.7.0
*/
- public static AsyncEntry asyncEntry(String name, int resourceType, EntryType type, Object[] args)
+ public static AsyncEntry asyncEntry(String name, int resourceType, EntryType trafficType, Object[] args)
throws BlockException {
- return Env.sph.asyncEntryWithType(name, resourceType, type, 1, false, args);
+ return Env.sph.asyncEntryWithType(name, resourceType, trafficType, 1, false, args);
}
/**
- * Record statistics and check all rules of the resource.
+ * Record statistics and perform rule checking for the given resource that indicates an async invocation.
*
* @param name the unique name for the protected resource
- * @param type the resource is an inbound or an outbound method. This is used
+ * @param trafficType the traffic type (inbound, outbound or internal). This is used
* to mark whether it can be blocked when the system is unstable,
* only inbound traffic could be blocked by {@link SystemRule}
* @param resourceType classification of the resource (e.g. Web or RPC)
- * @param acquireCount tokens required
- * @param args extra parameters
- * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded
+ * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
+ * @param args args for parameter flow control or customized slots
+ * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
+ * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
* @since 1.7.0
*/
- public static AsyncEntry asyncEntry(String name, int resourceType, EntryType type, int acquireCount,
+ public static AsyncEntry asyncEntry(String name, int resourceType, EntryType trafficType, int batchCount,
Object[] args) throws BlockException {
- return Env.sph.asyncEntryWithType(name, resourceType, type, acquireCount, false, args);
+ return Env.sph.asyncEntryWithType(name, resourceType, trafficType, batchCount, false, args);
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Tracer.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Tracer.java
index 4d0e6da3ae..299c80ee88 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Tracer.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Tracer.java
@@ -18,12 +18,9 @@
import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.context.NullContext;
-import com.alibaba.csp.sentinel.metric.extension.MetricExtensionProvider;
-import com.alibaba.csp.sentinel.node.ClusterNode;
-import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.slots.block.BlockException;
-import com.alibaba.csp.sentinel.metric.extension.MetricExtension;
import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.util.function.Predicate;
/**
* This class is used to record other exceptions except block exception.
@@ -36,35 +33,38 @@ public class Tracer {
protected static Class extends Throwable>[] traceClasses;
protected static Class extends Throwable>[] ignoreClasses;
+ protected static Predicate exceptionPredicate;
+
protected Tracer() {}
/**
- * Trace provided {@link Throwable} and increment exception count to entry in current context.
+ * Trace provided {@link Throwable} to the resource entry in current context.
*
* @param e exception to record
*/
public static void trace(Throwable e) {
- trace(e, 1);
+ traceContext(e, ContextUtil.getContext());
}
/**
- * Trace provided {@link Throwable} and add exception count to entry in current context.
+ * Trace provided {@link Throwable} to current entry in current context.
*
* @param e exception to record
* @param count exception count to add
*/
+ @Deprecated
public static void trace(Throwable e, int count) {
traceContext(e, count, ContextUtil.getContext());
}
/**
- * Trace provided {@link Throwable} and add exception count to current entry in provided context.
+ * Trace provided {@link Throwable} to current entry of given entrance context.
*
* @param e exception to record
- * @param count exception count to add
- * @since 1.4.2
+ * @param context target entrance context
+ * @since 1.8.0
*/
- public static void traceContext(Throwable e, int count, Context context) {
+ public static void traceContext(Throwable e, Context context) {
if (!shouldTrace(e)) {
return;
}
@@ -72,54 +72,47 @@ public static void traceContext(Throwable e, int count, Context context) {
if (context == null || context instanceof NullContext) {
return;
}
-
- DefaultNode curNode = (DefaultNode)context.getCurNode();
- traceExceptionToNode(e, count, context.getCurEntry(), curNode);
- }
-
- /**
- * Trace provided {@link Throwable} and increment exception count to provided entry.
- *
- * @param e exception to record
- * @since 1.4.2
- */
- public static void traceEntry(Throwable e, Entry entry) {
- traceEntry(e, 1, entry);
+ traceEntryInternal(e, context.getCurEntry());
}
/**
- * Trace provided {@link Throwable} and add exception count to provided entry.
+ * Trace provided {@link Throwable} and add exception count to current entry in provided context.
*
* @param e exception to record
* @param count exception count to add
* @since 1.4.2
*/
- public static void traceEntry(Throwable e, int count, Entry entry) {
+ @Deprecated
+ public static void traceContext(Throwable e, int count, Context context) {
if (!shouldTrace(e)) {
return;
}
- if (entry == null || entry.getCurNode() == null) {
+
+ if (context == null || context instanceof NullContext) {
return;
}
-
- DefaultNode curNode = (DefaultNode)entry.getCurNode();
- traceExceptionToNode(e, count, entry, curNode);
+ traceEntryInternal(e, context.getCurEntry());
}
- private static void traceExceptionToNode(Throwable t, int count, Entry entry, DefaultNode curNode) {
- if (curNode == null) {
+ /**
+ * Trace provided {@link Throwable} to the given resource entry.
+ *
+ * @param e exception to record
+ * @since 1.4.2
+ */
+ public static void traceEntry(Throwable e, Entry entry) {
+ if (!shouldTrace(e)) {
return;
}
- for (MetricExtension m : MetricExtensionProvider.getMetricExtensions()) {
- m.addException(entry.getResourceWrapper().getName(), count, t);
- }
+ traceEntryInternal(e, entry);
+ }
- // clusterNode can be null when Constants.ON is false.
- ClusterNode clusterNode = curNode.getClusterNode();
- if (clusterNode == null) {
+ private static void traceEntryInternal(/*@NeedToTrace*/ Throwable e, Entry entry) {
+ if (entry == null) {
return;
}
- clusterNode.trace(t, count);
+
+ entry.setError(e);
}
/**
@@ -174,6 +167,24 @@ public static Class extends Throwable>[] getExceptionsToIgnore() {
return ignoreClasses;
}
+ /**
+ * Get exception predicate
+ * @return the exception predicate.
+ */
+ public static Predicate extends Throwable> getExceptionPredicate() {
+ return exceptionPredicate;
+ }
+
+ /**
+ * set an exception predicate which indicates the exception should be traced(return true) or ignored(return false)
+ * except for {@link BlockException}
+ * @param exceptionPredicate the exception predicate
+ */
+ public static void setExceptionPredicate(Predicate exceptionPredicate) {
+ AssertUtil.notNull(exceptionPredicate, "exception predicate must not be null");
+ Tracer.exceptionPredicate = exceptionPredicate;
+ }
+
private static void checkNotNull(Class extends Throwable>[] classes) {
AssertUtil.notNull(classes, "trace or ignore classes must not be null");
for (Class extends Throwable> clazz : classes) {
@@ -191,6 +202,10 @@ protected static boolean shouldTrace(Throwable t) {
if (t == null || t instanceof BlockException) {
return false;
}
+ if (exceptionPredicate != null) {
+ return exceptionPredicate.test(t);
+ }
+
if (ignoreClasses != null) {
for (Class extends Throwable> clazz : ignoreClasses) {
if (clazz != null && clazz.isAssignableFrom(t.getClass())) {
@@ -208,4 +223,4 @@ protected static boolean shouldTrace(Throwable t) {
}
return true;
}
-}
+}
\ No newline at end of file
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/annotation/SentinelResource.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/annotation/SentinelResource.java
index 95f6584841..fe1ab396dd 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/annotation/SentinelResource.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/annotation/SentinelResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2018 Alibaba Group Holding Ltd.
+ * 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.
@@ -23,9 +23,10 @@
* The annotation indicates a definition of Sentinel resource.
*
* @author Eric Zhao
+ * @author zhaoyuguang
* @since 0.1.1
*/
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SentinelResource {
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResult.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResult.java
index 29fa064a2f..b16b76f0e8 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResult.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResult.java
@@ -30,14 +30,25 @@ public class TokenResult {
private int remaining;
private int waitInMs;
+ private long tokenId;
+
private Map attachments;
- public TokenResult() {}
+ public TokenResult() {
+ }
public TokenResult(Integer status) {
this.status = status;
}
+ public long getTokenId() {
+ return tokenId;
+ }
+
+ public void setTokenId(long tokenId) {
+ this.tokenId = tokenId;
+ }
+
public Integer getStatus() {
return status;
}
@@ -77,10 +88,11 @@ public TokenResult setAttachments(Map attachments) {
@Override
public String toString() {
return "TokenResult{" +
- "status=" + status +
- ", remaining=" + remaining +
- ", waitInMs=" + waitInMs +
- ", attachments=" + attachments +
- '}';
+ "status=" + status +
+ ", remaining=" + remaining +
+ ", waitInMs=" + waitInMs +
+ ", attachments=" + attachments +
+ ", tokenId=" + tokenId +
+ '}';
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResultStatus.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResultStatus.java
index ff5dddf5f1..4ba1b2621d 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResultStatus.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResultStatus.java
@@ -59,6 +59,15 @@ public final class TokenResultStatus {
* Token acquire failed (strategy not available).
*/
public static final int NOT_AVAILABLE = 5;
+ /**
+ * Token is successfully released.
+ */
+ public static final int RELEASE_OK = 6;
+ /**
+ * Token already is released before the request arrives.
+ */
+ public static final int ALREADY_RELEASE=7;
- private TokenResultStatus() {}
+ private TokenResultStatus() {
+ }
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenService.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenService.java
index f1d062921d..9e8c7a49fe 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenService.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenService.java
@@ -44,4 +44,20 @@ public interface TokenService {
* @return result of the token request
*/
TokenResult requestParamToken(Long ruleId, int acquireCount, Collection params);
+
+ /**
+ * Request acquire concurrent tokens from remote token server.
+ *
+ * @param clientAddress the address of the request belong.
+ * @param ruleId ruleId the unique rule ID
+ * @param acquireCount token count to acquire
+ * @return result of the token request
+ */
+ TokenResult requestConcurrentToken(String clientAddress,Long ruleId,int acquireCount);
+ /**
+ * Request release concurrent tokens from remote token server asynchronously.
+ *
+ * @param tokenId the unique token ID
+ */
+ void releaseConcurrentToken(Long tokenId);
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/client/TokenClientProvider.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/client/TokenClientProvider.java
index def2f01642..85c9da2fac 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/client/TokenClientProvider.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/client/TokenClientProvider.java
@@ -44,8 +44,8 @@ private static void resolveTokenClientInstance() {
"[TokenClientProvider] No existing cluster token client, cluster client mode will not be activated");
} else {
client = resolvedClient;
- RecordLog.info(
- "[TokenClientProvider] Cluster token client resolved: " + client.getClass().getCanonicalName());
+ RecordLog.info("[TokenClientProvider] Cluster token client resolved: {}",
+ client.getClass().getCanonicalName());
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/server/EmbeddedClusterTokenServerProvider.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/server/EmbeddedClusterTokenServerProvider.java
index 6b76af933e..2f55e8112c 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/server/EmbeddedClusterTokenServerProvider.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/server/EmbeddedClusterTokenServerProvider.java
@@ -36,7 +36,8 @@ private static void resolveInstance() {
RecordLog.warn("[EmbeddedClusterTokenServerProvider] No existing cluster token server, cluster server mode will not be activated");
} else {
server = s;
- RecordLog.info("[EmbeddedClusterTokenServerProvider] Cluster token server resolved: " + server.getClass().getCanonicalName());
+ RecordLog.info("[EmbeddedClusterTokenServerProvider] Cluster token server resolved: {}",
+ server.getClass().getCanonicalName());
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfig.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfig.java
index a0d7c2268d..f86bdc4e21 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfig.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfig.java
@@ -16,10 +16,10 @@
package com.alibaba.csp.sentinel.config;
import com.alibaba.csp.sentinel.log.RecordLog;
-import com.alibaba.csp.sentinel.util.AppNameUtil;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.csp.sentinel.util.StringUtil;
+import java.io.File;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
@@ -30,6 +30,7 @@
*
* @author leyou
* @author Eric Zhao
+ * @author Lin Liang
*/
public final class SentinelConfig {
@@ -41,9 +42,13 @@ public final class SentinelConfig {
public static final int APP_TYPE_COMMON = 0;
private static final Map props = new ConcurrentHashMap<>();
+
private static int appType = APP_TYPE_COMMON;
+ private static String appName = "";
- public static final String APP_TYPE = "csp.sentinel.app.type";
+ public static final String PROJECT_NAME_PROP_KEY = "project.name";
+ public static final String APP_NAME_PROP_KEY = "csp.sentinel.app.name";
+ public static final String APP_TYPE_PROP_KEY = "csp.sentinel.app.type";
public static final String CHARSET = "csp.sentinel.charset";
public static final String SINGLE_METRIC_FILE_SIZE = "csp.sentinel.metric.file.single.size";
public static final String TOTAL_METRIC_FILE_COUNT = "csp.sentinel.metric.file.total.count";
@@ -51,19 +56,19 @@ public final class SentinelConfig {
public static final String STATISTIC_MAX_RT = "csp.sentinel.statistic.max.rt";
public static final String SPI_CLASSLOADER = "csp.sentinel.spi.classloader";
- static final String DEFAULT_CHARSET = "UTF-8";
- static final long DEFAULT_SINGLE_METRIC_FILE_SIZE = 1024 * 1024 * 50;
- static final int DEFAULT_TOTAL_METRIC_FILE_COUNT = 6;
- static final int DEFAULT_COLD_FACTOR = 3;
-
- public static final int DEFAULT_STATISTIC_MAX_RT = 4900;
+ public static final String DEFAULT_CHARSET = "UTF-8";
+ public static final long DEFAULT_SINGLE_METRIC_FILE_SIZE = 1024 * 1024 * 50;
+ public static final int DEFAULT_TOTAL_METRIC_FILE_COUNT = 6;
+ public static final int DEFAULT_COLD_FACTOR = 3;
+ public static final int DEFAULT_STATISTIC_MAX_RT = 5000;
static {
try {
initialize();
loadProps();
+ resolveAppName();
resolveAppType();
- RecordLog.info("[SentinelConfig] Application type resolved: " + appType);
+ RecordLog.info("[SentinelConfig] Application type resolved: {}", appType);
} catch (Throwable ex) {
RecordLog.warn("[SentinelConfig] Failed to initialize", ex);
ex.printStackTrace();
@@ -72,7 +77,7 @@ public final class SentinelConfig {
private static void resolveAppType() {
try {
- String type = getConfig(APP_TYPE);
+ String type = getConfig(APP_TYPE_PROP_KEY);
if (type == null) {
appType = APP_TYPE_COMMON;
return;
@@ -134,7 +139,7 @@ public static void setConfigIfAbsent(String key, String value) {
}
public static String getAppName() {
- return AppNameUtil.getAppName();
+ return appName;
}
/**
@@ -189,9 +194,7 @@ public static int coldFactor() {
}
/**
- *
Get the max RT value that Sentinel could accept.
- *
Response time that exceeds {@code statisticMaxRt} will be recorded as this value.
- * The default value is {@link #DEFAULT_STATISTIC_MAX_RT}.
+ *
Get the max RT value that Sentinel could accept for system BBR strategy.
*
* @return the max allowed RT value
* @since 1.4.1
@@ -204,12 +207,85 @@ public static int statisticMaxRt() {
}
return Integer.parseInt(v);
} catch (Throwable throwable) {
- RecordLog.warn("[SentinelConfig] Invalid statisticMaxRt value: {0}, using the default value instead: "
+ RecordLog.warn("[SentinelConfig] Invalid statisticMaxRt value: {}, using the default value instead: "
+ DEFAULT_STATISTIC_MAX_RT, v, throwable);
SentinelConfig.setConfig(STATISTIC_MAX_RT, String.valueOf(DEFAULT_STATISTIC_MAX_RT));
return DEFAULT_STATISTIC_MAX_RT;
}
}
+ /**
+ * Function for resolving project name. The order is elaborated below:
+ *
+ *
+ *
Resolve the value from {@code CSP_SENTINEL_APP_NAME} system environment;
+ *
Resolve the value from {@code csp.sentinel.app.name} system property;
+ *
Resolve the value from {@code project.name} system property (for compatibility);
+ *
Resolve the value from {@code sun.java.command} system property, then remove path, arguments and ".jar" or ".JAR"
+ * suffix, use the result as app name. Note that whitespace in file name or path is not allowed, or a
+ * wrong app name may be gotten, For example:
+ *
+ *
+ * "test.Main" -> test.Main
+ * "/target/test.Main" -> test.Main
+ * "/target/test.Main args1" -> test.Main
+ * "Main.jar" -> Main
+ * "/target/Main.JAR args1" -> Main
+ * "Mai n.jar" -> Mai // whitespace in file name is not allowed
+ *
+ *
+ *
+ *
+ */
+ private static void resolveAppName() {
+ // Priority: system env -> csp.sentinel.app.name -> project.name -> main class (or jar) name
+ String envKey = toEnvKey(APP_NAME_PROP_KEY);
+ String n = System.getenv(envKey);
+ if (!StringUtil.isBlank(n)) {
+ appName = n;
+ RecordLog.info("App name resolved from system env {}: {}", envKey, appName);
+ return;
+ }
+ n = props.get(APP_NAME_PROP_KEY);
+ if (!StringUtil.isBlank(n)) {
+ appName = n;
+ RecordLog.info("App name resolved from property {}: {}", APP_NAME_PROP_KEY, appName);
+ return;
+ }
+ n = props.get(PROJECT_NAME_PROP_KEY);
+ if (!StringUtil.isBlank(n)) {
+ appName = n;
+ RecordLog.info("App name resolved from property {}: {}", PROJECT_NAME_PROP_KEY, appName);
+ return;
+ }
+ // Parse sun.java.command property by default.
+ String command = System.getProperty("sun.java.command");
+ if (StringUtil.isBlank(command)) {
+ RecordLog.warn("Cannot resolve default appName from property sun.java.command");
+ return;
+ }
+ command = command.split("\\s")[0];
+ String separator = File.separator;
+ if (command.contains(separator)) {
+ String[] strs;
+ if ("\\".equals(separator)) {
+ // Handle separator in Windows.
+ strs = command.split("\\\\");
+ } else {
+ strs = command.split(separator);
+ }
+ command = strs[strs.length - 1];
+ }
+ if (command.toLowerCase().endsWith(".jar")) {
+ command = command.substring(0, command.length() - 4);
+ }
+ appName = command;
+ RecordLog.info("App name resolved from default: {}", appName);
+ }
+
+ private static String toEnvKey(/*@NotBlank*/ String propKey) {
+ return propKey.toUpperCase().replace('.', '_');
+ }
+
private SentinelConfig() {}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfigLoader.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfigLoader.java
index 67cd4805df..e267ed7742 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfigLoader.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfigLoader.java
@@ -38,9 +38,6 @@ public final class SentinelConfigLoader {
public static final String SENTINEL_CONFIG_ENV_KEY = "CSP_SENTINEL_CONFIG_FILE";
public static final String SENTINEL_CONFIG_PROPERTY_KEY = "csp.sentinel.config.file";
- private static final String DIR_NAME = "logs" + File.separator + "csp";
- private static final String USER_HOME = "user.home";
-
private static final String DEFAULT_SENTINEL_CONFIG_FILE = "classpath:sentinel.properties";
private static Properties properties = new Properties();
@@ -64,19 +61,8 @@ private static void load() {
}
Properties p = ConfigUtil.loadProperties(fileName);
-
- // Compatible with legacy config file path.
- if (p == null) {
- String path = addSeparator(System.getProperty(USER_HOME)) + DIR_NAME + File.separator;
- fileName = path + AppNameUtil.getAppName() + ".properties";
- File file = new File(fileName);
- if (file.exists()) {
- p = ConfigUtil.loadProperties(fileName);
- }
- }
-
if (p != null && !p.isEmpty()) {
- RecordLog.info("[SentinelConfigLoader] Loading Sentinel config from " + fileName);
+ RecordLog.info("[SentinelConfigLoader] Loading Sentinel config from {}", fileName);
properties.putAll(p);
}
@@ -86,7 +72,7 @@ private static void load() {
String oldConfigValue = properties.getProperty(configKey);
properties.put(configKey, newConfigValue);
if (oldConfigValue != null) {
- RecordLog.info("[SentinelConfigLoader] JVM parameter overrides {0}: {1} -> {2}",
+ RecordLog.info("[SentinelConfigLoader] JVM parameter overrides {}: {} -> {}",
configKey, oldConfigValue, newConfigValue);
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/context/ContextUtil.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/context/ContextUtil.java
index 9af5f76292..5e0ae0236f 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/context/ContextUtil.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/context/ContextUtil.java
@@ -127,8 +127,8 @@ protected static Context trueEnter(String name, String origin) {
setNullContext();
return NULL_CONTEXT;
} else {
+ LOCK.lock();
try {
- LOCK.lock();
node = contextNameNodeMap.get(name);
if (node == null) {
if (contextNameNodeMap.size() > Constants.MAX_CONTEXT_NAME_SIZE) {
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/eagleeye/FastDateFormat.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/eagleeye/FastDateFormat.java
index 30a7cbade3..eefc6d4fa3 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/eagleeye/FastDateFormat.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/eagleeye/FastDateFormat.java
@@ -75,7 +75,7 @@ String formatWithoutMs(long timestamp) {
private SimpleDateFormat createSimpleDateFormat() {
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- fmt.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
+ fmt.setTimeZone(TimeZone.getDefault());
return fmt;
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/init/InitExecutor.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/init/InitExecutor.java
index e68fd599e3..8e63889db0 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/init/InitExecutor.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/init/InitExecutor.java
@@ -46,13 +46,13 @@ public static void doInit() {
ServiceLoader loader = ServiceLoaderUtil.getServiceLoader(InitFunc.class);
List initList = new ArrayList();
for (InitFunc initFunc : loader) {
- RecordLog.info("[InitExecutor] Found init func: " + initFunc.getClass().getCanonicalName());
+ RecordLog.info("[InitExecutor] Found init func: {}", initFunc.getClass().getCanonicalName());
insertSorted(initList, initFunc);
}
for (OrderWrapper w : initList) {
w.func.init();
- RecordLog.info(String.format("[InitExecutor] Executing %s with order %d",
- w.func.getClass().getCanonicalName(), w.order));
+ RecordLog.info("[InitExecutor] Executing {} with order {}",
+ w.func.getClass().getCanonicalName(), w.order);
}
} catch (Exception ex) {
RecordLog.warn("[InitExecutor] WARN: Initialization failed", ex);
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogBase.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogBase.java
index a237dde7d1..091ce15eae 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogBase.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogBase.java
@@ -15,19 +15,14 @@
*/
package com.alibaba.csp.sentinel.log;
-import com.alibaba.csp.sentinel.util.PidUtil;
import java.io.File;
-import java.io.IOException;
import java.util.Properties;
-import java.util.logging.Handler;
-import java.util.logging.Level;
-import java.util.logging.Logger;
import static com.alibaba.csp.sentinel.util.ConfigUtil.addSeparator;
/**
- *
The base class for logging.
+ *
The base config class for logging.
*
*
* The default log base directory is {@code ${user.home}/logs/csp/}. We can use the {@link #LOG_DIR}
@@ -36,7 +31,8 @@
* In this case, {@link #LOG_NAME_USE_PID} property could be configured as "true" to turn on this switch.
*
*
- * @author leyou
+ * @author Carpenter Lee
+ * @author Eric Zhao
*/
public class LogBase {
@@ -66,15 +62,15 @@ public class LogBase {
static {
try {
- initialize();
+ initializeDefault();
loadProperties();
} catch (Throwable t) {
- System.err.println("[LogBase] FATAL ERROR when initializing log class");
+ System.err.println("[LogBase] FATAL ERROR when initializing logging config");
t.printStackTrace();
}
}
- private static void initialize() {
+ private static void initializeDefault() {
logNameUsePid = false;
logOutputType = LOG_OUTPUT_TYPE_FILE;
logBaseDir = addSeparator(System.getProperty(USER_HOME)) + DIR_NAME + File.separator;
@@ -88,10 +84,10 @@ private static void loadProperties() {
if (!LOG_OUTPUT_TYPE_FILE.equalsIgnoreCase(logOutputType) && !LOG_OUTPUT_TYPE_CONSOLE.equalsIgnoreCase(logOutputType)) {
logOutputType = LOG_OUTPUT_TYPE_FILE;
}
- System.out.println("INFO: log output type is: " + logOutputType);
+ System.out.println("INFO: Sentinel log output type is: " + logOutputType);
logCharSet = properties.getProperty(LOG_CHARSET) == null ? logCharSet : properties.getProperty(LOG_CHARSET);
- System.out.println("INFO: log charset is: " + logCharSet);
+ System.out.println("INFO: Sentinel log charset is: " + logCharSet);
logBaseDir = properties.getProperty(LOG_DIR) == null ? logBaseDir : properties.getProperty(LOG_DIR);
@@ -99,15 +95,14 @@ private static void loadProperties() {
File dir = new File(logBaseDir);
if (!dir.exists()) {
if (!dir.mkdirs()) {
- System.err.println("ERROR: create log base dir error: " + logBaseDir);
+ System.err.println("ERROR: create Sentinel log base directory error: " + logBaseDir);
}
}
- System.out.println("INFO: log base dir is: " + logBaseDir);
-
+ System.out.println("INFO: Sentinel log base directory is: " + logBaseDir);
String usePid = properties.getProperty(LOG_NAME_USE_PID);
logNameUsePid = "true".equalsIgnoreCase(usePid);
- System.out.println("INFO: log name use pid is: " + logNameUsePid);
+ System.out.println("INFO: Sentinel log name use pid is: " + logNameUsePid);
}
@@ -147,64 +142,4 @@ public static String getLogCharset() {
return logCharSet;
}
- protected static void log(Logger logger, Handler handler, Level level, String detail, Object... params) {
- if (detail == null) {
- return;
- }
- LoggerUtils.disableOtherHandlers(logger, handler);
-
- FormattingTuple formattingTuple = MessageFormatter.arrayFormat(detail, params);
- String message = formattingTuple.getMessage();
- logger.log(level, message);
- }
-
- protected static void log(Logger logger, Handler handler, Level level, String detail, Throwable throwable) {
- if (detail == null) {
- return;
- }
- LoggerUtils.disableOtherHandlers(logger, handler);
- logger.log(level, detail, throwable);
- }
-
-
- protected static Handler makeLogger(String logName, Logger heliumRecordLog) {
- CspFormatter formatter = new CspFormatter();
-
- Handler handler = null;
-
- // Create handler according to logOutputType, set formatter to CspFormatter, set encoding to LOG_CHARSET
- switch (logOutputType) {
- case LOG_OUTPUT_TYPE_FILE:
- String fileName = LogBase.getLogBaseDir() + logName;
- if (isLogNameUsePid()) {
- fileName += ".pid" + PidUtil.getPid();
- }
- try {
- handler = new DateFileLogHandler(fileName + ".%d", 1024 * 1024 * 200, 4, true);
- handler.setFormatter(formatter);
- handler.setEncoding(logCharSet);
- } catch (IOException e) {
- e.printStackTrace();
- }
- break;
- case LOG_OUTPUT_TYPE_CONSOLE:
- try {
- handler = new ConsoleHandler();
- handler.setFormatter(formatter);
- handler.setEncoding(logCharSet);
- } catch (IOException e) {
- e.printStackTrace();
- }
- break;
- default:
- break;
- }
-
- if (handler != null) {
- LoggerUtils.disableOtherHandlers(heliumRecordLog, handler);
- }
- heliumRecordLog.setLevel(Level.ALL);
- return handler;
- }
-
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogTarget.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogTarget.java
index 0dc3ef975b..2a2e025a0f 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogTarget.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogTarget.java
@@ -19,14 +19,16 @@
/**
* @author xue8
+ * @since 1.7.2
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface LogTarget {
/**
- * Returns the kinds of log type.
- * @return Returns the kinds of log type
+ * Returns the logger name.
+ *
+ * @return the logger name. Record logger by default
*/
- LogType value() default LogType.RECORD_LOG;
+ String value() default RecordLog.LOGGER_NAME;
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/Logger.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/Logger.java
index 8b4d580944..75e5d701d2 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/Logger.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/Logger.java
@@ -16,18 +16,19 @@
package com.alibaba.csp.sentinel.log;
/**
- * Provide logger SPI interface.
- * The default implementation is {@link java.util.logging}.
- *
- * Notice, the placeholder only supports the most popular placeholder convention (slf4j).
- * So, if you're not using slf4j, you should create adapters compatible with placeholders "{}".
+ *
The universal logger SPI interface.
+ *
Notice: the placeholder only supports the most popular placeholder convention (slf4j).
+ * So, if you're not using slf4j, you should create adapters compatible with placeholders "{}".
*
* @author xue8
+ * @since 1.7.2
*/
public interface Logger {
+
/**
* Log a message at the INFO level according to the specified format
* and arguments.
+ *
* @param format the format string
* @param arguments a list of arguments
*/
@@ -45,6 +46,7 @@ public interface Logger {
/**
* Log a message at the WARN level according to the specified format
* and arguments.
+ *
* @param format the format string
* @param arguments a list of arguments
*/
@@ -62,6 +64,7 @@ public interface Logger {
/**
* Log a message at the TRACE level according to the specified format
* and arguments.
+ *
* @param format the format string
* @param arguments a list of arguments
*/
@@ -79,6 +82,7 @@ public interface Logger {
/**
* Log a message at the DEBUG level according to the specified format
* and arguments.
+ *
* @param format the format string
* @param arguments a list of arguments
*/
@@ -96,6 +100,7 @@ public interface Logger {
/**
* Log a message at the ERROR level according to the specified format
* and arguments.
+ *
* @param format the format string
* @param arguments a list of arguments
*/
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LoggerSpiProvider.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LoggerSpiProvider.java
new file mode 100644
index 0000000000..7bdb71b68f
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LoggerSpiProvider.java
@@ -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.log;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ServiceLoader;
+
+import com.alibaba.csp.sentinel.util.StringUtil;
+
+/**
+ * SPI provider of Sentinel {@link Logger}.
+ *
+ * @author Eric Zhao
+ * @since 1.7.2
+ */
+public final class LoggerSpiProvider {
+
+ private static final Map LOGGER_MAP = new HashMap<>();
+
+ static {
+ // NOTE: this class SHOULD NOT depend on any other Sentinel classes
+ // except the util classes to avoid circular dependency.
+ try {
+ resolveLoggers();
+ } catch (Throwable t) {
+ System.err.println("Failed to resolve Sentinel Logger SPI");
+ t.printStackTrace();
+ }
+ }
+
+ public static Logger getLogger(String name) {
+ if (name == null) {
+ return null;
+ }
+ return LOGGER_MAP.get(name);
+ }
+
+ private static void resolveLoggers() {
+ // NOTE: Here we cannot use {@code SpiLoader} directly because it depends on the RecordLog.
+ ServiceLoader loggerLoader = ServiceLoader.load(Logger.class);
+
+ for (Logger logger : loggerLoader) {
+ LogTarget annotation = logger.getClass().getAnnotation(LogTarget.class);
+ if (annotation == null) {
+ continue;
+ }
+ String name = annotation.value();
+ // Load first encountered logger if multiple loggers are associated with the same name.
+ if (StringUtil.isNotBlank(name) && !LOGGER_MAP.containsKey(name)) {
+ LOGGER_MAP.put(name, logger);
+ System.out.println("Sentinel Logger SPI loaded for <" + name + ">: "
+ + logger.getClass().getCanonicalName());
+ }
+ }
+ }
+
+ private LoggerSpiProvider() {}
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LoggerUtils.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LoggerUtils.java
deleted file mode 100755
index 2fe0d468ba..0000000000
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LoggerUtils.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.log;
-
-import java.util.logging.Handler;
-import java.util.logging.Logger;
-
-/**
- * Util class for logger.
- */
-class LoggerUtils {
-
- /**
- * Remove all current handlers from the logger and attach it with the given log handler.
- *
- * @param logger logger
- * @param handler the log handler
- */
- static void disableOtherHandlers(Logger logger, Handler handler) {
- if (logger == null) {
- return;
- }
-
- synchronized (logger) {
- Handler[] handlers = logger.getHandlers();
- if (handlers == null) {
- return;
- }
- if (handlers.length == 1 && handlers[0].equals(handler)) {
- return;
- }
-
- logger.setUseParentHandlers(false);
- // Remove all current handlers.
- for (Handler h : handlers) {
- logger.removeHandler(h);
- }
- // Attach the given handler.
- logger.addHandler(handler);
- }
- }
-}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/RecordLog.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/RecordLog.java
index da7bb2ed00..19ee01fe36 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/RecordLog.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/RecordLog.java
@@ -15,80 +15,74 @@
*/
package com.alibaba.csp.sentinel.log;
-import java.util.Iterator;
-import java.util.ServiceLoader;
+import com.alibaba.csp.sentinel.log.jul.JavaLoggingAdapter;
-/***
- * The basic logger for vital events.
+/**
+ * The basic biz logger of Sentinel.
*
* @author youji.zj
+ * @author Eric Zhao
*/
-public class RecordLog extends LogBase {
- private static com.alibaba.csp.sentinel.log.Logger log = null;
+public class RecordLog {
+
+ public static final String LOGGER_NAME = "sentinelRecordLogger";
+ public static final String DEFAULT_LOG_FILENAME = "sentinel-record.log";
+
+ private static com.alibaba.csp.sentinel.log.Logger logger = null;
static {
- ServiceLoader load = ServiceLoader.load(Logger.class);
- Logger logger = null;
- Iterator iterator = load.iterator();
- while (iterator.hasNext()) {
- Logger next = iterator.next();
- LogTarget annotation = next.getClass().getAnnotation(LogTarget.class);
- if (annotation == null) {
- continue;
+ try {
+ // Load user-defined logger implementation first.
+ logger = LoggerSpiProvider.getLogger(LOGGER_NAME);
+ if (logger == null) {
+ // If no customized loggers are provided, we use the default logger based on JUL.
+ logger = new JavaLoggingAdapter(LOGGER_NAME, DEFAULT_LOG_FILENAME);
}
- String value = annotation.value().name();
- if (value.equals(LogType.RECORD_LOG.name())) {
- logger = next;
- break;
- }
- }
- // Use user implementations.
- if (logger != null) {
- log = logger;
- } else {
- // Use default implementations.
- log = new RecordLogLogging();
+ } catch (Throwable t) {
+ System.err.println("Error: failed to initialize Sentinel RecordLog");
+ t.printStackTrace();
}
}
public static void info(String format, Object... arguments) {
- log.info(format, arguments);
+ logger.info(format, arguments);
}
public static void info(String msg, Throwable e) {
- log.info(msg, e);
+ logger.info(msg, e);
}
public static void warn(String format, Object... arguments) {
- log.warn(format, arguments);
+ logger.warn(format, arguments);
}
public static void warn(String msg, Throwable e) {
- log.warn(msg, e);
+ logger.warn(msg, e);
}
public static void trace(String format, Object... arguments) {
- log.trace(format, arguments);
+ logger.trace(format, arguments);
}
public static void trace(String msg, Throwable e) {
- log.trace(msg, e);
+ logger.trace(msg, e);
}
public static void debug(String format, Object... arguments) {
- log.debug(format, arguments);
+ logger.debug(format, arguments);
}
public static void debug(String msg, Throwable e) {
- log.debug(msg, e);
+ logger.debug(msg, e);
}
public static void error(String format, Object... arguments) {
- log.error(format, arguments);
+ logger.error(format, arguments);
}
public static void error(String msg, Throwable e) {
- log.error(msg, e);
+ logger.error(msg, e);
}
+ private RecordLog() {}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/BaseJulLogger.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/BaseJulLogger.java
new file mode 100644
index 0000000000..0490efd299
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/BaseJulLogger.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 1999-2019 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.log.jul;
+
+import java.io.IOException;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.alibaba.csp.sentinel.log.LogBase;
+import com.alibaba.csp.sentinel.util.PidUtil;
+
+import static com.alibaba.csp.sentinel.log.LogBase.LOG_OUTPUT_TYPE_CONSOLE;
+import static com.alibaba.csp.sentinel.log.LogBase.LOG_OUTPUT_TYPE_FILE;
+
+/**
+ * The default logger based on java.util.logging.
+ *
+ * @author Eric Zhao
+ * @since 1.7.2
+ */
+public class BaseJulLogger {
+
+ protected void log(Logger logger, Handler handler, Level level, String detail, Object... params) {
+ if (detail == null) {
+ return;
+ }
+ disableOtherHandlers(logger, handler);
+
+ // Compatible with slf4j placeholder format "{}".
+ FormattingTuple formattingTuple = MessageFormatter.arrayFormat(detail, params);
+ String message = formattingTuple.getMessage();
+ logger.log(level, message);
+ }
+
+ protected void log(Logger logger, Handler handler, Level level, String detail, Throwable throwable) {
+ if (detail == null) {
+ return;
+ }
+ disableOtherHandlers(logger, handler);
+ logger.log(level, detail, throwable);
+ }
+
+ protected Handler makeLoggingHandler(String logName, Logger heliumRecordLog) {
+ CspFormatter formatter = new CspFormatter();
+ String logCharSet = LogBase.getLogCharset();
+ Handler handler = null;
+
+ // Create handler according to logOutputType, set formatter to CspFormatter, set encoding to LOG_CHARSET
+ switch (LogBase.getLogOutputType()) {
+ case LOG_OUTPUT_TYPE_FILE:
+ String fileName = LogBase.getLogBaseDir() + logName;
+ if (LogBase.isLogNameUsePid()) {
+ fileName += ".pid" + PidUtil.getPid();
+ }
+ try {
+ handler = new DateFileLogHandler(fileName + ".%d", 1024 * 1024 * 200, 4, true);
+ handler.setFormatter(formatter);
+ handler.setEncoding(logCharSet);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ break;
+ case LOG_OUTPUT_TYPE_CONSOLE:
+ try {
+ handler = new ConsoleHandler();
+ handler.setFormatter(formatter);
+ handler.setEncoding(logCharSet);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (handler != null) {
+ disableOtherHandlers(heliumRecordLog, handler);
+ }
+
+ // Set log level to INFO by default
+ heliumRecordLog.setLevel(Level.INFO);
+ return handler;
+ }
+
+ /**
+ * Remove all current handlers from the logger and attach it with the given log handler.
+ *
+ * @param logger logger
+ * @param handler the log handler
+ */
+ static void disableOtherHandlers(Logger logger, Handler handler) {
+ if (logger == null) {
+ return;
+ }
+
+ synchronized (logger) {
+ Handler[] handlers = logger.getHandlers();
+ if (handlers == null) {
+ return;
+ }
+ if (handlers.length == 1 && handlers[0].equals(handler)) {
+ return;
+ }
+
+ logger.setUseParentHandlers(false);
+ // Remove all current handlers.
+ for (Handler h : handlers) {
+ logger.removeHandler(h);
+ }
+ // Attach the given handler.
+ logger.addHandler(handler);
+ }
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/ConsoleHandler.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/ConsoleHandler.java
similarity index 98%
rename from sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/ConsoleHandler.java
rename to sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/ConsoleHandler.java
index 7654fa2a03..589b094961 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/ConsoleHandler.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/ConsoleHandler.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.alibaba.csp.sentinel.log;
+package com.alibaba.csp.sentinel.log.jul;
import java.io.UnsupportedEncodingException;
import java.util.logging.*;
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/CspFormatter.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/CspFormatter.java
similarity index 97%
rename from sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/CspFormatter.java
rename to sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/CspFormatter.java
index 5d24c21217..b93f11af03 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/CspFormatter.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/CspFormatter.java
@@ -10,7 +10,7 @@
* 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.log;
+package com.alibaba.csp.sentinel.log.jul;
import java.io.PrintWriter;
import java.io.StringWriter;
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/DateFileLogHandler.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/DateFileLogHandler.java
similarity index 99%
rename from sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/DateFileLogHandler.java
rename to sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/DateFileLogHandler.java
index 347882208b..30a5ebeeec 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/DateFileLogHandler.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/DateFileLogHandler.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.alibaba.csp.sentinel.log;
+package com.alibaba.csp.sentinel.log.jul;
import java.io.File;
import java.io.IOException;
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/FormattingTuple.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/FormattingTuple.java
similarity index 90%
rename from sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/FormattingTuple.java
rename to sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/FormattingTuple.java
index cfaa1e64f6..acba7304b7 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/FormattingTuple.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/FormattingTuple.java
@@ -14,12 +14,8 @@
* limitations under the License.
*/
-/**
- * Copyright notice
- * This code copy from SLF4J which licensed under the MIT License.
- *
- */
-package com.alibaba.csp.sentinel.log;
+// Copyright notice: This code was copied from SLF4J which licensed under the MIT License.
+package com.alibaba.csp.sentinel.log.jul;
/**
* Holds the results of formatting done by {@link MessageFormatter}.
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/JavaLoggingAdapter.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/JavaLoggingAdapter.java
new file mode 100644
index 0000000000..a31a132b2b
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/JavaLoggingAdapter.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 1999-2019 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.log.jul;
+
+import java.util.logging.Handler;
+
+import com.alibaba.csp.sentinel.log.Logger;
+import com.alibaba.csp.sentinel.util.AssertUtil;
+
+/**
+ * JUL adapter for Sentinel {@link Logger} SPI.
+ *
+ * @author Eric Zhao
+ * @since 1.7.2
+ */
+public class JavaLoggingAdapter extends BaseJulLogger implements Logger {
+
+ private final String loggerName;
+ private final String fileNamePattern;
+
+ private final java.util.logging.Logger julLogger;
+ private final Handler logHandler;
+
+ public JavaLoggingAdapter(String loggerName, String fileNamePattern) {
+ AssertUtil.assertNotBlank(loggerName, "loggerName cannot be blank");
+ AssertUtil.assertNotBlank(fileNamePattern, "fileNamePattern cannot be blank");
+ this.loggerName = loggerName;
+ this.fileNamePattern = fileNamePattern;
+
+ this.julLogger = java.util.logging.Logger.getLogger(loggerName);
+ this.logHandler = makeLoggingHandler(fileNamePattern, julLogger);
+ }
+
+ @Override
+ public void info(String format, Object... arguments) {
+ log(julLogger, logHandler, Level.INFO, format, arguments);
+ }
+
+ @Override
+ public void info(String msg, Throwable e) {
+ log(julLogger, logHandler, Level.INFO, msg, e);
+ }
+
+ @Override
+ public void warn(String format, Object... arguments) {
+ log(julLogger, logHandler, Level.WARNING, format, arguments);
+ }
+
+ @Override
+ public void warn(String msg, Throwable e) {
+ log(julLogger, logHandler, Level.WARNING, msg, e);
+ }
+
+ @Override
+ public void trace(String format, Object... arguments) {
+ log(julLogger, logHandler, Level.TRACE, format, arguments);
+ }
+
+ @Override
+ public void trace(String msg, Throwable e) {
+ log(julLogger, logHandler, Level.TRACE, msg, e);
+ }
+
+ @Override
+ public void debug(String format, Object... arguments) {
+ log(julLogger, logHandler, Level.DEBUG, format, arguments);
+ }
+
+ @Override
+ public void debug(String msg, Throwable e) {
+ log(julLogger, logHandler, Level.DEBUG, msg, e);
+ }
+
+ @Override
+ public void error(String format, Object... arguments) {
+ log(julLogger, logHandler, Level.ERROR, format, arguments);
+ }
+
+ @Override
+ public void error(String msg, Throwable e) {
+ log(julLogger, logHandler, Level.ERROR, msg, e);
+ }
+
+ public String getLoggerName() {
+ return loggerName;
+ }
+
+ public String getFileNamePattern() {
+ return fileNamePattern;
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/Level.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/Level.java
similarity index 94%
rename from sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/Level.java
rename to sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/Level.java
index d64c4bb076..6dcc9f05b9 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/Level.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/Level.java
@@ -13,10 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.alibaba.csp.sentinel.log;
+package com.alibaba.csp.sentinel.log.jul;
/**
- * Logging levels
+ * JUL logging levels.
+ *
* @author xue8
*/
public class Level extends java.util.logging.Level {
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/MessageFormatter.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/MessageFormatter.java
similarity index 98%
rename from sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/MessageFormatter.java
rename to sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/MessageFormatter.java
index e2c30f2edd..2d038bd794 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/MessageFormatter.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/jul/MessageFormatter.java
@@ -14,18 +14,8 @@
* limitations under the License.
*/
-/**
- * Copyright notice
- * This code copy from SLF4J which licensed under the MIT License.
- *
- */
-
-/**
- * Copyright notice
- * This code copy from SLF4J which licensed under the MIT License.
- *
- */
-package com.alibaba.csp.sentinel.log;
+// Copyright notice: This code was copied from SLF4J which licensed under the MIT License.
+package com.alibaba.csp.sentinel.log.jul;
// contributors: lizongbo: proposed special treatment of array parameter values
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/AdvancedMetricExtension.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/AdvancedMetricExtension.java
new file mode 100644
index 0000000000..9bd5102f93
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/AdvancedMetricExtension.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1999-2019 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.metric.extension;
+
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/**
+ * Extended {@link MetricExtension} extending input parameters of each metric
+ * collection method with {@link EntryType}.
+ *
+ * @author bill_yip
+ * @author Eric Zhao
+ * @since 1.8.0
+ */
+public interface AdvancedMetricExtension extends MetricExtension {
+
+ /**
+ * Add current pass count of the resource name.
+ *
+ * @param rw resource representation (including resource name, traffic type, etc.)
+ * @param batchCount count to add
+ * @param args additional arguments of the resource, eg. if the resource is a method name,
+ * the args will be the parameters of the method.
+ */
+ void onPass(ResourceWrapper rw, int batchCount, Object[] args);
+
+ /**
+ * Add current block count of the resource name.
+ *
+ * @param rw resource representation (including resource name, traffic type, etc.)
+ * @param batchCount count to add
+ * @param origin the origin of caller (if present)
+ * @param e the associated {@code BlockException}
+ * @param args additional arguments of the resource, eg. if the resource is a method name,
+ * the args will be the parameters of the method.
+ */
+ void onBlocked(ResourceWrapper rw, int batchCount, String origin, BlockException e,
+ Object[] args);
+
+ /**
+ * Add current completed count of the resource name.
+ *
+ * @param rw resource representation (including resource name, traffic type, etc.)
+ * @param batchCount count to add
+ * @param rt response time of current invocation
+ * @param args additional arguments of the resource
+ */
+ void onComplete(ResourceWrapper rw, long rt, int batchCount, Object[] args);
+
+ /**
+ * Add current exception count of the resource name.
+ *
+ * @param rw resource representation (including resource name, traffic type, etc.)
+ * @param batchCount count to add
+ * @param throwable exception related.
+ * @param args additional arguments of the resource
+ */
+ void onError(ResourceWrapper rw, Throwable throwable, int batchCount, Object[] args);
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricCallbackInit.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricCallbackInit.java
index 0ffef9e12f..01c73d6a70 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricCallbackInit.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricCallbackInit.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright 1999-2019 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.metric.extension;
import com.alibaba.csp.sentinel.init.InitFunc;
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtension.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtension.java
index 8f21328b8f..88e6cc8968 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtension.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtension.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright 1999-2019 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.metric.extension;
import com.alibaba.csp.sentinel.slots.block.BlockException;
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtensionProvider.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtensionProvider.java
index 9980a6e137..75da7fe78e 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtensionProvider.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtensionProvider.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright 1999-2019 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.metric.extension;
import java.util.ArrayList;
@@ -7,7 +22,7 @@
import com.alibaba.csp.sentinel.util.SpiLoader;
/**
- * Get all {@link MetricExtension}s via SPI.
+ * Get all {@link MetricExtension} via SPI.
*
* @author Carpenter Lee
* @since 1.6.1
@@ -22,18 +37,19 @@ public class MetricExtensionProvider {
private static void resolveInstance() {
List extensions = SpiLoader.loadInstanceList(MetricExtension.class);
- if (extensions == null) {
- RecordLog.warn("[MetricExtensionProvider] WARN: No existing MetricExtension found");
+ if (extensions.isEmpty()) {
+ RecordLog.info("[MetricExtensionProvider] No existing MetricExtension found");
} else {
metricExtensions.addAll(extensions);
- RecordLog.info("[MetricExtensionProvider] MetricExtension resolved, size=" + extensions.size());
+ RecordLog.info("[MetricExtensionProvider] MetricExtension resolved, size={}", extensions.size());
}
}
/**
- * Get all metric extensions. DO NOT MODIFY the returned list, use {@link #addMetricExtension(MetricExtension)}.
+ *
Get all registered metric extensions.
+ *
DO NOT MODIFY the returned list, use {@link #addMetricExtension(MetricExtension)}.
*
- * @return all metric extensions.
+ * @return all registered metric extensions
*/
public static List getMetricExtensions() {
return metricExtensions;
@@ -42,7 +58,7 @@ public static List getMetricExtensions() {
/**
* Add metric extension.
*
- * Not that this method is NOT thread safe.
+ * Note that this method is NOT thread safe.
*
*
* @param metricExtension the metric extension to add.
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallback.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallback.java
index e5409970ee..147d6ad8a1 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallback.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallback.java
@@ -1,8 +1,24 @@
+/*
+ * Copyright 1999-2019 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.metric.extension.callback;
import com.alibaba.csp.sentinel.context.Context;
-import com.alibaba.csp.sentinel.metric.extension.MetricExtensionProvider;
+import com.alibaba.csp.sentinel.metric.extension.AdvancedMetricExtension;
import com.alibaba.csp.sentinel.metric.extension.MetricExtension;
+import com.alibaba.csp.sentinel.metric.extension.MetricExtensionProvider;
import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.slotchain.ProcessorSlotEntryCallback;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
@@ -15,20 +31,29 @@
* @since 1.6.1
*/
public class MetricEntryCallback implements ProcessorSlotEntryCallback {
+
@Override
- public void onPass(Context context, ResourceWrapper resourceWrapper, DefaultNode param,
- int count, Object... args) throws Exception {
+ public void onPass(Context context, ResourceWrapper rw, DefaultNode param, int count, Object... args)
+ throws Exception {
for (MetricExtension m : MetricExtensionProvider.getMetricExtensions()) {
- m.increaseThreadNum(resourceWrapper.getName(), args);
- m.addPass(resourceWrapper.getName(), count, args);
+ if (m instanceof AdvancedMetricExtension) {
+ ((AdvancedMetricExtension) m).onPass(rw, count, args);
+ } else {
+ m.increaseThreadNum(rw.getName(), args);
+ m.addPass(rw.getName(), count, args);
+ }
}
}
@Override
- public void onBlocked(BlockException ex, Context context, ResourceWrapper resourceWrapper,
- DefaultNode param, int count, Object... args) {
+ public void onBlocked(BlockException ex, Context context, ResourceWrapper resourceWrapper, DefaultNode param,
+ int count, Object... args) {
for (MetricExtension m : MetricExtensionProvider.getMetricExtensions()) {
- m.addBlock(resourceWrapper.getName(), count, context.getOrigin(), ex, args);
+ if (m instanceof AdvancedMetricExtension) {
+ ((AdvancedMetricExtension) m).onBlocked(resourceWrapper, count, context.getOrigin(), ex, args);
+ } else {
+ m.addBlock(resourceWrapper.getName(), count, context.getOrigin(), ex, args);
+ }
}
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallback.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallback.java
index 334a8f3a1b..a90211d999 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallback.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallback.java
@@ -1,8 +1,25 @@
+/*
+ * Copyright 1999-2019 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.metric.extension.callback;
+import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.context.Context;
-import com.alibaba.csp.sentinel.metric.extension.MetricExtensionProvider;
+import com.alibaba.csp.sentinel.metric.extension.AdvancedMetricExtension;
import com.alibaba.csp.sentinel.metric.extension.MetricExtension;
+import com.alibaba.csp.sentinel.metric.extension.MetricExtensionProvider;
import com.alibaba.csp.sentinel.slotchain.ProcessorSlotExitCallback;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
import com.alibaba.csp.sentinel.util.TimeUtil;
@@ -11,17 +28,42 @@
* Metric extension exit callback.
*
* @author Carpenter Lee
+ * @author Eric Zhao
* @since 1.6.1
*/
public class MetricExitCallback implements ProcessorSlotExitCallback {
+
@Override
- public void onExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
+ public void onExit(Context context, ResourceWrapper rw, int acquireCount, Object... args) {
+ Entry curEntry = context.getCurEntry();
+ if (curEntry == null) {
+ return;
+ }
for (MetricExtension m : MetricExtensionProvider.getMetricExtensions()) {
- if (context.getCurEntry().getError() == null) {
- long realRt = TimeUtil.currentTimeMillis() - context.getCurEntry().getCreateTime();
- m.addRt(resourceWrapper.getName(), realRt, args);
- m.addSuccess(resourceWrapper.getName(), count, args);
- m.decreaseThreadNum(resourceWrapper.getName(), args);
+ if (curEntry.getBlockError() != null) {
+ continue;
+ }
+ String resource = rw.getName();
+ Throwable ex = curEntry.getError();
+ long completeTime = curEntry.getCompleteTimestamp();
+ if (completeTime <= 0) {
+ completeTime = TimeUtil.currentTimeMillis();
+ }
+ long rt = completeTime - curEntry.getCreateTimestamp();
+
+ if (m instanceof AdvancedMetricExtension) {
+ // Since 1.8.0 (as a temporary workaround for compatibility)
+ ((AdvancedMetricExtension) m).onComplete(rw, rt, acquireCount, args);
+ if (ex != null) {
+ ((AdvancedMetricExtension) m).onError(rw, ex, acquireCount, args);
+ }
+ } else {
+ m.addRt(resource, rt, args);
+ m.addSuccess(resource, acquireCount, args);
+ m.decreaseThreadNum(resource, args);
+ if (null != ex) {
+ m.addException(resource, acquireCount, ex);
+ }
}
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/ClusterNode.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/ClusterNode.java
index 6f5f4ef4f8..1f16ce5b2a 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/ClusterNode.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/ClusterNode.java
@@ -101,8 +101,8 @@ public int getResourceType() {
public Node getOrCreateOriginNode(String origin) {
StatisticNode statisticNode = originCountMap.get(origin);
if (statisticNode == null) {
+ lock.lock();
try {
- lock.lock();
statisticNode = originCountMap.get(origin);
if (statisticNode == null) {
// The node is absent, create a new node for the origin.
@@ -123,18 +123,4 @@ public Map getOriginCountMap() {
return originCountMap;
}
- /**
- * Add exception count only when given {@code throwable} is not a {@link BlockException}.
- *
- * @param throwable target exception
- * @param count count to add
- */
- public void trace(Throwable throwable, int count) {
- if (count <= 0) {
- return;
- }
- if (!BlockException.isBlockException(throwable)) {
- this.increaseExceptionQps(count);
- }
- }
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/DefaultNode.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/DefaultNode.java
index 537ab687d0..f878637520 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/DefaultNode.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/DefaultNode.java
@@ -79,7 +79,7 @@ public void setClusterNode(ClusterNode clusterNode) {
*/
public void addChild(Node node) {
if (node == null) {
- RecordLog.warn("Trying to add null child to node <{0}>, ignored", id.getName());
+ RecordLog.warn("Trying to add null child to node <{}>, ignored", id.getName());
return;
}
if (!childList.contains(node)) {
@@ -91,7 +91,7 @@ public void addChild(Node node) {
childList = newSet;
}
}
- RecordLog.info("Add child <{0}> to node <{1}>", ((DefaultNode)node).id.getName(), id.getName());
+ RecordLog.info("Add child <{}> to node <{}>", ((DefaultNode)node).id.getName(), id.getName());
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/IntervalProperty.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/IntervalProperty.java
index 4f0dfa7b0b..8bdd4d7e7a 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/IntervalProperty.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/IntervalProperty.java
@@ -62,7 +62,7 @@ public static void updateInterval(int newInterval) {
INTERVAL = newInterval;
ClusterBuilderSlot.resetClusterNodes();
}
- RecordLog.info("[IntervalProperty] INTERVAL updated to: " + INTERVAL);
+ RecordLog.info("[IntervalProperty] INTERVAL updated to: {}", INTERVAL);
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/OccupyTimeoutProperty.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/OccupyTimeoutProperty.java
index a2d7ecf3cb..386b09c44e 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/OccupyTimeoutProperty.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/OccupyTimeoutProperty.java
@@ -67,13 +67,13 @@ public static void updateTimeout(int newInterval) {
return;
}
if (newInterval > IntervalProperty.INTERVAL) {
- RecordLog.warn("[OccupyTimeoutProperty] Illegal timeout value will be ignored: " + occupyTimeout
- + ", should <= " + IntervalProperty.INTERVAL);
+ RecordLog.warn("[OccupyTimeoutProperty] Illegal timeout value will be ignored: {}, should <= {}",
+ occupyTimeout, IntervalProperty.INTERVAL);
return;
}
if (newInterval != occupyTimeout) {
occupyTimeout = newInterval;
}
- RecordLog.info("[OccupyTimeoutProperty] occupyTimeout updated to: " + occupyTimeout);
+ RecordLog.info("[OccupyTimeoutProperty] occupyTimeout updated to: {}", occupyTimeout);
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/SampleCountProperty.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/SampleCountProperty.java
index 8001a80e6a..7d70b887e9 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/SampleCountProperty.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/SampleCountProperty.java
@@ -60,6 +60,6 @@ public static void updateSampleCount(int newSampleCount) {
SAMPLE_COUNT = newSampleCount;
ClusterBuilderSlot.resetClusterNodes();
}
- RecordLog.info("SAMPLE_COUNT updated to: " + SAMPLE_COUNT);
+ RecordLog.info("SAMPLE_COUNT updated to: {}", SAMPLE_COUNT);
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/metric/MetricWriter.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/metric/MetricWriter.java
index 30fd14ee02..7f4e230e8f 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/metric/MetricWriter.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/metric/MetricWriter.java
@@ -42,15 +42,15 @@
*
every metric file is accompanied with an index file, which file name is {@code ${metricFileName}.idx}
*
*
- * @author leyou
+ * @author Carpenter Lee
*/
public class MetricWriter {
private static final String CHARSET = SentinelConfig.charset();
- public static final String METRIC_BASE_DIR = RecordLog.getLogBaseDir();
+ public static final String METRIC_BASE_DIR = LogBase.getLogBaseDir();
/**
- * Note: {@link MetricFileNameComparator}'s implementation relays on the metric file name,
- * we should be careful when changing the metric file name.
+ * Note: {@link MetricFileNameComparator}'s implementation relies on the metric file name,
+ * so we should be careful when changing the metric file name.
*
* @see #formMetricFileName(String, int)
*/
@@ -92,9 +92,8 @@ public MetricWriter(long singleFileSize, int totalFileCount) {
if (singleFileSize <= 0 || totalFileCount <= 0) {
throw new IllegalArgumentException();
}
- RecordLog.info(
- "[MetricWriter] Creating new MetricWriter, singleFileSize=" + singleFileSize + ", totalFileCount="
- + totalFileCount);
+ RecordLog.info("[MetricWriter] Creating new MetricWriter, singleFileSize={}, totalFileCount={}",
+ singleFileSize, totalFileCount);
this.baseDir = METRIC_BASE_DIR;
File dir = new File(baseDir);
if (!dir.exists()) {
@@ -328,9 +327,9 @@ private void removeMoreFiles() throws Exception {
String fileName = list.get(i);
String indexFile = formIndexFileName(fileName);
new File(fileName).delete();
- RecordLog.info("[MetricWriter] Removing metric file: " + fileName);
+ RecordLog.info("[MetricWriter] Removing metric file: {}", fileName);
new File(indexFile).delete();
- RecordLog.info("[MetricWriter] Removing metric index file: " + indexFile);
+ RecordLog.info("[MetricWriter] Removing metric index file: {}", indexFile);
}
}
@@ -348,8 +347,8 @@ private void closeAndNewFile(String fileName) throws Exception {
String idxFile = formIndexFileName(fileName);
curMetricIndexFile = new File(idxFile);
outIndex = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(idxFile, append)));
- RecordLog.info("[MetricWriter] New metric file created: " + fileName);
- RecordLog.info("[MetricWriter] New metric index file created: " + idxFile);
+ RecordLog.info("[MetricWriter] New metric file created: {}", fileName);
+ RecordLog.info("[MetricWriter] New metric index file created: {}", idxFile);
}
private boolean validSize() throws Exception {
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/DynamicSentinelProperty.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/DynamicSentinelProperty.java
index c143710505..b432df7107 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/DynamicSentinelProperty.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/DynamicSentinelProperty.java
@@ -50,7 +50,7 @@ public boolean updateValue(T newValue) {
if (isEqual(value, newValue)) {
return false;
}
- RecordLog.info("[DynamicSentinelProperty] Config will be updated to: " + newValue);
+ RecordLog.info("[DynamicSentinelProperty] Config will be updated to: {}", newValue);
value = newValue;
for (PropertyListener listener : listeners) {
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/SlotChainProvider.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/SlotChainProvider.java
index 9aabbb3376..49ab60a358 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/SlotChainProvider.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/SlotChainProvider.java
@@ -48,8 +48,8 @@ public static ProcessorSlotChain newSlotChain() {
RecordLog.warn("[SlotChainProvider] Wrong state when resolving slot chain builder, using default");
slotChainBuilder = new DefaultSlotChainBuilder();
} else {
- RecordLog.info("[SlotChainProvider] Global slot chain builder resolved: "
- + slotChainBuilder.getClass().getCanonicalName());
+ RecordLog.info("[SlotChainProvider] Global slot chain builder resolved: {}",
+ slotChainBuilder.getClass().getCanonicalName());
}
return slotChainBuilder.build();
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/DefaultSlotChainBuilder.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/DefaultSlotChainBuilder.java
index 56aa851a13..cf0f72c5e5 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/DefaultSlotChainBuilder.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/DefaultSlotChainBuilder.java
@@ -15,17 +15,15 @@
*/
package com.alibaba.csp.sentinel.slots;
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
import com.alibaba.csp.sentinel.slotchain.DefaultProcessorSlotChain;
+import com.alibaba.csp.sentinel.slotchain.ProcessorSlot;
import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain;
import com.alibaba.csp.sentinel.slotchain.SlotChainBuilder;
-import com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot;
-import com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot;
-import com.alibaba.csp.sentinel.slots.block.flow.FlowSlot;
-import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
-import com.alibaba.csp.sentinel.slots.logger.LogSlot;
-import com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot;
-import com.alibaba.csp.sentinel.slots.statistic.StatisticSlot;
-import com.alibaba.csp.sentinel.slots.system.SystemSlot;
+import com.alibaba.csp.sentinel.util.SpiLoader;
+
+import java.util.List;
/**
* Builder for a default {@link ProcessorSlotChain}.
@@ -38,16 +36,18 @@ public class DefaultSlotChainBuilder implements SlotChainBuilder {
@Override
public ProcessorSlotChain build() {
ProcessorSlotChain chain = new DefaultProcessorSlotChain();
- chain.addLast(new NodeSelectorSlot());
- chain.addLast(new ClusterBuilderSlot());
- chain.addLast(new LogSlot());
- chain.addLast(new StatisticSlot());
- chain.addLast(new AuthoritySlot());
- chain.addLast(new SystemSlot());
- chain.addLast(new FlowSlot());
- chain.addLast(new DegradeSlot());
+
+ // Note: the instances of ProcessorSlot should be different, since they are not stateless.
+ List sortedSlotList = SpiLoader.loadPrototypeInstanceListSorted(ProcessorSlot.class);
+ for (ProcessorSlot slot : sortedSlotList) {
+ if (!(slot instanceof AbstractLinkedProcessorSlot)) {
+ RecordLog.warn("The ProcessorSlot(" + slot.getClass().getCanonicalName() + ") is not an instance of AbstractLinkedProcessorSlot, can't be added into ProcessorSlotChain");
+ continue;
+ }
+
+ chain.addLast((AbstractLinkedProcessorSlot>) slot);
+ }
return chain;
}
-
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/AbstractRule.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/AbstractRule.java
index 2a0d7260c1..f6f10a76b4 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/AbstractRule.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/AbstractRule.java
@@ -39,6 +39,7 @@ public abstract class AbstractRule implements Rule {
*/
private String limitApp;
+ @Override
public String getResource() {
return resource;
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/BlockException.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/BlockException.java
index ada98383f2..ad972d440c 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/BlockException.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/BlockException.java
@@ -23,7 +23,11 @@
*/
public abstract class BlockException extends Exception {
+ private static final int MAX_SEARCH_DEPTH = 10;
+
public static final String BLOCK_EXCEPTION_FLAG = "SentinelBlockException";
+ public static final String BLOCK_EXCEPTION_MSG_PREFIX = "SentinelBlockException: ";
+
/**
*
this constant RuntimeException has no stack trace, just has a message
* {@link #BLOCK_EXCEPTION_FLAG} that marks its name.
@@ -85,12 +89,18 @@ public void setRuleLimitApp(String ruleLimitApp) {
this.ruleLimitApp = ruleLimitApp;
}
+ public RuntimeException toRuntimeException() {
+ RuntimeException t = new RuntimeException(BLOCK_EXCEPTION_MSG_PREFIX + getClass().getSimpleName());
+ t.setStackTrace(sentinelStackTrace);
+ return t;
+ }
+
/**
* Check whether the exception is sentinel blocked exception. One exception is sentinel blocked
* exception only when:
*
*
the exception or its (sub-)cause is {@link BlockException}, or
- *
the exception's message is or any of its sub-cause's message equals to {@link #BLOCK_EXCEPTION_FLAG}
+ *
the exception's message or any of its sub-cause's message is prefixed by {@link #BLOCK_EXCEPTION_FLAG}
*
*
* @param t the exception.
@@ -103,8 +113,11 @@ public static boolean isBlockException(Throwable t) {
int counter = 0;
Throwable cause = t;
- while (cause != null && counter++ < 50) {
- if ((cause instanceof BlockException) || (BLOCK_EXCEPTION_FLAG.equals(cause.getMessage()))) {
+ while (cause != null && counter++ < MAX_SEARCH_DEPTH) {
+ if (cause instanceof BlockException) {
+ return true;
+ }
+ if (cause.getMessage() != null && cause.getMessage().startsWith(BLOCK_EXCEPTION_FLAG)) {
return true;
}
cause = cause.getCause();
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/Rule.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/Rule.java
index e429b2a575..35630edac0 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/Rule.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/Rule.java
@@ -15,9 +15,6 @@
*/
package com.alibaba.csp.sentinel.slots.block;
-import com.alibaba.csp.sentinel.context.Context;
-import com.alibaba.csp.sentinel.node.DefaultNode;
-
/**
* Base interface of all rules.
*
@@ -26,14 +23,10 @@
public interface Rule {
/**
- * Check whether current statistical indicators meet this rule, which means not exceeding any threshold.
+ * Get target resource of this rule.
*
- * @param context current {@link Context}
- * @param node current {@link com.alibaba.csp.sentinel.node.Node}
- * @param count tokens needed.
- * @param args arguments of the original invocation.
- * @return If current statistical indicators not exceeding any threshold return true, otherwise return false.
+ * @return target resource of this rule
*/
- boolean passCheck(Context context, DefaultNode node, int count, Object... args);
+ String getResource();
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/RuleConstant.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/RuleConstant.java
index e712ff343c..0b157675c5 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/RuleConstant.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/RuleConstant.java
@@ -51,6 +51,14 @@ public final class RuleConstant {
public static final int CONTROL_BEHAVIOR_RATE_LIMITER = 2;
public static final int CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER = 3;
+ public static final int DEFAULT_BLOCK_STRATEGY = 0;
+ public static final int TRY_AGAIN_BLOCK_STRATEGY = 1;
+ public static final int TRY_UNTIL_SUCCESS_BLOCK_STRATEGY = 2;
+
+ public static final int DEFAULT_RESOURCE_TIMEOUT_STRATEGY = 0;
+ public static final int RELEASE_RESOURCE_TIMEOUT_STRATEGY = 1;
+ public static final int KEEP_RESOURCE_TIMEOUT_STRATEGY = 2;
+
public static final String LIMIT_APP_DEFAULT = "default";
public static final String LIMIT_APP_OTHER = "other";
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthorityRule.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthorityRule.java
index a5b5f277d3..c1606d0aa3 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthorityRule.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthorityRule.java
@@ -15,8 +15,6 @@
*/
package com.alibaba.csp.sentinel.slots.block.authority;
-import com.alibaba.csp.sentinel.context.Context;
-import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.slots.block.AbstractRule;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
@@ -59,11 +57,6 @@ public int hashCode() {
return result;
}
- @Override
- public boolean passCheck(Context context, DefaultNode node, int count, Object... args) {
- return true;
- }
-
@Override
public String toString() {
return "AuthorityRule{" +
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthorityRuleManager.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthorityRuleManager.java
index 7962c50e00..22b698dd46 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthorityRuleManager.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthorityRuleManager.java
@@ -99,7 +99,7 @@ public void configUpdate(List conf) {
if (rules != null) {
authorityRules.putAll(rules);
}
- RecordLog.info("[AuthorityRuleManager] Authority rules received: " + authorityRules);
+ RecordLog.info("[AuthorityRuleManager] Authority rules received: {}", authorityRules);
}
private Map> loadAuthorityConf(List list) {
@@ -111,7 +111,7 @@ private Map> loadAuthorityConf(List li
for (AuthorityRule rule : list) {
if (!isValidRule(rule)) {
- RecordLog.warn("[AuthorityRuleManager] Ignoring invalid authority rule when loading new rules: " + rule);
+ RecordLog.warn("[AuthorityRuleManager] Ignoring invalid authority rule when loading new rules: {}", rule);
continue;
}
@@ -128,7 +128,7 @@ private Map> loadAuthorityConf(List li
newRuleMap.put(identity, ruleSet);
} else {
// One resource should only have at most one authority rule, so just ignore redundant rules.
- RecordLog.warn("[AuthorityRuleManager] Ignoring redundant rule: " + rule.toString());
+ RecordLog.warn("[AuthorityRuleManager] Ignoring redundant rule: {}", rule.toString());
}
}
@@ -143,7 +143,7 @@ public void configLoad(List value) {
if (rules != null) {
authorityRules.putAll(rules);
}
- RecordLog.info("[AuthorityRuleManager] Load authority rules: " + authorityRules);
+ RecordLog.info("[AuthorityRuleManager] Load authority rules: {}", authorityRules);
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthoritySlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthoritySlot.java
index f7920baa9c..617521a676 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthoritySlot.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthoritySlot.java
@@ -23,6 +23,7 @@
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
import com.alibaba.csp.sentinel.slotchain.ProcessorSlot;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
+import com.alibaba.csp.sentinel.spi.SpiOrder;
/**
* A {@link ProcessorSlot} that dedicates to {@link AuthorityRule} checking.
@@ -30,6 +31,7 @@
* @author leyou
* @author Eric Zhao
*/
+@SpiOrder(-6000)
public class AuthoritySlot extends AbstractLinkedProcessorSlot {
@Override
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRule.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRule.java
index aee71ff543..edc9f1b35b 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRule.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRule.java
@@ -15,19 +15,10 @@
*/
package com.alibaba.csp.sentinel.slots.block.degrade;
-import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory;
-import com.alibaba.csp.sentinel.context.Context;
-import com.alibaba.csp.sentinel.node.ClusterNode;
-import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.slots.block.AbstractRule;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
-import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
+import java.util.Objects;
/**
*
@@ -52,13 +43,10 @@
*
*
* @author jialiang.linjl
+ * @author Eric Zhao
*/
public class DegradeRule extends AbstractRule {
- @SuppressWarnings("PMD.ThreadPoolCreationRule")
- private static ScheduledExecutorService pool = Executors.newScheduledThreadPool(
- Runtime.getRuntime().availableProcessors(), new NamedThreadFactory("sentinel-degrade-reset-task", true));
-
public DegradeRule() {}
public DegradeRule(String resourceName) {
@@ -66,33 +54,34 @@ public DegradeRule(String resourceName) {
}
/**
- * RT threshold or exception ratio threshold count.
+ * Circuit breaking strategy (0: average RT, 1: exception ratio, 2: exception count).
*/
- private double count;
+ private int grade = RuleConstant.DEGRADE_GRADE_RT;
/**
- * Degrade recover timeout (in seconds) when degradation occurs.
+ * Threshold count.
*/
- private int timeWindow;
+ private double count;
/**
- * Degrade strategy (0: average RT, 1: exception ratio, 2: exception count).
+ * Recovery timeout (in seconds) when circuit breaker opens. After the timeout, the circuit breaker will
+ * transform to half-open state for trying a few requests.
*/
- private int grade = RuleConstant.DEGRADE_GRADE_RT;
+ private int timeWindow;
/**
- * Minimum number of consecutive slow requests that can trigger RT circuit breaking.
+ * Minimum number of requests (in an active statistic time span) that can trigger circuit breaking.
*
* @since 1.7.0
*/
- private int rtSlowRequestAmount = RuleConstant.DEGRADE_DEFAULT_SLOW_REQUEST_AMOUNT;
+ private int minRequestAmount = RuleConstant.DEGRADE_DEFAULT_MIN_REQUEST_AMOUNT;
/**
- * Minimum number of requests (in an active statistic time span) that can trigger circuit breaking.
- *
- * @since 1.7.0
+ * The threshold of slow request ratio in RT mode.
*/
- private int minRequestAmount = RuleConstant.DEGRADE_DEFAULT_MIN_REQUEST_AMOUNT;
+ private double slowRatioThreshold = 1.0d;
+
+ private int statIntervalMs = 1000;
public int getGrade() {
return grade;
@@ -121,21 +110,30 @@ public DegradeRule setTimeWindow(int timeWindow) {
return this;
}
- public int getRtSlowRequestAmount() {
- return rtSlowRequestAmount;
+ public int getMinRequestAmount() {
+ return minRequestAmount;
+ }
+
+ public DegradeRule setMinRequestAmount(int minRequestAmount) {
+ this.minRequestAmount = minRequestAmount;
+ return this;
+ }
+
+ public double getSlowRatioThreshold() {
+ return slowRatioThreshold;
}
- public DegradeRule setRtSlowRequestAmount(int rtSlowRequestAmount) {
- this.rtSlowRequestAmount = rtSlowRequestAmount;
+ public DegradeRule setSlowRatioThreshold(double slowRatioThreshold) {
+ this.slowRatioThreshold = slowRatioThreshold;
return this;
}
- public int getMinRequestAmount() {
- return minRequestAmount;
+ public int getStatIntervalMs() {
+ return statIntervalMs;
}
- public DegradeRule setMinRequestAmount(int minRequestAmount) {
- this.minRequestAmount = minRequestAmount;
+ public DegradeRule setStatIntervalMs(int statIntervalMs) {
+ this.statIntervalMs = statIntervalMs;
return this;
}
@@ -144,23 +142,19 @@ public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
if (!super.equals(o)) { return false; }
- DegradeRule that = (DegradeRule) o;
- return Double.compare(that.count, count) == 0 &&
- timeWindow == that.timeWindow &&
- grade == that.grade &&
- rtSlowRequestAmount == that.rtSlowRequestAmount &&
- minRequestAmount == that.minRequestAmount;
+ DegradeRule rule = (DegradeRule)o;
+ return Double.compare(rule.count, count) == 0 &&
+ timeWindow == rule.timeWindow &&
+ grade == rule.grade &&
+ minRequestAmount == rule.minRequestAmount &&
+ Double.compare(rule.slowRatioThreshold, slowRatioThreshold) == 0 &&
+ statIntervalMs == rule.statIntervalMs;
}
@Override
public int hashCode() {
- int result = super.hashCode();
- result = 31 * result + new Double(count).hashCode();
- result = 31 * result + timeWindow;
- result = 31 * result + grade;
- result = 31 * result + rtSlowRequestAmount;
- result = 31 * result + minRequestAmount;
- return result;
+ return Objects.hash(super.hashCode(), count, timeWindow, grade, minRequestAmount,
+ slowRatioThreshold, statIntervalMs);
}
@Override
@@ -171,84 +165,9 @@ public String toString() {
", count=" + count +
", limitApp=" + getLimitApp() +
", timeWindow=" + timeWindow +
- ", rtSlowRequestAmount=" + rtSlowRequestAmount +
", minRequestAmount=" + minRequestAmount +
- "}";
- }
-
- // Internal implementation (will be deprecated and moved outside).
-
- private AtomicLong passCount = new AtomicLong(0);
- private final AtomicBoolean cut = new AtomicBoolean(false);
-
- @Override
- public boolean passCheck(Context context, DefaultNode node, int acquireCount, Object... args) {
- if (cut.get()) {
- return false;
- }
-
- ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(this.getResource());
- if (clusterNode == null) {
- return true;
- }
-
- if (grade == RuleConstant.DEGRADE_GRADE_RT) {
- double rt = clusterNode.avgRt();
- if (rt < this.count) {
- passCount.set(0);
- return true;
- }
-
- // Sentinel will degrade the service only if count exceeds.
- if (passCount.incrementAndGet() < rtSlowRequestAmount) {
- return true;
- }
- } else if (grade == RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) {
- double exception = clusterNode.exceptionQps();
- double success = clusterNode.successQps();
- double total = clusterNode.totalQps();
- // If total amount is less than minRequestAmount, the request will pass.
- if (total < minRequestAmount) {
- return true;
- }
-
- // In the same aligned statistic time window,
- // "success" (aka. completed count) = exception count + non-exception count (realSuccess)
- double realSuccess = success - exception;
- if (realSuccess <= 0 && exception < minRequestAmount) {
- return true;
- }
-
- if (exception / success < count) {
- return true;
- }
- } else if (grade == RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {
- double exception = clusterNode.totalException();
- if (exception < count) {
- return true;
- }
- }
-
- if (cut.compareAndSet(false, true)) {
- ResetTask resetTask = new ResetTask(this);
- pool.schedule(resetTask, timeWindow, TimeUnit.SECONDS);
- }
-
- return false;
- }
-
- private static final class ResetTask implements Runnable {
-
- private DegradeRule rule;
-
- ResetTask(DegradeRule rule) {
- this.rule = rule;
- }
-
- @Override
- public void run() {
- rule.passCount.set(0);
- rule.cut.set(false);
- }
+ ", slowRatioThreshold=" + slowRatioThreshold +
+ ", statIntervalMs=" + statIntervalMs +
+ '}';
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRuleManager.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRuleManager.java
index 6cb09f8e97..a1214a7b14 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRuleManager.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRuleManager.java
@@ -21,29 +21,29 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import com.alibaba.csp.sentinel.config.SentinelConfig;
-import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.log.RecordLog;
-import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
import com.alibaba.csp.sentinel.property.PropertyListener;
import com.alibaba.csp.sentinel.property.SentinelProperty;
-import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
-import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
+import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.ExceptionCircuitBreaker;
+import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.ResponseTimeCircuitBreaker;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.csp.sentinel.util.StringUtil;
/**
+ * The rule manager for circuit breaking rules ({@link DegradeRule}).
+ *
* @author youji.zj
* @author jialiang.linjl
* @author Eric Zhao
*/
public final class DegradeRuleManager {
- private static final Map> degradeRules = new ConcurrentHashMap<>();
+ private static volatile Map> circuitBreakers = new HashMap<>();
+ private static volatile Map> ruleMap = new HashMap<>();
private static final RulePropertyListener LISTENER = new RulePropertyListener();
private static SentinelProperty> currentProperty
@@ -69,41 +69,37 @@ public static void register2Property(SentinelProperty> propert
}
}
- public static void checkDegrade(ResourceWrapper resource, Context context, DefaultNode node, int count)
- throws BlockException {
-
- Set rules = degradeRules.get(resource.getName());
- if (rules == null) {
- return;
- }
-
- for (DegradeRule rule : rules) {
- if (!rule.passCheck(context, node, count)) {
- throw new DegradeException(rule.getLimitApp(), rule);
- }
- }
+ static List getCircuitBreakers(String resourceName) {
+ return circuitBreakers.get(resourceName);
}
public static boolean hasConfig(String resource) {
if (resource == null) {
return false;
}
- return degradeRules.containsKey(resource);
+ return circuitBreakers.containsKey(resource);
}
/**
- * Get a copy of the rules.
+ *
Get existing circuit breaking rules.
+ *
Note: DO NOT modify the rules from the returned list directly.
+ * The behavior is undefined.
*
- * @return a new copy of the rules.
+ * @return list of existing circuit breaking rules, or empty list if no rules were loaded
*/
public static List getRules() {
List rules = new ArrayList<>();
- for (Map.Entry> entry : degradeRules.entrySet()) {
+ for (Map.Entry> entry : ruleMap.entrySet()) {
rules.addAll(entry.getValue());
}
return rules;
}
+ public static Set getRulesOfResource(String resource) {
+ AssertUtil.assertNotBlank(resource, "resource name cannot be blank");
+ return ruleMap.get(resource);
+ }
+
/**
* Load {@link DegradeRule}s, former rules will be replaced.
*
@@ -113,7 +109,7 @@ public static void loadRules(List rules) {
try {
currentProperty.updateValue(rules);
} catch (Throwable e) {
- RecordLog.warn("[DegradeRuleManager] Unexpected error when loading degrade rules", e);
+ RecordLog.error("[DegradeRuleManager] Unexpected error when loading degrade rules", e);
}
}
@@ -128,7 +124,7 @@ public static void loadRules(List rules) {
public static boolean setRulesForResource(String resourceName, Set rules) {
AssertUtil.notEmpty(resourceName, "resourceName cannot be empty");
try {
- Map> newRuleMap = new HashMap<>(degradeRules);
+ Map> newRuleMap = new HashMap<>(ruleMap);
if (rules == null) {
newRuleMap.remove(resourceName);
} else {
@@ -146,88 +142,127 @@ public static boolean setRulesForResource(String resourceName, Set
}
return currentProperty.updateValue(allRules);
} catch (Throwable e) {
- RecordLog.warn(
- "[DegradeRuleManager] Unexpected error when setting degrade rules for resource: " + resourceName, e);
+ RecordLog.error("[DegradeRuleManager] Unexpected error when setting circuit breaking"
+ + " rules for resource: " + resourceName, e);
+ return false;
+ }
+ }
+
+ private static CircuitBreaker getExistingSameCbOrNew(/*@Valid*/ DegradeRule rule) {
+ List cbs = getCircuitBreakers(rule.getResource());
+ if (cbs == null || cbs.isEmpty()) {
+ return newCircuitBreakerFrom(rule);
+ }
+ for (CircuitBreaker cb : cbs) {
+ if (rule.equals(cb.getRule())) {
+ // Reuse the circuit breaker if the rule remains unchanged.
+ return cb;
+ }
+ }
+ return newCircuitBreakerFrom(rule);
+ }
+
+ /**
+ * Create a circuit breaker instance from provided circuit breaking rule.
+ *
+ * @param rule a valid circuit breaking rule
+ * @return new circuit breaker based on provided rule; null if rule is invalid or unsupported type
+ */
+ private static CircuitBreaker newCircuitBreakerFrom(/*@Valid*/ DegradeRule rule) {
+ switch (rule.getGrade()) {
+ case RuleConstant.DEGRADE_GRADE_RT:
+ return new ResponseTimeCircuitBreaker(rule);
+ case RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO:
+ case RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT:
+ return new ExceptionCircuitBreaker(rule);
+ default:
+ return null;
+ }
+ }
+
+ public static boolean isValidRule(DegradeRule rule) {
+ boolean baseValid = rule != null && !StringUtil.isBlank(rule.getResource())
+ && rule.getCount() >= 0 && rule.getTimeWindow() > 0;
+ if (!baseValid) {
+ return false;
+ }
+ if (rule.getMinRequestAmount() <= 0 || rule.getStatIntervalMs() <= 0) {
return false;
}
+ switch (rule.getGrade()) {
+ case RuleConstant.DEGRADE_GRADE_RT:
+ return rule.getSlowRatioThreshold() >= 0 && rule.getSlowRatioThreshold() <= 1;
+ case RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO:
+ return rule.getCount() <= 1;
+ case RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT:
+ return true;
+ default:
+ return false;
+ }
}
private static class RulePropertyListener implements PropertyListener> {
+ private synchronized void reloadFrom(List list) {
+ Map> cbs = buildCircuitBreakers(list);
+ Map> rm = new HashMap<>(cbs.size());
+
+ for (Map.Entry> e : cbs.entrySet()) {
+ assert e.getValue() != null && !e.getValue().isEmpty();
+
+ Set rules = new HashSet<>(e.getValue().size());
+ for (CircuitBreaker cb : e.getValue()) {
+ rules.add(cb.getRule());
+ }
+ rm.put(e.getKey(), rules);
+ }
+
+ DegradeRuleManager.circuitBreakers = cbs;
+ DegradeRuleManager.ruleMap = rm;
+ }
+
@Override
public void configUpdate(List conf) {
- Map> rules = loadDegradeConf(conf);
- if (rules != null) {
- degradeRules.clear();
- degradeRules.putAll(rules);
- }
- RecordLog.info("[DegradeRuleManager] Degrade rules received: " + degradeRules);
+ reloadFrom(conf);
+ RecordLog.info("[DegradeRuleManager] Degrade rules has been updated to: {}", ruleMap);
}
@Override
public void configLoad(List conf) {
- Map> rules = loadDegradeConf(conf);
- if (rules != null) {
- degradeRules.clear();
- degradeRules.putAll(rules);
- }
- RecordLog.info("[DegradeRuleManager] Degrade rules loaded: " + degradeRules);
+ reloadFrom(conf);
+ RecordLog.info("[DegradeRuleManager] Degrade rules loaded: {}", ruleMap);
}
- private Map> loadDegradeConf(List list) {
- Map> newRuleMap = new ConcurrentHashMap<>();
-
+ private Map> buildCircuitBreakers(List list) {
+ Map> cbMap = new HashMap<>(8);
if (list == null || list.isEmpty()) {
- return newRuleMap;
+ return cbMap;
}
-
for (DegradeRule rule : list) {
if (!isValidRule(rule)) {
- RecordLog.warn(
- "[DegradeRuleManager] Ignoring invalid degrade rule when loading new rules: " + rule);
+ RecordLog.warn("[DegradeRuleManager] Ignoring invalid rule when loading new rules: {}", rule);
continue;
}
if (StringUtil.isBlank(rule.getLimitApp())) {
rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
}
-
- String identity = rule.getResource();
- Set ruleSet = newRuleMap.get(identity);
- if (ruleSet == null) {
- ruleSet = new HashSet<>();
- newRuleMap.put(identity, ruleSet);
+ CircuitBreaker cb = getExistingSameCbOrNew(rule);
+ if (cb == null) {
+ RecordLog.warn("[DegradeRuleManager] Unknown circuit breaking strategy, ignoring: {}", rule);
+ continue;
}
- ruleSet.add(rule);
- }
- return newRuleMap;
- }
- }
+ String resourceName = rule.getResource();
- public static boolean isValidRule(DegradeRule rule) {
- boolean baseValid = rule != null && !StringUtil.isBlank(rule.getResource())
- && rule.getCount() >= 0 && rule.getTimeWindow() > 0;
- if (!baseValid) {
- return false;
- }
- int maxAllowedRt = SentinelConfig.statisticMaxRt();
- if (rule.getGrade() == RuleConstant.DEGRADE_GRADE_RT) {
- if (rule.getRtSlowRequestAmount() <= 0) {
- return false;
- }
- // Warn for RT mode that exceeds the {@code TIME_DROP_VALVE}.
- if (rule.getCount() > maxAllowedRt) {
- RecordLog.warn(String.format("[DegradeRuleManager] WARN: setting large RT threshold (%.1f ms)"
- + " in RT mode will not take effect since it exceeds the max allowed value (%d ms)",
- rule.getCount(), maxAllowedRt));
+ List cbList = cbMap.get(resourceName);
+ if (cbList == null) {
+ cbList = new ArrayList<>();
+ cbMap.put(resourceName, cbList);
+ }
+ cbList.add(cb);
}
+ return cbMap;
}
-
- // Check exception ratio mode.
- if (rule.getGrade() == RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) {
- return rule.getCount() <= 1 && rule.getMinRequestAmount() > 0;
- }
- return true;
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeSlot.java
index f76d81f8cd..c66d056d4f 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeSlot.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeSlot.java
@@ -15,28 +15,67 @@
*/
package com.alibaba.csp.sentinel.slots.block.degrade;
+import java.util.List;
+
+import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
import com.alibaba.csp.sentinel.slotchain.ProcessorSlot;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
+import com.alibaba.csp.sentinel.spi.SpiOrder;
/**
- * A {@link ProcessorSlot} dedicates to {@link DegradeRule} checking.
+ * A {@link ProcessorSlot} dedicates to circuit breaking.
*
- * @author leyou
+ * @author Carpenter Lee
+ * @author Eric Zhao
*/
+@SpiOrder(-1000)
public class DegradeSlot extends AbstractLinkedProcessorSlot {
@Override
- public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args)
- throws Throwable {
- DegradeRuleManager.checkDegrade(resourceWrapper, context, node, count);
+ public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
+ boolean prioritized, Object... args) throws Throwable {
+ performChecking(context, resourceWrapper);
+
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
+ void performChecking(Context context, ResourceWrapper r) throws BlockException {
+ List circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());
+ if (circuitBreakers == null || circuitBreakers.isEmpty()) {
+ return;
+ }
+ for (CircuitBreaker cb : circuitBreakers) {
+ if (!cb.tryPass(context)) {
+ throw new DegradeException(cb.getRule().getLimitApp(), cb.getRule());
+ }
+ }
+ }
+
@Override
- public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
- fireExit(context, resourceWrapper, count, args);
+ public void exit(Context context, ResourceWrapper r, int count, Object... args) {
+ Entry curEntry = context.getCurEntry();
+ if (curEntry.getBlockError() != null) {
+ fireExit(context, r, count, args);
+ return;
+ }
+ List circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());
+ if (circuitBreakers == null || circuitBreakers.isEmpty()) {
+ fireExit(context, r, count, args);
+ return;
+ }
+
+ if (curEntry.getBlockError() == null) {
+ // passed request
+ for (CircuitBreaker circuitBreaker : circuitBreakers) {
+ circuitBreaker.onRequestComplete(context);
+ }
+ }
+
+ fireExit(context, r, count, args);
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/AbstractCircuitBreaker.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/AbstractCircuitBreaker.java
new file mode 100644
index 0000000000..86be556fe9
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/AbstractCircuitBreaker.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 1999-2019 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.slots.block.degrade.circuitbreaker;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
+import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.util.TimeUtil;
+import com.alibaba.csp.sentinel.util.function.BiConsumer;
+
+/**
+ * @author Eric Zhao
+ * @since 1.8.0
+ */
+public abstract class AbstractCircuitBreaker implements CircuitBreaker {
+
+ protected final DegradeRule rule;
+ protected final int recoveryTimeoutMs;
+
+ private final EventObserverRegistry observerRegistry;
+
+ protected final AtomicReference currentState = new AtomicReference<>(State.CLOSED);
+ protected volatile long nextRetryTimestamp;
+
+ public AbstractCircuitBreaker(DegradeRule rule) {
+ this(rule, EventObserverRegistry.getInstance());
+ }
+
+ AbstractCircuitBreaker(DegradeRule rule, EventObserverRegistry observerRegistry) {
+ AssertUtil.notNull(observerRegistry, "observerRegistry cannot be null");
+ if (!DegradeRuleManager.isValidRule(rule)) {
+ throw new IllegalArgumentException("Invalid DegradeRule: " + rule);
+ }
+ this.observerRegistry = observerRegistry;
+ this.rule = rule;
+ this.recoveryTimeoutMs = rule.getTimeWindow() * 1000;
+ }
+
+ @Override
+ public DegradeRule getRule() {
+ return rule;
+ }
+
+ @Override
+ public State currentState() {
+ return currentState.get();
+ }
+
+ @Override
+ public boolean tryPass(Context context) {
+ // Template implementation.
+ if (currentState.get() == State.CLOSED) {
+ return true;
+ }
+ if (currentState.get() == State.OPEN) {
+ // For half-open state we allow a request for probing.
+ return retryTimeoutArrived() && fromOpenToHalfOpen(context);
+ }
+ return false;
+ }
+
+ /**
+ * Reset the statistic data.
+ */
+ abstract void resetStat();
+
+ protected boolean retryTimeoutArrived() {
+ return TimeUtil.currentTimeMillis() >= nextRetryTimestamp;
+ }
+
+ protected void updateNextRetryTimestamp() {
+ this.nextRetryTimestamp = TimeUtil.currentTimeMillis() + recoveryTimeoutMs;
+ }
+
+ protected boolean fromCloseToOpen(double snapshotValue) {
+ State prev = State.CLOSED;
+ if (currentState.compareAndSet(prev, State.OPEN)) {
+ updateNextRetryTimestamp();
+
+ notifyObservers(prev, State.OPEN, snapshotValue);
+ return true;
+ }
+ return false;
+ }
+
+ protected boolean fromOpenToHalfOpen(Context context) {
+ if (currentState.compareAndSet(State.OPEN, State.HALF_OPEN)) {
+ notifyObservers(State.OPEN, State.HALF_OPEN, null);
+ Entry entry = context.getCurEntry();
+ entry.whenTerminate(new BiConsumer() {
+ @Override
+ public void accept(Context context, Entry entry) {
+ // Note: This works as a temporary workaround for https://github.com/alibaba/Sentinel/issues/1638
+ // Without the hook, the circuit breaker won't recover from half-open state in some circumstances
+ // when the request is actually blocked by upcoming rules (not only degrade rules).
+ if (entry.getBlockError() != null) {
+ // Fallback to OPEN due to detecting request is blocked
+ currentState.compareAndSet(State.HALF_OPEN, State.OPEN);
+ notifyObservers(State.HALF_OPEN, State.OPEN, 1.0d);
+ }
+ }
+ });
+ return true;
+ }
+ return false;
+ }
+
+ private void notifyObservers(CircuitBreaker.State prevState, CircuitBreaker.State newState, Double snapshotValue) {
+ for (CircuitBreakerStateChangeObserver observer : observerRegistry.getStateChangeObservers()) {
+ observer.onStateChange(prevState, newState, rule, snapshotValue);
+ }
+ }
+
+ protected boolean fromHalfOpenToOpen(double snapshotValue) {
+ if (currentState.compareAndSet(State.HALF_OPEN, State.OPEN)) {
+ updateNextRetryTimestamp();
+ notifyObservers(State.HALF_OPEN, State.OPEN, snapshotValue);
+ return true;
+ }
+ return false;
+ }
+
+ protected boolean fromHalfOpenToClose() {
+ if (currentState.compareAndSet(State.HALF_OPEN, State.CLOSED)) {
+ resetStat();
+ notifyObservers(State.HALF_OPEN, State.CLOSED, null);
+ return true;
+ }
+ return false;
+ }
+
+ protected void transformToOpen(double triggerValue) {
+ State cs = currentState.get();
+ switch (cs) {
+ case CLOSED:
+ fromCloseToOpen(triggerValue);
+ break;
+ case HALF_OPEN:
+ fromHalfOpenToOpen(triggerValue);
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/CircuitBreaker.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/CircuitBreaker.java
new file mode 100644
index 0000000000..0ecc26c6a3
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/CircuitBreaker.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 1999-2019 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.slots.block.degrade.circuitbreaker;
+
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
+
+/**
+ *
+ *
+ * @author Eric Zhao
+ */
+public interface CircuitBreaker {
+
+ /**
+ * Get the associated circuit breaking rule.
+ *
+ * @return associated circuit breaking rule
+ */
+ DegradeRule getRule();
+
+ /**
+ * Acquires permission of an invocation only if it is available at the time of invoking.
+ *
+ * @param context context of current invocation
+ * @return {@code true} if permission was acquired and {@code false} otherwise
+ */
+ boolean tryPass(Context context);
+
+ /**
+ * Get current state of the circuit breaker.
+ *
+ * @return current state of the circuit breaker
+ */
+ State currentState();
+
+ /**
+ *
Record a completed request with the context and handle state transformation of the circuit breaker.
+ *
Called when a passed invocation finished.
+ *
+ * @param context context of current invocation
+ */
+ void onRequestComplete(Context context);
+
+ /**
+ * Circuit breaker state.
+ */
+ enum State {
+ /**
+ * In {@code OPEN} state, all requests will be rejected until the next recovery time point.
+ */
+ OPEN,
+ /**
+ * In {@code HALF_OPEN} state, the circuit breaker will allow a "probe" invocation.
+ * If the invocation is abnormal according to the strategy (e.g. it's slow), the circuit breaker
+ * will re-transform to the {@code OPEN} state and wait for the next recovery time point;
+ * otherwise the resource will be regarded as "recovered" and the circuit breaker
+ * will cease cutting off requests and transform to {@code CLOSED} state.
+ */
+ HALF_OPEN,
+ /**
+ * In {@code CLOSED} state, all requests are permitted. When current metric value exceeds the threshold,
+ * the circuit breaker will transform to {@code OPEN} state.
+ */
+ CLOSED
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/CircuitBreakerStateChangeObserver.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/CircuitBreakerStateChangeObserver.java
new file mode 100644
index 0000000000..e7e5b2dc76
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/CircuitBreakerStateChangeObserver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2019 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.slots.block.degrade.circuitbreaker;
+
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
+
+/**
+ * @author Eric Zhao
+ * @since 1.8.0
+ */
+public interface CircuitBreakerStateChangeObserver {
+
+ /**
+ *
Observer method triggered when circuit breaker state changed. The transformation could be:
+ *
+ *
From {@code CLOSED} to {@code OPEN} (with the triggered metric)
+ *
From {@code OPEN} to {@code HALF_OPEN}
+ *
From {@code OPEN} to {@code CLOSED}
+ *
From {@code HALF_OPEN} to {@code OPEN} (with the triggered metric)
+ *
+ *
+ * @param prevState previous state of the circuit breaker
+ * @param newState new state of the circuit breaker
+ * @param rule associated rule
+ * @param snapshotValue triggered value on circuit breaker opens (null if the new state is CLOSED or HALF_OPEN)
+ */
+ void onStateChange(CircuitBreaker.State prevState, CircuitBreaker.State newState, DegradeRule rule,
+ Double snapshotValue);
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/CircuitBreakerStrategy.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/CircuitBreakerStrategy.java
new file mode 100644
index 0000000000..79d3b12a32
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/CircuitBreakerStrategy.java
@@ -0,0 +1,46 @@
+/*
+ * 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.slots.block.degrade.circuitbreaker;
+
+/**
+ * @author Eric Zhao
+ * @since 1.8.0
+ */
+public enum CircuitBreakerStrategy {
+
+ /**
+ * Circuit breaker opens (cuts off) when slow request ratio exceeds the threshold.
+ */
+ SLOW_REQUEST_RATIO(0),
+ /**
+ * Circuit breaker opens (cuts off) when error ratio exceeds the threshold.
+ */
+ ERROR_RATIO(1),
+ /**
+ * Circuit breaker opens (cuts off) when error count exceeds the threshold.
+ */
+ ERROR_COUNT(2);
+
+ private int type;
+
+ CircuitBreakerStrategy(int type) {
+ this.type = type;
+ }
+
+ public int getType() {
+ return type;
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/EventObserverRegistry.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/EventObserverRegistry.java
new file mode 100644
index 0000000000..2243eebaa5
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/EventObserverRegistry.java
@@ -0,0 +1,70 @@
+/*
+ * 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.slots.block.degrade.circuitbreaker;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.csp.sentinel.util.AssertUtil;
+
+/**
+ *
Registry for circuit breaker event observers.
+ *
+ * @author Eric Zhao
+ * @since 1.8.0
+ */
+public class EventObserverRegistry {
+
+ private final Map stateChangeObserverMap = new HashMap<>();
+
+ /**
+ * Register a circuit breaker state change observer.
+ *
+ * @param name observer name
+ * @param observer a valid observer
+ */
+ public void addStateChangeObserver(String name, CircuitBreakerStateChangeObserver observer) {
+ AssertUtil.notNull(name, "name cannot be null");
+ AssertUtil.notNull(observer, "observer cannot be null");
+ stateChangeObserverMap.put(name, observer);
+ }
+
+ public boolean removeStateChangeObserver(String name) {
+ AssertUtil.notNull(name, "name cannot be null");
+ return stateChangeObserverMap.remove(name) != null;
+ }
+
+ /**
+ * Get all registered state chane observers.
+ *
+ * @return all registered state chane observers
+ */
+ public List getStateChangeObservers() {
+ return new ArrayList<>(stateChangeObserverMap.values());
+ }
+
+ public static EventObserverRegistry getInstance() {
+ return InstanceHolder.instance;
+ }
+
+ private static class InstanceHolder {
+ private static EventObserverRegistry instance = new EventObserverRegistry();
+ }
+
+ EventObserverRegistry() {}
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/ExceptionCircuitBreaker.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/ExceptionCircuitBreaker.java
new file mode 100644
index 0000000000..be8c1b20ab
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/ExceptionCircuitBreaker.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 1999-2019 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.slots.block.degrade.circuitbreaker;
+
+import java.util.List;
+
+import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
+import com.alibaba.csp.sentinel.slots.statistic.base.LeapArray;
+import com.alibaba.csp.sentinel.slots.statistic.base.LongAdder;
+import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;
+import com.alibaba.csp.sentinel.util.AssertUtil;
+
+import static com.alibaba.csp.sentinel.slots.block.RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO;
+import static com.alibaba.csp.sentinel.slots.block.RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT;
+
+/**
+ * @author Eric Zhao
+ * @since 1.8.0
+ */
+public class ExceptionCircuitBreaker extends AbstractCircuitBreaker {
+
+ private final int strategy;
+ private final int minRequestAmount;
+ private final double threshold;
+
+ private final LeapArray stat;
+
+ public ExceptionCircuitBreaker(DegradeRule rule) {
+ this(rule, new SimpleErrorCounterLeapArray(1, rule.getStatIntervalMs()));
+ }
+
+ ExceptionCircuitBreaker(DegradeRule rule, LeapArray stat) {
+ super(rule);
+ this.strategy = rule.getGrade();
+ boolean modeOk = strategy == DEGRADE_GRADE_EXCEPTION_RATIO || strategy == DEGRADE_GRADE_EXCEPTION_COUNT;
+ AssertUtil.isTrue(modeOk, "rule strategy should be error-ratio or error-count");
+ AssertUtil.notNull(stat, "stat cannot be null");
+ this.minRequestAmount = rule.getMinRequestAmount();
+ this.threshold = rule.getCount();
+ this.stat = stat;
+ }
+
+ @Override
+ protected void resetStat() {
+ // Reset current bucket (bucket count = 1).
+ stat.currentWindow().value().reset();
+ }
+
+ @Override
+ public void onRequestComplete(Context context) {
+ Entry entry = context.getCurEntry();
+ if (entry == null) {
+ return;
+ }
+ Throwable error = entry.getError();
+ SimpleErrorCounter counter = stat.currentWindow().value();
+ if (error != null) {
+ counter.getErrorCount().add(1);
+ }
+ counter.getTotalCount().add(1);
+
+ handleStateChangeWhenThresholdExceeded(error);
+ }
+
+ private void handleStateChangeWhenThresholdExceeded(Throwable error) {
+ if (currentState.get() == State.OPEN) {
+ return;
+ }
+
+ if (currentState.get() == State.HALF_OPEN) {
+ // In detecting request
+ if (error == null) {
+ fromHalfOpenToClose();
+ } else {
+ fromHalfOpenToOpen(1.0d);
+ }
+ return;
+ }
+
+ List counters = stat.values();
+ long errCount = 0;
+ long totalCount = 0;
+ for (SimpleErrorCounter counter : counters) {
+ errCount += counter.errorCount.sum();
+ totalCount += counter.totalCount.sum();
+ }
+ if (totalCount < minRequestAmount) {
+ return;
+ }
+ double curCount = errCount;
+ if (strategy == DEGRADE_GRADE_EXCEPTION_RATIO) {
+ // Use errorRatio
+ curCount = errCount * 1.0d / totalCount;
+ }
+ if (curCount > threshold) {
+ transformToOpen(curCount);
+ }
+ }
+
+ static class SimpleErrorCounter {
+ private LongAdder errorCount;
+ private LongAdder totalCount;
+
+ public SimpleErrorCounter() {
+ this.errorCount = new LongAdder();
+ this.totalCount = new LongAdder();
+ }
+
+ public LongAdder getErrorCount() {
+ return errorCount;
+ }
+
+ public LongAdder getTotalCount() {
+ return totalCount;
+ }
+
+ public SimpleErrorCounter reset() {
+ errorCount.reset();
+ totalCount.reset();
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "SimpleErrorCounter{" +
+ "errorCount=" + errorCount +
+ ", totalCount=" + totalCount +
+ '}';
+ }
+ }
+
+ static class SimpleErrorCounterLeapArray extends LeapArray {
+
+ public SimpleErrorCounterLeapArray(int sampleCount, int intervalInMs) {
+ super(sampleCount, intervalInMs);
+ }
+
+ @Override
+ public SimpleErrorCounter newEmptyBucket(long timeMillis) {
+ return new SimpleErrorCounter();
+ }
+
+ @Override
+ protected WindowWrap resetWindowTo(WindowWrap w, long startTime) {
+ // Update the start time and reset value.
+ w.resetTo(startTime);
+ w.value().reset();
+ return w;
+ }
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/ResponseTimeCircuitBreaker.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/ResponseTimeCircuitBreaker.java
new file mode 100644
index 0000000000..2ab98d4dfd
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/ResponseTimeCircuitBreaker.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 1999-2019 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.slots.block.degrade.circuitbreaker;
+
+import java.util.List;
+
+import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
+import com.alibaba.csp.sentinel.slots.statistic.base.LeapArray;
+import com.alibaba.csp.sentinel.slots.statistic.base.LongAdder;
+import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;
+import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.util.TimeUtil;
+
+/**
+ * @author Eric Zhao
+ * @since 1.8.0
+ */
+public class ResponseTimeCircuitBreaker extends AbstractCircuitBreaker {
+
+ private static final double SLOW_REQUEST_RATIO_MAX_VALUE = 1.0d;
+
+ private final long maxAllowedRt;
+ private final double maxSlowRequestRatio;
+ private final int minRequestAmount;
+
+ private final LeapArray slidingCounter;
+
+ public ResponseTimeCircuitBreaker(DegradeRule rule) {
+ this(rule, new SlowRequestLeapArray(1, rule.getStatIntervalMs()));
+ }
+
+ ResponseTimeCircuitBreaker(DegradeRule rule, LeapArray stat) {
+ super(rule);
+ AssertUtil.isTrue(rule.getGrade() == RuleConstant.DEGRADE_GRADE_RT, "rule metric type should be RT");
+ AssertUtil.notNull(stat, "stat cannot be null");
+ this.maxAllowedRt = Math.round(rule.getCount());
+ this.maxSlowRequestRatio = rule.getSlowRatioThreshold();
+ this.minRequestAmount = rule.getMinRequestAmount();
+ this.slidingCounter = stat;
+ }
+
+ @Override
+ public void resetStat() {
+ // Reset current bucket (bucket count = 1).
+ slidingCounter.currentWindow().value().reset();
+ }
+
+ @Override
+ public void onRequestComplete(Context context) {
+ SlowRequestCounter counter = slidingCounter.currentWindow().value();
+ Entry entry = context.getCurEntry();
+ if (entry == null) {
+ return;
+ }
+ long completeTime = entry.getCompleteTimestamp();
+ if (completeTime <= 0) {
+ completeTime = TimeUtil.currentTimeMillis();
+ }
+ long rt = completeTime - entry.getCreateTimestamp();
+ if (rt > maxAllowedRt) {
+ counter.slowCount.add(1);
+ }
+ counter.totalCount.add(1);
+
+ handleStateChangeWhenThresholdExceeded(rt);
+ }
+
+ private void handleStateChangeWhenThresholdExceeded(long rt) {
+ if (currentState.get() == State.OPEN) {
+ return;
+ }
+
+ if (currentState.get() == State.HALF_OPEN) {
+ // In detecting request
+ // TODO: improve logic for half-open recovery
+ if (rt > maxAllowedRt) {
+ fromHalfOpenToOpen(1.0d);
+ } else {
+ fromHalfOpenToClose();
+ }
+ return;
+ }
+
+ List counters = slidingCounter.values();
+ long slowCount = 0;
+ long totalCount = 0;
+ for (SlowRequestCounter counter : counters) {
+ slowCount += counter.slowCount.sum();
+ totalCount += counter.totalCount.sum();
+ }
+ if (totalCount < minRequestAmount) {
+ return;
+ }
+ double currentRatio = slowCount * 1.0d / totalCount;
+ if (currentRatio > maxSlowRequestRatio) {
+ transformToOpen(currentRatio);
+ }
+ if (Double.compare(currentRatio, maxSlowRequestRatio) == 0 &&
+ Double.compare(maxSlowRequestRatio, SLOW_REQUEST_RATIO_MAX_VALUE) == 0) {
+ transformToOpen(currentRatio);
+ }
+ }
+
+ static class SlowRequestCounter {
+ private LongAdder slowCount;
+ private LongAdder totalCount;
+
+ public SlowRequestCounter() {
+ this.slowCount = new LongAdder();
+ this.totalCount = new LongAdder();
+ }
+
+ public LongAdder getSlowCount() {
+ return slowCount;
+ }
+
+ public LongAdder getTotalCount() {
+ return totalCount;
+ }
+
+ public SlowRequestCounter reset() {
+ slowCount.reset();
+ totalCount.reset();
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "SlowRequestCounter{" +
+ "slowCount=" + slowCount +
+ ", totalCount=" + totalCount +
+ '}';
+ }
+ }
+
+ static class SlowRequestLeapArray extends LeapArray {
+
+ public SlowRequestLeapArray(int sampleCount, int intervalInMs) {
+ super(sampleCount, intervalInMs);
+ }
+
+ @Override
+ public SlowRequestCounter newEmptyBucket(long timeMillis) {
+ return new SlowRequestCounter();
+ }
+
+ @Override
+ protected WindowWrap resetWindowTo(WindowWrap w, long startTime) {
+ w.resetTo(startTime);
+ w.value().reset();
+ return w;
+ }
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/ClusterFlowConfig.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/ClusterFlowConfig.java
index 2b3564f2e6..0ce97548ff 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/ClusterFlowConfig.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/ClusterFlowConfig.java
@@ -18,6 +18,8 @@
import com.alibaba.csp.sentinel.slots.block.ClusterRuleConstant;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import java.util.Objects;
+
/**
* Flow rule config in cluster mode.
*
@@ -48,6 +50,61 @@ public class ClusterFlowConfig {
*/
private int windowIntervalMs = RuleConstant.DEFAULT_WINDOW_INTERVAL_MS;
+ /**
+ * if the client keep the token for more than resourceTimeout,resourceTimeoutStrategy will work.
+ */
+ private long resourceTimeout = 2000;
+
+ /**
+ * 0:ignore,1:release the token.
+ */
+ private int resourceTimeoutStrategy = RuleConstant.DEFAULT_RESOURCE_TIMEOUT_STRATEGY;
+
+ /**
+ * if the request(prioritized=true) is block,acquireRefuseStrategy will work..
+ * 0:ignore and block.
+ * 1:try again .
+ * 2:try until success.
+ */
+ private int acquireRefuseStrategy = RuleConstant.DEFAULT_BLOCK_STRATEGY;
+
+ /**
+ * if a client is offline,the server will delete all the token the client holds after clientOfflineTime.
+ */
+ private long clientOfflineTime = 2000;
+
+ public long getResourceTimeout() {
+ return resourceTimeout;
+ }
+
+ public void setResourceTimeout(long resourceTimeout) {
+ this.resourceTimeout = resourceTimeout;
+ }
+
+ public int getResourceTimeoutStrategy() {
+ return resourceTimeoutStrategy;
+ }
+
+ public void setResourceTimeoutStrategy(int resourceTimeoutStrategy) {
+ this.resourceTimeoutStrategy = resourceTimeoutStrategy;
+ }
+
+ public int getAcquireRefuseStrategy() {
+ return acquireRefuseStrategy;
+ }
+
+ public void setAcquireRefuseStrategy(int acquireRefuseStrategy) {
+ this.acquireRefuseStrategy = acquireRefuseStrategy;
+ }
+
+ public long getClientOfflineTime() {
+ return clientOfflineTime;
+ }
+
+ public void setClientOfflineTime(long clientOfflineTime) {
+ this.clientOfflineTime = clientOfflineTime;
+ }
+
public Long getFlowId() {
return flowId;
}
@@ -104,17 +161,43 @@ public ClusterFlowConfig setWindowIntervalMs(int windowIntervalMs) {
@Override
public boolean equals(Object o) {
- if (this == o) { return true; }
- if (o == null || getClass() != o.getClass()) { return false; }
-
- ClusterFlowConfig that = (ClusterFlowConfig)o;
-
- if (thresholdType != that.thresholdType) { return false; }
- if (fallbackToLocalWhenFail != that.fallbackToLocalWhenFail) { return false; }
- if (strategy != that.strategy) { return false; }
- if (sampleCount != that.sampleCount) { return false; }
- if (windowIntervalMs != that.windowIntervalMs) { return false; }
- return flowId != null ? flowId.equals(that.flowId) : that.flowId == null;
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ClusterFlowConfig that = (ClusterFlowConfig) o;
+
+ if (thresholdType != that.thresholdType) {
+ return false;
+ }
+ if (fallbackToLocalWhenFail != that.fallbackToLocalWhenFail) {
+ return false;
+ }
+ if (strategy != that.strategy) {
+ return false;
+ }
+ if (sampleCount != that.sampleCount) {
+ return false;
+ }
+ if (windowIntervalMs != that.windowIntervalMs) {
+ return false;
+ }
+ if (resourceTimeout != that.resourceTimeout) {
+ return false;
+ }
+ if (clientOfflineTime != that.clientOfflineTime) {
+ return false;
+ }
+ if (resourceTimeoutStrategy != that.resourceTimeoutStrategy) {
+ return false;
+ }
+ if (acquireRefuseStrategy != that.acquireRefuseStrategy) {
+ return false;
+ }
+ return Objects.equals(flowId, that.flowId);
}
@Override
@@ -125,18 +208,26 @@ public int hashCode() {
result = 31 * result + strategy;
result = 31 * result + sampleCount;
result = 31 * result + windowIntervalMs;
+ result = (int) (31 * result + resourceTimeout);
+ result = (int) (31 * result + clientOfflineTime);
+ result = 31 * result + resourceTimeoutStrategy;
+ result = 31 * result + acquireRefuseStrategy;
return result;
}
@Override
public String toString() {
return "ClusterFlowConfig{" +
- "flowId=" + flowId +
- ", thresholdType=" + thresholdType +
- ", fallbackToLocalWhenFail=" + fallbackToLocalWhenFail +
- ", strategy=" + strategy +
- ", sampleCount=" + sampleCount +
- ", windowIntervalMs=" + windowIntervalMs +
- '}';
+ "flowId=" + flowId +
+ ", thresholdType=" + thresholdType +
+ ", fallbackToLocalWhenFail=" + fallbackToLocalWhenFail +
+ ", strategy=" + strategy +
+ ", sampleCount=" + sampleCount +
+ ", windowIntervalMs=" + windowIntervalMs +
+ ", resourceTimeout=" + resourceTimeout +
+ ", resourceTimeoutStrategy=" + resourceTimeoutStrategy +
+ ", acquireRefuseStrategy=" + acquireRefuseStrategy +
+ ", clientOfflineTime=" + clientOfflineTime +
+ '}';
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRule.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRule.java
index e7413ea219..f05d1cbff4 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRule.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRule.java
@@ -15,8 +15,6 @@
*/
package com.alibaba.csp.sentinel.slots.block.flow;
-import com.alibaba.csp.sentinel.context.Context;
-import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.slots.block.AbstractRule;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
@@ -186,11 +184,6 @@ public FlowRule setClusterConfig(ClusterFlowConfig clusterConfig) {
return this;
}
- @Override
- public boolean passCheck(Context context, DefaultNode node, int acquireCount, Object... args) {
- return true;
- }
-
@Override
public boolean equals(Object o) {
if (this == o) { return true; }
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleManager.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleManager.java
index 40d2f92a5a..dfcc4cb591 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleManager.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleManager.java
@@ -16,12 +16,14 @@
package com.alibaba.csp.sentinel.slots.block.flow;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory;
import com.alibaba.csp.sentinel.log.RecordLog;
@@ -43,10 +45,11 @@
*
* @author jialiang.linjl
* @author Eric Zhao
+ * @author Weihua
*/
public class FlowRuleManager {
- private static final Map> flowRules = new ConcurrentHashMap>();
+ private static final AtomicReference