dengsixing 3 年之前
父节点
当前提交
2b4b9e5c25
共有 100 个文件被更改,包括 1932 次插入771 次删除
  1. 3 0
      .gitignore
  2. 0 24
      .travis.yml
  3. 30 18
      README.md
  4. 52 28
      doc/awesome-sentinel.md
  5. 二进制
      doc/image/sentinel-opensource-eco-landscape-en.png
  6. 38 7
      pom.xml
  7. 9 1
      sentinel-adapter/pom.xml
  8. 14 10
      sentinel-adapter/sentinel-apache-dubbo-adapter/README.md
  9. 2 2
      sentinel-adapter/sentinel-apache-dubbo-adapter/pom.xml
  10. 5 3
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAppContextFilter.java
  11. 44 3
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtils.java
  12. 94 23
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java
  13. 33 22
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java
  14. 3 3
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DefaultDubboFallback.java
  15. 7 15
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistry.java
  16. 39 4
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/BaseTest.java
  17. 1 1
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAppContextFilterTest.java
  18. 140 8
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtilsTest.java
  19. 282 30
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java
  20. 28 20
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilterTest.java
  21. 24 9
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java
  22. 1 0
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/DemoService.java
  23. 5 0
      sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/impl/DemoServiceImpl.java
  24. 1 6
      sentinel-adapter/sentinel-api-gateway-adapter-common/pom.xml
  25. 6 6
      sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManager.java
  26. 71 7
      sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayApiDefinitionGroupCommandHandler.java
  27. 44 9
      sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayRuleCommandHandler.java
  28. 5 5
      sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/GatewayParamParser.java
  29. 59 54
      sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleManager.java
  30. 2 0
      sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewayFlowSlot.java
  31. 9 33
      sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewaySlotChainBuilder.java
  32. 0 1
      sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.slotchain.SlotChainBuilder
  33. 29 0
      sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/GatewayParamParserTest.java
  34. 11 7
      sentinel-adapter/sentinel-dubbo-adapter/README.md
  35. 1 1
      sentinel-adapter/sentinel-dubbo-adapter/pom.xml
  36. 23 1
      sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/AbstractDubboFilter.java
  37. 3 1
      sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAppContextFilter.java
  38. 17 10
      sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java
  39. 22 12
      sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java
  40. 5 2
      sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DefaultDubboFallback.java
  41. 9 11
      sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistry.java
  42. 51 3
      sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/AbstractDubboFilterTest.java
  43. 4 4
      sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java
  44. 4 4
      sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilterTest.java
  45. 8 7
      sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java
  46. 0 2
      sentinel-adapter/sentinel-grpc-adapter/README.md
  47. 4 4
      sentinel-adapter/sentinel-grpc-adapter/pom.xml
  48. 36 33
      sentinel-adapter/sentinel-grpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcClientInterceptor.java
  49. 50 31
      sentinel-adapter/sentinel-grpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcServerInterceptor.java
  50. 7 9
      sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/FooServiceClient.java
  51. 42 10
      sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/FooServiceImpl.java
  52. 1 0
      sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/GrpcTestServer.java
  53. 38 25
      sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcClientInterceptorTest.java
  54. 34 23
      sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcServerInterceptorTest.java
  55. 1 2
      sentinel-adapter/sentinel-grpc-adapter/src/test/proto/example.proto
  56. 0 2
      sentinel-adapter/sentinel-reactor-adapter/README.md
  57. 1 1
      sentinel-adapter/sentinel-reactor-adapter/pom.xml
  58. 25 0
      sentinel-adapter/sentinel-reactor-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/reactor/EntryConfig.java
  59. 3 3
      sentinel-adapter/sentinel-reactor-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/reactor/SentinelReactorSubscriber.java
  60. 22 0
      sentinel-adapter/sentinel-reactor-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/reactor/MonoSentinelOperatorIntegrationTest.java
  61. 0 2
      sentinel-adapter/sentinel-spring-cloud-gateway-adapter/README.md
  62. 1 1
      sentinel-adapter/sentinel-spring-cloud-gateway-adapter/pom.xml
  63. 22 4
      sentinel-adapter/sentinel-spring-cloud-gateway-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/sc/SentinelGatewayFilter.java
  64. 1 1
      sentinel-adapter/sentinel-spring-cloud-gateway-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/sc/ServerWebExchangeItemParser.java
  65. 9 12
      sentinel-adapter/sentinel-spring-cloud-gateway-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/sc/api/GatewayApiMatcherManager.java
  66. 1 3
      sentinel-adapter/sentinel-spring-webflux-adapter/README.md
  67. 1 1
      sentinel-adapter/sentinel-spring-webflux-adapter/pom.xml
  68. 14 11
      sentinel-adapter/sentinel-spring-webflux-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webflux/SentinelWebFluxFilter.java
  69. 45 1
      sentinel-adapter/sentinel-spring-webflux-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webflux/SentinelWebFluxIntegrationTest.java
  70. 17 7
      sentinel-adapter/sentinel-web-servlet/README.md
  71. 1 1
      sentinel-adapter/sentinel-web-servlet/pom.xml
  72. 42 30
      sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/CommonFilter.java
  73. 5 12
      sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/CommonTotalFilter.java
  74. 56 4
      sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/config/WebServletConfig.java
  75. 4 2
      sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/util/FilterUtil.java
  76. 45 3
      sentinel-adapter/sentinel-web-servlet/src/test/java/com/alibaba/csp/sentinel/adapter/servlet/CommonFilterTest.java
  77. 5 0
      sentinel-adapter/sentinel-web-servlet/src/test/java/com/alibaba/csp/sentinel/adapter/servlet/TestController.java
  78. 2 1
      sentinel-adapter/sentinel-web-servlet/src/test/java/com/alibaba/csp/sentinel/adapter/servletmethod/CommonFilterMethodTest.java
  79. 1 1
      sentinel-adapter/sentinel-web-servlet/src/test/java/com/alibaba/csp/sentinel/adapter/servletmethod/FilterMethodConfig.java
  80. 1 1
      sentinel-adapter/sentinel-zuul-adapter/README.md
  81. 7 1
      sentinel-adapter/sentinel-zuul-adapter/pom.xml
  82. 8 2
      sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/PrefixRoutePathMatcher.java
  83. 9 4
      sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/RegexRoutePathMatcher.java
  84. 17 12
      sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelEntryUtils.java
  85. 11 8
      sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java
  86. 1 12
      sentinel-benchmark/pom.xml
  87. 3 5
      sentinel-cluster/pom.xml
  88. 6 1
      sentinel-cluster/sentinel-cluster-client-default/pom.xml
  89. 11 2
      sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/DefaultClusterTokenClient.java
  90. 10 8
      sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/NettyTransportClient.java
  91. 7 5
      sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/ClientEntityCodecProvider.java
  92. 1 1
      sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/DefaultRequestEntityWriter.java
  93. 1 1
      sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/DefaultResponseEntityDecoder.java
  94. 1 1
      sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/data/FlowRequestDataWriter.java
  95. 43 40
      sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/data/ParamFlowRequestDataWriter.java
  96. 6 1
      sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/data/PingResponseDataDecoder.java
  97. 3 3
      sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/config/ClusterClientConfigManager.java
  98. 5 5
      sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/handler/TokenClientHandler.java
  99. 7 1
      sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/init/DefaultClusterClientInitFunc.java
  100. 0 0
      sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/command/handler/ModifyClusterClientConfigHandler.java

+ 3 - 0
.gitignore

@@ -4,6 +4,9 @@
 out
 out
 gen
 gen
 
 
+# Visual Studio Code
+.history/
+
 # Maven
 # Maven
 target/
 target/
 pom.xml.tag
 pom.xml.tag

+ 0 - 24
.travis.yml

@@ -1,24 +0,0 @@
-language: java
-
-sudo: required
-dist: trusty
-
-matrix:
-  include:
-  - jdk: oraclejdk8
-    env: BUILD_JDK=ORACLE_JDK_8
-  - jdk: oraclejdk11
-    env: BUILD_JDK=ORACLE_JDK_11
-  allow_failures:
-  - env: BUILD_JDK=ORACLE_JDK_11
-
-# https://docs.travis-ci.com/user/languages/java/#maven-dependency-management
-install:
-  - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -DminimumPriority=1
-
-after_success:
-  - bash <(curl -s https://codecov.io/bash)
-
-cache:
-  directories:
-  - $HOME/.m2/

+ 30 - 18
README.md

@@ -1,8 +1,8 @@
 <img src="https://user-images.githubusercontent.com/9434884/43697219-3cb4ef3a-9975-11e8-9a9c-73f4f537442d.png" alt="Sentinel Logo" width="50%">
 <img src="https://user-images.githubusercontent.com/9434884/43697219-3cb4ef3a-9975-11e8-9a9c-73f4f537442d.png" alt="Sentinel Logo" width="50%">
 
 
-# Sentinel: Sentinel of Your Application
+# Sentinel: The Sentinel of Your Microservices
 
 
-[![Travis Build Status](https://travis-ci.org/alibaba/Sentinel.svg?branch=master)](https://travis-ci.org/alibaba/Sentinel)
+[![Sentinel CI](https://github.com/alibaba/Sentinel/actions/workflows/ci.yml/badge.svg)](https://github.com/alibaba/Sentinel/actions/workflows/ci.yml)
 [![Codecov](https://codecov.io/gh/alibaba/Sentinel/branch/master/graph/badge.svg)](https://codecov.io/gh/alibaba/Sentinel)
 [![Codecov](https://codecov.io/gh/alibaba/Sentinel/branch/master/graph/badge.svg)](https://codecov.io/gh/alibaba/Sentinel)
 [![Maven Central](https://img.shields.io/maven-central/v/com.alibaba.csp/sentinel-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:com.alibaba.csp%20AND%20a:sentinel-core)
 [![Maven Central](https://img.shields.io/maven-central/v/com.alibaba.csp/sentinel-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:com.alibaba.csp%20AND%20a:sentinel-core)
 [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
 [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
@@ -11,13 +11,15 @@
 ## Introduction
 ## Introduction
 
 
 As distributed systems become increasingly popular, the reliability between services is becoming more important than ever before.
 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:
 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.
 - **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.
 - **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.
 - **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.
 - **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:
 Features overview:
@@ -26,12 +28,15 @@ Features overview:
 
 
 ## Documentation
 ## Documentation
 
 
+See the [Sentinel](https://sentinelguard.io/) for the document website.
+
 See the [中文文档](https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D) for document in Chinese.
 See the [中文文档](https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D) for document in Chinese.
 
 
 See the [Wiki](https://github.com/alibaba/Sentinel/wiki) for full documentation, examples, blog posts, operational details and other information.
 See the [Wiki](https://github.com/alibaba/Sentinel/wiki) for full documentation, examples, blog posts, operational details and other information.
 
 
-Sentinel provides integration module for various open-source frameworks and libraries
-(e.g. Spring Cloud, Apache Dubbo, gRPC, Spring WebFlux, Reactor). You can refer to [the document](https://github.com/alibaba/Sentinel/wiki/Adapters-to-Popular-Framework) for more information.
+Sentinel provides integration modules for various open-source frameworks
+(e.g. Spring Cloud, Apache Dubbo, gRPC, Spring WebFlux, Reactor) and service mesh.
+You can refer to [the document](https://github.com/alibaba/Sentinel/wiki/Adapters-to-Popular-Framework) for more information.
 
 
 If you are using Sentinel, please [**leave a comment here**](https://github.com/alibaba/Sentinel/issues/18) to tell us your scenario to make Sentinel better.
 If you are using Sentinel, please [**leave a comment here**](https://github.com/alibaba/Sentinel/issues/18) to tell us your scenario to make Sentinel better.
 It's also encouraged to add the link of your blog post, tutorial, demo or customized components to [**Awesome Sentinel**](./doc/awesome-sentinel.md).
 It's also encouraged to add the link of your blog post, tutorial, demo or customized components to [**Awesome Sentinel**](./doc/awesome-sentinel.md).
@@ -46,16 +51,16 @@ Below is a simple demo that guides new users to use Sentinel in just 3 steps. It
 
 
 ### 1. Add Dependency
 ### 1. Add Dependency
 
 
-**Note:** Sentinel requires Java 7 or later.
+**Note:** Sentinel requires JDK 1.8 or later.
 
 
-If your application is build in Maven, just add the following dependency in `pom.xml`.
+If you're using Maven, just add the following dependency in `pom.xml`.
 
 
 ```xml
 ```xml
 <!-- replace here with the latest version -->
 <!-- replace here with the latest version -->
 <dependency>
 <dependency>
     <groupId>com.alibaba.csp</groupId>
     <groupId>com.alibaba.csp</groupId>
     <artifactId>sentinel-core</artifactId>
     <artifactId>sentinel-core</artifactId>
-    <version>1.6.2</version>
+    <version>1.8.3</version>
 </dependency>
 </dependency>
 ```
 ```
 
 
@@ -74,9 +79,10 @@ try (Entry entry = SphU.entry("HelloWorld")) {
     // Handle rejected request.
     // Handle rejected request.
     e.printStackTrace();
     e.printStackTrace();
 }
 }
+// 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
 ### 3. Define Rules
 
 
@@ -109,7 +115,7 @@ After running the demo for a while, you can see the following records in `~/logs
 1529998908000|2018-06-26 15:41:48|HelloWorld|20|19502|20|0|0   |0
 1529998908000|2018-06-26 15:41:48|HelloWorld|20|19502|20|0|0   |0
 1529998909000|2018-06-26 15:41:49|HelloWorld|20|18386|20|0|0   |0
 1529998909000|2018-06-26 15:41:49|HelloWorld|20|18386|20|0|0   |0
 
 
-p stands for incoming request, block for blocked by rules, success for success handled by Sentinel, e for exception count, rt for average response time (ms), occupied stands for occupiedPassQps since 1.5.0 which enable us booking more than 1 shot when entering.
+p stands for incoming request, block for blocked by rules, s for success handled by Sentinel, e for exception count, rt for average response time (ms), occupied stands for occupiedPassQps since 1.5.0 which enable us booking more than 1 shot when entering.
 ```
 ```
 
 
 This shows that the demo can print "hello world" 20 times per second.
 This shows that the demo can print "hello world" 20 times per second.
@@ -122,23 +128,28 @@ Samples can be found in the [sentinel-demo](https://github.com/alibaba/Sentinel/
 
 
 ### 5. Start Dashboard
 ### 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.
 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)
+
 For details please refer to [Dashboard](https://github.com/alibaba/Sentinel/wiki/Dashboard).
 For details please refer to [Dashboard](https://github.com/alibaba/Sentinel/wiki/Dashboard).
 
 
 ## Trouble Shooting and Logs
 ## Trouble Shooting and Logs
 
 
-Sentinel will generate logs for troubleshooting. All the information can be found in [logs](https://github.com/alibaba/Sentinel/wiki/Logs).
+Sentinel will generate logs for troubleshooting and real-time monitoring.
+All the information can be found in [logs](https://github.com/alibaba/Sentinel/wiki/Logs).
 
 
 ## Bugs and Feedback
 ## Bugs and Feedback
 
 
 For bug report, questions and discussions please submit [GitHub Issues](https://github.com/alibaba/sentinel/issues).
 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
 ## 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).
 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).
 
 
@@ -150,18 +161,19 @@ And thanks for all [contributors](https://github.com/alibaba/Sentinel/graphs/con
 
 
 ## Who is using
 ## 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)
 ![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)
 ![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)
 ![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)
 ![二维火](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)
 ![文轩在线](http://static.winxuancdn.com/css/v2/images/logo.png)
 ![客如云](https://www.keruyun.com/static/krynew/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://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)
 ![金汇金融](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.52shangou.com/shandianbang/official-source/3.1.1/build/images/logo.png)
-![拼多多](http://cdn.pinduoduo.com/assets/img/pdd_logo_v3.png)

+ 52 - 28
doc/awesome-sentinel.md

@@ -2,51 +2,75 @@
 
 
 [![Awesome](https://awesome.re/badge-flat.svg)](https://awesome.re)
 [![Awesome](https://awesome.re/badge-flat.svg)](https://awesome.re)
 
 
-A curated list of awesome things (e.g. sample, extensions, blogs) for [Sentinel](https://github.com/alibaba/Sentinel).
+A curated list of awesome things (e.g. samples, third-party extensions, blog posts) for [Sentinel](https://github.com/alibaba/Sentinel).
 
 
-If you want your component to appear here, send a pull request to this repository to add it.
+If you want your component to appear here, feel free to submit a pull request to this repository to add it.
 You can refer to the [awesome contribution guidelines](https://github.com/sentinel-group/sentinel-awesome/blob/master/CONTRIBUTING.md).
 You can refer to the [awesome contribution guidelines](https://github.com/sentinel-group/sentinel-awesome/blob/master/CONTRIBUTING.md).
 
 
 You can also add to [sentinel-group/sentinel-awesome](https://github.com/sentinel-group/sentinel-awesome).
 You can also add to [sentinel-group/sentinel-awesome](https://github.com/sentinel-group/sentinel-awesome).
 
 
 ## Contents
 ## Contents
 
 
+- [Presentations](#presentations)
 - [Tutorials](#tutorials)
 - [Tutorials](#tutorials)
-- [Samples / Demos](#samples--demos)
+- [Demos](#demos)
 - [Extensions / Integrations](#extensions--integrations)
 - [Extensions / Integrations](#extensions--integrations)
-- [Blogs](#blogs)
+- [Blog Posts](#blog-posts)
+
+## Presentations
+
+- Sentinel 1.6.0 网关流控新特性介绍-Eric Zhao (Dubbo Tech Day-201905-Beijing): [PDF](https://github.com/sentinel-group/sentinel-awesome/blob/master/slides/Sentinel%201.6.0%20网关流控新特性介绍-Eric%20Zhao-DTED-201905.pdf)
+- Sentinel 微服务流控降级实践-Eric Zhao (Dubbo Tech Day-201907-Shenzhen): [PDF](https://github.com/sentinel-group/sentinel-awesome/blob/master/slides/Sentinel%20微服务流控降级实践-Eric%20Zhao-DTED-201907.pdf)
+- Sentinel 1.7.0 新特性展望-Eric Zhao (Dubbo Tech Day-201910-Chengdu): [PDF](https://github.com/sentinel-group/sentinel-awesome/blob/master/slides/Sentinel%201.7.0%20新特性展望-Eric%20Zhao-DTED-201910.pdf)
 
 
 ## Tutorials
 ## Tutorials
 
 
-## Samples / Demos
+- [Sentinel Guides](https://github.com/sentinel-group/sentinel-guides)
+
+## Demos
 
 
-- [sentinel-zuul-example](https://github.com/tigerMoon/sentinel-zuul-sample): A simple project integration Sentinel to Spring Cloud Zuul which provide Service and API Path level flow control management by [tiger](https://github.com/tigerMoon)
+- [sentinel-zuul-example](https://github.com/tigerMoon/sentinel-zuul-sample): A simple project integration Sentinel to Spring Cloud Zuul which provides Service and API Path level flow control management by [tiger](https://github.com/tigerMoon)
 
 
 ## Extensions / Integrations
 ## Extensions / Integrations
 
 
 - [sentinel-support](https://github.com/cdfive/sentinel-support): A support project for convenient Sentinel integration including properties file configuration, ActiveMQ integration and a JdbcDataSource implementation by [cdfive](https://github.com/cdfive)
 - [sentinel-support](https://github.com/cdfive/sentinel-support): A support project for convenient Sentinel integration including properties file configuration, ActiveMQ integration and a JdbcDataSource implementation by [cdfive](https://github.com/cdfive)
+- [Sentinel dashboard multi-data-source adapter](https://github.com/finefuture/sentinel-dashboard-X): Sentinel dashboard multi-data-source adapter has integrated Apollo and Nacos configuration center for bidirectional modification persistence. Implemented by [finefuture](https://github.com/finefuture)
+- [Sentinel Rule Annotation Support](https://github.com/code1986/sentinel-lib): A third-party library that supports configuring flow rule and degrade rule using annotation. Implemented by [code1986](https://github.com/code1986)
+- [sentinel-pigeon-adapter](https://github.com/wchswchs/sentinel-pigeon): A RPC framework Pigeon adapter for Sentinel including provider and invoker rate limiting implementation by [wchswchs](https://github.com/wchswchs)
 
 
-## Blogs
+## Blog Posts
 
 
-- [Sentinel 为 Dubbo 服务保驾护航](http://dubbo.apache.org/zh-cn/blog/sentinel-introduction-for-dubbo.html) by [Eric Zhao](https://github.com/sczyh30)
-- [Sentinel 与 Hystrix 的对比](https://github.com/alibaba/Sentinel/wiki/Sentinel-%E4%B8%8E-Hystrix-%E7%9A%84%E5%AF%B9%E6%AF%94) by [Eric Zhao](https://github.com/sczyh30)
-- [Guideline: 从 Hystrix 迁移到 Sentinel](https://github.com/alibaba/Sentinel/wiki/Guideline:-%E4%BB%8E-Hystrix-%E8%BF%81%E7%A7%BB%E5%88%B0-Sentinel) by [Eric Zhao](https://github.com/sczyh30)
-- [Sentinel 控制台监控数据持久化【MySQL】(Spring Data JPA)](https://www.cnblogs.com/cdfive2018/p/9838577.html) by [cdfive](https://github.com/cdfive)
+- [Sentinel 为 Dubbo 服务保驾护航](https://dubbo.apache.org/zh/blog/2018/07/27/sentinel-为-dubbo-服务保驾护航) by [Eric Zhao](https://github.com/sczyh30)
+- [在生产环境中使用 Sentinel 控制台](https://github.com/alibaba/Sentinel/wiki/在生产环境中使用-Sentinel) by [Eric Zhao](https://github.com/sczyh30)
+- [Sentinel 与 Hystrix 的对比](https://sentinelguard.io/zh-cn/blog/sentinel-vs-hystrix.html) by [Eric Zhao](https://github.com/sczyh30)
+- [Guideline: 从 Hystrix 迁移到 Sentinel](https://sentinelguard.io/zh-cn/blog/guideline-migrate-from-hystrix-to-sentinel.html) by [Eric Zhao](https://github.com/sczyh30)
+- [Sentinel 控制台监控数据持久化【MySQL】](https://www.cnblogs.com/cdfive2018/p/9838577.html) by [cdfive](https://github.com/cdfive)
 - [Sentinel 控制台监控数据持久化【InfluxDB】](https://www.cnblogs.com/cdfive2018/p/9914838.html) by [cdfive](https://github.com/cdfive)
 - [Sentinel 控制台监控数据持久化【InfluxDB】](https://www.cnblogs.com/cdfive2018/p/9914838.html) by [cdfive](https://github.com/cdfive)
-- [Sentinel一体化监控解决方案 CrateDB+Grafana](https://blog.csdn.net/huyong1990/article/details/82392386) by [Young Hu](https://github.com/YoungHu)
-- [Sentinel 原理-全解析](https://mp.weixin.qq.com/s/7_pCkamNv0269e5l9_Wz7w) by [houyi](https://github.com/all4you)
-- [Sentinel 原理-调用链](https://mp.weixin.qq.com/s/UEzwD22YC6jpp02foNSXnw) by [houyi](https://github.com/all4you)
-- [Sentinel 原理-滑动窗口](https://mp.weixin.qq.com/s/B1_7Kb_CxeKEAv43kdCWOA) by [houyi](https://github.com/all4you)
-- [Sentinel 实战-限流](https://mp.weixin.qq.com/s/rjyU37Dm-sxNln7GUD8tOw) by [houyi](https://github.com/all4you)
-- [Sentinel 实战-控制台](https://mp.weixin.qq.com/s/23EDFHMXLwsDqw-4O5dR5A) by [houyi](https://github.com/all4you)
-- [Sentinel 实战-规则持久化](https://mp.weixin.qq.com/s/twMFiBfRawKLR-1-N-f1yw) by [houyi](https://github.com/all4you)
-- [sentinel 深入浅出之原理篇SlotChain](https://www.jianshu.com/p/a7a405de3a12) by [shxz130](https://github.com/shxz130)
-- [sentinel 深入浅出之原理篇Context初始化&Entry初始化](https://www.jianshu.com/p/e39ac47cd893) by [shxz130](https://github.com/shxz130)
-- [sentinel 深入浅出之原理篇NodeSelectorSlot](https://www.jianshu.com/p/9a380ba188ab) by [shxz130](https://github.com/shxz130)
-- [sentinel 深入浅出之原理篇ClusterBuilderSlot](https://www.jianshu.com/p/0b0b5d8888a2) by [shxz130](https://github.com/shxz130)
-- [sentinel 深入浅出之原理篇StatisticSlot&滑动窗口](https://www.jianshu.com/p/9620298fd15a) by [shxz130](https://github.com/shxz130)
-- [sentinel 深入浅出之原理篇SystemSlot](https://www.jianshu.com/p/bfad1b7d0cde) by [shxz130](https://github.com/shxz130)
-- [sentinel 深入浅出之原理篇AuthoritySlot](https://www.jianshu.com/p/c5312c2242b3) by [shxz130](https://github.com/shxz130)
-- [sentinel 深入浅出之原理篇FlowSlot](https://www.jianshu.com/p/53218d0d273e) by [shxz130](https://github.com/shxz130)
-- [sentinel 深入浅出之原理篇DegradeSlot](https://www.jianshu.com/p/e910d4840e4a) by [shxz130](https://github.com/shxz130)
-- [sentinel 深入浅出之原理篇协议拓展dubbo,grpc,web-servlet](https://www.jianshu.com/p/579bff0f34be) by [shxz130](https://github.com/shxz130)
+- [Sentinel 控制台监控数据持久化【Apollo】](https://blog.csdn.net/caodegao/article/details/100009618) by [cookiejoo](https://github.com/cookiejoo)
+- [Sentinel一体化监控解决方案 CrateDB + Grafana](https://blog.csdn.net/huyong1990/article/details/82392386) by [Young Hu](https://github.com/YoungHu)
+- Sentinel 源码解析系列 by [houyi](https://github.com/all4you)
+  - [Sentinel 原理-全解析](https://mp.weixin.qq.com/s/7_pCkamNv0269e5l9_Wz7w)
+  - [Sentinel 原理-调用链](https://mp.weixin.qq.com/s/UEzwD22YC6jpp02foNSXnw)
+  - [Sentinel 原理-滑动窗口](https://mp.weixin.qq.com/s/B1_7Kb_CxeKEAv43kdCWOA)
+  - [Sentinel 实战-限流](https://mp.weixin.qq.com/s/rjyU37Dm-sxNln7GUD8tOw)
+  - [Sentinel 实战-控制台](https://mp.weixin.qq.com/s/23EDFHMXLwsDqw-4O5dR5A)
+  - [Sentinel 实战-规则持久化](https://mp.weixin.qq.com/s/twMFiBfRawKLR-1-N-f1yw)
+- Sentinel 学习笔记 by [ro9er](https://github.com/ro9er)
+  - [Sentinel 学习笔记(1)-- 流量统计代码解析](https://www.jianshu.com/p/7936d7a57924)
+  - [Sentinel 学习笔记(2)-- 流量控制代码分析](https://www.jianshu.com/p/938709e94e43)
+  - [Sentinel 学习笔记(3)-- 上下文统计Node建立分析](https://www.jianshu.com/p/cfdf525248c1)
+- [大流量下的服务质量治理 Dubbo Sentinel 初涉](https://mp.weixin.qq.com/s/ergr_siI07VwwSRPFgsLvQ) by [RyuGrade](https://github.com/RyuGrade)
+- Sentinel 深入浅出系列 by [shxz130](https://github.com/shxz130)
+  - [Sentinel 深入浅出之原理篇 SlotChain](https://www.jianshu.com/p/a7a405de3a12)
+  - [Sentinel 深入浅出之原理篇 Context初始化 & Entry初始化](https://www.jianshu.com/p/e39ac47cd893)
+  - [Sentinel 深入浅出之原理篇 NodeSelectorSlot](https://www.jianshu.com/p/9a380ba188ab)
+  - [Sentinel 深入浅出之原理篇 ClusterBuilderSlot](https://www.jianshu.com/p/0b0b5d8888a2)
+  - [Sentinel 深入浅出之原理篇 StatisticSlot&滑动窗口](https://www.jianshu.com/p/9620298fd15a)
+  - [Sentinel 深入浅出之原理篇 SystemSlot](https://www.jianshu.com/p/bfad1b7d0cde)
+  - [Sentinel 深入浅出之原理篇 AuthoritySlot](https://www.jianshu.com/p/c5312c2242b3)
+  - [Sentinel 深入浅出之原理篇 FlowSlot](https://www.jianshu.com/p/53218d0d273e)
+  - [Sentinel 深入浅出之原理篇 DegradeSlot](https://www.jianshu.com/p/e910d4840e4a)
+- [Alibaba Sentinel RESTful 接口流控处理优化](https://www.jianshu.com/p/96f5980d9798) by [luanlouis](https://github.com/luanlouis)
+- [Sentinel 控制台前端开发环境搭建](https://www.cnblogs.com/cdfive2018/p/11084001.html) by [cdfive](https://github.com/cdfive)
+- [阿里 Sentinel 源码解析](https://www.javadoop.com/post/sentinel) by [Javadoop](https://www.javadoop.com)
+- [Introduction to Alibaba Sentinel](https://www.baeldung.com/java-sentinel-intro) by [Amit Bhave](https://github.com/Amitbhave)

二进制
doc/image/sentinel-opensource-eco-landscape-en.png


+ 38 - 7
pom.xml

@@ -5,7 +5,7 @@
 
 
     <groupId>com.alibaba.csp</groupId>
     <groupId>com.alibaba.csp</groupId>
     <artifactId>sentinel-parent</artifactId>
     <artifactId>sentinel-parent</artifactId>
-    <version>1.6.2</version>
+    <version>1.8.3</version>
     <packaging>pom</packaging>
     <packaging>pom</packaging>
 
 
     <name>${project.artifactId}</name>
     <name>${project.artifactId}</name>
@@ -41,19 +41,20 @@
 
 
     <properties>
     <properties>
         <!-- Compile libs -->
         <!-- Compile libs -->
-        <fastjson.version>1.2.56</fastjson.version>
+        <fastjson.version>1.2.75</fastjson.version>
+        <javax.annotation-api.version>1.3.2</javax.annotation-api.version>
 
 
         <!-- Test libs -->
         <!-- Test libs -->
         <junit.version>4.12</junit.version>
         <junit.version>4.12</junit.version>
-        <mockito.version>2.21.0</mockito.version>
+        <mockito.version>2.28.2</mockito.version>
         <assertj.version>3.12.1</assertj.version>
         <assertj.version>3.12.1</assertj.version>
         <awaitility.version>3.1.5</awaitility.version>
         <awaitility.version>3.1.5</awaitility.version>
         <powermock.version>2.0.0</powermock.version>
         <powermock.version>2.0.0</powermock.version>
 
 
         <!-- Build -->
         <!-- Build -->
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <java.source.version>1.7</java.source.version>
-        <java.target.version>1.7</java.target.version>
+        <java.source.version>1.8</java.source.version>
+        <java.target.version>1.8</java.target.version>
         <java.encoding>UTF-8</java.encoding>
         <java.encoding>UTF-8</java.encoding>
         <maven.compiler.version>3.8.0</maven.compiler.version>
         <maven.compiler.version>3.8.0</maven.compiler.version>
         <maven.surefire.version>2.22.1</maven.surefire.version>
         <maven.surefire.version>2.22.1</maven.surefire.version>
@@ -71,11 +72,14 @@
         <module>sentinel-extension</module>
         <module>sentinel-extension</module>
         <module>sentinel-transport</module>
         <module>sentinel-transport</module>
         <module>sentinel-adapter</module>
         <module>sentinel-adapter</module>
+        <module>sentinel-cluster</module>
+        <module>sentinel-logging</module>
+
         <module>sentinel-dashboard</module>
         <module>sentinel-dashboard</module>
 
 
         <module>sentinel-demo</module>
         <module>sentinel-demo</module>
         <module>sentinel-benchmark</module>
         <module>sentinel-benchmark</module>
-        <module>sentinel-cluster</module>
+
     </modules>
     </modules>
 
 
     <dependencyManagement>
     <dependencyManagement>
@@ -97,6 +101,11 @@
             </dependency>
             </dependency>
             <dependency>
             <dependency>
                 <groupId>com.alibaba.csp</groupId>
                 <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-annotation-cdi-interceptor</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
                 <artifactId>sentinel-parameter-flow-control</artifactId>
                 <artifactId>sentinel-parameter-flow-control</artifactId>
                 <version>${project.version}</version>
                 <version>${project.version}</version>
             </dependency>
             </dependency>
@@ -122,6 +131,11 @@
             </dependency>
             </dependency>
             <dependency>
             <dependency>
                 <groupId>com.alibaba.csp</groupId>
                 <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-datasource-etcd</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
                 <artifactId>sentinel-transport-simple-http</artifactId>
                 <artifactId>sentinel-transport-simple-http</artifactId>
                 <version>${project.version}</version>
                 <version>${project.version}</version>
             </dependency>
             </dependency>
@@ -132,6 +146,11 @@
             </dependency>
             </dependency>
             <dependency>
             <dependency>
                 <groupId>com.alibaba.csp</groupId>
                 <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-transport-spring-mvc</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
                 <artifactId>sentinel-transport-common</artifactId>
                 <artifactId>sentinel-transport-common</artifactId>
                 <version>${project.version}</version>
                 <version>${project.version}</version>
             </dependency>
             </dependency>
@@ -145,7 +164,13 @@
                 <artifactId>sentinel-adapter</artifactId>
                 <artifactId>sentinel-adapter</artifactId>
                 <version>${project.version}</version>
                 <version>${project.version}</version>
             </dependency>
             </dependency>
-
+            
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-metric-exporter</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            
             <dependency>
             <dependency>
                 <groupId>com.alibaba</groupId>
                 <groupId>com.alibaba</groupId>
                 <artifactId>fastjson</artifactId>
                 <artifactId>fastjson</artifactId>
@@ -258,6 +283,7 @@
                 <configuration>
                 <configuration>
                     <!-- CircleCI build workaround -->
                     <!-- CircleCI build workaround -->
                     <argLine>@{argLine} -Xms1024m -Xmx2048m</argLine>
                     <argLine>@{argLine} -Xms1024m -Xmx2048m</argLine>
+                    <argLine>-Dfile.encoding=UTF-8</argLine>
                     <useSystemClassLoader>false</useSystemClassLoader>
                     <useSystemClassLoader>false</useSystemClassLoader>
                 </configuration>
                 </configuration>
             </plugin>
             </plugin>
@@ -293,6 +319,11 @@
                     <artifactId>maven-jar-plugin</artifactId>
                     <artifactId>maven-jar-plugin</artifactId>
                     <version>${maven.jar.version}</version>
                     <version>${maven.jar.version}</version>
                 </plugin>
                 </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-gpg-plugin</artifactId>
+                    <version>${maven.gpg.version}</version>
+                </plugin>
             </plugins>
             </plugins>
         </pluginManagement>
         </pluginManagement>
     </build>
     </build>

+ 9 - 1
sentinel-adapter/pom.xml

@@ -7,7 +7,7 @@
     <parent>
     <parent>
         <groupId>com.alibaba.csp</groupId>
         <groupId>com.alibaba.csp</groupId>
         <artifactId>sentinel-parent</artifactId>
         <artifactId>sentinel-parent</artifactId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     </parent>
     <artifactId>sentinel-adapter</artifactId>
     <artifactId>sentinel-adapter</artifactId>
     <packaging>pom</packaging>
     <packaging>pom</packaging>
@@ -18,12 +18,20 @@
         <module>sentinel-web-servlet</module>
         <module>sentinel-web-servlet</module>
         <module>sentinel-dubbo-adapter</module>
         <module>sentinel-dubbo-adapter</module>
         <module>sentinel-apache-dubbo-adapter</module>
         <module>sentinel-apache-dubbo-adapter</module>
+        <module>sentinel-apache-httpclient-adapter</module>
+        <module>sentinel-sofa-rpc-adapter</module>
         <module>sentinel-grpc-adapter</module>
         <module>sentinel-grpc-adapter</module>
         <module>sentinel-zuul-adapter</module>
         <module>sentinel-zuul-adapter</module>
         <module>sentinel-reactor-adapter</module>
         <module>sentinel-reactor-adapter</module>
         <module>sentinel-spring-webflux-adapter</module>
         <module>sentinel-spring-webflux-adapter</module>
         <module>sentinel-api-gateway-adapter-common</module>
         <module>sentinel-api-gateway-adapter-common</module>
         <module>sentinel-spring-cloud-gateway-adapter</module>
         <module>sentinel-spring-cloud-gateway-adapter</module>
+        <module>sentinel-spring-webmvc-adapter</module>
+        <module>sentinel-zuul2-adapter</module>
+        <module>sentinel-okhttp-adapter</module>
+        <module>sentinel-jax-rs-adapter</module>
+        <module>sentinel-quarkus-adapter</module>
+        <module>sentinel-motan-adapter</module>
     </modules>
     </modules>
 
 
     <dependencyManagement>
     <dependencyManagement>

+ 14 - 10
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)。
 > 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 Sentinel filters are **enabled by default**. Once you add the dependency,
 the Dubbo services and methods will become protected resources in Sentinel,
 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.
 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:
 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.
 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
 ## 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.
 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,
 > 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
 ## Global fallback
 
 
 Sentinel Dubbo Adapter supports global fallback configuration.
 Sentinel Dubbo Adapter supports global fallback configuration.
 The global fallback will handle exceptions and give replacement result when blocked by
 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
 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.
 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.

+ 2 - 2
sentinel-adapter/sentinel-apache-dubbo-adapter/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>sentinel-adapter</artifactId>
         <artifactId>sentinel-adapter</artifactId>
         <groupId>com.alibaba.csp</groupId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 
@@ -15,7 +15,7 @@
     <properties>
     <properties>
         <java.source.version>1.8</java.source.version>
         <java.source.version>1.8</java.source.version>
         <java.target.version>1.8</java.target.version>
         <java.target.version>1.8</java.target.version>
-        <apache.dubbo.version>2.7.1</apache.dubbo.version>
+        <apache.dubbo.version>2.7.13</apache.dubbo.version>
     </properties>
     </properties>
 
 
     <dependencies>
     <dependencies>

+ 5 - 3
sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAppContextFilter.java

@@ -15,7 +15,7 @@
  */
  */
 package com.alibaba.csp.sentinel.adapter.dubbo;
 package com.alibaba.csp.sentinel.adapter.dubbo;
 
 
-import org.apache.dubbo.common.Constants;
+import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invocation;
@@ -24,17 +24,19 @@ import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcException;
 
 
+import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
+
 /**
 /**
  * Puts current consumer's application name in the attachment of each invocation.
  * Puts current consumer's application name in the attachment of each invocation.
  *
  *
  * @author Eric Zhao
  * @author Eric Zhao
  */
  */
-@Activate(group = "consumer")
+@Activate(group = CONSUMER)
 public class DubboAppContextFilter implements Filter {
 public class DubboAppContextFilter implements Filter {
 
 
     @Override
     @Override
     public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
     public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
-        String application = invoker.getUrl().getParameter(Constants.APPLICATION_KEY);
+        String application = invoker.getUrl().getParameter(CommonConstants.APPLICATION_KEY);
         if (application != null) {
         if (application != null) {
             RpcContext.getContext().setAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, application);
             RpcContext.getContext().setAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, application);
         }
         }

+ 44 - 3
sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtils.java

@@ -15,6 +15,8 @@
  */
  */
 package com.alibaba.csp.sentinel.adapter.dubbo;
 package com.alibaba.csp.sentinel.adapter.dubbo;
 
 
+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.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Invoker;
 
 
@@ -32,9 +34,14 @@ public final class DubboUtils {
         return invocation.getAttachment(SENTINEL_DUBBO_APPLICATION_KEY, defaultValue);
         return invocation.getAttachment(SENTINEL_DUBBO_APPLICATION_KEY, defaultValue);
     }
     }
 
 
-    public static String getResourceName(Invoker<?> invoker, Invocation invocation) {
+    public static String getMethodResourceName(Invoker<?> invoker, Invocation invocation){
+        return getMethodResourceName(invoker, invocation, false);
+    }
+
+    public static String getMethodResourceName(Invoker<?> invoker, Invocation invocation, Boolean useGroupAndVersion) {
         StringBuilder buf = new StringBuilder(64);
         StringBuilder buf = new StringBuilder(64);
-        buf.append(invoker.getInterface().getName())
+        String interfaceResource = useGroupAndVersion ? invoker.getUrl().getColonSeparatedKey() : invoker.getInterface().getName();
+        buf.append(interfaceResource)
             .append(":")
             .append(":")
             .append(invocation.getMethodName())
             .append(invocation.getMethodName())
             .append("(");
             .append("(");
@@ -50,5 +57,39 @@ public final class DubboUtils {
         return buf.toString();
         return buf.toString();
     }
     }
 
 
-    private DubboUtils() {}
+    public static String getMethodResourceName(Invoker<?> invoker, Invocation invocation, String prefix) {
+        if (StringUtil.isNotBlank(prefix)) {
+            return new StringBuilder(64)
+                    .append(prefix)
+                    .append(getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboInterfaceGroupAndVersionEnabled()))
+                    .toString();
+        } else {
+            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() {
+    }
 }
 }

+ 94 - 23
sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java

@@ -15,24 +15,24 @@
  */
  */
 package com.alibaba.csp.sentinel.adapter.dubbo;
 package com.alibaba.csp.sentinel.adapter.dubbo;
 
 
-import com.alibaba.csp.sentinel.Entry;
-import com.alibaba.csp.sentinel.EntryType;
-import com.alibaba.csp.sentinel.SphU;
-import com.alibaba.csp.sentinel.Tracer;
+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.log.RecordLog;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 
 
-import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.rpc.Filter;
-import org.apache.dubbo.rpc.Invocation;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.Result;
-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;
 
 
 /**
 /**
  * <p>Dubbo service consumer filter for Sentinel. Auto activated by default.</p>
  * <p>Dubbo service consumer filter for Sentinel. Auto activated by default.</p>
- *
+ * <p>
  * If you want to disable the consumer filter, you can configure:
  * If you want to disable the consumer filter, you can configure:
  * <pre>
  * <pre>
  * &lt;dubbo:consumer filter="-sentinel.dubbo.consumer.filter"/&gt;
  * &lt;dubbo:consumer filter="-sentinel.dubbo.consumer.filter"/&gt;
@@ -40,44 +40,115 @@ import org.apache.dubbo.rpc.RpcException;
  *
  *
  * @author Carpenter Lee
  * @author Carpenter Lee
  * @author Eric Zhao
  * @author Eric Zhao
+ * @author Lin Liang
  */
  */
-@Activate(group = "consumer")
-public class SentinelDubboConsumerFilter implements Filter {
+@Activate(group = CONSUMER)
+public class SentinelDubboConsumerFilter extends BaseSentinelDubboFilter {
 
 
     public SentinelDubboConsumerFilter() {
     public SentinelDubboConsumerFilter() {
         RecordLog.info("Sentinel Apache Dubbo consumer filter initialized");
         RecordLog.info("Sentinel Apache Dubbo consumer filter initialized");
     }
     }
 
 
     @Override
     @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 {
     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 interfaceEntry = null;
         Entry methodEntry = null;
         Entry methodEntry = null;
+        String prefix = DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey();
+        String interfaceResourceName = getInterfaceName(invoker, prefix);
+        String methodResourceName = getMethodName(invoker, invocation, prefix);
         try {
         try {
-            String resourceName = DubboUtils.getResourceName(invoker, invocation);
-            interfaceEntry = SphU.entry(invoker.getInterface().getName(), EntryType.OUT);
-            methodEntry = SphU.entry(resourceName, EntryType.OUT);
-
+            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);
             Result result = invoker.invoke(invocation);
             if (result.hasException()) {
             if (result.hasException()) {
-                Throwable e = result.getException();
-                // Record common exception.
-                Tracer.traceEntry(e, interfaceEntry);
-                Tracer.traceEntry(e, methodEntry);
+                Tracer.traceEntry(result.getException(), interfaceEntry);
+                Tracer.traceEntry(result.getException(), methodEntry);
             }
             }
             return result;
             return result;
         } catch (BlockException e) {
         } catch (BlockException e) {
-            return DubboFallbackRegistry.getConsumerFallback().handle(invoker, invocation, e);
+            return DubboAdapterGlobalConfig.getConsumerFallback().handle(invoker, invocation, e);
         } catch (RpcException e) {
         } catch (RpcException e) {
             Tracer.traceEntry(e, interfaceEntry);
             Tracer.traceEntry(e, interfaceEntry);
             Tracer.traceEntry(e, methodEntry);
             Tracer.traceEntry(e, methodEntry);
             throw e;
             throw e;
         } finally {
         } finally {
             if (methodEntry != null) {
             if (methodEntry != null) {
-                methodEntry.exit();
+                methodEntry.exit(1, invocation.getArguments());
             }
             }
             if (interfaceEntry != null) {
             if (interfaceEntry != null) {
                 interfaceEntry.exit();
                 interfaceEntry.exit();
             }
             }
         }
         }
     }
     }
+
+    private Result asyncInvoke(Invoker<?> invoker, Invocation invocation) {
+        LinkedList<EntryHolder> 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) {
+            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();
+        }
+    }
 }
 }

+ 33 - 22
sentinel-adapter/sentinel-apache-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java

@@ -15,26 +15,24 @@
  */
  */
 package com.alibaba.csp.sentinel.adapter.dubbo;
 package com.alibaba.csp.sentinel.adapter.dubbo;
 
 
-import com.alibaba.csp.sentinel.Entry;
-import com.alibaba.csp.sentinel.EntryType;
-import com.alibaba.csp.sentinel.SphU;
-import com.alibaba.csp.sentinel.Tracer;
-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.context.ContextUtil;
 import com.alibaba.csp.sentinel.log.RecordLog;
 import com.alibaba.csp.sentinel.log.RecordLog;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 
 
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcException;
 
 
+import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
+
 /**
 /**
  * <p>Apache Dubbo service provider filter that enables integration with Sentinel. Auto activated by default.</p>
  * <p>Apache Dubbo service provider filter that enables integration with Sentinel. Auto activated by default.</p>
  * <p>Note: this only works for Apache Dubbo 2.7.x or above version.</p>
  * <p>Note: this only works for Apache Dubbo 2.7.x or above version.</p>
- *
+ * <p>
  * If you want to disable the provider filter, you can configure:
  * If you want to disable the provider filter, you can configure:
  * <pre>
  * <pre>
  * &lt;dubbo:provider filter="-sentinel.dubbo.provider.filter"/&gt;
  * &lt;dubbo:provider filter="-sentinel.dubbo.provider.filter"/&gt;
@@ -43,39 +41,50 @@ import org.apache.dubbo.rpc.RpcException;
  * @author Carpenter Lee
  * @author Carpenter Lee
  * @author Eric Zhao
  * @author Eric Zhao
  */
  */
-@Activate(group = "provider")
-public class SentinelDubboProviderFilter implements Filter {
+@Activate(group = PROVIDER)
+public class SentinelDubboProviderFilter extends BaseSentinelDubboFilter {
 
 
     public SentinelDubboProviderFilter() {
     public SentinelDubboProviderFilter() {
         RecordLog.info("Sentinel Apache Dubbo provider filter initialized");
         RecordLog.info("Sentinel Apache Dubbo provider filter initialized");
     }
     }
 
 
     @Override
     @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 {
     public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
         // Get origin caller.
         // Get origin caller.
-        String application = DubboUtils.getApplication(invocation, "");
-
+        String origin = DubboAdapterGlobalConfig.getOriginParser().parse(invoker, invocation);
+        if (null == origin) {
+            origin = "";
+        }
         Entry interfaceEntry = null;
         Entry interfaceEntry = null;
         Entry methodEntry = null;
         Entry methodEntry = null;
+        String prefix = DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey();
+        String interfaceResourceName = getInterfaceName(invoker, prefix);
+        String methodResourceName = getMethodName(invoker, invocation, prefix);
         try {
         try {
-            String resourceName = DubboUtils.getResourceName(invoker, invocation);
-            String interfaceName = invoker.getInterface().getName();
             // Only need to create entrance context at provider side, as context will take effect
             // Only need to create entrance context at provider side, as context will take effect
             // at entrance of invocation chain only (for inbound traffic).
             // at entrance of invocation chain only (for inbound traffic).
-            ContextUtil.enter(resourceName, application);
-            interfaceEntry = SphU.entry(interfaceName, EntryType.IN);
-            methodEntry = SphU.entry(resourceName, EntryType.IN, 1, invocation.getArguments());
-
+            ContextUtil.enter(methodResourceName, origin);
+            interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN);
+            methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN,
+                invocation.getArguments());
             Result result = invoker.invoke(invocation);
             Result result = invoker.invoke(invocation);
             if (result.hasException()) {
             if (result.hasException()) {
-                Throwable e = result.getException();
-                // Record common exception.
-                Tracer.traceEntry(e, interfaceEntry);
-                Tracer.traceEntry(e, methodEntry);
+                Tracer.traceEntry(result.getException(), interfaceEntry);
+                Tracer.traceEntry(result.getException(), methodEntry);
             }
             }
             return result;
             return result;
         } catch (BlockException e) {
         } catch (BlockException e) {
-            return DubboFallbackRegistry.getProviderFallback().handle(invoker, invocation, e);
+            return DubboAdapterGlobalConfig.getProviderFallback().handle(invoker, invocation, e);
         } catch (RpcException e) {
         } catch (RpcException e) {
             Tracer.traceEntry(e, interfaceEntry);
             Tracer.traceEntry(e, interfaceEntry);
             Tracer.traceEntry(e, methodEntry);
             Tracer.traceEntry(e, methodEntry);
@@ -90,4 +99,6 @@ public class SentinelDubboProviderFilter implements Filter {
             ContextUtil.exit();
             ContextUtil.exit();
         }
         }
     }
     }
+
 }
 }
+

+ 3 - 3
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;
 package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
 
 
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 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.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.Result;
@@ -29,7 +29,7 @@ public class DefaultDubboFallback implements DubboFallback {
 
 
     @Override
     @Override
     public Result handle(Invoker<?> invoker, Invocation invocation, BlockException ex) {
     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);
     }
     }
 }
 }

+ 7 - 15
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;
 package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
 
 
-import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
 
 
 /**
 /**
  * <p>Global fallback registry for Dubbo.</p>
  * <p>Global fallback registry for Dubbo.</p>
  *
  *
- * <p>
- * Note: Circuit breaking is mainly designed for consumer. The provider should not
- * give fallback result in most circumstances.
- * </p>
- *
  * @author Eric Zhao
  * @author Eric Zhao
+ * @deprecated use {@link DubboAdapterGlobalConfig} instead since 1.8.0.
  */
  */
+@Deprecated
 public final class DubboFallbackRegistry {
 public final class DubboFallbackRegistry {
 
 
-    private static volatile DubboFallback consumerFallback = new DefaultDubboFallback();
-    private static volatile DubboFallback providerFallback = new DefaultDubboFallback();
-
     public static DubboFallback getConsumerFallback() {
     public static DubboFallback getConsumerFallback() {
-        return consumerFallback;
+        return DubboAdapterGlobalConfig.getConsumerFallback();
     }
     }
 
 
     public static void setConsumerFallback(DubboFallback consumerFallback) {
     public static void setConsumerFallback(DubboFallback consumerFallback) {
-        AssertUtil.notNull(consumerFallback, "consumerFallback cannot be null");
-        DubboFallbackRegistry.consumerFallback = consumerFallback;
+        DubboAdapterGlobalConfig.setConsumerFallback(consumerFallback);
     }
     }
 
 
     public static DubboFallback getProviderFallback() {
     public static DubboFallback getProviderFallback() {
-        return providerFallback;
+        return DubboAdapterGlobalConfig.getProviderFallback();
     }
     }
 
 
     public static void setProviderFallback(DubboFallback providerFallback) {
     public static void setProviderFallback(DubboFallback providerFallback) {
-        AssertUtil.notNull(providerFallback, "providerFallback cannot be null");
-        DubboFallbackRegistry.providerFallback = providerFallback;
+        DubboAdapterGlobalConfig.setProviderFallback(providerFallback);
     }
     }
 
 
     private DubboFallbackRegistry() {}
     private DubboFallbackRegistry() {}

+ 39 - 4
sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/BaseTest.java

@@ -15,26 +15,61 @@
  */
  */
 package com.alibaba.csp.sentinel;
 package com.alibaba.csp.sentinel;
 
 
+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 com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
-
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcContext;
 
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
 /**
 /**
  * Base test class, provide common methods for subClass
  * Base test class, provide common methods for subClass
  * The package is same as CtSph, to call CtSph.resetChainMap() method for test
  * The package is same as CtSph, to call CtSph.resetChainMap() method for test
- *
+ * <p>
  * Note: Only for test. DO NOT USE IN PRODUCTION!
  * Note: Only for test. DO NOT USE IN PRODUCTION!
  *
  *
  * @author cdfive
  * @author cdfive
+ * @author lianglin
  */
  */
 public class BaseTest {
 public class BaseTest {
 
 
+
     /**
     /**
      * Clean up resources for context, clusterNodeMap, processorSlotChainMap
      * Clean up resources for context, clusterNodeMap, processorSlotChainMap
      */
      */
-    protected static void cleanUpAll() {
-        RpcContext.removeContext();
+    public void cleanUpAll() {
+        try {
+            clearDubboContext();
+            cleanUpCstContext();
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void cleanUpCstContext() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
         ClusterBuilderSlot.getClusterNodeMap().clear();
         ClusterBuilderSlot.getClusterNodeMap().clear();
         CtSph.resetChainMap();
         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();
+
     }
     }
 }
 }

+ 1 - 1
sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAppContextFilterTest.java

@@ -70,6 +70,6 @@ public class DubboAppContextFilterTest extends BaseTest {
         verify(invoker).invoke(invocation);
         verify(invoker).invoke(invocation);
 
 
         String application = RpcContext.getContext().getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY);
         String application = RpcContext.getContext().getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY);
-        assertNull(application);
+        assertEquals(application, "");
     }
     }
 }
 }

+ 140 - 8
sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtilsTest.java

@@ -15,15 +15,21 @@
  */
  */
 package com.alibaba.csp.sentinel.adapter.dubbo;
 package com.alibaba.csp.sentinel.adapter.dubbo;
 
 
+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;
+import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Invoker;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.Test;
 
 
 import java.lang.reflect.Method;
 import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.HashMap;
 
 
-import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
-
+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.assertEquals;
 import static org.junit.Assert.fail;
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.*;
 import static org.mockito.Mockito.*;
@@ -33,12 +39,30 @@ import static org.mockito.Mockito.*;
  */
  */
 public class DubboUtilsTest {
 public class DubboUtilsTest {
 
 
+    @Before
+    public void setUp() {
+        SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "true");
+        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(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "");
+        SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "");
+        SentinelConfig.setConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
+    }
+
+
     @Test
     @Test
     public void testGetApplication() {
     public void testGetApplication() {
         Invocation invocation = mock(Invocation.class);
         Invocation invocation = mock(Invocation.class);
         when(invocation.getAttachments()).thenReturn(new HashMap<>());
         when(invocation.getAttachments()).thenReturn(new HashMap<>());
         when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, ""))
         when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, ""))
-            .thenReturn("consumerA");
+                .thenReturn("consumerA");
 
 
         String application = DubboUtils.getApplication(invocation, "");
         String application = DubboUtils.getApplication(invocation, "");
         verify(invocation).getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, "");
         verify(invocation).getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, "");
@@ -51,7 +75,7 @@ public class DubboUtilsTest {
         Invocation invocation = mock(Invocation.class);
         Invocation invocation = mock(Invocation.class);
         when(invocation.getAttachments()).thenReturn(null);
         when(invocation.getAttachments()).thenReturn(null);
         when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, ""))
         when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, ""))
-            .thenReturn("consumerA");
+                .thenReturn("consumerA");
 
 
         DubboUtils.getApplication(invocation, "");
         DubboUtils.getApplication(invocation, "");
 
 
@@ -59,17 +83,125 @@ public class DubboUtilsTest {
     }
     }
 
 
     @Test
     @Test
-    public void testGetResourceName() {
+    public void testGetResourceName() throws NoSuchMethodException {
         Invoker invoker = mock(Invoker.class);
         Invoker invoker = mock(Invoker.class);
         when(invoker.getInterface()).thenReturn(DemoService.class);
         when(invoker.getInterface()).thenReturn(DemoService.class);
 
 
         Invocation invocation = mock(Invocation.class);
         Invocation invocation = mock(Invocation.class);
-        Method method = DemoService.class.getMethods()[0];
+        Method method = DemoService.class.getDeclaredMethod("sayHello", String.class, int.class);
         when(invocation.getMethodName()).thenReturn(method.getName());
         when(invocation.getMethodName()).thenReturn(method.getName());
         when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
         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);
         assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
+
+    }
+
+    @Test
+    public void testGetResourceNameWithGroupAndVersion() throws NoSuchMethodException {
+        Invoker 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 invocation = mock(Invocation.class);
+        Method method = DemoService.class.getDeclaredMethod("sayHello", String.class, int.class);
+        when(invocation.getMethodName()).thenReturn(method.getName());
+        when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
+
+        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);
+    }
+
+
+    @Test
+    public void testGetResourceNameWithPrefix() throws NoSuchMethodException {
+        Invoker invoker = mock(Invoker.class);
+        when(invoker.getInterface()).thenReturn(DemoService.class);
+
+        Invocation invocation = mock(Invocation.class);
+        Method method = DemoService.class.getDeclaredMethod("sayHello", String.class, int.class);
+        when(invocation.getMethodName()).thenReturn(method.getName());
+        when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
+
+        //test with default prefix
+        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.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(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.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);
+
     }
     }
-}
+}

+ 282 - 30
sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java

@@ -16,10 +16,12 @@
 package com.alibaba.csp.sentinel.adapter.dubbo;
 package com.alibaba.csp.sentinel.adapter.dubbo;
 
 
 import com.alibaba.csp.sentinel.BaseTest;
 import com.alibaba.csp.sentinel.BaseTest;
-import com.alibaba.csp.sentinel.Constants;
+import com.alibaba.csp.sentinel.DubboTestUtil;
 import com.alibaba.csp.sentinel.Entry;
 import com.alibaba.csp.sentinel.Entry;
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.EntryType;
-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.DubboFallback;
+import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
 import com.alibaba.csp.sentinel.context.Context;
 import com.alibaba.csp.sentinel.context.Context;
 import com.alibaba.csp.sentinel.context.ContextUtil;
 import com.alibaba.csp.sentinel.context.ContextUtil;
 import com.alibaba.csp.sentinel.node.ClusterNode;
 import com.alibaba.csp.sentinel.node.ClusterNode;
@@ -27,56 +29,187 @@ import com.alibaba.csp.sentinel.node.DefaultNode;
 import com.alibaba.csp.sentinel.node.Node;
 import com.alibaba.csp.sentinel.node.Node;
 import com.alibaba.csp.sentinel.node.StatisticNode;
 import com.alibaba.csp.sentinel.node.StatisticNode;
 import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
 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.DegradeRule;
+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.Invocation;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.*;
+import org.apache.dubbo.rpc.support.RpcUtils;
 import org.junit.After;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.Test;
 
 
-import java.lang.reflect.Method;
-import java.util.Map;
-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.*;
 import static org.junit.Assert.*;
 import static org.mockito.Mockito.*;
 import static org.mockito.Mockito.*;
 
 
 /**
 /**
  * @author cdfive
  * @author cdfive
+ * @author lianglin
  */
  */
 public class SentinelDubboConsumerFilterTest extends BaseTest {
 public class SentinelDubboConsumerFilterTest extends BaseTest {
 
 
-    private SentinelDubboConsumerFilter filter = new SentinelDubboConsumerFilter();
+    private final SentinelDubboConsumerFilter consumerFilter = new SentinelDubboConsumerFilter();
 
 
     @Before
     @Before
     public void setUp() {
     public void setUp() {
         cleanUpAll();
         cleanUpAll();
+        initFallback();
     }
     }
 
 
     @After
     @After
-    public void cleanUp() {
+    public void destroy() {
         cleanUpAll();
         cleanUpAll();
     }
     }
 
 
     @Test
     @Test
-    public void testInvoke() {
-        final Invoker invoker = mock(Invoker.class);
-        when(invoker.getInterface()).thenReturn(DemoService.class);
+    public void testInterfaceLevelFollowControlAsync() throws InterruptedException {
 
 
-        final Invocation invocation = mock(Invocation.class);
-        Method method = DemoService.class.getMethods()[0];
-        when(invocation.getMethodName()).thenReturn(method.getName());
-        when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
+        Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
+        Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
+
+        when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
+        initFlowRule(DubboUtils.getInterfaceName(invoker));
+
+        Result result1 = invokeDubboRpc(false, invoker, invocation);
+        assertEquals("normal", result1.getValue());
+
+        // should fallback because the qps > 1
+        Result result2 = invokeDubboRpc(false, invoker, invocation);
+        assertEquals("fallback", result2.getValue());
+
+        // sleeping 1000 ms to reset qps
+        Thread.sleep(1000);
+        Result result3 = invokeDubboRpc(false, invoker, invocation);
+        assertEquals("normal", result3.getValue());
+
+        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));
+
+        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++) {
+            invokeDubboRpc(true, invoker, invocation);
+            verifyInvocationStructureForCallFinish(invoker, invocation);
+        }
+
+        Result result2 = invokeDubboRpc(false, invoker, invocation);
+        assertEquals("fallback", result2.getValue());
+
+        // sleeping 1000 ms to reset exception
+        Thread.sleep(1000);
+        Result result3 = invokeDubboRpc(false, invoker, invocation);
+        assertEquals("normal", result3.getValue());
+
+        Context context = ContextUtil.getContext();
+        assertNull(context);
+    }
+
+    @Test
+    public void testDegradeSync() throws InterruptedException {
+
+        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++) {
+            invokeDubboRpc(true, invoker, invocation);
+            verifyInvocationStructureForCallFinish(invoker, invocation);
+        }
+
+        Result result2 = invokeDubboRpc(false, invoker, invocation);
+        assertEquals("fallback", result2.getValue());
+
+        // sleeping 1000 ms to reset exception
+        Thread.sleep(1000);
+        Result result3 = invokeDubboRpc(false, invoker, invocation);
+        assertEquals("normal", result3.getValue());
+
+        Context context = ContextUtil.getContext();
+        assertNull(context);
+    }
+
+    @Test
+    public void testMethodFlowControlAsync() {
+
+        Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
+        Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
+
+        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 = invokeDubboRpc(false, invoker, invocation);
+        assertEquals("fallback", fallback.getValue());
+        verifyInvocationStructureForCallFinish(invoker, invocation);
+
+    }
+
+    @Test
+    public void testInvokeAsync() {
+
+        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;
+        });
+        consumerFilter.invoke(invoker, invocation);
+        verify(invoker).invoke(invocation);
+
+        Context context = ContextUtil.getContext();
+        assertNotNull(context);
+    }
+
+    @Test
+    public void testInvokeSync() {
+
+        Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
+        Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
 
 
         final Result result = mock(Result.class);
         final Result result = mock(Result.class);
         when(result.hasException()).thenReturn(false);
         when(result.hasException()).thenReturn(false);
+        when(result.getException()).thenReturn(new Exception());
         when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> {
         when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> {
             verifyInvocationStructure(invoker, invocation);
             verifyInvocationStructure(invoker, invocation);
             return result;
             return result;
         });
         });
 
 
-        filter.invoke(invoker, invocation);
+        consumerFilter.invoke(invoker, invocation);
         verify(invoker).invoke(invocation);
         verify(invoker).invoke(invocation);
 
 
         Context context = ContextUtil.getContext();
         Context context = ContextUtil.getContext();
@@ -92,34 +225,36 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
     private void verifyInvocationStructure(Invoker invoker, Invocation invocation) {
     private void verifyInvocationStructure(Invoker invoker, Invocation invocation) {
         Context context = ContextUtil.getContext();
         Context context = ContextUtil.getContext();
         assertNotNull(context);
         assertNotNull(context);
-
         // As not call ContextUtil.enter(resourceName, application) in SentinelDubboConsumerFilter, use default 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
         // If consumer is on the top of Dubbo RPC invocation chain, use default context
-        String resourceName = DubboUtils.getResourceName(invoker, invocation);
-        assertEquals(Constants.CONTEXT_DEFAULT_NAME, context.getName());
+        String resourceName = consumerFilter.getMethodName(invoker, invocation, null);
+        assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, context.getName());
         assertEquals("", context.getOrigin());
         assertEquals("", context.getOrigin());
 
 
         DefaultNode entranceNode = context.getEntranceNode();
         DefaultNode entranceNode = context.getEntranceNode();
         ResourceWrapper entranceResource = entranceNode.getId();
         ResourceWrapper entranceResource = entranceNode.getId();
-        assertEquals(Constants.CONTEXT_DEFAULT_NAME, entranceResource.getName());
-        assertSame(EntryType.IN, entranceResource.getType());
+
+        assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, entranceResource.getName());
+        assertSame(EntryType.IN, entranceResource.getEntryType());
 
 
         // As SphU.entry(interfaceName, EntryType.OUT);
         // As SphU.entry(interfaceName, EntryType.OUT);
         Set<Node> childList = entranceNode.getChildList();
         Set<Node> childList = entranceNode.getChildList();
         assertEquals(1, childList.size());
         assertEquals(1, childList.size());
-        DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
+        DefaultNode interfaceNode = getNode(DubboUtils.getInterfaceName(invoker), entranceNode);
         ResourceWrapper interfaceResource = interfaceNode.getId();
         ResourceWrapper interfaceResource = interfaceNode.getId();
-        assertEquals(DemoService.class.getName(), interfaceResource.getName());
-        assertSame(EntryType.OUT, interfaceResource.getType());
+
+        assertEquals(DubboUtils.getInterfaceName(invoker), interfaceResource.getName());
+        assertSame(EntryType.OUT, interfaceResource.getEntryType());
 
 
         // As SphU.entry(resourceName, EntryType.OUT);
         // As SphU.entry(resourceName, EntryType.OUT);
         childList = interfaceNode.getChildList();
         childList = interfaceNode.getChildList();
         assertEquals(1, childList.size());
         assertEquals(1, childList.size());
-        DefaultNode methodNode = (DefaultNode) childList.iterator().next();
+        DefaultNode methodNode = getNode(resourceName, entranceNode);
         ResourceWrapper methodResource = methodNode.getId();
         ResourceWrapper methodResource = methodNode.getId();
         assertEquals(resourceName, methodResource.getName());
         assertEquals(resourceName, methodResource.getName());
-        assertSame(EntryType.OUT, methodResource.getType());
+        assertSame(EntryType.OUT, methodResource.getEntryType());
 
 
         // Verify curEntry
         // Verify curEntry
         Entry curEntry = context.getCurEntry();
         Entry curEntry = context.getCurEntry();
@@ -130,7 +265,60 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
         // Verify clusterNode
         // Verify clusterNode
         ClusterNode methodClusterNode = methodNode.getClusterNode();
         ClusterNode methodClusterNode = methodNode.getClusterNode();
         ClusterNode interfaceClusterNode = interfaceNode.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<String, StatisticNode> methodOriginCountMap = methodClusterNode.getOriginCountMap();
+        assertEquals(0, methodOriginCountMap.size());
+
+        Map<String, StatisticNode> interfaceOriginCountMap = interfaceClusterNode.getOriginCountMap();
+        assertEquals(0, interfaceOriginCountMap.size());
+    }
+
+    private void verifyInvocationStructureForAsyncCall(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
+        // If consumer is on the top of Dubbo RPC invocation chain, use default context
+        String resourceName = consumerFilter.getMethodName(invoker, invocation, null);
+        assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, context.getName());
+        assertEquals("", context.getOrigin());
+
+        DefaultNode entranceNode = context.getEntranceNode();
+        ResourceWrapper entranceResource = entranceNode.getId();
+        assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, entranceResource.getName());
+        assertSame(EntryType.IN, entranceResource.getEntryType());
+
+        // As SphU.entry(interfaceName, EntryType.OUT);
+        Set<Node> childList = entranceNode.getChildList();
+        assertEquals(2, childList.size());
+        DefaultNode interfaceNode = getNode(DubboUtils.getInterfaceName(invoker), entranceNode);
+        ResourceWrapper interfaceResource = interfaceNode.getId();
+        assertEquals(DubboUtils.getInterfaceName(invoker), interfaceResource.getName());
+        assertSame(EntryType.OUT, interfaceResource.getEntryType());
+
+        // As SphU.entry(resourceName, EntryType.OUT);
+        childList = interfaceNode.getChildList();
+        assertEquals(0, childList.size());
+        DefaultNode methodNode = getNode(resourceName, entranceNode);
+        ResourceWrapper methodResource = methodNode.getId();
+        assertEquals(resourceName, methodResource.getName());
+        assertSame(EntryType.OUT, methodResource.getEntryType());
+
+        // Verify curEntry
+        // nothing will bind to local context when use the AsyncEntry
+        Entry curEntry = context.getCurEntry();
+        assertNull(curEntry);
+
+        // Verify clusterNode
+        ClusterNode methodClusterNode = methodNode.getClusterNode();
+        ClusterNode interfaceClusterNode = interfaceNode.getClusterNode();
+        assertNotSame(methodClusterNode,
+            interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode
 
 
         // As context origin is "", the StatisticNode should not be created in originCountMap of ClusterNode
         // As context origin is "", the StatisticNode should not be created in originCountMap of ClusterNode
         Map<String, StatisticNode> methodOriginCountMap = methodClusterNode.getOriginCountMap();
         Map<String, StatisticNode> methodOriginCountMap = methodClusterNode.getOriginCountMap();
@@ -139,4 +327,68 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
         Map<String, StatisticNode> interfaceOriginCountMap = interfaceClusterNode.getOriginCountMap();
         Map<String, StatisticNode> interfaceOriginCountMap = interfaceClusterNode.getOriginCountMap();
         assertEquals(0, interfaceOriginCountMap.size());
         assertEquals(0, interfaceOriginCountMap.size());
     }
     }
+
+    private void verifyInvocationStructureForCallFinish(Invoker invoker, Invocation invocation) {
+        Context context = ContextUtil.getContext();
+        assertNull(context);
+        String methodResourceName = consumerFilter.getMethodName(invoker, invocation, null);
+        Entry[] entries = (Entry[]) RpcContext.getContext().get(methodResourceName);
+        assertNull(entries);
+    }
+
+    private DefaultNode getNode(String resourceName, DefaultNode root) {
+
+        Queue<DefaultNode> queue = new LinkedList<>();
+        queue.offer(root);
+        while (!queue.isEmpty()) {
+            DefaultNode temp = queue.poll();
+            if (temp.getId().getName().equals(resourceName)) {
+                return temp;
+            }
+            for (Node node : temp.getChildList()) {
+                queue.offer((DefaultNode) node);
+            }
+        }
+        return null;
+    }
+
+    private void initFlowRule(String resource) {
+        FlowRule flowRule = new FlowRule(resource);
+        flowRule.setCount(1);
+        flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
+        List<FlowRule> 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<DegradeRule> 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);
+    }
+
 }
 }

+ 28 - 20
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;
 package com.alibaba.csp.sentinel.adapter.dubbo;
 
 
 import com.alibaba.csp.sentinel.BaseTest;
 import com.alibaba.csp.sentinel.BaseTest;
+import com.alibaba.csp.sentinel.DubboTestUtil;
 import com.alibaba.csp.sentinel.Entry;
 import com.alibaba.csp.sentinel.Entry;
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
 import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
@@ -26,7 +27,8 @@ import com.alibaba.csp.sentinel.node.DefaultNode;
 import com.alibaba.csp.sentinel.node.Node;
 import com.alibaba.csp.sentinel.node.Node;
 import com.alibaba.csp.sentinel.node.StatisticNode;
 import com.alibaba.csp.sentinel.node.StatisticNode;
 import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
 import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
-
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.Result;
@@ -34,7 +36,6 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.Test;
 
 
-import java.lang.reflect.Method;
 import java.util.Map;
 import java.util.Map;
 import java.util.Set;
 import java.util.Set;
 
 
@@ -43,37 +44,41 @@ import static org.mockito.Mockito.*;
 
 
 /**
 /**
  * @author cdfive
  * @author cdfive
+ * @author lianglin
  */
  */
 public class SentinelDubboProviderFilterTest extends BaseTest {
 public class SentinelDubboProviderFilterTest extends BaseTest {
 
 
+
     private SentinelDubboProviderFilter filter = new SentinelDubboProviderFilter();
     private SentinelDubboProviderFilter filter = new SentinelDubboProviderFilter();
 
 
+
     @Before
     @Before
     public void setUp() {
     public void setUp() {
         cleanUpAll();
         cleanUpAll();
     }
     }
 
 
     @After
     @After
-    public void cleanUp() {
+    public void destroy() {
         cleanUpAll();
         cleanUpAll();
     }
     }
 
 
     @Test
     @Test
     public void testInvoke() {
     public void testInvoke() {
+
         final String originApplication = "consumerA";
         final String originApplication = "consumerA";
 
 
-        final Invoker invoker = mock(Invoker.class);
-        when(invoker.getInterface()).thenReturn(DemoService.class);
+        URL url = DubboTestUtil.getDefaultTestURL();
+        url = url.addParameter(CommonConstants.SIDE_KEY, CommonConstants.PROVIDER_SIDE);
+        Invoker invoker = DubboTestUtil.getMockInvoker(url, DemoService.class);
 
 
-        final Invocation invocation = mock(Invocation.class);
-        Method method = DemoService.class.getMethods()[0];
-        when(invocation.getMethodName()).thenReturn(method.getName());
-        when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
+        Invocation invocation = DubboTestUtil.getMockInvocation(DemoService.class.getMethods()[0]);
         when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, ""))
         when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, ""))
-            .thenReturn(originApplication);
+                .thenReturn(originApplication);
 
 
         final Result result = mock(Result.class);
         final Result result = mock(Result.class);
         when(result.hasException()).thenReturn(false);
         when(result.hasException()).thenReturn(false);
+        when(result.getException()).thenReturn(new Exception());
+
         when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> {
         when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> {
             verifyInvocationStructure(originApplication, invoker, invocation);
             verifyInvocationStructure(originApplication, invoker, invocation);
             return result;
             return result;
@@ -88,39 +93,40 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
 
 
     /**
     /**
      * Simply verify invocation structure in memory:
      * Simply verify invocation structure in memory:
-     * EntranceNode(resourceName)
+     * EntranceNode(methodResourceName)
      * --InterfaceNode(interfaceName)
      * --InterfaceNode(interfaceName)
-     * ----MethodNode(resourceName)
+     * ----MethodNode(methodResourceName)
      */
      */
     private void verifyInvocationStructure(String originApplication, Invoker invoker, Invocation invocation) {
     private void verifyInvocationStructure(String originApplication, Invoker invoker, Invocation invocation) {
         Context context = ContextUtil.getContext();
         Context context = ContextUtil.getContext();
         assertNotNull(context);
         assertNotNull(context);
 
 
         // As ContextUtil.enter(resourceName, application) in SentinelDubboProviderFilter
         // As ContextUtil.enter(resourceName, application) in SentinelDubboProviderFilter
-        String resourceName = DubboUtils.getResourceName(invoker, invocation);
-        assertEquals(resourceName, context.getName());
+        String methodResourceName = filter.getMethodName(invoker, invocation, null);
+        assertEquals(methodResourceName, context.getName());
         assertEquals(originApplication, context.getOrigin());
         assertEquals(originApplication, context.getOrigin());
 
 
         DefaultNode entranceNode = context.getEntranceNode();
         DefaultNode entranceNode = context.getEntranceNode();
         ResourceWrapper entranceResource = entranceNode.getId();
         ResourceWrapper entranceResource = entranceNode.getId();
-        assertEquals(resourceName, entranceResource.getName());
-        assertSame(EntryType.IN, entranceResource.getType());
+        assertEquals(methodResourceName, entranceResource.getName());
+        assertSame(EntryType.IN, entranceResource.getEntryType());
 
 
         // As SphU.entry(interfaceName, EntryType.IN);
         // As SphU.entry(interfaceName, EntryType.IN);
         Set<Node> childList = entranceNode.getChildList();
         Set<Node> childList = entranceNode.getChildList();
         assertEquals(1, childList.size());
         assertEquals(1, childList.size());
         DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
         DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
         ResourceWrapper interfaceResource = interfaceNode.getId();
         ResourceWrapper interfaceResource = interfaceNode.getId();
-        assertEquals(DemoService.class.getName(), interfaceResource.getName());
-        assertSame(EntryType.IN, interfaceResource.getType());
+
+        assertEquals(filter.getInterfaceName(invoker, null), interfaceResource.getName());
+        assertSame(EntryType.IN, interfaceResource.getEntryType());
 
 
         // As SphU.entry(resourceName, EntryType.IN, 1, invocation.getArguments());
         // As SphU.entry(resourceName, EntryType.IN, 1, invocation.getArguments());
         childList = interfaceNode.getChildList();
         childList = interfaceNode.getChildList();
         assertEquals(1, childList.size());
         assertEquals(1, childList.size());
         DefaultNode methodNode = (DefaultNode) childList.iterator().next();
         DefaultNode methodNode = (DefaultNode) childList.iterator().next();
         ResourceWrapper methodResource = methodNode.getId();
         ResourceWrapper methodResource = methodNode.getId();
-        assertEquals(resourceName, methodResource.getName());
-        assertSame(EntryType.IN, methodResource.getType());
+        assertEquals(methodResourceName, methodResource.getName());
+        assertSame(EntryType.IN, methodResource.getEntryType());
 
 
         // Verify curEntry
         // Verify curEntry
         Entry curEntry = context.getCurEntry();
         Entry curEntry = context.getCurEntry();
@@ -142,4 +148,6 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
         assertEquals(1, interfaceOriginCountMap.size());
         assertEquals(1, interfaceOriginCountMap.size());
         assertTrue(interfaceOriginCountMap.containsKey(originApplication));
         assertTrue(interfaceOriginCountMap.containsKey(originApplication));
     }
     }
+
+
 }
 }

+ 24 - 9
sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java

@@ -15,13 +15,15 @@
  */
  */
 package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
 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.BlockException;
-import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
 
 
+import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.Result;
-import org.apache.dubbo.rpc.RpcResult;
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.Test;
 
 
 /**
 /**
@@ -29,20 +31,33 @@ import org.junit.Test;
  */
  */
 public class DubboFallbackRegistryTest {
 public class DubboFallbackRegistryTest {
 
 
-    @Test(expected = SentinelRpcException.class)
+    @Before
+    public void setUp() {
+        DubboAdapterGlobalConfig.setConsumerFallback(new DefaultDubboFallback());
+    }
+
+    @After
+    public void tearDown() {
+        DubboAdapterGlobalConfig.setConsumerFallback(new DefaultDubboFallback());
+    }
+
+    @Test
     public void testDefaultFallback() {
     public void testDefaultFallback() {
-        // Test for default.
+        // Test for default fallback.
         BlockException ex = new FlowException("xxx");
         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
     @Test
     public void testCustomFallback() {
     public void testCustomFallback() {
         BlockException ex = new FlowException("xxx");
         BlockException ex = new FlowException("xxx");
-        DubboFallbackRegistry.setConsumerFallback(
-            (invoker, invocation, e) -> new RpcResult("Error: " + e.getClass().getName()));
-        Result result = DubboFallbackRegistry.getConsumerFallback()
+        DubboAdapterGlobalConfig.setConsumerFallback(
+            (invoker, invocation, e) -> AsyncRpcResult
+                .newDefaultAsyncResult("Error: " + e.getClass().getName(), invocation));
+        Result result = DubboAdapterGlobalConfig.getConsumerFallback()
             .handle(null, null, ex);
             .handle(null, null, ex);
         Assert.assertFalse("The invocation should not fail", result.hasException());
         Assert.assertFalse("The invocation should not fail", result.hasException());
         Assert.assertEquals("Error: " + ex.getClass().getName(), result.getValue());
         Assert.assertEquals("Error: " + ex.getClass().getName(), result.getValue());

+ 1 - 0
sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/DemoService.java

@@ -20,4 +20,5 @@ package com.alibaba.csp.sentinel.adapter.dubbo.provider;
  */
  */
 public interface DemoService {
 public interface DemoService {
     String sayHello(String name, int n);
     String sayHello(String name, int n);
+    String sayHi(String name,int n);
 }
 }

+ 5 - 0
sentinel-adapter/sentinel-apache-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/impl/DemoServiceImpl.java

@@ -24,4 +24,9 @@ public class DemoServiceImpl implements DemoService {
     public String sayHello(String name, int n) {
     public String sayHello(String name, int n) {
         return "Hello " + name + ", " + n;
         return "Hello " + name + ", " + n;
     }
     }
+
+    @Override
+    public String sayHi(String name, int n) {
+        return "Hi " + name + ", " + n;
+    }
 }
 }

+ 1 - 6
sentinel-adapter/sentinel-api-gateway-adapter-common/pom.xml

@@ -5,18 +5,13 @@
     <parent>
     <parent>
         <artifactId>sentinel-adapter</artifactId>
         <artifactId>sentinel-adapter</artifactId>
         <groupId>com.alibaba.csp</groupId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 
     <artifactId>sentinel-api-gateway-adapter-common</artifactId>
     <artifactId>sentinel-api-gateway-adapter-common</artifactId>
     <packaging>jar</packaging>
     <packaging>jar</packaging>
 
 
-    <properties>
-        <java.source.version>1.7</java.source.version>
-        <java.target.version>1.7</java.target.version>
-    </properties>
-
     <dependencies>
     <dependencies>
         <dependency>
         <dependency>
             <groupId>com.alibaba.csp</groupId>
             <groupId>com.alibaba.csp</groupId>

+ 6 - 6
sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/GatewayApiDefinitionManager.java

@@ -27,7 +27,7 @@ import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
 import com.alibaba.csp.sentinel.property.PropertyListener;
 import com.alibaba.csp.sentinel.property.PropertyListener;
 import com.alibaba.csp.sentinel.property.SentinelProperty;
 import com.alibaba.csp.sentinel.property.SentinelProperty;
 import com.alibaba.csp.sentinel.util.AssertUtil;
 import com.alibaba.csp.sentinel.util.AssertUtil;
-import com.alibaba.csp.sentinel.util.SpiLoader;
+import com.alibaba.csp.sentinel.spi.SpiLoader;
 import com.alibaba.csp.sentinel.util.StringUtil;
 import com.alibaba.csp.sentinel.util.StringUtil;
 
 
 /**
 /**
@@ -59,11 +59,11 @@ public final class GatewayApiDefinitionManager {
     }
     }
 
 
     private static void initializeApiChangeObserverSpi() {
     private static void initializeApiChangeObserverSpi() {
-        List<ApiDefinitionChangeObserver> listeners = SpiLoader.loadInstanceList(ApiDefinitionChangeObserver.class);
+        List<ApiDefinitionChangeObserver> listeners = SpiLoader.of(ApiDefinitionChangeObserver.class).loadInstanceList();
         for (ApiDefinitionChangeObserver e : listeners) {
         for (ApiDefinitionChangeObserver e : listeners) {
             API_CHANGE_OBSERVERS.put(e.getClass().getCanonicalName(), e);
             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 @@ public final class GatewayApiDefinitionManager {
         @Override
         @Override
         public void configUpdate(Set<ApiDefinition> set) {
         public void configUpdate(Set<ApiDefinition> set) {
             applyApiUpdateInternal(set);
             applyApiUpdateInternal(set);
-            RecordLog.info("[GatewayApiDefinitionManager] Api definition updated: " + API_MAP);
+            RecordLog.info("[GatewayApiDefinitionManager] Api definition updated: {}", API_MAP);
         }
         }
 
 
         @Override
         @Override
         public void configLoad(Set<ApiDefinition> set) {
         public void configLoad(Set<ApiDefinition> set) {
             applyApiUpdateInternal(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<ApiDefinition> set) {
         private static synchronized void applyApiUpdateInternal(Set<ApiDefinition> set) {

+ 71 - 7
sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayApiDefinitionGroupCommandHandler.java

@@ -15,19 +15,24 @@
  */
  */
 package com.alibaba.csp.sentinel.adapter.gateway.common.command;
 package com.alibaba.csp.sentinel.adapter.gateway.common.command;
 
 
-import java.net.URLDecoder;
-import java.util.HashSet;
-import java.util.List;
-
 import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
 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.GatewayApiDefinitionManager;
 import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
 import com.alibaba.csp.sentinel.command.CommandHandler;
 import com.alibaba.csp.sentinel.command.CommandHandler;
 import com.alibaba.csp.sentinel.command.CommandRequest;
 import com.alibaba.csp.sentinel.command.CommandRequest;
 import com.alibaba.csp.sentinel.command.CommandResponse;
 import com.alibaba.csp.sentinel.command.CommandResponse;
 import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
 import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
+import com.alibaba.csp.sentinel.datasource.WritableDataSource;
 import com.alibaba.csp.sentinel.log.RecordLog;
 import com.alibaba.csp.sentinel.log.RecordLog;
 import com.alibaba.csp.sentinel.util.StringUtil;
 import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+import java.net.URLDecoder;
+import java.util.HashSet;
+import java.util.Set;
 
 
 /**
 /**
  * @author Eric Zhao
  * @author Eric Zhao
@@ -36,6 +41,8 @@ import com.alibaba.fastjson.JSONArray;
 @CommandMapping(name = "gateway/updateApiDefinitions", desc = "")
 @CommandMapping(name = "gateway/updateApiDefinitions", desc = "")
 public class UpdateGatewayApiDefinitionGroupCommandHandler implements CommandHandler<String> {
 public class UpdateGatewayApiDefinitionGroupCommandHandler implements CommandHandler<String> {
 
 
+    private static WritableDataSource<Set<ApiDefinition>> apiDefinitionWds = null;
+
     @Override
     @Override
     public CommandResponse<String> handle(CommandRequest request) {
     public CommandResponse<String> handle(CommandRequest request) {
         String data = request.getParam("data");
         String data = request.getParam("data");
@@ -49,13 +56,70 @@ public class UpdateGatewayApiDefinitionGroupCommandHandler implements CommandHan
             return CommandResponse.ofFailure(e, "decode gateway API definition data error");
             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;
         String result = SUCCESS_MSG;
-        List<ApiDefinition> apiDefinitions = JSONArray.parseArray(data, ApiDefinition.class);
-        GatewayApiDefinitionManager.loadApiDefinitions(new HashSet<>(apiDefinitions));
+
+        Set<ApiDefinition> apiDefinitions = parseJson(data);
+        GatewayApiDefinitionManager.loadApiDefinitions(apiDefinitions);
+        if (!writeToDataSource(apiDefinitionWds, apiDefinitions)) {
+            result = WRITE_DS_FAILURE_MSG;
+        }
         return CommandResponse.ofSuccess(result);
         return CommandResponse.ofSuccess(result);
     }
     }
 
 
     private static final String SUCCESS_MSG = "success";
     private static final String SUCCESS_MSG = "success";
+    private static final String WRITE_DS_FAILURE_MSG = "partial success (write data source failed)";
+
+    /**
+     * Parse json data to set of {@link ApiDefinition}.
+     *
+     * Since the predicateItems of {@link ApiDefinition} is set of interface,
+     * here we parse predicateItems to {@link ApiPathPredicateItem} temporarily.
+     */
+    private Set<ApiDefinition> parseJson(String data) {
+        Set<ApiDefinition> apiDefinitions = new HashSet<>();
+        JSONArray array = JSON.parseArray(data);
+        for (Object obj : array) {
+            JSONObject o = (JSONObject)obj;
+            ApiDefinition apiDefinition = new ApiDefinition((o.getString("apiName")));
+            Set<ApiPredicateItem> predicateItems = new HashSet<>();
+            JSONArray itemArray = o.getJSONArray("predicateItems");
+            if (itemArray != null) {
+                predicateItems.addAll(itemArray.toJavaList(ApiPathPredicateItem.class));
+            }
+            apiDefinition.setPredicateItems(predicateItems);
+            apiDefinitions.add(apiDefinition);
+        }
+
+        return apiDefinitions;
+    }
+
+    /**
+     * Write target value to given data source.
+     *
+     * @param dataSource writable data source
+     * @param value target value to save
+     * @param <T> value type
+     * @return true if write successful or data source is empty; false if error occurs
+     */
+    private <T> boolean writeToDataSource(WritableDataSource<T> dataSource, T value) {
+        if (dataSource != null) {
+            try {
+                dataSource.write(value);
+            } catch (Exception e) {
+                RecordLog.warn("Write data source failed", e);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public synchronized static WritableDataSource<Set<ApiDefinition>> getWritableDataSource() {
+        return apiDefinitionWds;
+    }
+
+    public synchronized static void setWritableDataSource(WritableDataSource<Set<ApiDefinition>> apiDefinitionWds) {
+        UpdateGatewayApiDefinitionGroupCommandHandler.apiDefinitionWds = apiDefinitionWds;
+    }
 }
 }

+ 44 - 9
sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayRuleCommandHandler.java

@@ -15,19 +15,20 @@
  */
  */
 package com.alibaba.csp.sentinel.adapter.gateway.common.command;
 package com.alibaba.csp.sentinel.adapter.gateway.common.command;
 
 
-import java.net.URLDecoder;
-import java.util.HashSet;
-import java.util.List;
-
 import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
 import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
 import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
 import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
 import com.alibaba.csp.sentinel.command.CommandHandler;
 import com.alibaba.csp.sentinel.command.CommandHandler;
 import com.alibaba.csp.sentinel.command.CommandRequest;
 import com.alibaba.csp.sentinel.command.CommandRequest;
 import com.alibaba.csp.sentinel.command.CommandResponse;
 import com.alibaba.csp.sentinel.command.CommandResponse;
 import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
 import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
+import com.alibaba.csp.sentinel.datasource.WritableDataSource;
 import com.alibaba.csp.sentinel.log.RecordLog;
 import com.alibaba.csp.sentinel.log.RecordLog;
 import com.alibaba.csp.sentinel.util.StringUtil;
 import com.alibaba.csp.sentinel.util.StringUtil;
-import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.TypeReference;
+
+import java.net.URLDecoder;
+import java.util.Set;
 
 
 /**
 /**
  * @author Eric Zhao
  * @author Eric Zhao
@@ -35,6 +36,7 @@ import com.alibaba.fastjson.JSONArray;
  */
  */
 @CommandMapping(name = "gateway/updateRules", desc = "Update gateway rules")
 @CommandMapping(name = "gateway/updateRules", desc = "Update gateway rules")
 public class UpdateGatewayRuleCommandHandler implements CommandHandler<String> {
 public class UpdateGatewayRuleCommandHandler implements CommandHandler<String> {
+    private static WritableDataSource<Set<GatewayFlowRule>> gatewayFlowWds = null;
 
 
     @Override
     @Override
     public CommandResponse<String> handle(CommandRequest request) {
     public CommandResponse<String> handle(CommandRequest request) {
@@ -49,13 +51,46 @@ public class UpdateGatewayRuleCommandHandler implements CommandHandler<String> {
             return CommandResponse.ofFailure(e, "decode gateway rule data error");
             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;
         String result = SUCCESS_MSG;
-        List<GatewayFlowRule> flowRules = JSONArray.parseArray(data, GatewayFlowRule.class);
-        GatewayRuleManager.loadRules(new HashSet<>(flowRules));
+	    Set<GatewayFlowRule> flowRules = JSON.parseObject(data, new TypeReference<Set<GatewayFlowRule>>() {
+	    });
+        GatewayRuleManager.loadRules(flowRules);
+        if (!writeToDataSource(gatewayFlowWds, flowRules)) {
+            result = WRITE_DS_FAILURE_MSG;
+        }
         return CommandResponse.ofSuccess(result);
         return CommandResponse.ofSuccess(result);
     }
     }
 
 
+    /**
+     * Write target value to given data source.
+     *
+     * @param dataSource writable data source
+     * @param value target value to save
+     * @param <T> value type
+     * @return true if write successful or data source is empty; false if error occurs
+     */
+    private <T> boolean writeToDataSource(WritableDataSource<T> dataSource, T value) {
+        if (dataSource != null) {
+            try {
+                dataSource.write(value);
+            } catch (Exception e) {
+                RecordLog.warn("Write data source failed", e);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public synchronized static WritableDataSource<Set<GatewayFlowRule>> getWritableDataSource() {
+        return gatewayFlowWds;
+    }
+
+    public synchronized static void setWritableDataSource(WritableDataSource<Set<GatewayFlowRule>> gatewayFlowWds) {
+        UpdateGatewayRuleCommandHandler.gatewayFlowWds = gatewayFlowWds;
+    }
+
     private static final String SUCCESS_MSG = "success";
     private static final String SUCCESS_MSG = "success";
-}
+    private static final String WRITE_DS_FAILURE_MSG = "partial success (write data source failed)";
+}

+ 5 - 5
sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/GatewayParamParser.java

@@ -103,7 +103,7 @@ public class GatewayParamParser<T> {
     private String parseClientIp(/*@Valid*/ GatewayParamFlowItem item, T request) {
     private String parseClientIp(/*@Valid*/ GatewayParamFlowItem item, T request) {
         String clientIp = requestItemParser.getRemoteAddress(request);
         String clientIp = requestItemParser.getRemoteAddress(request);
         String pattern = item.getPattern();
         String pattern = item.getPattern();
-        if (pattern == null) {
+        if (StringUtil.isEmpty(pattern)) {
             return clientIp;
             return clientIp;
         }
         }
         return parseWithMatchStrategyInternal(item.getMatchStrategy(), clientIp, pattern);
         return parseWithMatchStrategyInternal(item.getMatchStrategy(), clientIp, pattern);
@@ -114,7 +114,7 @@ public class GatewayParamParser<T> {
         String pattern = item.getPattern();
         String pattern = item.getPattern();
         // TODO: what if the header has multiple values?
         // TODO: what if the header has multiple values?
         String headerValue = requestItemParser.getHeader(request, headerKey);
         String headerValue = requestItemParser.getHeader(request, headerKey);
-        if (pattern == null) {
+        if (StringUtil.isEmpty(pattern)) {
             return headerValue;
             return headerValue;
         }
         }
         // Match value according to regex pattern or exact mode.
         // Match value according to regex pattern or exact mode.
@@ -124,7 +124,7 @@ public class GatewayParamParser<T> {
     private String parseHost(/*@Valid*/ GatewayParamFlowItem item, T request) {
     private String parseHost(/*@Valid*/ GatewayParamFlowItem item, T request) {
         String pattern = item.getPattern();
         String pattern = item.getPattern();
         String host = requestItemParser.getHeader(request, "Host");
         String host = requestItemParser.getHeader(request, "Host");
-        if (pattern == null) {
+        if (StringUtil.isEmpty(pattern)) {
             return host;
             return host;
         }
         }
         // Match value according to regex pattern or exact mode.
         // Match value according to regex pattern or exact mode.
@@ -135,7 +135,7 @@ public class GatewayParamParser<T> {
         String paramName = item.getFieldName();
         String paramName = item.getFieldName();
         String pattern = item.getPattern();
         String pattern = item.getPattern();
         String param = requestItemParser.getUrlParam(request, paramName);
         String param = requestItemParser.getUrlParam(request, paramName);
-        if (pattern == null) {
+        if (StringUtil.isEmpty(pattern)) {
             return param;
             return param;
         }
         }
         // Match value according to regex pattern or exact mode.
         // Match value according to regex pattern or exact mode.
@@ -146,7 +146,7 @@ public class GatewayParamParser<T> {
         String cookieName = item.getFieldName();
         String cookieName = item.getFieldName();
         String pattern = item.getPattern();
         String pattern = item.getPattern();
         String param = requestItemParser.getCookieValue(request, cookieName);
         String param = requestItemParser.getCookieValue(request, cookieName);
-        if (pattern == null) {
+        if (StringUtil.isEmpty(pattern)) {
             return param;
             return param;
         }
         }
         // Match value according to regex pattern or exact mode.
         // Match value according to regex pattern or exact mode.

+ 59 - 54
sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleManager.java

@@ -15,15 +15,6 @@
  */
  */
 package com.alibaba.csp.sentinel.adapter.gateway.common.rule;
 package com.alibaba.csp.sentinel.adapter.gateway.common.rule;
 
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
 import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
 import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
 import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayRegexCache;
 import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayRegexCache;
 import com.alibaba.csp.sentinel.log.RecordLog;
 import com.alibaba.csp.sentinel.log.RecordLog;
@@ -33,10 +24,14 @@ import com.alibaba.csp.sentinel.property.SentinelProperty;
 import com.alibaba.csp.sentinel.slots.block.RuleConstant;
 import com.alibaba.csp.sentinel.slots.block.RuleConstant;
 import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
 import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
 import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleUtil;
 import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleUtil;
+import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetric;
 import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetricStorage;
 import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetricStorage;
 import com.alibaba.csp.sentinel.util.AssertUtil;
 import com.alibaba.csp.sentinel.util.AssertUtil;
 import com.alibaba.csp.sentinel.util.StringUtil;
 import com.alibaba.csp.sentinel.util.StringUtil;
 
 
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
 /**
 /**
  * @author Eric Zhao
  * @author Eric Zhao
  * @since 1.6.0
  * @since 1.6.0
@@ -51,12 +46,20 @@ public final class GatewayRuleManager {
     private static final Map<String, List<ParamFlowRule>> CONVERTED_PARAM_RULE_MAP = new ConcurrentHashMap<>();
     private static final Map<String, List<ParamFlowRule>> CONVERTED_PARAM_RULE_MAP = new ConcurrentHashMap<>();
 
 
     private static final GatewayRulePropertyListener LISTENER = new GatewayRulePropertyListener();
     private static final GatewayRulePropertyListener LISTENER = new GatewayRulePropertyListener();
+    private static final Set<Integer> 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<Set<GatewayFlowRule>> currentProperty = new DynamicSentinelProperty<>();
     private static SentinelProperty<Set<GatewayFlowRule>> currentProperty = new DynamicSentinelProperty<>();
 
 
     static {
     static {
         currentProperty.addListener(LISTENER);
         currentProperty.addListener(LISTENER);
     }
     }
 
 
+    private GatewayRuleManager() {
+    }
+
     public static void register2Property(SentinelProperty<Set<GatewayFlowRule>> property) {
     public static void register2Property(SentinelProperty<Set<GatewayFlowRule>> property) {
         AssertUtil.notNull(property, "property cannot be null");
         AssertUtil.notNull(property, "property cannot be null");
         synchronized (LISTENER) {
         synchronized (LISTENER) {
@@ -111,18 +114,48 @@ public final class GatewayRuleManager {
         return CONVERTED_PARAM_RULE_MAP.get(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<Set<GatewayFlowRule>> {
     private static final class GatewayRulePropertyListener implements PropertyListener<Set<GatewayFlowRule>> {
 
 
         @Override
         @Override
         public void configUpdate(Set<GatewayFlowRule> conf) {
         public void configUpdate(Set<GatewayFlowRule> conf) {
             applyGatewayRuleInternal(conf);
             applyGatewayRuleInternal(conf);
-            RecordLog.info("[GatewayRuleManager] Gateway flow rules received: " + GATEWAY_RULE_MAP);
+            RecordLog.info("[GatewayRuleManager] Gateway flow rules received: {}", GATEWAY_RULE_MAP);
         }
         }
 
 
         @Override
         @Override
         public void configLoad(Set<GatewayFlowRule> conf) {
         public void configLoad(Set<GatewayFlowRule> conf) {
             applyGatewayRuleInternal(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<String, Integer> idxMap, String resourceName) {
         private int getIdxInternal(Map<String, Integer> idxMap, String resourceName) {
@@ -136,7 +169,7 @@ public final class GatewayRuleManager {
         private void cacheRegexPattern(/*@NonNull*/ GatewayParamFlowItem item) {
         private void cacheRegexPattern(/*@NonNull*/ GatewayParamFlowItem item) {
             String pattern = item.getPattern();
             String pattern = item.getPattern();
             if (StringUtil.isNotEmpty(pattern) &&
             if (StringUtil.isNotEmpty(pattern) &&
-                item.getMatchStrategy() == SentinelGatewayConstants.PARAM_MATCH_STRATEGY_REGEX) {
+                    item.getMatchStrategy() == SentinelGatewayConstants.PARAM_MATCH_STRATEGY_REGEX) {
                 if (GatewayRegexCache.getRegexPattern(pattern) == null) {
                 if (GatewayRegexCache.getRegexPattern(pattern) == null) {
                     GatewayRegexCache.addRegexPattern(pattern);
                     GatewayRegexCache.addRegexPattern(pattern);
                 }
                 }
@@ -205,7 +238,7 @@ public final class GatewayRuleManager {
 
 
         private void applyToConvertedParamMap(Set<ParamFlowRule> paramFlowRules) {
         private void applyToConvertedParamMap(Set<ParamFlowRule> paramFlowRules) {
             Map<String, List<ParamFlowRule>> newRuleMap = ParamFlowRuleUtil.buildParamRuleMap(
             Map<String, List<ParamFlowRule>> newRuleMap = ParamFlowRuleUtil.buildParamRuleMap(
-                new ArrayList<>(paramFlowRules));
+                    new ArrayList<>(paramFlowRules));
             if (newRuleMap == null || newRuleMap.isEmpty()) {
             if (newRuleMap == null || newRuleMap.isEmpty()) {
                 // No parameter flow rules, so clear all the metrics.
                 // No parameter flow rules, so clear all the metrics.
                 for (String resource : CONVERTED_PARAM_RULE_MAP.keySet()) {
                 for (String resource : CONVERTED_PARAM_RULE_MAP.keySet()) {
@@ -217,10 +250,20 @@ public final class GatewayRuleManager {
             }
             }
 
 
             // Clear unused parameter metrics.
             // Clear unused parameter metrics.
-            Set<String> previousResources = CONVERTED_PARAM_RULE_MAP.keySet();
-            for (String resource : previousResources) {
+            for (Map.Entry<String, List<ParamFlowRule>> entry : CONVERTED_PARAM_RULE_MAP.entrySet()) {
+                String resource = entry.getKey();
                 if (!newRuleMap.containsKey(resource)) {
                 if (!newRuleMap.containsKey(resource)) {
                     ParameterMetricStorage.clearParamMetricForResource(resource);
                     ParameterMetricStorage.clearParamMetricForResource(resource);
+                    continue;
+                }
+                List<ParamFlowRule> newRuleList = newRuleMap.get(resource);
+                List<ParamFlowRule> 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 @@ public final class GatewayRuleManager {
             CONVERTED_PARAM_RULE_MAP.clear();
             CONVERTED_PARAM_RULE_MAP.clear();
             CONVERTED_PARAM_RULE_MAP.putAll(newRuleMap);
             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<Integer> 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() {}
 }
 }

+ 2 - 0
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.ParamFlowChecker;
 import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
 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.ParamFlowRule;
 import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetricStorage;
 import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetricStorage;
+import com.alibaba.csp.sentinel.spi.Spi;
 
 
 /**
 /**
  * @author Eric Zhao
  * @author Eric Zhao
  * @since 1.6.1
  * @since 1.6.1
  */
  */
+@Spi(order = -4000)
 public class GatewayFlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
 public class GatewayFlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
 
 
     @Override
     @Override

+ 9 - 33
sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewaySlotChainBuilder.java

@@ -15,43 +15,19 @@
  */
  */
 package com.alibaba.csp.sentinel.adapter.gateway.common.slot;
 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
  * @author Eric Zhao
  * @since 1.6.1
  * @since 1.6.1
+ *
+ * @deprecated since 1.7.2, we can use @Spi(order = -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;
-    }
 }
 }

+ 0 - 1
sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.slotchain.SlotChainBuilder

@@ -1 +0,0 @@
-com.alibaba.csp.sentinel.adapter.gateway.common.slot.GatewaySlotChainBuilder

+ 29 - 0
sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/GatewayParamParserTest.java

@@ -187,6 +187,35 @@ public class GatewayParamParserTest {
     }
     }
 
 
     @Test
     @Test
+    public void testParseParametersWithEmptyItemPattern() {
+        RequestItemParser<Object> itemParser = mock(RequestItemParser.class);
+        GatewayParamParser<Object> paramParser = new GatewayParamParser<>(itemParser);
+        // Create a fake request.
+        Object request = new Object();
+        // Prepare gateway rules.
+        Set<GatewayFlowRule> rules = new HashSet<>();
+        final String routeId = "my_test_route_DS(*H";
+        final String headerName = "X-Sentinel-Flag";
+        GatewayFlowRule routeRule1 = new GatewayFlowRule(routeId)
+            .setCount(10)
+            .setIntervalSec(2)
+            .setParamItem(new GatewayParamFlowItem()
+                .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
+                .setFieldName(headerName)
+                .setPattern("")
+                .setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_EXACT)
+            );
+        rules.add(routeRule1);
+        GatewayRuleManager.loadRules(rules);
+
+        mockSingleHeader(itemParser, headerName, "Sent1nel");
+        Object[] params = paramParser.parseParameterFor(routeId, request, routeIdPredicate);
+        assertThat(params.length).isEqualTo(1);
+        // Empty pattern should not take effect.
+        assertThat(params[routeRule1.getParamItem().getIndex()]).isEqualTo("Sent1nel");
+    }
+
+    @Test
     public void testParseParametersWithItemPatternMatching() {
     public void testParseParametersWithItemPatternMatching() {
         RequestItemParser<Object> itemParser = mock(RequestItemParser.class);
         RequestItemParser<Object> itemParser = mock(RequestItemParser.class);
         GatewayParamParser<Object> paramParser = new GatewayParamParser<>(itemParser);
         GatewayParamParser<Object> paramParser = new GatewayParamParser<>(itemParser);

+ 11 - 7
sentinel-adapter/sentinel-dubbo-adapter/README.md

@@ -1,6 +1,6 @@
 # Sentinel Dubbo Adapter
 # 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
 Sentinel Dubbo Adapter provides service consumer filter and provider filter
 for [Dubbo](https://dubbo.apache.org/en-us/) services.
 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.
 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,
 > 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
 ## Global fallback
 
 
 Sentinel Dubbo Adapter supports global fallback configuration.
 Sentinel Dubbo Adapter supports global fallback configuration.
 The global fallback will handle exceptions and give replacement result when blocked by
 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
 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.
 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.

+ 1 - 1
sentinel-adapter/sentinel-dubbo-adapter/pom.xml

@@ -6,7 +6,7 @@
     <parent>
     <parent>
         <groupId>com.alibaba.csp</groupId>
         <groupId>com.alibaba.csp</groupId>
         <artifactId>sentinel-adapter</artifactId>
         <artifactId>sentinel-adapter</artifactId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>sentinel-dubbo-adapter</artifactId>
     <artifactId>sentinel-dubbo-adapter</artifactId>

+ 23 - 1
sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/AbstractDubboFilter.java

@@ -15,6 +15,7 @@
  */
  */
 package com.alibaba.csp.sentinel.adapter.dubbo;
 package com.alibaba.csp.sentinel.adapter.dubbo;
 
 
+import com.alibaba.csp.sentinel.util.StringUtil;
 import com.alibaba.dubbo.rpc.Filter;
 import com.alibaba.dubbo.rpc.Filter;
 import com.alibaba.dubbo.rpc.Invocation;
 import com.alibaba.dubbo.rpc.Invocation;
 import com.alibaba.dubbo.rpc.Invoker;
 import com.alibaba.dubbo.rpc.Invoker;
@@ -24,7 +25,7 @@ import com.alibaba.dubbo.rpc.Invoker;
  */
  */
 abstract class AbstractDubboFilter implements Filter {
 abstract class AbstractDubboFilter implements Filter {
 
 
-    protected String getResourceName(Invoker<?> invoker, Invocation invocation) {
+    protected String getMethodResourceName(Invoker<?> invoker, Invocation invocation) {
         StringBuilder buf = new StringBuilder(64);
         StringBuilder buf = new StringBuilder(64);
         buf.append(invoker.getInterface().getName())
         buf.append(invoker.getInterface().getName())
             .append(":")
             .append(":")
@@ -41,4 +42,25 @@ abstract class AbstractDubboFilter implements Filter {
         buf.append(")");
         buf.append(")");
         return buf.toString();
         return buf.toString();
     }
     }
+
+    protected String getMethodResourceName(Invoker<?> invoker, Invocation invocation, String prefix) {
+        if (StringUtil.isBlank(prefix)) {
+            return getMethodResourceName(invoker, invocation);
+        }
+        StringBuilder buf = new StringBuilder(64);
+        return buf.append(prefix)
+            .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);
+    }
 }
 }

+ 3 - 1
sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAppContextFilter.java

@@ -24,12 +24,14 @@ import com.alibaba.dubbo.rpc.Result;
 import com.alibaba.dubbo.rpc.RpcContext;
 import com.alibaba.dubbo.rpc.RpcContext;
 import com.alibaba.dubbo.rpc.RpcException;
 import com.alibaba.dubbo.rpc.RpcException;
 
 
+import static com.alibaba.dubbo.common.Constants.CONSUMER;
+
 /**
 /**
  * Puts current consumer's application name in the attachment of each invocation.
  * Puts current consumer's application name in the attachment of each invocation.
  *
  *
  * @author Eric Zhao
  * @author Eric Zhao
  */
  */
-@Activate(group = "consumer")
+@Activate(group = CONSUMER)
 public class DubboAppContextFilter implements Filter {
 public class DubboAppContextFilter implements Filter {
 
 
     @Override
     @Override

+ 17 - 10
sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java

@@ -17,10 +17,9 @@ package com.alibaba.csp.sentinel.adapter.dubbo;
 
 
 import com.alibaba.csp.sentinel.Entry;
 import com.alibaba.csp.sentinel.Entry;
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.ResourceTypeConstants;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.Tracer;
 import com.alibaba.csp.sentinel.Tracer;
-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.log.RecordLog;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 import com.alibaba.dubbo.common.extension.Activate;
 import com.alibaba.dubbo.common.extension.Activate;
@@ -30,6 +29,8 @@ import com.alibaba.dubbo.rpc.Invoker;
 import com.alibaba.dubbo.rpc.Result;
 import com.alibaba.dubbo.rpc.Result;
 import com.alibaba.dubbo.rpc.RpcException;
 import com.alibaba.dubbo.rpc.RpcException;
 
 
+import static com.alibaba.dubbo.common.Constants.CONSUMER;
+
 /**
 /**
  * <p>Dubbo service consumer filter for Sentinel. Auto activated by default.</p>
  * <p>Dubbo service consumer filter for Sentinel. Auto activated by default.</p>
  *
  *
@@ -41,7 +42,7 @@ import com.alibaba.dubbo.rpc.RpcException;
  * @author leyou
  * @author leyou
  * @author Eric Zhao
  * @author Eric Zhao
  */
  */
-@Activate(group = "consumer")
+@Activate(group = CONSUMER)
 public class SentinelDubboConsumerFilter extends AbstractDubboFilter implements Filter {
 public class SentinelDubboConsumerFilter extends AbstractDubboFilter implements Filter {
 
 
     public SentinelDubboConsumerFilter() {
     public SentinelDubboConsumerFilter() {
@@ -53,24 +54,30 @@ public class SentinelDubboConsumerFilter extends AbstractDubboFilter implements
         Entry interfaceEntry = null;
         Entry interfaceEntry = null;
         Entry methodEntry = null;
         Entry methodEntry = null;
         try {
         try {
-            String resourceName = getResourceName(invoker, invocation);
-            interfaceEntry = SphU.entry(invoker.getInterface().getName(), EntryType.OUT);
-            methodEntry = SphU.entry(resourceName, 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);
             Result result = invoker.invoke(invocation);
             if (result.hasException()) {
             if (result.hasException()) {
+                Throwable e = result.getException();
                 // Record common exception.
                 // Record common exception.
-                Tracer.trace(result.getException());
+                Tracer.traceEntry(e, interfaceEntry);
+                Tracer.traceEntry(e, methodEntry);
             }
             }
             return result;
             return result;
         } catch (BlockException e) {
         } catch (BlockException e) {
-            return DubboFallbackRegistry.getConsumerFallback().handle(invoker, invocation, e);
+            return DubboAdapterGlobalConfig.getConsumerFallback().handle(invoker, invocation, e);
         } catch (RpcException e) {
         } catch (RpcException e) {
-            Tracer.trace(e);
+            Tracer.traceEntry(e, interfaceEntry);
+            Tracer.traceEntry(e, methodEntry);
             throw e;
             throw e;
         } finally {
         } finally {
             if (methodEntry != null) {
             if (methodEntry != null) {
-                methodEntry.exit();
+                methodEntry.exit(1, invocation.getArguments());
             }
             }
             if (interfaceEntry != null) {
             if (interfaceEntry != null) {
                 interfaceEntry.exit();
                 interfaceEntry.exit();

+ 22 - 12
sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java

@@ -17,13 +17,12 @@ package com.alibaba.csp.sentinel.adapter.dubbo;
 
 
 import com.alibaba.csp.sentinel.Entry;
 import com.alibaba.csp.sentinel.Entry;
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.ResourceTypeConstants;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.Tracer;
 import com.alibaba.csp.sentinel.Tracer;
-import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
 import com.alibaba.csp.sentinel.context.ContextUtil;
 import com.alibaba.csp.sentinel.context.ContextUtil;
 import com.alibaba.csp.sentinel.log.RecordLog;
 import com.alibaba.csp.sentinel.log.RecordLog;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
-import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
 import com.alibaba.dubbo.common.extension.Activate;
 import com.alibaba.dubbo.common.extension.Activate;
 import com.alibaba.dubbo.rpc.Filter;
 import com.alibaba.dubbo.rpc.Filter;
 import com.alibaba.dubbo.rpc.Invocation;
 import com.alibaba.dubbo.rpc.Invocation;
@@ -31,6 +30,8 @@ import com.alibaba.dubbo.rpc.Invoker;
 import com.alibaba.dubbo.rpc.Result;
 import com.alibaba.dubbo.rpc.Result;
 import com.alibaba.dubbo.rpc.RpcException;
 import com.alibaba.dubbo.rpc.RpcException;
 
 
+import static com.alibaba.dubbo.common.Constants.PROVIDER;
+
 /**
 /**
  * <p>Dubbo service provider filter for Sentinel. Auto activated by default.</p>
  * <p>Dubbo service provider filter for Sentinel. Auto activated by default.</p>
  *
  *
@@ -42,7 +43,7 @@ import com.alibaba.dubbo.rpc.RpcException;
  * @author leyou
  * @author leyou
  * @author Eric Zhao
  * @author Eric Zhao
  */
  */
-@Activate(group = "provider")
+@Activate(group = PROVIDER)
 public class SentinelDubboProviderFilter extends AbstractDubboFilter implements Filter {
 public class SentinelDubboProviderFilter extends AbstractDubboFilter implements Filter {
 
 
     public SentinelDubboProviderFilter() {
     public SentinelDubboProviderFilter() {
@@ -52,26 +53,35 @@ public class SentinelDubboProviderFilter extends AbstractDubboFilter implements
     @Override
     @Override
     public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
     public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
         // Get origin caller.
         // Get origin caller.
-        String application = DubboUtils.getApplication(invocation, "");
+        String origin = DubboAdapterGlobalConfig.getOriginParser().parse(invoker, invocation);
+        if (null == origin) {
+            origin = "";
+        }
 
 
         Entry interfaceEntry = null;
         Entry interfaceEntry = null;
         Entry methodEntry = null;
         Entry methodEntry = null;
         try {
         try {
-            String resourceName = getResourceName(invoker, invocation);
-            String interfaceName = invoker.getInterface().getName();
-            ContextUtil.enter(resourceName, application);
-            interfaceEntry = SphU.entry(interfaceName, EntryType.IN);
-            methodEntry = SphU.entry(resourceName, EntryType.IN, 1, invocation.getArguments());
+            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(methodResourceName, ResourceTypeConstants.COMMON_RPC,
+                EntryType.IN, invocation.getArguments());
 
 
             Result result = invoker.invoke(invocation);
             Result result = invoker.invoke(invocation);
             if (result.hasException()) {
             if (result.hasException()) {
-                Tracer.trace(result.getException());
+                Throwable e = result.getException();
+                // Record common exception.
+                Tracer.traceEntry(e, interfaceEntry);
+                Tracer.traceEntry(e, methodEntry);
             }
             }
             return result;
             return result;
         } catch (BlockException e) {
         } catch (BlockException e) {
-            return DubboFallbackRegistry.getProviderFallback().handle(invoker, invocation, e);
+            return DubboAdapterGlobalConfig.getProviderFallback().handle(invoker, invocation, e);
         } catch (RpcException e) {
         } catch (RpcException e) {
-            Tracer.trace(e);
+            Tracer.traceEntry(e, interfaceEntry);
+            Tracer.traceEntry(e, methodEntry);
             throw e;
             throw e;
         } finally {
         } finally {
             if (methodEntry != null) {
             if (methodEntry != null) {

+ 5 - 2
sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DefaultDubboFallback.java

@@ -20,6 +20,7 @@ import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
 import com.alibaba.dubbo.rpc.Invocation;
 import com.alibaba.dubbo.rpc.Invocation;
 import com.alibaba.dubbo.rpc.Invoker;
 import com.alibaba.dubbo.rpc.Invoker;
 import com.alibaba.dubbo.rpc.Result;
 import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcResult;
 
 
 /**
 /**
  * @author Eric Zhao
  * @author Eric Zhao
@@ -28,7 +29,9 @@ public class DefaultDubboFallback implements DubboFallback {
 
 
     @Override
     @Override
     public Result handle(Invoker<?> invoker, Invocation invocation, BlockException ex) {
     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;
     }
     }
 }
 }

+ 9 - 11
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;
 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.
+ * <p>Global fallback registry for Dubbo.</p>
  *
  *
  * @author Eric Zhao
  * @author Eric Zhao
+ * @deprecated use {@link DubboAdapterGlobalConfig} instead.
  */
  */
+@Deprecated
 public final class DubboFallbackRegistry {
 public final class DubboFallbackRegistry {
 
 
-    private static volatile DubboFallback consumerFallback = new DefaultDubboFallback();
-    private static volatile DubboFallback providerFallback = new DefaultDubboFallback();
-
     public static DubboFallback getConsumerFallback() {
     public static DubboFallback getConsumerFallback() {
-        return consumerFallback;
+        return DubboAdapterGlobalConfig.getConsumerFallback();
     }
     }
 
 
     public static void setConsumerFallback(DubboFallback consumerFallback) {
     public static void setConsumerFallback(DubboFallback consumerFallback) {
-        DubboFallbackRegistry.consumerFallback = consumerFallback;
+        DubboAdapterGlobalConfig.setConsumerFallback(consumerFallback);
     }
     }
 
 
     public static DubboFallback getProviderFallback() {
     public static DubboFallback getProviderFallback() {
-        return providerFallback;
+        return DubboAdapterGlobalConfig.getProviderFallback();
     }
     }
 
 
     public static void setProviderFallback(DubboFallback providerFallback) {
     public static void setProviderFallback(DubboFallback providerFallback) {
-        DubboFallbackRegistry.providerFallback = providerFallback;
+        DubboAdapterGlobalConfig.setProviderFallback(providerFallback);
     }
     }
 
 
     private DubboFallbackRegistry() {}
     private DubboFallbackRegistry() {}

+ 51 - 3
sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/AbstractDubboFilterTest.java

@@ -1,22 +1,41 @@
 package com.alibaba.csp.sentinel.adapter.dubbo;
 package com.alibaba.csp.sentinel.adapter.dubbo;
 
 
 import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
 import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
+import com.alibaba.csp.sentinel.config.SentinelConfig;
 import com.alibaba.dubbo.rpc.Invocation;
 import com.alibaba.dubbo.rpc.Invocation;
 import com.alibaba.dubbo.rpc.Invoker;
 import com.alibaba.dubbo.rpc.Invoker;
 import com.alibaba.dubbo.rpc.Result;
 import com.alibaba.dubbo.rpc.Result;
 import com.alibaba.dubbo.rpc.RpcException;
 import com.alibaba.dubbo.rpc.RpcException;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.Test;
 
 
 import java.lang.reflect.Method;
 import java.lang.reflect.Method;
 
 
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 
 /**
 /**
  * @author cdfive
  * @author cdfive
  */
  */
 public class AbstractDubboFilterTest {
 public class AbstractDubboFilterTest {
 
 
+
+    @Before
+    public void setUp() {
+        SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "true");
+        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(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "");
+        SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "");
+    }
+
     private AbstractDubboFilter filter = new AbstractDubboFilter() {
     private AbstractDubboFilter filter = new AbstractDubboFilter() {
         @Override
         @Override
         public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
         public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
@@ -24,6 +43,7 @@ public class AbstractDubboFilterTest {
         }
         }
     };
     };
 
 
+
     @Test
     @Test
     public void testGetResourceName() {
     public void testGetResourceName() {
         Invoker invoker = mock(Invoker.class);
         Invoker invoker = mock(Invoker.class);
@@ -34,8 +54,36 @@ public class AbstractDubboFilterTest {
         when(invocation.getMethodName()).thenReturn(method.getName());
         when(invocation.getMethodName()).thenReturn(method.getName());
         when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
         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);
         assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
     }
     }
+
+    @Test
+    public void testGetResourceNameWithPrefix() {
+        Invoker invoker = mock(Invoker.class);
+        when(invoker.getInterface()).thenReturn(DemoService.class);
+
+        Invocation invocation = mock(Invocation.class);
+        Method method = DemoService.class.getMethods()[0];
+        when(invocation.getMethodName()).thenReturn(method.getName());
+        when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
+
+        //test with default prefix
+        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.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(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.getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboConsumerPrefix());
+        assertEquals("my:dubbo:consumer:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
+
+    }
 }
 }

+ 4 - 4
sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilterTest.java

@@ -85,14 +85,14 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
         // As not call ContextUtil.enter(resourceName, application) in SentinelDubboConsumerFilter, use default 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
         // 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(Constants.CONTEXT_DEFAULT_NAME, context.getName());
         assertEquals("", context.getOrigin());
         assertEquals("", context.getOrigin());
 
 
         DefaultNode entranceNode = context.getEntranceNode();
         DefaultNode entranceNode = context.getEntranceNode();
         ResourceWrapper entranceResource = entranceNode.getId();
         ResourceWrapper entranceResource = entranceNode.getId();
         assertEquals(Constants.CONTEXT_DEFAULT_NAME, entranceResource.getName());
         assertEquals(Constants.CONTEXT_DEFAULT_NAME, entranceResource.getName());
-        assertSame(EntryType.IN, entranceResource.getType());
+        assertSame(EntryType.IN, entranceResource.getEntryType());
 
 
         // As SphU.entry(interfaceName, EntryType.OUT);
         // As SphU.entry(interfaceName, EntryType.OUT);
         Set<Node> childList = entranceNode.getChildList();
         Set<Node> childList = entranceNode.getChildList();
@@ -100,7 +100,7 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
         DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
         DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
         ResourceWrapper interfaceResource = interfaceNode.getId();
         ResourceWrapper interfaceResource = interfaceNode.getId();
         assertEquals(DemoService.class.getName(), interfaceResource.getName());
         assertEquals(DemoService.class.getName(), interfaceResource.getName());
-        assertSame(EntryType.OUT, interfaceResource.getType());
+        assertSame(EntryType.OUT, interfaceResource.getEntryType());
 
 
         // As SphU.entry(resourceName, EntryType.OUT);
         // As SphU.entry(resourceName, EntryType.OUT);
         childList = interfaceNode.getChildList();
         childList = interfaceNode.getChildList();
@@ -108,7 +108,7 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
         DefaultNode methodNode = (DefaultNode) childList.iterator().next();
         DefaultNode methodNode = (DefaultNode) childList.iterator().next();
         ResourceWrapper methodResource = methodNode.getId();
         ResourceWrapper methodResource = methodNode.getId();
         assertEquals(resourceName, methodResource.getName());
         assertEquals(resourceName, methodResource.getName());
-        assertSame(EntryType.OUT, methodResource.getType());
+        assertSame(EntryType.OUT, methodResource.getEntryType());
 
 
         // Verify curEntry
         // Verify curEntry
         Entry curEntry = context.getCurEntry();
         Entry curEntry = context.getCurEntry();

+ 4 - 4
sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilterTest.java

@@ -85,14 +85,14 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
         assertNotNull(context);
         assertNotNull(context);
 
 
         // As ContextUtil.enter(resourceName, application) in SentinelDubboProviderFilter
         // As ContextUtil.enter(resourceName, application) in SentinelDubboProviderFilter
-        String resourceName = filter.getResourceName(invoker, invocation);
+        String resourceName = filter.getMethodResourceName(invoker, invocation);
         assertEquals(resourceName, context.getName());
         assertEquals(resourceName, context.getName());
         assertEquals(originApplication, context.getOrigin());
         assertEquals(originApplication, context.getOrigin());
 
 
         DefaultNode entranceNode = context.getEntranceNode();
         DefaultNode entranceNode = context.getEntranceNode();
         ResourceWrapper entranceResource = entranceNode.getId();
         ResourceWrapper entranceResource = entranceNode.getId();
         assertEquals(resourceName, entranceResource.getName());
         assertEquals(resourceName, entranceResource.getName());
-        assertSame(EntryType.IN, entranceResource.getType());
+        assertSame(EntryType.IN, entranceResource.getEntryType());
 
 
         // As SphU.entry(interfaceName, EntryType.IN);
         // As SphU.entry(interfaceName, EntryType.IN);
         Set<Node> childList = entranceNode.getChildList();
         Set<Node> childList = entranceNode.getChildList();
@@ -100,7 +100,7 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
         DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
         DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
         ResourceWrapper interfaceResource = interfaceNode.getId();
         ResourceWrapper interfaceResource = interfaceNode.getId();
         assertEquals(DemoService.class.getName(), interfaceResource.getName());
         assertEquals(DemoService.class.getName(), interfaceResource.getName());
-        assertSame(EntryType.IN, interfaceResource.getType());
+        assertSame(EntryType.IN, interfaceResource.getEntryType());
 
 
         // As SphU.entry(resourceName, EntryType.IN, 1, invocation.getArguments());
         // As SphU.entry(resourceName, EntryType.IN, 1, invocation.getArguments());
         childList = interfaceNode.getChildList();
         childList = interfaceNode.getChildList();
@@ -108,7 +108,7 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
         DefaultNode methodNode = (DefaultNode) childList.iterator().next();
         DefaultNode methodNode = (DefaultNode) childList.iterator().next();
         ResourceWrapper methodResource = methodNode.getId();
         ResourceWrapper methodResource = methodNode.getId();
         assertEquals(resourceName, methodResource.getName());
         assertEquals(resourceName, methodResource.getName());
-        assertSame(EntryType.IN, methodResource.getType());
+        assertSame(EntryType.IN, methodResource.getEntryType());
 
 
         // Verify curEntry
         // Verify curEntry
         Entry curEntry = context.getCurEntry();
         Entry curEntry = context.getCurEntry();

+ 8 - 7
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;
 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.BlockException;
 import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
 import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
@@ -22,7 +23,6 @@ import com.alibaba.dubbo.rpc.Invocation;
 import com.alibaba.dubbo.rpc.Invoker;
 import com.alibaba.dubbo.rpc.Invoker;
 import com.alibaba.dubbo.rpc.Result;
 import com.alibaba.dubbo.rpc.Result;
 import com.alibaba.dubbo.rpc.RpcResult;
 import com.alibaba.dubbo.rpc.RpcResult;
-
 import org.junit.Assert;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.Test;
 
 
@@ -31,24 +31,25 @@ import org.junit.Test;
  */
  */
 public class DubboFallbackRegistryTest {
 public class DubboFallbackRegistryTest {
 
 
-    @Test(expected = SentinelRpcException.class)
+    @Test
     public void testDefaultFallback() {
     public void testDefaultFallback() {
-        // Test for default.
+        // Test for default fallback.
         BlockException ex = new FlowException("xxx");
         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
     @Test
     public void testCustomFallback() {
     public void testCustomFallback() {
         BlockException ex = new FlowException("xxx");
         BlockException ex = new FlowException("xxx");
-        DubboFallbackRegistry.setConsumerFallback(new DubboFallback() {
+        DubboAdapterGlobalConfig.setConsumerFallback(new DubboFallback() {
             @Override
             @Override
             public Result handle(Invoker<?> invoker, Invocation invocation, BlockException e) {
             public Result handle(Invoker<?> invoker, Invocation invocation, BlockException e) {
                 return new RpcResult("Error: " + e.getClass().getName());
                 return new RpcResult("Error: " + e.getClass().getName());
             }
             }
         });
         });
-        Result result = DubboFallbackRegistry.getConsumerFallback()
+        Result result = DubboAdapterGlobalConfig.getConsumerFallback()
             .handle(null, null, ex);
             .handle(null, null, ex);
         Assert.assertFalse("The invocation should not fail", result.hasException());
         Assert.assertFalse("The invocation should not fail", result.hasException());
         Assert.assertEquals("Error: " + ex.getClass().getName(), result.getValue());
         Assert.assertEquals("Error: " + ex.getClass().getName(), result.getValue());

+ 0 - 2
sentinel-adapter/sentinel-grpc-adapter/README.md

@@ -3,7 +3,6 @@
 Sentinel gRPC Adapter provides client and server interceptor for gRPC services.
 Sentinel gRPC Adapter provides client and server interceptor for gRPC services.
 
 
 > Note that currently the interceptor only supports unary methods in gRPC.
 > Note that currently the interceptor only supports unary methods in gRPC.
-> In some circumstances (e.g. asynchronous call), the RT metrics might not be accurate.
 
 
 ## Client Interceptor
 ## Client Interceptor
 
 
@@ -35,4 +34,3 @@ Server server = ServerBuilder.forPort(port)
      .intercept(new SentinelGrpcServerInterceptor()) // Add the server interceptor.
      .intercept(new SentinelGrpcServerInterceptor()) // Add the server interceptor.
      .build();
      .build();
 ```
 ```
-

+ 4 - 4
sentinel-adapter/sentinel-grpc-adapter/pom.xml

@@ -5,14 +5,14 @@
     <parent>
     <parent>
         <artifactId>sentinel-adapter</artifactId>
         <artifactId>sentinel-adapter</artifactId>
         <groupId>com.alibaba.csp</groupId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>sentinel-grpc-adapter</artifactId>
     <artifactId>sentinel-grpc-adapter</artifactId>
     <packaging>jar</packaging>
     <packaging>jar</packaging>
 
 
     <properties>
     <properties>
-        <grpc.version>1.13.1</grpc.version>
+        <grpc.version>1.30.2</grpc.version>
     </properties>
     </properties>
 
 
     <dependencies>
     <dependencies>
@@ -43,7 +43,7 @@
         <dependency>
         <dependency>
             <groupId>javax.annotation</groupId>
             <groupId>javax.annotation</groupId>
             <artifactId>javax.annotation-api</artifactId>
             <artifactId>javax.annotation-api</artifactId>
-            <version>1.2</version>
+            <version>${javax.annotation-api.version}</version>
         </dependency>
         </dependency>
 
 
 
 
@@ -95,4 +95,4 @@
             </plugin>
             </plugin>
         </plugins>
         </plugins>
     </build>
     </build>
-</project>
+</project>

+ 36 - 33
sentinel-adapter/sentinel-grpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcClientInterceptor.java

@@ -15,28 +15,27 @@
  */
  */
 package com.alibaba.csp.sentinel.adapter.grpc;
 package com.alibaba.csp.sentinel.adapter.grpc;
 
 
-import javax.annotation.Nullable;
-
 import com.alibaba.csp.sentinel.Entry;
 import com.alibaba.csp.sentinel.Entry;
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.Tracer;
 import com.alibaba.csp.sentinel.Tracer;
-import com.alibaba.csp.sentinel.context.ContextUtil;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
-
 import io.grpc.CallOptions;
 import io.grpc.CallOptions;
 import io.grpc.Channel;
 import io.grpc.Channel;
 import io.grpc.ClientCall;
 import io.grpc.ClientCall;
 import io.grpc.ClientInterceptor;
 import io.grpc.ClientInterceptor;
 import io.grpc.ForwardingClientCall;
 import io.grpc.ForwardingClientCall;
-import io.grpc.ForwardingClientCallListener.SimpleForwardingClientCallListener;
+import io.grpc.ForwardingClientCallListener;
 import io.grpc.Metadata;
 import io.grpc.Metadata;
 import io.grpc.MethodDescriptor;
 import io.grpc.MethodDescriptor;
 import io.grpc.Status;
 import io.grpc.Status;
 
 
+import javax.annotation.Nullable;
+import java.util.concurrent.atomic.AtomicReference;
+
 /**
 /**
  * <p>gRPC client interceptor for Sentinel. Currently it only works with unary methods.</p>
  * <p>gRPC client interceptor for Sentinel. Currently it only works with unary methods.</p>
- *
+ * <p>
  * Example code:
  * Example code:
  * <pre>
  * <pre>
  * public class ServiceClient {
  * public class ServiceClient {
@@ -52,50 +51,59 @@ import io.grpc.Status;
  *
  *
  * }
  * }
  * </pre>
  * </pre>
- *
+ * <p>
  * For server interceptor, see {@link SentinelGrpcServerInterceptor}.
  * For server interceptor, see {@link SentinelGrpcServerInterceptor}.
  *
  *
  * @author Eric Zhao
  * @author Eric Zhao
  */
  */
 public class SentinelGrpcClientInterceptor implements ClientInterceptor {
 public class SentinelGrpcClientInterceptor implements ClientInterceptor {
-
     private static final Status FLOW_CONTROL_BLOCK = Status.UNAVAILABLE.withDescription(
     private static final Status FLOW_CONTROL_BLOCK = Status.UNAVAILABLE.withDescription(
-        "Flow control limit exceeded (client side)");
+            "Flow control limit exceeded (client side)");
 
 
     @Override
     @Override
     public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> methodDescriptor,
     public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> methodDescriptor,
                                                                CallOptions callOptions, Channel channel) {
                                                                CallOptions callOptions, Channel channel) {
-        String resourceName = methodDescriptor.getFullMethodName();
+        String fullMethodName = methodDescriptor.getFullMethodName();
         Entry entry = null;
         Entry entry = null;
         try {
         try {
-            entry = SphU.entry(resourceName, EntryType.OUT);
+            entry = SphU.asyncEntry(fullMethodName, EntryType.OUT);
+            final AtomicReference<Entry> atomicReferenceEntry = new AtomicReference<>(entry);
             // Allow access, forward the call.
             // Allow access, forward the call.
             return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(
             return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(
-                channel.newCall(methodDescriptor, callOptions)) {
+                    channel.newCall(methodDescriptor, callOptions)) {
                 @Override
                 @Override
                 public void start(Listener<RespT> responseListener, Metadata headers) {
                 public void start(Listener<RespT> responseListener, Metadata headers) {
-                    super.start(new SimpleForwardingClientCallListener<RespT>(responseListener) {
-                        @Override
-                        public void onReady() {
-                            super.onReady();
-                        }
-
+                    super.start(new ForwardingClientCallListener.SimpleForwardingClientCallListener<RespT>(responseListener) {
                         @Override
                         @Override
                         public void onClose(Status status, Metadata trailers) {
                         public void onClose(Status status, Metadata trailers) {
-                            super.onClose(status, trailers);
-                            // Record the exception metrics.
-                            if (!status.isOk()) {
-                                recordException(status.asRuntimeException());
+                            Entry entry = atomicReferenceEntry.get();
+                            if (entry != null) {
+                                // Record the exception metrics.
+                                if (!status.isOk()) {
+                                    Tracer.traceEntry(status.asRuntimeException(), entry);
+                                }
+                                entry.exit();
+                                atomicReferenceEntry.set(null);
                             }
                             }
+                            super.onClose(status, trailers);
                         }
                         }
                     }, headers);
                     }, headers);
                 }
                 }
 
 
+                /**
+                 * Some Exceptions will only call cancel.
+                 */
                 @Override
                 @Override
                 public void cancel(@Nullable String message, @Nullable Throwable cause) {
                 public void cancel(@Nullable String message, @Nullable Throwable cause) {
+                    Entry entry = atomicReferenceEntry.get();
+                    // Some Exceptions will call onClose and cancel.
+                    if (entry != null) {
+                        // Record the exception metrics.
+                        Tracer.traceEntry(cause, entry);
+                        entry.exit();
+                        atomicReferenceEntry.set(null);
+                    }
                     super.cancel(message, cause);
                     super.cancel(message, cause);
-                    // Record the exception metrics.
-                    recordException(cause);
                 }
                 }
             };
             };
         } catch (BlockException e) {
         } catch (BlockException e) {
@@ -108,32 +116,27 @@ public class SentinelGrpcClientInterceptor implements ClientInterceptor {
 
 
                 @Override
                 @Override
                 public void request(int numMessages) {
                 public void request(int numMessages) {
-
                 }
                 }
 
 
                 @Override
                 @Override
                 public void cancel(@Nullable String message, @Nullable Throwable cause) {
                 public void cancel(@Nullable String message, @Nullable Throwable cause) {
-
                 }
                 }
 
 
                 @Override
                 @Override
                 public void halfClose() {
                 public void halfClose() {
-
                 }
                 }
 
 
                 @Override
                 @Override
                 public void sendMessage(ReqT message) {
                 public void sendMessage(ReqT message) {
-
                 }
                 }
             };
             };
-        } finally {
+        } catch (RuntimeException e) {
+            // Catch the RuntimeException newCall throws, entry is guaranteed to exit.
             if (entry != null) {
             if (entry != null) {
+                Tracer.traceEntry(e, entry);
                 entry.exit();
                 entry.exit();
             }
             }
+            throw e;
         }
         }
     }
     }
-
-    private void recordException(Throwable t) {
-        Tracer.trace(t);
-    }
 }
 }

+ 50 - 31
sentinel-adapter/sentinel-grpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcServerInterceptor.java

@@ -19,21 +19,21 @@ import com.alibaba.csp.sentinel.Entry;
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.Tracer;
 import com.alibaba.csp.sentinel.Tracer;
-import com.alibaba.csp.sentinel.context.ContextUtil;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
-
 import io.grpc.ForwardingServerCall;
 import io.grpc.ForwardingServerCall;
 import io.grpc.ForwardingServerCallListener;
 import io.grpc.ForwardingServerCallListener;
 import io.grpc.Metadata;
 import io.grpc.Metadata;
 import io.grpc.ServerCall;
 import io.grpc.ServerCall;
-import io.grpc.ServerCall.Listener;
 import io.grpc.ServerCallHandler;
 import io.grpc.ServerCallHandler;
 import io.grpc.ServerInterceptor;
 import io.grpc.ServerInterceptor;
 import io.grpc.Status;
 import io.grpc.Status;
+import io.grpc.StatusRuntimeException;
+
+import java.util.concurrent.atomic.AtomicReference;
 
 
 /**
 /**
  * <p>gRPC server interceptor for Sentinel. Currently it only works with unary methods.</p>
  * <p>gRPC server interceptor for Sentinel. Currently it only works with unary methods.</p>
- *
+ * <p>
  * Example code:
  * Example code:
  * <pre>
  * <pre>
  * Server server = ServerBuilder.forPort(port)
  * Server server = ServerBuilder.forPort(port)
@@ -41,50 +41,69 @@ import io.grpc.Status;
  *      .intercept(new SentinelGrpcServerInterceptor()) // Add the server interceptor.
  *      .intercept(new SentinelGrpcServerInterceptor()) // Add the server interceptor.
  *      .build();
  *      .build();
  * </pre>
  * </pre>
- *
+ * <p>
  * For client interceptor, see {@link SentinelGrpcClientInterceptor}.
  * For client interceptor, see {@link SentinelGrpcClientInterceptor}.
  *
  *
  * @author Eric Zhao
  * @author Eric Zhao
  */
  */
 public class SentinelGrpcServerInterceptor implements ServerInterceptor {
 public class SentinelGrpcServerInterceptor implements ServerInterceptor {
-
     private static final Status FLOW_CONTROL_BLOCK = Status.UNAVAILABLE.withDescription(
     private static final Status FLOW_CONTROL_BLOCK = Status.UNAVAILABLE.withDescription(
-        "Flow control limit exceeded (server side)");
+            "Flow control limit exceeded (server side)");
+    private static final StatusRuntimeException STATUS_RUNTIME_EXCEPTION = new StatusRuntimeException(Status.CANCELLED);
 
 
     @Override
     @Override
-    public <ReqT, RespT> Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> serverCall, Metadata metadata,
-                                                      ServerCallHandler<ReqT, RespT> serverCallHandler) {
-        String resourceName = serverCall.getMethodDescriptor().getFullMethodName();
+    public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
+        String fullMethodName = call.getMethodDescriptor().getFullMethodName();
         // Remote address: serverCall.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR);
         // Remote address: serverCall.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR);
         Entry entry = null;
         Entry entry = null;
         try {
         try {
-            ContextUtil.enter(resourceName);
-            entry = SphU.entry(resourceName, EntryType.IN);
+            entry = SphU.asyncEntry(fullMethodName, EntryType.IN);
+            final AtomicReference<Entry> atomicReferenceEntry = new AtomicReference<>(entry);
             // Allow access, forward the call.
             // Allow access, forward the call.
             return new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(
             return new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(
-                serverCallHandler.startCall(
-                    new ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT>(serverCall) {
-                        @Override
-                        public void close(Status status, Metadata trailers) {
-                            super.close(status, trailers);
-                            // Record the exception metrics.
-                            if (!status.isOk()) {
-                                recordException(status.asRuntimeException());
-                            }
-                        }
-                    }, metadata)) {};
+                    next.startCall(
+                            new ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT>(call) {
+                                @Override
+                                public void close(Status status, Metadata trailers) {
+                                    Entry entry = atomicReferenceEntry.get();
+                                    if (entry != null) {
+                                        // Record the exception metrics.
+                                        if (!status.isOk()) {
+                                            Tracer.traceEntry(status.asRuntimeException(), entry);
+                                        }
+                                        //entry exit when the call be closed
+                                        entry.exit();
+                                    }
+                                    super.close(status, trailers);
+                                }
+                            }, headers)) {
+                /**
+                 * If call was canceled, onCancel will be called. and the close will not be called
+                 * so the server is encouraged to abort processing to save resources by onCancel
+                 * @see ServerCall.Listener#onCancel()
+                 */
+                @Override
+                public void onCancel() {
+                    Entry entry = atomicReferenceEntry.get();
+                    if (entry != null) {
+                        Tracer.traceEntry(STATUS_RUNTIME_EXCEPTION, entry);
+                        entry.exit();
+                        atomicReferenceEntry.set(null);
+                    }
+                    super.onCancel();
+                }
+            };
         } catch (BlockException e) {
         } catch (BlockException e) {
-            serverCall.close(FLOW_CONTROL_BLOCK, new Metadata());
-            return new ServerCall.Listener<ReqT>() {};
-        } finally {
+            call.close(FLOW_CONTROL_BLOCK, new Metadata());
+            return new ServerCall.Listener<ReqT>() {
+            };
+        } catch (RuntimeException e) {
+            // Catch the RuntimeException startCall throws, entry is guaranteed to exit.
             if (entry != null) {
             if (entry != null) {
+                Tracer.traceEntry(e, entry);
                 entry.exit();
                 entry.exit();
             }
             }
-            ContextUtil.exit();
+            throw e;
         }
         }
     }
     }
-
-    private void recordException(Throwable t) {
-        Tracer.trace(t);
-    }
 }
 }

+ 7 - 9
sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/FooServiceClient.java

@@ -15,38 +15,36 @@
  */
  */
 package com.alibaba.csp.sentinel.adapter.grpc;
 package com.alibaba.csp.sentinel.adapter.grpc;
 
 
-import java.util.concurrent.TimeUnit;
-
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooResponse;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooResponse;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooServiceGrpc;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooServiceGrpc;
-
 import io.grpc.ClientInterceptor;
 import io.grpc.ClientInterceptor;
 import io.grpc.ManagedChannel;
 import io.grpc.ManagedChannel;
 import io.grpc.ManagedChannelBuilder;
 import io.grpc.ManagedChannelBuilder;
 
 
+import java.util.concurrent.TimeUnit;
+
 /**
 /**
  * A simple wrapped gRPC client for FooService.
  * A simple wrapped gRPC client for FooService.
  *
  *
  * @author Eric Zhao
  * @author Eric Zhao
  */
  */
 final class FooServiceClient {
 final class FooServiceClient {
-
     private final ManagedChannel channel;
     private final ManagedChannel channel;
     private final FooServiceGrpc.FooServiceBlockingStub blockingStub;
     private final FooServiceGrpc.FooServiceBlockingStub blockingStub;
 
 
     FooServiceClient(String host, int port) {
     FooServiceClient(String host, int port) {
         this.channel = ManagedChannelBuilder.forAddress(host, port)
         this.channel = ManagedChannelBuilder.forAddress(host, port)
-            .usePlaintext()
-            .build();
+                .usePlaintext()
+                .build();
         this.blockingStub = FooServiceGrpc.newBlockingStub(this.channel);
         this.blockingStub = FooServiceGrpc.newBlockingStub(this.channel);
     }
     }
 
 
     FooServiceClient(String host, int port, ClientInterceptor interceptor) {
     FooServiceClient(String host, int port, ClientInterceptor interceptor) {
         this.channel = ManagedChannelBuilder.forAddress(host, port)
         this.channel = ManagedChannelBuilder.forAddress(host, port)
-            .usePlaintext()
-            .intercept(interceptor)
-            .build();
+                .usePlaintext()
+                .intercept(interceptor)
+                .build();
         this.blockingStub = FooServiceGrpc.newBlockingStub(this.channel);
         this.blockingStub = FooServiceGrpc.newBlockingStub(this.channel);
     }
     }
 
 

+ 42 - 10
sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/FooServiceImpl.java

@@ -18,27 +18,59 @@ package com.alibaba.csp.sentinel.adapter.grpc;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooResponse;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooResponse;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooServiceGrpc;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooServiceGrpc;
-
 import io.grpc.stub.StreamObserver;
 import io.grpc.stub.StreamObserver;
 
 
 /**
 /**
  * Implementation of FooService defined in proto.
  * Implementation of FooService defined in proto.
  */
  */
 class FooServiceImpl extends FooServiceGrpc.FooServiceImplBase {
 class FooServiceImpl extends FooServiceGrpc.FooServiceImplBase {
-
     @Override
     @Override
     public void sayHello(FooRequest request, StreamObserver<FooResponse> responseObserver) {
     public void sayHello(FooRequest request, StreamObserver<FooResponse> responseObserver) {
-        String message = String.format("Hello %s! Your ID is %d.", request.getName(), request.getId());
-        FooResponse response = FooResponse.newBuilder().setMessage(message).build();
-        responseObserver.onNext(response);
-        responseObserver.onCompleted();
+        int id = request.getId();
+        switch (id) {
+            // Exception test
+            case -1:
+                responseObserver.onError(new IllegalAccessException("The id is error!"));
+                break;
+            // RT test
+            case -2:
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    responseObserver.onError(e);
+                    break;
+                }
+            default:
+                String message = String.format("Hello %s! Your ID is %d.", request.getName(), id);
+                FooResponse response = FooResponse.newBuilder().setMessage(message).build();
+                responseObserver.onNext(response);
+                responseObserver.onCompleted();
+                break;
+        }
     }
     }
 
 
     @Override
     @Override
     public void anotherHello(FooRequest request, StreamObserver<FooResponse> responseObserver) {
     public void anotherHello(FooRequest request, StreamObserver<FooResponse> responseObserver) {
-        String message = String.format("Good day, %s (%d)", request.getName(), request.getId());
-        FooResponse response = FooResponse.newBuilder().setMessage(message).build();
-        responseObserver.onNext(response);
-        responseObserver.onCompleted();
+        int id = request.getId();
+        switch (id) {
+            // Exception test
+            case -1:
+                responseObserver.onError(new IllegalAccessException("The id is error!"));
+                break;
+            // RT test
+            case -2:
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    responseObserver.onError(e);
+                    break;
+                }
+            default:
+                String message = String.format("Good day, %s (%d)", request.getName(), id);
+                FooResponse response = FooResponse.newBuilder().setMessage(message).build();
+                responseObserver.onNext(response);
+                responseObserver.onCompleted();
+                break;
+        }
     }
     }
 }
 }

+ 1 - 0
sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/GrpcTestServer.java

@@ -19,6 +19,7 @@ import io.grpc.Server;
 import io.grpc.ServerBuilder;
 import io.grpc.ServerBuilder;
 
 
 import java.io.IOException;
 import java.io.IOException;
+import java.util.concurrent.Executors;
 
 
 class GrpcTestServer {
 class GrpcTestServer {
 
 

+ 38 - 25
sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcClientInterceptorTest.java

@@ -15,8 +15,6 @@
  */
  */
 package com.alibaba.csp.sentinel.adapter.grpc;
 package com.alibaba.csp.sentinel.adapter.grpc;
 
 
-import java.util.Collections;
-
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooResponse;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooResponse;
@@ -25,12 +23,17 @@ import com.alibaba.csp.sentinel.slots.block.RuleConstant;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
 import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
 import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
-
 import io.grpc.StatusRuntimeException;
 import io.grpc.StatusRuntimeException;
 import org.junit.After;
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.Test;
 
 
-import static org.junit.Assert.*;
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 
 /**
 /**
  * Test cases for {@link SentinelGrpcClientInterceptor}.
  * Test cases for {@link SentinelGrpcClientInterceptor}.
@@ -38,48 +41,52 @@ import static org.junit.Assert.*;
  * @author Eric Zhao
  * @author Eric Zhao
  */
  */
 public class SentinelGrpcClientInterceptorTest {
 public class SentinelGrpcClientInterceptorTest {
-
-    private final String resourceName = "com.alibaba.sentinel.examples.FooService/sayHello";
-    private final int threshold = 2;
+    private final String fullMethodName = "com.alibaba.sentinel.examples.FooService/sayHello";
     private final GrpcTestServer server = new GrpcTestServer();
     private final GrpcTestServer server = new GrpcTestServer();
+    private FooServiceClient client;
 
 
     private void configureFlowRule(int count) {
     private void configureFlowRule(int count) {
         FlowRule rule = new FlowRule()
         FlowRule rule = new FlowRule()
-            .setCount(count)
-            .setGrade(RuleConstant.FLOW_GRADE_QPS)
-            .setResource(resourceName)
-            .setLimitApp("default")
-            .as(FlowRule.class);
+                .setCount(count)
+                .setGrade(RuleConstant.FLOW_GRADE_QPS)
+                .setResource(fullMethodName)
+                .setLimitApp("default")
+                .as(FlowRule.class);
         FlowRuleManager.loadRules(Collections.singletonList(rule));
         FlowRuleManager.loadRules(Collections.singletonList(rule));
     }
     }
 
 
     @Test
     @Test
     public void testGrpcClientInterceptor() throws Exception {
     public void testGrpcClientInterceptor() throws Exception {
         final int port = 19328;
         final int port = 19328;
-
-        configureFlowRule(threshold);
         server.start(port, false);
         server.start(port, false);
+        client = new FooServiceClient("localhost", port, new SentinelGrpcClientInterceptor());
 
 
-        FooServiceClient client = new FooServiceClient("localhost", port, new SentinelGrpcClientInterceptor());
-
-        assertTrue(sendRequest(client));
-        ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(resourceName, EntryType.OUT);
+        configureFlowRule(Integer.MAX_VALUE);
+        assertTrue(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(666).build()));
+        ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(fullMethodName, EntryType.OUT);
         assertNotNull(clusterNode);
         assertNotNull(clusterNode);
-        assertEquals(1, clusterNode.totalRequest() - clusterNode.blockRequest());
+        assertEquals(1, clusterNode.totalPass());
 
 
         // Not allowed to pass.
         // Not allowed to pass.
         configureFlowRule(0);
         configureFlowRule(0);
-
         // The second request will be blocked.
         // The second request will be blocked.
-        assertFalse(sendRequest(client));
+        assertFalse(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(666).build()));
         assertEquals(1, clusterNode.blockRequest());
         assertEquals(1, clusterNode.blockRequest());
 
 
+        configureFlowRule(Integer.MAX_VALUE);
+        assertFalse(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(-1).build()));
+        assertEquals(1, clusterNode.totalException());
+
+        configureFlowRule(Integer.MAX_VALUE);
+        assertTrue(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(-2).build()));
+        assertTrue(clusterNode.avgRt() >= 1000);
+
         server.stop();
         server.stop();
     }
     }
 
 
-    private boolean sendRequest(FooServiceClient client) {
+    private boolean sendRequest(FooRequest request) {
         try {
         try {
-            FooResponse response = client.sayHello(FooRequest.newBuilder().setName("Sentinel").setId(666).build());
+            FooResponse response = client.sayHello(request);
             System.out.println("Response: " + response);
             System.out.println("Response: " + response);
             return true;
             return true;
         } catch (StatusRuntimeException ex) {
         } catch (StatusRuntimeException ex) {
@@ -88,9 +95,15 @@ public class SentinelGrpcClientInterceptorTest {
         }
         }
     }
     }
 
 
+    @Before
+    public void cleanUpBefore() {
+        FlowRuleManager.loadRules(null);
+        ClusterBuilderSlot.getClusterNodeMap().clear();
+    }
+
     @After
     @After
-    public void cleanUp() {
+    public void cleanUpAfter() {
         FlowRuleManager.loadRules(null);
         FlowRuleManager.loadRules(null);
         ClusterBuilderSlot.getClusterNodeMap().clear();
         ClusterBuilderSlot.getClusterNodeMap().clear();
     }
     }
-}
+}

+ 34 - 23
sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcServerInterceptorTest.java

@@ -15,8 +15,6 @@
  */
  */
 package com.alibaba.csp.sentinel.adapter.grpc;
 package com.alibaba.csp.sentinel.adapter.grpc;
 
 
-import java.util.Collections;
-
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooResponse;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooResponse;
@@ -25,12 +23,17 @@ import com.alibaba.csp.sentinel.slots.block.RuleConstant;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
 import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
 import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
-
 import io.grpc.StatusRuntimeException;
 import io.grpc.StatusRuntimeException;
 import org.junit.After;
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.Test;
 
 
-import static org.junit.Assert.*;
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 
 /**
 /**
  * Test cases for {@link SentinelGrpcServerInterceptor}.
  * Test cases for {@link SentinelGrpcServerInterceptor}.
@@ -38,49 +41,52 @@ import static org.junit.Assert.*;
  * @author Eric Zhao
  * @author Eric Zhao
  */
  */
 public class SentinelGrpcServerInterceptorTest {
 public class SentinelGrpcServerInterceptorTest {
-
     private final String resourceName = "com.alibaba.sentinel.examples.FooService/anotherHello";
     private final String resourceName = "com.alibaba.sentinel.examples.FooService/anotherHello";
-    private final int threshold = 4;
     private final GrpcTestServer server = new GrpcTestServer();
     private final GrpcTestServer server = new GrpcTestServer();
-
     private FooServiceClient client;
     private FooServiceClient client;
 
 
     private void configureFlowRule(int count) {
     private void configureFlowRule(int count) {
         FlowRule rule = new FlowRule()
         FlowRule rule = new FlowRule()
-            .setCount(count)
-            .setGrade(RuleConstant.FLOW_GRADE_QPS)
-            .setResource(resourceName)
-            .setLimitApp("default")
-            .as(FlowRule.class);
+                .setCount(count)
+                .setGrade(RuleConstant.FLOW_GRADE_QPS)
+                .setResource(resourceName)
+                .setLimitApp("default")
+                .as(FlowRule.class);
         FlowRuleManager.loadRules(Collections.singletonList(rule));
         FlowRuleManager.loadRules(Collections.singletonList(rule));
     }
     }
 
 
     @Test
     @Test
     public void testGrpcServerInterceptor() throws Exception {
     public void testGrpcServerInterceptor() throws Exception {
         final int port = 19329;
         final int port = 19329;
-        client = new FooServiceClient("localhost", port);
-
-        configureFlowRule(threshold);
         server.start(port, true);
         server.start(port, true);
+        client = new FooServiceClient("localhost", port);
 
 
-        assertTrue(sendRequest());
+        configureFlowRule(Integer.MAX_VALUE);
+        assertTrue(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(666).build()));
         ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(resourceName, EntryType.IN);
         ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(resourceName, EntryType.IN);
         assertNotNull(clusterNode);
         assertNotNull(clusterNode);
-        assertEquals(1, clusterNode.totalRequest() - clusterNode.blockRequest());
+        assertEquals(1, clusterNode.totalPass());
 
 
         // Not allowed to pass.
         // Not allowed to pass.
         configureFlowRule(0);
         configureFlowRule(0);
-
         // The second request will be blocked.
         // The second request will be blocked.
-        assertFalse(sendRequest());
+        assertFalse(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(666).build()));
         assertEquals(1, clusterNode.blockRequest());
         assertEquals(1, clusterNode.blockRequest());
 
 
+        configureFlowRule(Integer.MAX_VALUE);
+        assertFalse(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(-1).build()));
+        assertEquals(1, clusterNode.totalException());
+
+        configureFlowRule(Integer.MAX_VALUE);
+        assertTrue(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(-2).build()));
+        assertTrue(clusterNode.avgRt() >= 1000);
+
         server.stop();
         server.stop();
     }
     }
 
 
-    private boolean sendRequest() {
+    private boolean sendRequest(FooRequest request) {
         try {
         try {
-            FooResponse response = client.anotherHello(FooRequest.newBuilder().setName("Sentinel").setId(666).build());
+            FooResponse response = client.anotherHello(request);
             System.out.println("Response: " + response);
             System.out.println("Response: " + response);
             return true;
             return true;
         } catch (StatusRuntimeException ex) {
         } catch (StatusRuntimeException ex) {
@@ -89,10 +95,15 @@ public class SentinelGrpcServerInterceptorTest {
         }
         }
     }
     }
 
 
+    @Before
+    public void cleanUpBefore() {
+        FlowRuleManager.loadRules(null);
+        ClusterBuilderSlot.getClusterNodeMap().clear();
+    }
 
 
     @After
     @After
-    public void cleanUp() {
+    public void cleanUpAfter() {
         FlowRuleManager.loadRules(null);
         FlowRuleManager.loadRules(null);
         ClusterBuilderSlot.getClusterNodeMap().clear();
         ClusterBuilderSlot.getClusterNodeMap().clear();
     }
     }
-}
+}

+ 1 - 2
sentinel-adapter/sentinel-grpc-adapter/src/test/proto/example.proto

@@ -18,6 +18,5 @@ message FooResponse {
 // Example service definition.
 // Example service definition.
 service FooService {
 service FooService {
     rpc sayHello(FooRequest) returns (FooResponse) {}
     rpc sayHello(FooRequest) returns (FooResponse) {}
-
     rpc anotherHello(FooRequest) returns (FooResponse) {}
     rpc anotherHello(FooRequest) returns (FooResponse) {}
-}
+}

+ 0 - 2
sentinel-adapter/sentinel-reactor-adapter/README.md

@@ -1,7 +1,5 @@
 # Sentinel Reactor Adapter
 # Sentinel Reactor Adapter
 
 
-> Note: this module requires Java 8 or later version.
-
 Sentinel provides integration module for [Reactor](https://projectreactor.io/).
 Sentinel provides integration module for [Reactor](https://projectreactor.io/).
 
 
 Add the following dependency in `pom.xml` (if you are using Maven):
 Add the following dependency in `pom.xml` (if you are using Maven):

+ 1 - 1
sentinel-adapter/sentinel-reactor-adapter/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>sentinel-adapter</artifactId>
         <artifactId>sentinel-adapter</artifactId>
         <groupId>com.alibaba.csp</groupId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 

+ 25 - 0
sentinel-adapter/sentinel-reactor-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/reactor/EntryConfig.java

@@ -18,6 +18,7 @@ package com.alibaba.csp.sentinel.adapter.reactor;
 import java.util.Arrays;
 import java.util.Arrays;
 
 
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.ResourceTypeConstants;
 import com.alibaba.csp.sentinel.util.AssertUtil;
 import com.alibaba.csp.sentinel.util.AssertUtil;
 
 
 /**
 /**
@@ -28,6 +29,8 @@ public class EntryConfig {
 
 
     private final String resourceName;
     private final String resourceName;
     private final EntryType entryType;
     private final EntryType entryType;
+    private final int resourceType;
+
     private final int acquireCount;
     private final int acquireCount;
     private final Object[] args;
     private final Object[] args;
     private final ContextConfig contextConfig;
     private final ContextConfig contextConfig;
@@ -44,17 +47,31 @@ public class EntryConfig {
         this(resourceName, entryType, 1, new Object[0], contextConfig);
         this(resourceName, entryType, 1, new Object[0], contextConfig);
     }
     }
 
 
+    public EntryConfig(String resourceName, int resourceType, EntryType entryType, ContextConfig contextConfig) {
+        this(resourceName, resourceType, entryType, 1, new Object[0], contextConfig);
+    }
+
     public EntryConfig(String resourceName, EntryType entryType, int acquireCount, Object[] args) {
     public EntryConfig(String resourceName, EntryType entryType, int acquireCount, Object[] args) {
         this(resourceName, entryType, acquireCount, args, null);
         this(resourceName, entryType, acquireCount, args, null);
     }
     }
 
 
     public EntryConfig(String resourceName, EntryType entryType, int acquireCount, Object[] args,
     public EntryConfig(String resourceName, EntryType entryType, int acquireCount, Object[] args,
                        ContextConfig contextConfig) {
                        ContextConfig contextConfig) {
+        this(resourceName, ResourceTypeConstants.COMMON, entryType, acquireCount, args, contextConfig);
+    }
+
+    public EntryConfig(String resourceName, int resourceType, EntryType entryType, int acquireCount, Object[] args) {
+        this(resourceName, resourceType, entryType, acquireCount, args, null);
+    }
+
+    public EntryConfig(String resourceName, int resourceType, EntryType entryType, int acquireCount, Object[] args,
+                       ContextConfig contextConfig) {
         AssertUtil.assertNotBlank(resourceName, "resourceName cannot be blank");
         AssertUtil.assertNotBlank(resourceName, "resourceName cannot be blank");
         AssertUtil.notNull(entryType, "entryType cannot be null");
         AssertUtil.notNull(entryType, "entryType cannot be null");
         AssertUtil.isTrue(acquireCount > 0, "acquireCount should be positive");
         AssertUtil.isTrue(acquireCount > 0, "acquireCount should be positive");
         this.resourceName = resourceName;
         this.resourceName = resourceName;
         this.entryType = entryType;
         this.entryType = entryType;
+        this.resourceType = resourceType;
         this.acquireCount = acquireCount;
         this.acquireCount = acquireCount;
         this.args = args;
         this.args = args;
         // Constructed ContextConfig should be valid here. Null is allowed here.
         // Constructed ContextConfig should be valid here. Null is allowed here.
@@ -81,11 +98,19 @@ public class EntryConfig {
         return contextConfig;
         return contextConfig;
     }
     }
 
 
+    /**
+     * @since 1.7.0
+     */
+    public int getResourceType() {
+        return resourceType;
+    }
+
     @Override
     @Override
     public String toString() {
     public String toString() {
         return "EntryConfig{" +
         return "EntryConfig{" +
             "resourceName='" + resourceName + '\'' +
             "resourceName='" + resourceName + '\'' +
             ", entryType=" + entryType +
             ", entryType=" + entryType +
+            ", resourceType=" + resourceType +
             ", acquireCount=" + acquireCount +
             ", acquireCount=" + acquireCount +
             ", args=" + Arrays.toString(args) +
             ", args=" + Arrays.toString(args) +
             ", contextConfig=" + contextConfig +
             ", contextConfig=" + contextConfig +

+ 3 - 3
sentinel-adapter/sentinel-reactor-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/reactor/SentinelReactorSubscriber.java

@@ -89,8 +89,8 @@ public class SentinelReactorSubscriber<T> extends InheritableBaseSubscriber<T> {
             ContextUtil.enter(sentinelContextConfig.getContextName(), sentinelContextConfig.getOrigin());
             ContextUtil.enter(sentinelContextConfig.getContextName(), sentinelContextConfig.getOrigin());
         }
         }
         try {
         try {
-            AsyncEntry entry = SphU.asyncEntry(entryConfig.getResourceName(), entryConfig.getEntryType(),
-                entryConfig.getAcquireCount(), entryConfig.getArgs());
+            AsyncEntry entry = SphU.asyncEntry(entryConfig.getResourceName(), entryConfig.getResourceType(),
+                entryConfig.getEntryType(), entryConfig.getAcquireCount(), entryConfig.getArgs());
             this.currentEntry = entry;
             this.currentEntry = entry;
             actual.onSubscribe(this);
             actual.onSubscribe(this);
         } catch (BlockException ex) {
         } catch (BlockException ex) {
@@ -155,7 +155,7 @@ public class SentinelReactorSubscriber<T> extends InheritableBaseSubscriber<T> {
 
 
     @Override
     @Override
     protected void hookOnCancel() {
     protected void hookOnCancel() {
-
+        tryCompleteEntry();
     }
     }
 
 
     private boolean tryCompleteEntry() {
     private boolean tryCompleteEntry() {

+ 22 - 0
sentinel-adapter/sentinel-reactor-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/reactor/MonoSentinelOperatorIntegrationTest.java

@@ -162,6 +162,28 @@ public class MonoSentinelOperatorIntegrationTest {
         assertEquals(1, cn.totalException());
         assertEquals(1, cn.totalException());
     }
     }
 
 
+    @Test
+    public void testMultipleReactorTransformerLatterFlowControl() {
+        String resourceName1 = createResourceName("testMultipleReactorTransformerLatterFlowControl1");
+        String resourceName2 = createResourceName("testMultipleReactorTransformerLatterFlowControl2");
+        FlowRuleManager.loadRules(Collections.singletonList(
+            new FlowRule(resourceName2).setCount(0)
+        ));
+        StepVerifier.create(Mono.just(2)
+            .transform(new SentinelReactorTransformer<>(resourceName1))
+            .transform(new SentinelReactorTransformer<>(resourceName2)))
+            .expectError(BlockException.class)
+            .verify();
+
+        ClusterNode cn1 = ClusterBuilderSlot.getClusterNode(resourceName1);
+        assertNotNull(cn1);
+        ClusterNode cn2 = ClusterBuilderSlot.getClusterNode(resourceName2);
+        assertNotNull(cn2);
+        assertEquals(1, cn2.blockRequest());
+        assertEquals(1, cn1.totalSuccess());
+        FlowRuleManager.loadRules(new ArrayList<>());
+    }
+
     private String createResourceName(String resourceName) {
     private String createResourceName(String resourceName) {
         return "reactor_test_mono_" + resourceName;
         return "reactor_test_mono_" + resourceName;
     }
     }

+ 0 - 2
sentinel-adapter/sentinel-spring-cloud-gateway-adapter/README.md

@@ -1,7 +1,5 @@
 # Sentinel Spring Cloud Gateway Adapter
 # Sentinel Spring Cloud Gateway Adapter
 
 
-> Note: this module requires Java 8 or later version.
-
 Sentinel provides integration module with Spring Cloud Gateway.
 Sentinel provides integration module with Spring Cloud Gateway.
 The integration module is based on the Sentinel Reactor Adapter.
 The integration module is based on the Sentinel Reactor Adapter.
 
 

+ 1 - 1
sentinel-adapter/sentinel-spring-cloud-gateway-adapter/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>sentinel-adapter</artifactId>
         <artifactId>sentinel-adapter</artifactId>
         <groupId>com.alibaba.csp</groupId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 

+ 22 - 4
sentinel-adapter/sentinel-spring-cloud-gateway-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/sc/SentinelGatewayFilter.java

@@ -20,6 +20,7 @@ import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.ResourceTypeConstants;
 import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
 import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
 import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser;
 import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser;
 import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
 import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
@@ -34,6 +35,7 @@ import org.springframework.cloud.gateway.filter.GatewayFilterChain;
 import org.springframework.cloud.gateway.filter.GlobalFilter;
 import org.springframework.cloud.gateway.filter.GlobalFilter;
 import org.springframework.cloud.gateway.route.Route;
 import org.springframework.cloud.gateway.route.Route;
 import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
 import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
+import org.springframework.core.Ordered;
 import org.springframework.web.server.ServerWebExchange;
 import org.springframework.web.server.ServerWebExchange;
 import reactor.core.publisher.Mono;
 import reactor.core.publisher.Mono;
 
 
@@ -41,7 +43,17 @@ import reactor.core.publisher.Mono;
  * @author Eric Zhao
  * @author Eric Zhao
  * @since 1.6.0
  * @since 1.6.0
  */
  */
-public class SentinelGatewayFilter implements GatewayFilter, GlobalFilter {
+public class SentinelGatewayFilter implements GatewayFilter, GlobalFilter, Ordered {
+
+    private final int order;
+
+    public SentinelGatewayFilter() {
+        this(Ordered.HIGHEST_PRECEDENCE);
+    }
+
+    public SentinelGatewayFilter(int order) {
+        this.order = order;
+    }
 
 
     private final GatewayParamParser<ServerWebExchange> paramParser = new GatewayParamParser<>(
     private final GatewayParamParser<ServerWebExchange> paramParser = new GatewayParamParser<>(
         new ServerWebExchangeItemParser());
         new ServerWebExchangeItemParser());
@@ -59,8 +71,8 @@ public class SentinelGatewayFilter implements GatewayFilter, GlobalFilter {
                 .map(f -> f.apply(exchange))
                 .map(f -> f.apply(exchange))
                 .orElse("");
                 .orElse("");
             asyncResult = asyncResult.transform(
             asyncResult = asyncResult.transform(
-                new SentinelReactorTransformer<>(new EntryConfig(routeId, EntryType.IN,
-                    1, params, new ContextConfig(contextName(routeId), origin)))
+                new SentinelReactorTransformer<>(new EntryConfig(routeId, ResourceTypeConstants.COMMON_API_GATEWAY,
+                    EntryType.IN, 1, params, new ContextConfig(contextName(routeId), origin)))
             );
             );
         }
         }
 
 
@@ -69,7 +81,8 @@ public class SentinelGatewayFilter implements GatewayFilter, GlobalFilter {
             Object[] params = paramParser.parseParameterFor(apiName, exchange,
             Object[] params = paramParser.parseParameterFor(apiName, exchange,
                 r -> r.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME);
                 r -> r.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME);
             asyncResult = asyncResult.transform(
             asyncResult = asyncResult.transform(
-                new SentinelReactorTransformer<>(new EntryConfig(apiName, EntryType.IN, 1, params))
+                new SentinelReactorTransformer<>(new EntryConfig(apiName, ResourceTypeConstants.COMMON_API_GATEWAY,
+                    EntryType.IN, 1, params))
             );
             );
         }
         }
 
 
@@ -87,4 +100,9 @@ public class SentinelGatewayFilter implements GatewayFilter, GlobalFilter {
             .map(WebExchangeApiMatcher::getApiName)
             .map(WebExchangeApiMatcher::getApiName)
             .collect(Collectors.toSet());
             .collect(Collectors.toSet());
     }
     }
+
+    @Override
+    public int getOrder() {
+        return order;
+    }
 }
 }

+ 1 - 1
sentinel-adapter/sentinel-spring-cloud-gateway-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/sc/ServerWebExchangeItemParser.java

@@ -55,7 +55,7 @@ public class ServerWebExchangeItemParser implements RequestItemParser<ServerWebE
 
 
     @Override
     @Override
     public String getCookieValue(ServerWebExchange exchange, String cookieName) {
     public String getCookieValue(ServerWebExchange exchange, String cookieName) {
-        return Optional.ofNullable(exchange.getResponse().getCookies().getFirst(cookieName))
+        return Optional.ofNullable(exchange.getRequest().getCookies().getFirst(cookieName))
             .map(HttpCookie::getValue)
             .map(HttpCookie::getValue)
             .orElse(null);
             .orElse(null);
     }
     }

+ 9 - 12
sentinel-adapter/sentinel-spring-cloud-gateway-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/sc/api/GatewayApiMatcherManager.java

@@ -15,23 +15,23 @@
  */
  */
 package com.alibaba.csp.sentinel.adapter.gateway.sc.api;
 package com.alibaba.csp.sentinel.adapter.gateway.sc.api;
 
 
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
+import com.alibaba.csp.sentinel.adapter.gateway.sc.api.matcher.WebExchangeApiMatcher;
+
 import java.util.Collections;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.Set;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
-import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
-import com.alibaba.csp.sentinel.adapter.gateway.sc.api.matcher.WebExchangeApiMatcher;
-
 /**
 /**
  * @author Eric Zhao
  * @author Eric Zhao
  * @since 1.6.0
  * @since 1.6.0
  */
  */
 public final class GatewayApiMatcherManager {
 public final class GatewayApiMatcherManager {
 
 
-    private static final Map<String, WebExchangeApiMatcher> API_MATCHER_MAP = new ConcurrentHashMap<>();
+    private static volatile Map<String, WebExchangeApiMatcher> API_MATCHER_MAP = new HashMap<>();
 
 
     public static Map<String, WebExchangeApiMatcher> getApiMatcherMap() {
     public static Map<String, WebExchangeApiMatcher> getApiMatcherMap() {
         return Collections.unmodifiableMap(API_MATCHER_MAP);
         return Collections.unmodifiableMap(API_MATCHER_MAP);
@@ -50,15 +50,12 @@ public final class GatewayApiMatcherManager {
     }
     }
 
 
     static synchronized void loadApiDefinitions(/*@Valid*/ Set<ApiDefinition> definitions) {
     static synchronized void loadApiDefinitions(/*@Valid*/ Set<ApiDefinition> definitions) {
-        if (definitions == null || definitions.isEmpty()) {
-            API_MATCHER_MAP.clear();
-            return;
+        Map<String, WebExchangeApiMatcher> apiMatcherMap = new HashMap<>();
+        for (ApiDefinition definition : definitions) {
+            apiMatcherMap.put(definition.getApiName(), new WebExchangeApiMatcher(definition));
         }
         }
-        definitions.forEach(GatewayApiMatcherManager::addApiDefinition);
-    }
 
 
-    static void addApiDefinition(ApiDefinition definition) {
-        API_MATCHER_MAP.put(definition.getApiName(), new WebExchangeApiMatcher(definition));
+        API_MATCHER_MAP = apiMatcherMap;
     }
     }
 
 
     private GatewayApiMatcherManager() {}
     private GatewayApiMatcherManager() {}

+ 1 - 3
sentinel-adapter/sentinel-spring-webflux-adapter/README.md

@@ -1,7 +1,5 @@
 # Sentinel Spring WebFlux Adapter
 # Sentinel Spring WebFlux Adapter
 
 
-> Note: this module requires Java 8 or later version.
-
 Sentinel provides integration module with Spring WebFlux, so reactive web applications can also leverage Sentinel's flow control
 Sentinel provides integration module with Spring WebFlux, so reactive web applications can also leverage Sentinel's flow control
 and circuit breaking to achieve reliability. The integration module is based on the Sentinel Reactor Adapter.
 and circuit breaking to achieve reliability. The integration module is based on the Sentinel Reactor Adapter.
 
 
@@ -50,7 +48,7 @@ public class WebFluxConfig {
 You can register various customized callback in `WebFluxCallbackManager`:
 You can register various customized callback in `WebFluxCallbackManager`:
 
 
 - `setBlockHandler`: register a customized `BlockRequestHandler` to handle the blocked request. The default implementation is `DefaultBlockRequestHandler`, which returns default message like `Blocked by Sentinel: FlowException`.
 - `setBlockHandler`: register a customized `BlockRequestHandler` to handle the blocked request. The default implementation is `DefaultBlockRequestHandler`, which returns default message like `Blocked by Sentinel: FlowException`.
-- `setUrlCleaner`: used for normalization of URL. The function type is `(ServerWebExchange, String) → String`, which means `(webExchange, originalUrl) → finalUrl`.
+- `setUrlCleaner`: used for normalization of URL. The function type is `(ServerWebExchange, String) → String`, which means `(webExchange, originalUrl) → finalUrl`, if the finalUrl is `"""` or `null`, the URLs will be excluded (since Sentinel 1.7.0)..
 - `setRequestOriginParser`: used to resolve the origin from the HTTP request. The function type is `ServerWebExchange → String`.
 - `setRequestOriginParser`: used to resolve the origin from the HTTP request. The function type is `ServerWebExchange → String`.
 
 
 You can also refer to the demo: [sentinel-demo-spring-webflux](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-spring-webflux).
 You can also refer to the demo: [sentinel-demo-spring-webflux](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-spring-webflux).

+ 1 - 1
sentinel-adapter/sentinel-spring-webflux-adapter/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>sentinel-adapter</artifactId>
         <artifactId>sentinel-adapter</artifactId>
         <groupId>com.alibaba.csp</groupId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 

+ 14 - 11
sentinel-adapter/sentinel-spring-webflux-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webflux/SentinelWebFluxFilter.java

@@ -18,10 +18,12 @@ package com.alibaba.csp.sentinel.adapter.spring.webflux;
 import java.util.Optional;
 import java.util.Optional;
 
 
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.ResourceTypeConstants;
 import com.alibaba.csp.sentinel.adapter.reactor.ContextConfig;
 import com.alibaba.csp.sentinel.adapter.reactor.ContextConfig;
 import com.alibaba.csp.sentinel.adapter.reactor.EntryConfig;
 import com.alibaba.csp.sentinel.adapter.reactor.EntryConfig;
 import com.alibaba.csp.sentinel.adapter.reactor.SentinelReactorTransformer;
 import com.alibaba.csp.sentinel.adapter.reactor.SentinelReactorTransformer;
 import com.alibaba.csp.sentinel.adapter.spring.webflux.callback.WebFluxCallbackManager;
 import com.alibaba.csp.sentinel.adapter.spring.webflux.callback.WebFluxCallbackManager;
+import com.alibaba.csp.sentinel.util.StringUtil;
 
 
 import org.springframework.web.server.ServerWebExchange;
 import org.springframework.web.server.ServerWebExchange;
 import org.springframework.web.server.WebFilter;
 import org.springframework.web.server.WebFilter;
@@ -36,24 +38,25 @@ public class SentinelWebFluxFilter implements WebFilter {
 
 
     @Override
     @Override
     public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
     public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
-        return chain.filter(exchange)
-            .transform(buildSentinelTransformer(exchange));
-    }
-
-    private SentinelReactorTransformer<Void> buildSentinelTransformer(ServerWebExchange exchange) {
         // Maybe we can get the URL pattern elsewhere via:
         // Maybe we can get the URL pattern elsewhere via:
         // exchange.getAttributeOrDefault(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, path)
         // exchange.getAttributeOrDefault(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, path)
-
         String path = exchange.getRequest().getPath().value();
         String path = exchange.getRequest().getPath().value();
-        String finalPath = Optional.ofNullable(WebFluxCallbackManager.getUrlCleaner())
-            .map(f -> f.apply(exchange, path))
-            .orElse(path);
+
+        String finalPath = WebFluxCallbackManager.getUrlCleaner().apply(exchange, path);
+        if (StringUtil.isEmpty(finalPath)) {
+            return chain.filter(exchange);
+        }
+        return chain.filter(exchange)
+            .transform(buildSentinelTransformer(exchange, finalPath));
+    }
+
+    private SentinelReactorTransformer<Void> buildSentinelTransformer(ServerWebExchange exchange, String finalPath) {
         String origin = Optional.ofNullable(WebFluxCallbackManager.getRequestOriginParser())
         String origin = Optional.ofNullable(WebFluxCallbackManager.getRequestOriginParser())
             .map(f -> f.apply(exchange))
             .map(f -> f.apply(exchange))
             .orElse(EMPTY_ORIGIN);
             .orElse(EMPTY_ORIGIN);
 
 
-        return new SentinelReactorTransformer<>(
-            new EntryConfig(finalPath, EntryType.IN, new ContextConfig(finalPath, origin)));
+        return new SentinelReactorTransformer<>(new EntryConfig(finalPath, ResourceTypeConstants.COMMON_WEB,
+            EntryType.IN, new ContextConfig(finalPath, origin)));
     }
     }
 
 
     private static final String EMPTY_ORIGIN = "";
     private static final String EMPTY_ORIGIN = "";

+ 45 - 1
sentinel-adapter/sentinel-spring-webflux-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webflux/SentinelWebFluxIntegrationTest.java

@@ -72,6 +72,30 @@ public class SentinelWebFluxIntegrationTest {
     }
     }
 
 
     @Test
     @Test
+    public void testWebFluxRouterFunction() throws Exception {
+
+        String url = "/router/hello";
+        this.webClient.get()
+                .uri(url)
+                .accept(MediaType.TEXT_PLAIN)
+                .exchange()
+                .expectStatus().isOk()
+                .expectBody(String.class).isEqualTo(HELLO_STR);
+
+        ClusterNode cn = ClusterBuilderSlot.getClusterNode(url);
+        assertNotNull(cn);
+        assertEquals(1, cn.passQps(), 0.01);
+
+        configureRulesFor(url, 0);
+        this.webClient.get()
+                .uri(url)
+                .accept(MediaType.TEXT_PLAIN)
+                .exchange()
+                .expectStatus().isEqualTo(HttpStatus.TOO_MANY_REQUESTS)
+                .expectBody(String.class).value(StringContains.containsString(BLOCK_MSG_PREFIX));
+    }
+
+    @Test
     public void testCustomizedUrlCleaner() throws Exception {
     public void testCustomizedUrlCleaner() throws Exception {
         final String fooPrefix = "/foo/";
         final String fooPrefix = "/foo/";
         String url1 = fooPrefix + 1;
         String url1 = fooPrefix + 1;
@@ -102,6 +126,26 @@ public class SentinelWebFluxIntegrationTest {
     }
     }
 
 
     @Test
     @Test
+    public void testCustomizedIgnoreUrlCleaner() throws Exception {
+        final String fooPrefix = "/foo/";
+        String url1 = fooPrefix + 1;
+        WebFluxCallbackManager.setUrlCleaner(((exchange, originUrl) -> {
+            if (originUrl.startsWith(fooPrefix)) {
+                return "";
+            }
+            return originUrl;
+        }));
+        this.webClient.get()
+                .uri(url1)
+                .exchange()
+                .expectStatus().isOk()
+                .expectBody(String.class).isEqualTo("Hello 1");
+
+        assertNull(ClusterBuilderSlot.getClusterNode(url1));
+        WebFluxCallbackManager.resetUrlCleaner();
+    }
+
+    @Test
     public void testCustomizedBlockRequestHandler() throws Exception {
     public void testCustomizedBlockRequestHandler() throws Exception {
         String url = "/error";
         String url = "/error";
         String prefix = "blocked: ";
         String prefix = "blocked: ";
@@ -166,4 +210,4 @@ public class SentinelWebFluxIntegrationTest {
         FlowRuleManager.loadRules(new ArrayList<>());
         FlowRuleManager.loadRules(new ArrayList<>());
         ClusterBuilderSlot.resetClusterNodes();
         ClusterBuilderSlot.resetClusterNodes();
     }
     }
-}
+}

+ 17 - 7
sentinel-adapter/sentinel-web-servlet/README.md

@@ -1,6 +1,7 @@
 # Sentinel Web Servlet Filter
 # Sentinel Web Servlet Filter
 
 
-Sentinel provides Servlet filter integration to enable flow control for web requests. Add the following dependency in `pom.xml` (if you are using Maven):
+Sentinel provides Servlet filter integration to enable flow control for web requests.
+Add the following dependency in `pom.xml` (if you are using Maven):
 
 
 ```xml
 ```xml
 <dependency>
 <dependency>
@@ -10,7 +11,7 @@ Sentinel provides Servlet filter integration to enable flow control for web requ
 </dependency>
 </dependency>
 ```
 ```
 
 
-To use the filter, you can simply configure your `web.xml` with:
+To activate the filter, you can simply configure your `web.xml` with:
 
 
 ```xml
 ```xml
 <filter>
 <filter>
@@ -34,16 +35,21 @@ public class FilterConfig {
     public FilterRegistrationBean sentinelFilterRegistration() {
     public FilterRegistrationBean sentinelFilterRegistration() {
         FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
         FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
         registration.setFilter(new CommonFilter());
         registration.setFilter(new CommonFilter());
+        // Set the matching URL pattern for the filter.
         registration.addUrlPatterns("/*");
         registration.addUrlPatterns("/*");
-        registration.setName("sentinelFilter");
+        registration.setName("sentinelCommonFilter");
         registration.setOrder(1);
         registration.setOrder(1);
-
+        // Set whether to support the specified HTTP method prefix for the filter.
+        registration.addInitParameter(CommonFilter.HTTP_METHOD_SPECIFY, "false");
         return registration;
         return registration;
     }
     }
 }
 }
 ```
 ```
 
 
-When a request is blocked, Sentinel servlet filter will give a default page indicating the request blocked.
+When a request is blocked, Sentinel servlet filter will display a default page indicating the request is rejected.
+The HTTP status code of the default block page is **429 (Too Many Requests)**. You can customize it
+via the `csp.sentinel.web.servlet.block.status` configuration item (since 1.7.0).
+
 If customized block page is set (via `WebServletConfig.setBlockPage(blockPage)` method),
 If customized block page is set (via `WebServletConfig.setBlockPage(blockPage)` method),
 the filter will redirect the request to provided URL. You can also implement your own
 the filter will redirect the request to provided URL. You can also implement your own
 block handler (the `UrlBlockHandler` interface) and register to `WebCallbackManager`.
 block handler (the `UrlBlockHandler` interface) and register to `WebCallbackManager`.
@@ -52,5 +58,9 @@ The `UrlCleaner` interface is designed for clean and unify the URL resource.
 For REST APIs, you have to clean the URL resource (e.g. `/foo/1` and `/foo/2` -> `/foo/:id`), or
 For REST APIs, you have to clean the URL resource (e.g. `/foo/1` and `/foo/2` -> `/foo/:id`), or
 the amount of context and resources will exceed the threshold.
 the amount of context and resources will exceed the threshold.
 
 
-`RequestOriginParser` interface is useful for extracting request origin (e.g. IP or appName from HTTP Header)
-from HTTP request. You can implement your own `RequestOriginParser` and register to `WebCallbackManager`.
+If you need to exclude some URLs (that should not be recorded as Sentinel resources), you could also
+leverage the `UrlCleaner` interface. You may unify the unwanted URLs to the empty string `""` or `null`,
+then the URLs will be excluded (since Sentinel 1.6.3).
+
+The `RequestOriginParser` interface is useful for extracting request origin (e.g. IP or appName from HTTP Header)
+from HTTP request. You can implement your own `RequestOriginParser` and register to `WebCallbackManager`.

+ 1 - 1
sentinel-adapter/sentinel-web-servlet/pom.xml

@@ -6,7 +6,7 @@
     <parent>
     <parent>
         <groupId>com.alibaba.csp</groupId>
         <groupId>com.alibaba.csp</groupId>
         <artifactId>sentinel-adapter</artifactId>
         <artifactId>sentinel-adapter</artifactId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     </parent>
 
 
     <artifactId>sentinel-web-servlet</artifactId>
     <artifactId>sentinel-web-servlet</artifactId>

+ 42 - 30
sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/CommonFilter.java

@@ -28,40 +28,58 @@ import javax.servlet.http.HttpServletResponse;
 
 
 import com.alibaba.csp.sentinel.Entry;
 import com.alibaba.csp.sentinel.Entry;
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.ResourceTypeConstants;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.Tracer;
 import com.alibaba.csp.sentinel.Tracer;
 import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
 import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
 import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner;
 import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner;
 import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
 import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
+import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig;
 import com.alibaba.csp.sentinel.adapter.servlet.util.FilterUtil;
 import com.alibaba.csp.sentinel.adapter.servlet.util.FilterUtil;
 import com.alibaba.csp.sentinel.context.ContextUtil;
 import com.alibaba.csp.sentinel.context.ContextUtil;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 import com.alibaba.csp.sentinel.util.StringUtil;
 import com.alibaba.csp.sentinel.util.StringUtil;
 
 
-/***
+/**
  * Servlet filter that integrates with Sentinel.
  * Servlet filter that integrates with Sentinel.
  *
  *
  * @author youji.zj
  * @author youji.zj
  * @author Eric Zhao
  * @author Eric Zhao
+ * @author zhaoyuguang
  */
  */
 public class CommonFilter implements Filter {
 public class CommonFilter implements Filter {
 
 
-    private final static String HTTP_METHOD_SPECIFY = "HTTP_METHOD_SPECIFY";
+    /**
+     * Specify whether the URL resource name should contain the HTTP method prefix (e.g. {@code POST:}).
+     */
+    public static final String HTTP_METHOD_SPECIFY = "HTTP_METHOD_SPECIFY";
+    /**
+     * 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.
+     *
+     * @since 1.7.0
+     */
+    public static final String WEB_CONTEXT_UNIFY = "WEB_CONTEXT_UNIFY";
+
     private final static String COLON = ":";
     private final static String COLON = ":";
+
     private boolean httpMethodSpecify = false;
     private boolean httpMethodSpecify = false;
+    private boolean webContextUnify = true;
 
 
     @Override
     @Override
     public void init(FilterConfig filterConfig) {
     public void init(FilterConfig filterConfig) {
         httpMethodSpecify = Boolean.parseBoolean(filterConfig.getInitParameter(HTTP_METHOD_SPECIFY));
         httpMethodSpecify = Boolean.parseBoolean(filterConfig.getInitParameter(HTTP_METHOD_SPECIFY));
+        if (filterConfig.getInitParameter(WEB_CONTEXT_UNIFY) != null) {
+            webContextUnify = Boolean.parseBoolean(filterConfig.getInitParameter(WEB_CONTEXT_UNIFY));
+        }
     }
     }
 
 
     @Override
     @Override
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
             throws IOException, ServletException {
             throws IOException, ServletException {
         HttpServletRequest sRequest = (HttpServletRequest) request;
         HttpServletRequest sRequest = (HttpServletRequest) request;
-        Entry entry = null;
-
-        Entry methodEntry = null;
+        Entry urlEntry = null;
 
 
         try {
         try {
             String target = FilterUtil.filterTarget(sRequest);
             String target = FilterUtil.filterTarget(sRequest);
@@ -73,39 +91,33 @@ public class CommonFilter implements Filter {
                 target = urlCleaner.clean(target);
                 target = urlCleaner.clean(target);
             }
             }
 
 
-            // Parse the request origin using registered origin parser.
-            String origin = parseOrigin(sRequest);
-
-            ContextUtil.enter(target, origin);
-            entry = SphU.entry(target, EntryType.IN);
-
-
-            // Add method specification if necessary
-            if (httpMethodSpecify) {
-                methodEntry = SphU.entry(sRequest.getMethod().toUpperCase() + COLON + target,
-                        EntryType.IN);
+            // If you intend to exclude some URLs, you can convert the URLs to the empty string ""
+            // in the UrlCleaner implementation.
+            if (!StringUtil.isEmpty(target)) {
+                // Parse the request origin using registered origin parser.
+                String origin = parseOrigin(sRequest);
+                String contextName = webContextUnify ? WebServletConfig.WEB_SERVLET_CONTEXT_NAME : target;
+                ContextUtil.enter(contextName, origin);
+
+                if (httpMethodSpecify) {
+                    // Add HTTP method prefix if necessary.
+                    String pathWithHttpMethod = sRequest.getMethod().toUpperCase() + COLON + target;
+                    urlEntry = SphU.entry(pathWithHttpMethod, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
+                } else {
+                    urlEntry = SphU.entry(target, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
+                }
             }
             }
-
             chain.doFilter(request, response);
             chain.doFilter(request, response);
         } catch (BlockException e) {
         } catch (BlockException e) {
             HttpServletResponse sResponse = (HttpServletResponse) response;
             HttpServletResponse sResponse = (HttpServletResponse) response;
             // Return the block page, or redirect to another URL.
             // Return the block page, or redirect to another URL.
             WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse, e);
             WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse, e);
-        } catch (IOException e2) {
-            Tracer.trace(e2);
+        } catch (IOException | ServletException | RuntimeException e2) {
+            Tracer.traceEntry(e2, urlEntry);
             throw e2;
             throw e2;
-        } catch (ServletException e3) {
-            Tracer.trace(e3);
-            throw e3;
-        } catch (RuntimeException e4) {
-            Tracer.trace(e4);
-            throw e4;
         } finally {
         } finally {
-            if (methodEntry != null) {
-                methodEntry.exit();
-            }
-            if (entry != null) {
-                entry.exit();
+            if (urlEntry != null) {
+                urlEntry.exit();
             }
             }
             ContextUtil.exit();
             ContextUtil.exit();
         }
         }

+ 5 - 12
sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/CommonTotalFilter.java

@@ -27,10 +27,11 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletResponse;
 
 
 import com.alibaba.csp.sentinel.Entry;
 import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.ResourceTypeConstants;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.Tracer;
 import com.alibaba.csp.sentinel.Tracer;
 import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
 import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
-import com.alibaba.csp.sentinel.adapter.servlet.util.FilterUtil;
+import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig;
 import com.alibaba.csp.sentinel.context.ContextUtil;
 import com.alibaba.csp.sentinel.context.ContextUtil;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 
 
@@ -52,26 +53,18 @@ public class CommonTotalFilter implements Filter {
     public void doFilter(ServletRequest request, ServletResponse response,
     public void doFilter(ServletRequest request, ServletResponse response,
                          FilterChain chain) throws IOException, ServletException {
                          FilterChain chain) throws IOException, ServletException {
         HttpServletRequest sRequest = (HttpServletRequest)request;
         HttpServletRequest sRequest = (HttpServletRequest)request;
-        String target = FilterUtil.filterTarget(sRequest);
-        target = WebCallbackManager.getUrlCleaner().clean(target);
 
 
         Entry entry = null;
         Entry entry = null;
         try {
         try {
-            ContextUtil.enter(target);
-            entry = SphU.entry(TOTAL_URL_REQUEST);
+            ContextUtil.enter(WebServletConfig.WEB_SERVLET_CONTEXT_NAME);
+            entry = SphU.entry(TOTAL_URL_REQUEST, ResourceTypeConstants.COMMON_WEB);
             chain.doFilter(request, response);
             chain.doFilter(request, response);
         } catch (BlockException e) {
         } catch (BlockException e) {
             HttpServletResponse sResponse = (HttpServletResponse)response;
             HttpServletResponse sResponse = (HttpServletResponse)response;
             WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse, e);
             WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse, e);
-        } catch (IOException e2) {
+        } catch (IOException | ServletException | RuntimeException e2) {
             Tracer.trace(e2);
             Tracer.trace(e2);
             throw e2;
             throw e2;
-        } catch (ServletException e3) {
-            Tracer.trace(e3);
-            throw e3;
-        } catch (RuntimeException e4) {
-            Tracer.trace(e4);
-            throw e4;
         } finally {
         } finally {
             if (entry != null) {
             if (entry != null) {
                 entry.exit();
                 entry.exit();

+ 56 - 4
sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/config/WebServletConfig.java

@@ -18,13 +18,23 @@ package com.alibaba.csp.sentinel.adapter.servlet.config;
 import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
 import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
 import com.alibaba.csp.sentinel.adapter.servlet.CommonTotalFilter;
 import com.alibaba.csp.sentinel.adapter.servlet.CommonTotalFilter;
 import com.alibaba.csp.sentinel.config.SentinelConfig;
 import com.alibaba.csp.sentinel.config.SentinelConfig;
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.util.StringUtil;
 
 
 /**
 /**
+ * The configuration center for Web Servlet adapter.
+ *
  * @author leyou
  * @author leyou
+ * @author zhaoyuguang
  */
  */
-public class WebServletConfig {
+public final class WebServletConfig {
+
+    public static final String WEB_SERVLET_CONTEXT_NAME = "sentinel_web_servlet_context";
 
 
-    public static final String BLOCK_PAGE = "csp.sentinel.web.servlet.block.page";
+    public static final String BLOCK_PAGE_URL_CONF_KEY = "csp.sentinel.web.servlet.block.page";
+    public static final String BLOCK_PAGE_HTTP_STATUS_CONF_KEY = "csp.sentinel.web.servlet.block.status";
+
+    private static final int HTTP_STATUS_TOO_MANY_REQUESTS = 429;
 
 
     /**
     /**
      * Get redirecting page when Sentinel blocking for {@link CommonFilter} or
      * Get redirecting page when Sentinel blocking for {@link CommonFilter} or
@@ -33,10 +43,52 @@ public class WebServletConfig {
      * @return the block page URL, maybe null if not configured.
      * @return the block page URL, maybe null if not configured.
      */
      */
     public static String getBlockPage() {
     public static String getBlockPage() {
-        return SentinelConfig.getConfig(BLOCK_PAGE);
+        return SentinelConfig.getConfig(BLOCK_PAGE_URL_CONF_KEY);
     }
     }
 
 
     public static void setBlockPage(String blockPage) {
     public static void setBlockPage(String blockPage) {
-        SentinelConfig.setConfig(BLOCK_PAGE, blockPage);
+        SentinelConfig.setConfig(BLOCK_PAGE_URL_CONF_KEY, blockPage);
+    }
+
+    /**
+     * <p>Get the HTTP status when using the default block page.</p>
+     * <p>You can set the status code with the {@code -Dcsp.sentinel.web.servlet.block.status}
+     * property. When the property is empty or invalid, Sentinel will use 429 (Too Many Requests)
+     * as the default status code.</p>
+     *
+     * @return the HTTP status of the default block page
+     * @since 1.7.0
+     */
+    public static int getBlockPageHttpStatus() {
+        String value = SentinelConfig.getConfig(BLOCK_PAGE_HTTP_STATUS_CONF_KEY);
+        if (StringUtil.isEmpty(value)) {
+            return HTTP_STATUS_TOO_MANY_REQUESTS;
+        }
+        try {
+            int s = Integer.parseInt(value);
+            if (s <= 0) {
+                throw new IllegalArgumentException("Invalid status code: " + s);
+            }
+            return s;
+        } catch (Exception e) {
+            RecordLog.warn("[WebServletConfig] Invalid block HTTP status (" + value + "), using default 429");
+            setBlockPageHttpStatus(HTTP_STATUS_TOO_MANY_REQUESTS);
+        }
+        return HTTP_STATUS_TOO_MANY_REQUESTS;
     }
     }
+
+    /**
+     * Set the HTTP status of the default block page.
+     *
+     * @param httpStatus the HTTP status of the default block page
+     * @since 1.7.0
+     */
+    public static void setBlockPageHttpStatus(int httpStatus) {
+        if (httpStatus <= 0) {
+            throw new IllegalArgumentException("Invalid HTTP status code: " + httpStatus);
+        }
+        SentinelConfig.setConfig(BLOCK_PAGE_HTTP_STATUS_CONF_KEY, String.valueOf(httpStatus));
+    }
+
+    private WebServletConfig() {}
 }
 }

+ 4 - 2
sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/util/FilterUtil.java

@@ -27,6 +27,7 @@ import com.alibaba.csp.sentinel.util.StringUtil;
 /**
 /**
  * Util class for web servlet filter.
  * Util class for web servlet filter.
  *
  *
+ * @author zhaoyuguang
  * @author youji.zj
  * @author youji.zj
  * @author Eric Zhao
  * @author Eric Zhao
  */
  */
@@ -65,7 +66,7 @@ public final class FilterUtil {
         }
         }
 
 
         if (StringUtil.isBlank(WebServletConfig.getBlockPage())) {
         if (StringUtil.isBlank(WebServletConfig.getBlockPage())) {
-            writeDefaultBlockedPage(response);
+            writeDefaultBlockedPage(response, WebServletConfig.getBlockPageHttpStatus());
         } else {
         } else {
             String redirectUrl = WebServletConfig.getBlockPage() + "?http_referer=" + url.toString();
             String redirectUrl = WebServletConfig.getBlockPage() + "?http_referer=" + url.toString();
             // Redirect to the customized block page.
             // Redirect to the customized block page.
@@ -73,7 +74,8 @@ public final class FilterUtil {
         }
         }
     }
     }
 
 
-    private static void writeDefaultBlockedPage(HttpServletResponse response) throws IOException {
+    private static void writeDefaultBlockedPage(HttpServletResponse response, int httpStatus) throws IOException {
+        response.setStatus(httpStatus);
         PrintWriter out = response.getWriter();
         PrintWriter out = response.getWriter();
         out.print(DEFAULT_BLOCK_MSG);
         out.print(DEFAULT_BLOCK_MSG);
         out.flush();
         out.flush();

+ 45 - 3
sentinel-adapter/sentinel-web-servlet/src/test/java/com/alibaba/csp/sentinel/adapter/servlet/CommonFilterTest.java

@@ -19,6 +19,7 @@ import java.util.Collections;
 
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequest;
 
 
+import com.alibaba.csp.sentinel.Constants;
 import com.alibaba.csp.sentinel.adapter.servlet.callback.DefaultUrlCleaner;
 import com.alibaba.csp.sentinel.adapter.servlet.callback.DefaultUrlCleaner;
 import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
 import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
 import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner;
 import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner;
@@ -26,6 +27,8 @@ import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
 import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig;
 import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig;
 import com.alibaba.csp.sentinel.adapter.servlet.util.FilterUtil;
 import com.alibaba.csp.sentinel.adapter.servlet.util.FilterUtil;
 import com.alibaba.csp.sentinel.node.ClusterNode;
 import com.alibaba.csp.sentinel.node.ClusterNode;
+import com.alibaba.csp.sentinel.node.EntranceNode;
+import com.alibaba.csp.sentinel.node.Node;
 import com.alibaba.csp.sentinel.slots.block.RuleConstant;
 import com.alibaba.csp.sentinel.slots.block.RuleConstant;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
@@ -38,15 +41,17 @@ import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.http.MediaType;
 import org.springframework.test.context.junit4.SpringRunner;
 import org.springframework.test.context.junit4.SpringRunner;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.MockMvc;
 
 
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
 
 
 /**
 /**
+ * @author zhaoyuguang
  * @author Eric Zhao
  * @author Eric Zhao
  */
  */
 @RunWith(SpringRunner.class)
 @RunWith(SpringRunner.class)
@@ -76,6 +81,7 @@ public class CommonFilterTest {
 
 
     @Test
     @Test
     public void testCommonFilterMiscellaneous() throws Exception {
     public void testCommonFilterMiscellaneous() throws Exception {
+        Constants.ROOT.removeChildList();
         String url = "/hello";
         String url = "/hello";
         this.mvc.perform(get(url))
         this.mvc.perform(get(url))
             .andExpect(status().isOk())
             .andExpect(status().isOk())
@@ -85,22 +91,39 @@ public class CommonFilterTest {
         assertNotNull(cn);
         assertNotNull(cn);
         assertEquals(1, cn.passQps(), 0.01);
         assertEquals(1, cn.passQps(), 0.01);
 
 
+        String context = "";
+        for (Node n : Constants.ROOT.getChildList()) {
+            if (n instanceof EntranceNode) {
+                String id = ((EntranceNode) n).getId().getName();
+                if (url.equals(id)) {
+                    context = ((EntranceNode) n).getId().getName();
+                }
+            }
+        }
+        assertEquals("", context);
+
         testCommonBlockAndRedirectBlockPage(url, cn);
         testCommonBlockAndRedirectBlockPage(url, cn);
 
 
         // Test for url cleaner.
         // Test for url cleaner.
         testUrlCleaner();
         testUrlCleaner();
-
+        testUrlExclusion();
         testCustomOriginParser();
         testCustomOriginParser();
     }
     }
 
 
     private void testCommonBlockAndRedirectBlockPage(String url, ClusterNode cn) throws Exception {
     private void testCommonBlockAndRedirectBlockPage(String url, ClusterNode cn) throws Exception {
         configureRulesFor(url, 0);
         configureRulesFor(url, 0);
         // The request will be blocked and response is default block message.
         // The request will be blocked and response is default block message.
+        WebServletConfig.setBlockPageHttpStatus(HttpStatus.OK.value());
         this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN))
         this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN))
             .andExpect(status().isOk())
             .andExpect(status().isOk())
             .andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG));
             .andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG));
         assertEquals(1, cn.blockQps(), 0.01);
         assertEquals(1, cn.blockQps(), 0.01);
 
 
+        WebServletConfig.setBlockPageHttpStatus(HttpStatus.TOO_MANY_REQUESTS.value());
+        this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN))
+                .andExpect(status().isTooManyRequests())
+                .andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG));
+
         // Test for redirect.
         // Test for redirect.
         String redirectUrl = "http://some-location.com";
         String redirectUrl = "http://some-location.com";
         WebServletConfig.setBlockPage(redirectUrl);
         WebServletConfig.setBlockPage(redirectUrl);
@@ -139,6 +162,25 @@ public class CommonFilterTest {
         WebCallbackManager.setUrlCleaner(new DefaultUrlCleaner());
         WebCallbackManager.setUrlCleaner(new DefaultUrlCleaner());
     }
     }
 
 
+    private void testUrlExclusion() throws Exception {
+        final String excludePrefix = "/exclude/";
+        String url = excludePrefix + 1;
+        WebCallbackManager.setUrlCleaner(new UrlCleaner() {
+            @Override
+            public String clean(String originUrl) {
+                if(originUrl.startsWith(excludePrefix)) {
+                    return "";
+                }
+                return originUrl;
+            }
+        });
+        this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN))
+                .andExpect(status().isOk())
+                .andExpect(content().string("Exclude 1"));
+        assertNull(ClusterBuilderSlot.getClusterNode(url));
+        WebCallbackManager.setUrlCleaner(new DefaultUrlCleaner());
+    }
+
     private void testCustomOriginParser() throws Exception {
     private void testCustomOriginParser() throws Exception {
         String url = "/hello";
         String url = "/hello";
         String limitOrigin = "userA";
         String limitOrigin = "userA";
@@ -158,7 +200,7 @@ public class CommonFilterTest {
             .andExpect(content().string(HELLO_STR));
             .andExpect(content().string(HELLO_STR));
         // This will be blocked.
         // This will be blocked.
         this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN).header(headerName, limitOrigin))
         this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN).header(headerName, limitOrigin))
-            .andExpect(status().isOk())
+            .andExpect(status().isTooManyRequests())
             .andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG));
             .andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG));
         this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN))
         this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN))
             .andExpect(status().isOk())
             .andExpect(status().isOk())

+ 5 - 0
sentinel-adapter/sentinel-web-servlet/src/test/java/com/alibaba/csp/sentinel/adapter/servlet/TestController.java

@@ -39,4 +39,9 @@ public class TestController {
     public String apiFoo(@PathVariable("id") Long id) {
     public String apiFoo(@PathVariable("id") Long id) {
         return "Hello " + id;
         return "Hello " + id;
     }
     }
+
+    @GetMapping("/exclude/{id}")
+    public String apiExclude(@PathVariable("id") Long id) {
+        return "Exclude " + id;
+    }
 }
 }

+ 2 - 1
sentinel-adapter/sentinel-web-servlet/src/test/java/com/alibaba/csp/sentinel/adapter/servletmethod/CommonFilterMethodTest.java

@@ -41,6 +41,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
 
 
 /**
 /**
+ * @author zhaoyuguang
  * @author Roger Law
  * @author Roger Law
  */
  */
 @RunWith(SpringRunner.class)
 @RunWith(SpringRunner.class)
@@ -107,7 +108,7 @@ public class CommonFilterMethodTest {
         configureRulesFor(GET + ":" + url, 0);
         configureRulesFor(GET + ":" + url, 0);
         // The request will be blocked and response is default block message.
         // The request will be blocked and response is default block message.
         this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN))
         this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN))
-                .andExpect(status().isOk())
+                .andExpect(status().isTooManyRequests())
                 .andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG));
                 .andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG));
         assertEquals(1, cnGet.blockQps(), 0.01);
         assertEquals(1, cnGet.blockQps(), 0.01);
 
 

+ 1 - 1
sentinel-adapter/sentinel-web-servlet/src/test/java/com/alibaba/csp/sentinel/adapter/servletmethod/FilterMethodConfig.java

@@ -16,7 +16,7 @@ public class FilterMethodConfig {
         FilterRegistrationBean registration = new FilterRegistrationBean();
         FilterRegistrationBean registration = new FilterRegistrationBean();
         registration.setFilter(new CommonFilter());
         registration.setFilter(new CommonFilter());
         registration.addUrlPatterns("/*");
         registration.addUrlPatterns("/*");
-        registration.addInitParameter("HTTP_METHOD_SPECIFY", "true");
+        registration.addInitParameter(CommonFilter.HTTP_METHOD_SPECIFY, "true");
         registration.setName("sentinelFilter");
         registration.setName("sentinelFilter");
         registration.setOrder(1);
         registration.setOrder(1);
 
 

+ 1 - 1
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.
 - `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.
 - `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.
 
 
 <img width="792" src="https://user-images.githubusercontent.com/9305625/47277113-6b5da780-d5ef-11e8-8a0a-93a6b09b0887.png">
 <img width="792" src="https://user-images.githubusercontent.com/9305625/47277113-6b5da780-d5ef-11e8-8a0a-93a6b09b0887.png">
 
 

+ 7 - 1
sentinel-adapter/sentinel-zuul-adapter/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>sentinel-adapter</artifactId>
         <artifactId>sentinel-adapter</artifactId>
         <groupId>com.alibaba.csp</groupId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>sentinel-zuul-adapter</artifactId>
     <artifactId>sentinel-zuul-adapter</artifactId>
@@ -69,6 +69,12 @@
             <artifactId>mockito-core</artifactId>
             <artifactId>mockito-core</artifactId>
             <scope>test</scope>
             <scope>test</scope>
         </dependency>
         </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-test</artifactId>
+            <version>4.3.20.RELEASE</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     </dependencies>
 
 
 </project>
 </project>

+ 8 - 2
sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/PrefixRoutePathMatcher.java

@@ -17,11 +17,12 @@ package com.alibaba.csp.sentinel.adapter.gateway.zuul.api.route;
 
 
 import com.alibaba.csp.sentinel.util.AssertUtil;
 import com.alibaba.csp.sentinel.util.AssertUtil;
 import com.alibaba.csp.sentinel.util.function.Predicate;
 import com.alibaba.csp.sentinel.util.function.Predicate;
-
 import com.netflix.zuul.context.RequestContext;
 import com.netflix.zuul.context.RequestContext;
 import org.springframework.util.AntPathMatcher;
 import org.springframework.util.AntPathMatcher;
 import org.springframework.util.PathMatcher;
 import org.springframework.util.PathMatcher;
 
 
+import javax.servlet.http.HttpServletRequest;
+
 /**
 /**
  * @author Eric Zhao
  * @author Eric Zhao
  * @since 1.6.0
  * @since 1.6.0
@@ -42,7 +43,12 @@ public class PrefixRoutePathMatcher implements Predicate<RequestContext> {
 
 
     @Override
     @Override
     public boolean test(RequestContext context) {
     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) {
         if (canMatch) {
             return pathMatcher.match(pattern, path);
             return pathMatcher.match(pattern, path);
         }
         }

+ 9 - 4
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;
 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.AssertUtil;
 import com.alibaba.csp.sentinel.util.function.Predicate;
 import com.alibaba.csp.sentinel.util.function.Predicate;
-
 import com.netflix.zuul.context.RequestContext;
 import com.netflix.zuul.context.RequestContext;
 
 
+import javax.servlet.http.HttpServletRequest;
+import java.util.regex.Pattern;
+
 /**
 /**
  * @author Eric Zhao
  * @author Eric Zhao
  * @since 1.6.0
  * @since 1.6.0
@@ -39,7 +39,12 @@ public class RegexRoutePathMatcher implements Predicate<RequestContext> {
 
 
     @Override
     @Override
     public boolean test(RequestContext context) {
     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();
         return regex.matcher(path).matches();
     }
     }
 
 

+ 17 - 12
sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelEntryUtils.java

@@ -17,7 +17,7 @@ package com.alibaba.csp.sentinel.adapter.gateway.zuul.filters;
 
 
 import java.util.Deque;
 import java.util.Deque;
 
 
-import com.alibaba.csp.sentinel.AsyncEntry;
+import com.alibaba.csp.sentinel.Entry;
 import com.alibaba.csp.sentinel.Tracer;
 import com.alibaba.csp.sentinel.Tracer;
 import com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant;
 import com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant;
 import com.alibaba.csp.sentinel.context.ContextUtil;
 import com.alibaba.csp.sentinel.context.ContextUtil;
@@ -34,11 +34,11 @@ final class SentinelEntryUtils {
     static void tryExitFromCurrentContext() {
     static void tryExitFromCurrentContext() {
         RequestContext ctx = RequestContext.getCurrentContext();
         RequestContext ctx = RequestContext.getCurrentContext();
         if (ctx.containsKey(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY)) {
         if (ctx.containsKey(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY)) {
-            Deque<AsyncEntry> asyncEntries = (Deque<AsyncEntry>) ctx.get(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY);
-            AsyncEntry entry;
-            while (!asyncEntries.isEmpty()) {
-                entry = asyncEntries.pop();
-                entry.exit();
+            Deque<EntryHolder> holders = (Deque<EntryHolder>) ctx.get(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY);
+            EntryHolder holder;
+            while (!holders.isEmpty()) {
+                holder = holders.pop();
+                exit(holder);
             }
             }
             ctx.remove(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY);
             ctx.remove(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY);
         }
         }
@@ -50,17 +50,22 @@ final class SentinelEntryUtils {
     static void tryTraceExceptionThenExitFromCurrentContext(Throwable t) {
     static void tryTraceExceptionThenExitFromCurrentContext(Throwable t) {
         RequestContext ctx = RequestContext.getCurrentContext();
         RequestContext ctx = RequestContext.getCurrentContext();
         if (ctx.containsKey(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY)) {
         if (ctx.containsKey(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY)) {
-            Deque<AsyncEntry> asyncEntries = (Deque<AsyncEntry>) ctx.get(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY);
-            AsyncEntry entry;
-            while (!asyncEntries.isEmpty()) {
-                entry = asyncEntries.pop();
-                Tracer.traceEntry(t, entry);
-                entry.exit();
+            Deque<EntryHolder> holders = (Deque<EntryHolder>) ctx.get(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY);
+            EntryHolder holder;
+            while (!holders.isEmpty()) {
+                holder = holders.pop();
+                Tracer.traceEntry(t, holder.getEntry());
+                exit(holder);
             }
             }
             ctx.remove(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY);
             ctx.remove(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY);
         }
         }
         ContextUtil.exit();
         ContextUtil.exit();
     }
     }
 
 
+    static void exit(EntryHolder holder) {
+        Entry entry = holder.getEntry();
+        entry.exit(1, holder.getParams());
+    }
+
     private SentinelEntryUtils() {}
     private SentinelEntryUtils() {}
 }
 }

+ 11 - 8
sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java

@@ -23,6 +23,7 @@ import java.util.Set;
 
 
 import com.alibaba.csp.sentinel.AsyncEntry;
 import com.alibaba.csp.sentinel.AsyncEntry;
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.ResourceTypeConstants;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser;
 import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser;
 import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
 import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
@@ -88,7 +89,7 @@ public class SentinelZuulPreFilter extends ZuulFilter {
     }
     }
 
 
     private void doSentinelEntry(String resourceName, final int resType, RequestContext requestContext,
     private void doSentinelEntry(String resourceName, final int resType, RequestContext requestContext,
-                                 Deque<AsyncEntry> asyncEntries) throws BlockException {
+                                 Deque<EntryHolder> holders) throws BlockException {
         Object[] params = paramParser.parseParameterFor(resourceName, requestContext,
         Object[] params = paramParser.parseParameterFor(resourceName, requestContext,
             new Predicate<GatewayFlowRule>() {
             new Predicate<GatewayFlowRule>() {
                 @Override
                 @Override
@@ -96,8 +97,10 @@ public class SentinelZuulPreFilter extends ZuulFilter {
                     return r.getResourceMode() == resType;
                     return r.getResourceMode() == resType;
                 }
                 }
             });
             });
-        AsyncEntry entry = SphU.asyncEntry(resourceName, EntryType.IN, 1, params);
-        asyncEntries.push(entry);
+        AsyncEntry entry = SphU.asyncEntry(resourceName, ResourceTypeConstants.COMMON_API_GATEWAY,
+                EntryType.IN, params);
+        EntryHolder holder = new EntryHolder(entry, params);
+        holders.push(holder);
     }
     }
 
 
     @Override
     @Override
@@ -106,12 +109,12 @@ public class SentinelZuulPreFilter extends ZuulFilter {
         String origin = parseOrigin(ctx.getRequest());
         String origin = parseOrigin(ctx.getRequest());
         String routeId = (String)ctx.get(ZuulConstant.PROXY_ID_KEY);
         String routeId = (String)ctx.get(ZuulConstant.PROXY_ID_KEY);
 
 
-        Deque<AsyncEntry> asyncEntries = new ArrayDeque<>();
+        Deque<EntryHolder> holders = new ArrayDeque<>();
         String fallBackRoute = routeId;
         String fallBackRoute = routeId;
         try {
         try {
             if (StringUtil.isNotBlank(routeId)) {
             if (StringUtil.isNotBlank(routeId)) {
                 ContextUtil.enter(GATEWAY_CONTEXT_ROUTE_PREFIX + routeId, origin);
                 ContextUtil.enter(GATEWAY_CONTEXT_ROUTE_PREFIX + routeId, origin);
-                doSentinelEntry(routeId, RESOURCE_MODE_ROUTE_ID, ctx, asyncEntries);
+                doSentinelEntry(routeId, RESOURCE_MODE_ROUTE_ID, ctx, holders);
             }
             }
 
 
             Set<String> matchingApis = pickMatchingApiDefinitions(ctx);
             Set<String> matchingApis = pickMatchingApiDefinitions(ctx);
@@ -120,7 +123,7 @@ public class SentinelZuulPreFilter extends ZuulFilter {
             }
             }
             for (String apiName : matchingApis) {
             for (String apiName : matchingApis) {
                 fallBackRoute = apiName;
                 fallBackRoute = apiName;
-                doSentinelEntry(apiName, RESOURCE_MODE_CUSTOM_API_NAME, ctx, asyncEntries);
+                doSentinelEntry(apiName, RESOURCE_MODE_CUSTOM_API_NAME, ctx, holders);
             }
             }
         } catch (BlockException ex) {
         } catch (BlockException ex) {
             ZuulBlockFallbackProvider zuulBlockFallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(
             ZuulBlockFallbackProvider zuulBlockFallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(
@@ -138,8 +141,8 @@ public class SentinelZuulPreFilter extends ZuulFilter {
         } finally {
         } finally {
             // We don't exit the entry here. We need to exit the entries in post filter to record Rt correctly.
             // We don't exit the entry here. We need to exit the entries in post filter to record Rt correctly.
             // So here the entries will be carried in the request context.
             // So here the entries will be carried in the request context.
-            if (!asyncEntries.isEmpty()) {
-                ctx.put(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY, asyncEntries);
+            if (!holders.isEmpty()) {
+                ctx.put(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY, holders);
             }
             }
         }
         }
         return null;
         return null;

+ 1 - 12
sentinel-benchmark/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>sentinel-parent</artifactId>
         <artifactId>sentinel-parent</artifactId>
         <groupId>com.alibaba.csp</groupId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 
@@ -40,7 +40,6 @@
     <properties>
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <jmh.version>1.21</jmh.version>
         <jmh.version>1.21</jmh.version>
-        <javac.target>1.8</javac.target>
         <uberjar.name>benchmarks</uberjar.name>
         <uberjar.name>benchmarks</uberjar.name>
     </properties>
     </properties>
 
 
@@ -48,16 +47,6 @@
         <plugins>
         <plugins>
             <plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <version>${maven.compiler.version}</version>
-                <configuration>
-                    <compilerVersion>${javac.target}</compilerVersion>
-                    <source>${javac.target}</source>
-                    <target>${javac.target}</target>
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-pmd-plugin</artifactId>
                 <artifactId>maven-pmd-plugin</artifactId>
                 <version>${maven.pmd.version}</version>
                 <version>${maven.pmd.version}</version>
                 <configuration>
                 <configuration>

+ 3 - 5
sentinel-cluster/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>sentinel-parent</artifactId>
         <artifactId>sentinel-parent</artifactId>
         <groupId>com.alibaba.csp</groupId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
     <packaging>pom</packaging>
     <packaging>pom</packaging>
@@ -13,16 +13,14 @@
     <description>The parent module of Sentinel cluster server</description>
     <description>The parent module of Sentinel cluster server</description>
 
 
     <properties>
     <properties>
-        <java.source.version>1.7</java.source.version>
-        <java.target.version>1.7</java.target.version>
-
-        <netty.version>4.1.31.Final</netty.version>
+        <netty.version>4.1.48.Final</netty.version>
     </properties>
     </properties>
 
 
     <modules>
     <modules>
         <module>sentinel-cluster-client-default</module>
         <module>sentinel-cluster-client-default</module>
         <module>sentinel-cluster-server-default</module>
         <module>sentinel-cluster-server-default</module>
         <module>sentinel-cluster-common-default</module>
         <module>sentinel-cluster-common-default</module>
+        <module>sentinel-cluster-server-envoy-rls</module>
     </modules>
     </modules>
 
 
     <dependencyManagement>
     <dependencyManagement>

+ 6 - 1
sentinel-cluster/sentinel-cluster-client-default/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>sentinel-cluster</artifactId>
         <artifactId>sentinel-cluster</artifactId>
         <groupId>com.alibaba.csp</groupId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 
@@ -53,5 +53,10 @@
             <artifactId>mockito-core</artifactId>
             <artifactId>mockito-core</artifactId>
             <scope>test</scope>
             <scope>test</scope>
         </dependency>
         </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     </dependencies>
 </project>
 </project>

+ 11 - 2
sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/DefaultClusterTokenClient.java

@@ -79,7 +79,7 @@ public class DefaultClusterTokenClient implements ClusterTokenClient {
         try {
         try {
             this.transportClient = new NettyTransportClient(host, port);
             this.transportClient = new NettyTransportClient(host, port);
             this.serverDescriptor = new TokenServerDescriptor(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) {
         } catch (Exception ex) {
             RecordLog.warn("[DefaultClusterTokenClient] Failed to initialize new token client", ex);
             RecordLog.warn("[DefaultClusterTokenClient] Failed to initialize new token client", ex);
         }
         }
@@ -97,7 +97,7 @@ public class DefaultClusterTokenClient implements ClusterTokenClient {
             this.transportClient = new NettyTransportClient(config.getServerHost(), config.getServerPort());
             this.transportClient = new NettyTransportClient(config.getServerHost(), config.getServerPort());
             this.serverDescriptor = new TokenServerDescriptor(config.getServerHost(), config.getServerPort());
             this.serverDescriptor = new TokenServerDescriptor(config.getServerHost(), config.getServerPort());
             startClientIfScheduled();
             startClientIfScheduled();
-            RecordLog.info("[DefaultClusterTokenClient] New client created: " + serverDescriptor);
+            RecordLog.info("[DefaultClusterTokenClient] New client created: {}", serverDescriptor);
         } catch (Exception ex) {
         } catch (Exception ex) {
             RecordLog.warn("[DefaultClusterTokenClient] Failed to change remote token server", ex);
             RecordLog.warn("[DefaultClusterTokenClient] Failed to change remote token server", ex);
         }
         }
@@ -182,6 +182,15 @@ public class DefaultClusterTokenClient implements ClusterTokenClient {
         }
         }
     }
     }
 
 
+    @Override
+    public TokenResult requestConcurrentToken(String clientAddress, Long ruleId, int acquireCount) {
+        return null;
+    }
+
+    @Override
+    public void releaseConcurrentToken(Long tokenId) {
+    }
+
     private void logForResult(TokenResult result) {
     private void logForResult(TokenResult result) {
         switch (result.getStatus()) {
         switch (result.getStatus()) {
             case TokenResultStatus.NO_RULE_EXISTS:
             case TokenResultStatus.NO_RULE_EXISTS:

+ 10 - 8
sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/NettyTransportClient.java

@@ -62,7 +62,7 @@ public class NettyTransportClient implements ClusterTransportClient {
 
 
     @SuppressWarnings("PMD.ThreadPoolCreationRule")
     @SuppressWarnings("PMD.ThreadPoolCreationRule")
     private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1,
     private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1,
-        new NamedThreadFactory("sentinel-cluster-transport-client-scheduler"));
+        new NamedThreadFactory("sentinel-cluster-transport-client-scheduler", true));
 
 
     public static final int RECONNECT_DELAY_MS = 2000;
     public static final int RECONNECT_DELAY_MS = 2000;
 
 
@@ -126,8 +126,7 @@ public class NettyTransportClient implements ClusterTransportClient {
                     } else {
                     } else {
                         failConnectedTime.set(0);
                         failConnectedTime.set(0);
                         channel = future.channel();
                         channel = future.channel();
-                        RecordLog.info(
-                            "[NettyTransportClient] Successfully connect to server <" + host + ":" + port + ">");
+                        RecordLog.info("[NettyTransportClient] Successfully connect to server <{}:{}>", host, port);
                     }
                     }
                 }
                 }
             });
             });
@@ -144,7 +143,7 @@ public class NettyTransportClient implements ClusterTransportClient {
                 @Override
                 @Override
                 public void run() {
                 public void run() {
                     if (shouldRetry.get()) {
                     if (shouldRetry.get()) {
-                        RecordLog.info("[NettyTransportClient] Reconnecting to server <" + host + ":" + port + ">");
+                        RecordLog.info("[NettyTransportClient] Reconnecting to server <{}:{}>", host, port);
                         try {
                         try {
                             startInternal();
                             startInternal();
                         } catch (Exception e) {
                         } catch (Exception e) {
@@ -238,10 +237,12 @@ public class NettyTransportClient implements ClusterTransportClient {
     }
     }
 
 
     private int getCurrentId() {
     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<ClusterResponse> sendRequestAsync(ClusterRequest request) {
     /*public CompletableFuture<ClusterResponse> sendRequestAsync(ClusterRequest request) {
@@ -266,5 +267,6 @@ public class NettyTransportClient implements ClusterTransportClient {
         return future;
         return future;
     }*/
     }*/
 
 
+    private static final int MIN_ID = 1;
     private static final int MAX_ID = 999_999_999;
     private static final int MAX_ID = 999_999_999;
 }
 }

+ 7 - 5
sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/ClientEntityCodecProvider.java

@@ -15,7 +15,7 @@
  */
  */
 package com.alibaba.csp.sentinel.cluster.client.codec;
 package com.alibaba.csp.sentinel.cluster.client.codec;
 
 
-import com.alibaba.csp.sentinel.util.SpiLoader;
+import com.alibaba.csp.sentinel.spi.SpiLoader;
 import com.alibaba.csp.sentinel.cluster.codec.request.RequestEntityWriter;
 import com.alibaba.csp.sentinel.cluster.codec.request.RequestEntityWriter;
 import com.alibaba.csp.sentinel.cluster.codec.response.ResponseEntityDecoder;
 import com.alibaba.csp.sentinel.cluster.codec.response.ResponseEntityDecoder;
 import com.alibaba.csp.sentinel.log.RecordLog;
 import com.alibaba.csp.sentinel.log.RecordLog;
@@ -34,19 +34,21 @@ public final class ClientEntityCodecProvider {
     }
     }
 
 
     private static void resolveInstance() {
     private static void resolveInstance() {
-        RequestEntityWriter writer = SpiLoader.loadFirstInstance(RequestEntityWriter.class);
+        RequestEntityWriter writer = SpiLoader.of(RequestEntityWriter.class).loadFirstInstance();
         if (writer == null) {
         if (writer == null) {
             RecordLog.warn("[ClientEntityCodecProvider] No existing request entity writer, resolve failed");
             RecordLog.warn("[ClientEntityCodecProvider] No existing request entity writer, resolve failed");
         } else {
         } else {
             requestEntityWriter = writer;
             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);
+        ResponseEntityDecoder decoder = SpiLoader.of(ResponseEntityDecoder.class).loadFirstInstance();
         if (decoder == null) {
         if (decoder == null) {
             RecordLog.warn("[ClientEntityCodecProvider] No existing response entity decoder, resolve failed");
             RecordLog.warn("[ClientEntityCodecProvider] No existing response entity decoder, resolve failed");
         } else {
         } else {
             responseEntityDecoder = decoder;
             responseEntityDecoder = decoder;
-            RecordLog.info("[ClientEntityCodecProvider] Response entity decoder resolved: " + responseEntityDecoder.getClass().getCanonicalName());
+            RecordLog.info("[ClientEntityCodecProvider] Response entity decoder resolved: {}",
+                responseEntityDecoder.getClass().getCanonicalName());
         }
         }
     }
     }
 
 

+ 1 - 1
sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/DefaultRequestEntityWriter.java

@@ -36,7 +36,7 @@ public class DefaultRequestEntityWriter implements RequestEntityWriter<ClusterRe
         EntityWriter<Object, ByteBuf> requestDataWriter = RequestDataWriterRegistry.getWriter(type);
         EntityWriter<Object, ByteBuf> requestDataWriter = RequestDataWriterRegistry.getWriter(type);
 
 
         if (requestDataWriter == null) {
         if (requestDataWriter == null) {
-            RecordLog.warn("[DefaultRequestEntityWriter] Cannot find matching request writer for type <{0}>,"
+            RecordLog.warn("[DefaultRequestEntityWriter] Cannot find matching request writer for type <{}>,"
                 + " dropping the request", type);
                 + " dropping the request", type);
             return;
             return;
         }
         }

+ 1 - 1
sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/DefaultResponseEntityDecoder.java

@@ -47,7 +47,7 @@ public class DefaultResponseEntityDecoder implements ResponseEntityDecoder<ByteB
 
 
             EntityDecoder<ByteBuf, ?> decoder = ResponseDataDecodeRegistry.getDecoder(type);
             EntityDecoder<ByteBuf, ?> decoder = ResponseDataDecodeRegistry.getDecoder(type);
             if (decoder == null) {
             if (decoder == null) {
-                RecordLog.warn("Unknown type of response data decoder: {0}", type);
+                RecordLog.warn("Unknown type of response data decoder: {}", type);
                 return null;
                 return null;
             }
             }
 
 

+ 1 - 1
sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/data/FlowRequestDataWriter.java

@@ -22,7 +22,7 @@ import io.netty.buffer.ByteBuf;
 
 
 /**
 /**
  * +-------------------+--------------+----------------+---------------+------------------+
  * +-------------------+--------------+----------------+---------------+------------------+
- * | RequestID(8 byte) | Type(1 byte) | FlowID(4 byte) | Count(4 byte) | PriorityFlag (1) |
+ * | RequestID(8 byte) | Type(1 byte) | FlowID(8 byte) | Count(4 byte) | PriorityFlag (1) |
  * +-------------------+--------------+----------------+---------------+------------------+
  * +-------------------+--------------+----------------+---------------+------------------+
  *
  *
  * @author Eric Zhao
  * @author Eric Zhao

+ 43 - 40
sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/data/ParamFlowRequestDataWriter.java

@@ -15,16 +15,17 @@
  */
  */
 package com.alibaba.csp.sentinel.cluster.client.codec.data;
 package com.alibaba.csp.sentinel.cluster.client.codec.data;
 
 
-import java.util.Collection;
-
 import com.alibaba.csp.sentinel.cluster.ClusterConstants;
 import com.alibaba.csp.sentinel.cluster.ClusterConstants;
 import com.alibaba.csp.sentinel.cluster.codec.EntityWriter;
 import com.alibaba.csp.sentinel.cluster.codec.EntityWriter;
 import com.alibaba.csp.sentinel.cluster.request.data.ParamFlowRequestData;
 import com.alibaba.csp.sentinel.cluster.request.data.ParamFlowRequestData;
 import com.alibaba.csp.sentinel.log.RecordLog;
 import com.alibaba.csp.sentinel.log.RecordLog;
 import com.alibaba.csp.sentinel.util.AssertUtil;
 import com.alibaba.csp.sentinel.util.AssertUtil;
-
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBuf;
 
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
 /**
 /**
  * @author jialiang.linjl
  * @author jialiang.linjl
  * @author Eric Zhao
  * @author Eric Zhao
@@ -50,41 +51,67 @@ public class ParamFlowRequestDataWriter implements EntityWriter<ParamFlowRequest
 
 
         Collection<Object> params = entity.getParams();
         Collection<Object> params = entity.getParams();
 
 
-        // Write parameter amount.
-        int amount = calculateParamAmount(params);
-        target.writeInt(amount);
+        params = resolveValidParams(params);
+        target.writeInt(params.size());
 
 
         // Serialize parameters with type flag.
         // Serialize parameters with type flag.
-        for (Object param : entity.getParams()) {
+        for (Object param : params) {
             encodeValue(param, target);
             encodeValue(param, target);
         }
         }
     }
     }
 
 
+    /**
+     * Get valid parameters in provided parameter list
+     *
+     * @param params
+     * @return
+     */
+    public List<Object> resolveValidParams(Collection<Object> params) {
+        List<Object> validParams = new ArrayList<>();
+        int size = 0;
+        for (Object param : params) {
+            int s = calculateParamTransportSize(param);
+            if (s <= 0) {
+                RecordLog.warn("[ParamFlowRequestDataWriter] WARN: Non-primitive type detected in params of "
+                        + "cluster parameter flow control, which is not supported: " + param);
+                continue;
+            }
+            if (size + s > maxParamByteSize) {
+                RecordLog.warn("[ParamFlowRequestDataWriter] WARN: params size is too big." +
+                        " the configure value is : " + maxParamByteSize + ", the params size is: " + params.size());
+                break;
+            }
+            size += s;
+            validParams.add(param);
+        }
+        return validParams;
+    }
+
     private void encodeValue(Object param, ByteBuf target) {
     private void encodeValue(Object param, ByteBuf target) {
         // Handle primitive type.
         // Handle primitive type.
         if (param instanceof Integer || int.class.isInstance(param)) {
         if (param instanceof Integer || int.class.isInstance(param)) {
             target.writeByte(ClusterConstants.PARAM_TYPE_INTEGER);
             target.writeByte(ClusterConstants.PARAM_TYPE_INTEGER);
-            target.writeInt((Integer)param);
+            target.writeInt((Integer) param);
         } else if (param instanceof String) {
         } else if (param instanceof String) {
-            encodeString((String)param, target);
+            encodeString((String) param, target);
         } else if (boolean.class.isInstance(param) || param instanceof Boolean) {
         } else if (boolean.class.isInstance(param) || param instanceof Boolean) {
             target.writeByte(ClusterConstants.PARAM_TYPE_BOOLEAN);
             target.writeByte(ClusterConstants.PARAM_TYPE_BOOLEAN);
-            target.writeBoolean((Boolean)param);
+            target.writeBoolean((Boolean) param);
         } else if (long.class.isInstance(param) || param instanceof Long) {
         } else if (long.class.isInstance(param) || param instanceof Long) {
             target.writeByte(ClusterConstants.PARAM_TYPE_LONG);
             target.writeByte(ClusterConstants.PARAM_TYPE_LONG);
-            target.writeLong((Long)param);
+            target.writeLong((Long) param);
         } else if (double.class.isInstance(param) || param instanceof Double) {
         } else if (double.class.isInstance(param) || param instanceof Double) {
             target.writeByte(ClusterConstants.PARAM_TYPE_DOUBLE);
             target.writeByte(ClusterConstants.PARAM_TYPE_DOUBLE);
-            target.writeDouble((Double)param);
+            target.writeDouble((Double) param);
         } else if (float.class.isInstance(param) || param instanceof Float) {
         } else if (float.class.isInstance(param) || param instanceof Float) {
             target.writeByte(ClusterConstants.PARAM_TYPE_FLOAT);
             target.writeByte(ClusterConstants.PARAM_TYPE_FLOAT);
-            target.writeFloat((Float)param);
+            target.writeFloat((Float) param);
         } else if (byte.class.isInstance(param) || param instanceof Byte) {
         } else if (byte.class.isInstance(param) || param instanceof Byte) {
             target.writeByte(ClusterConstants.PARAM_TYPE_BYTE);
             target.writeByte(ClusterConstants.PARAM_TYPE_BYTE);
-            target.writeByte((Byte)param);
+            target.writeByte((Byte) param);
         } else if (short.class.isInstance(param) || param instanceof Short) {
         } else if (short.class.isInstance(param) || param instanceof Short) {
             target.writeByte(ClusterConstants.PARAM_TYPE_SHORT);
             target.writeByte(ClusterConstants.PARAM_TYPE_SHORT);
-            target.writeShort((Short)param);
+            target.writeShort((Short) param);
         } else {
         } else {
             // Unexpected type, drop.
             // Unexpected type, drop.
         }
         }
@@ -97,30 +124,6 @@ public class ParamFlowRequestDataWriter implements EntityWriter<ParamFlowRequest
         target.writeBytes(tmpChars);
         target.writeBytes(tmpChars);
     }
     }
 
 
-    /**
-     * Calculate amount of valid parameters in provided parameter list.
-     *
-     * @param params non-empty parameter list
-     * @return amount of valid parameters
-     */
-    int calculateParamAmount(/*@NonEmpty*/ Collection<Object> params) {
-        int size = 0;
-        int length = 0;
-        for (Object param : params) {
-            int s = calculateParamTransportSize(param);
-            if (s <= 0) {
-                RecordLog.warn("[ParamFlowRequestDataWriter] WARN: Non-primitive type detected in params of "
-                    + "cluster parameter flow control, which is not supported: " + param);
-                continue;
-            }
-            if (size + s > maxParamByteSize) {
-                break;
-            }
-            size += s;
-            length++;
-        }
-        return length;
-    }
 
 
     int calculateParamTransportSize(Object value) {
     int calculateParamTransportSize(Object value) {
         if (value == null) {
         if (value == null) {
@@ -132,7 +135,7 @@ public class ParamFlowRequestDataWriter implements EntityWriter<ParamFlowRequest
             return 5;
             return 5;
         } else if (value instanceof String) {
         } else if (value instanceof String) {
             // Layout for string: |type flag(1)|length(4)|string content|
             // Layout for string: |type flag(1)|length(4)|string content|
-            String tmpValue = (String)value;
+            String tmpValue = (String) value;
             byte[] tmpChars = tmpValue.getBytes();
             byte[] tmpChars = tmpValue.getBytes();
             return 1 + 4 + tmpChars.length;
             return 1 + 4 + tmpChars.length;
         } else if (boolean.class.isInstance(value) || value instanceof Boolean) {
         } else if (boolean.class.isInstance(value) || value instanceof Boolean) {

+ 6 - 1
sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/codec/data/PingResponseDataDecoder.java

@@ -27,9 +27,14 @@ public class PingResponseDataDecoder implements EntityDecoder<ByteBuf, Integer>
 
 
     @Override
     @Override
     public Integer decode(ByteBuf source) {
     public Integer decode(ByteBuf source) {
-        if (source.readableBytes() >= 1) {
+        int size = source.readableBytes();
+        if (size == 1) {
+            // Compatible with old version (< 1.7.0).
             return (int) source.readByte();
             return (int) source.readByte();
         }
         }
+        if (size >= 4) {
+            return source.readInt();
+        }
         return -1;
         return -1;
     }
     }
 }
 }

+ 3 - 3
sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/config/ClusterClientConfigManager.java

@@ -131,7 +131,7 @@ public final class ClusterClientConfigManager {
                 return;
                 return;
             }
             }
 
 
-            RecordLog.info("[ClusterClientConfigManager] Assign to new target token server: " + config);
+            RecordLog.info("[ClusterClientConfigManager] Assign to new target token server: {}", config);
 
 
             updateServerAssignment(config);
             updateServerAssignment(config);
         }
         }
@@ -156,11 +156,11 @@ public final class ClusterClientConfigManager {
         private synchronized void applyConfig(ClusterClientConfig config) {
         private synchronized void applyConfig(ClusterClientConfig config) {
             if (!isValidClientConfig(config)) {
             if (!isValidClientConfig(config)) {
                 RecordLog.warn(
                 RecordLog.warn(
-                    "[ClusterClientConfigManager] Invalid cluster client config, ignoring: " + config);
+                    "[ClusterClientConfigManager] Invalid cluster client config, ignoring: {}", config);
                 return;
                 return;
             }
             }
 
 
-            RecordLog.info("[ClusterClientConfigManager] Updating to new client config: " + config);
+            RecordLog.info("[ClusterClientConfigManager] Updating to new client config: {}", config);
 
 
             updateClientConfigChange(config);
             updateClientConfigChange(config);
         }
         }

+ 5 - 5
sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/handler/TokenClientHandler.java

@@ -48,7 +48,7 @@ public class TokenClientHandler extends ChannelInboundHandlerAdapter {
     public void channelActive(ChannelHandlerContext ctx) throws Exception {
     public void channelActive(ChannelHandlerContext ctx) throws Exception {
         currentState.set(ClientConstants.CLIENT_STATUS_STARTED);
         currentState.set(ClientConstants.CLIENT_STATUS_STARTED);
         fireClientPing(ctx);
         fireClientPing(ctx);
-        RecordLog.info("[TokenClientHandler] Client handler active, remote address: " + getRemoteAddress(ctx));
+        RecordLog.info("[TokenClientHandler] Client handler active, remote address: {}", getRemoteAddress(ctx));
     }
     }
 
 
     @Override
     @Override
@@ -76,10 +76,10 @@ public class TokenClientHandler extends ChannelInboundHandlerAdapter {
     private void handlePingResponse(ChannelHandlerContext ctx, ClusterResponse response) {
     private void handlePingResponse(ChannelHandlerContext ctx, ClusterResponse response) {
         if (response.getStatus() == ClusterConstants.RESPONSE_STATUS_OK) {
         if (response.getStatus() == ClusterConstants.RESPONSE_STATUS_OK) {
             int count = (int) response.getData();
             int count = (int) response.getData();
-            RecordLog.info("[TokenClientHandler] Client ping OK (target server: {0}, connected count: {1})",
+            RecordLog.info("[TokenClientHandler] Client ping OK (target server: {}, connected count: {})",
                 getRemoteAddress(ctx), count);
                 getRemoteAddress(ctx), count);
         } else {
         } else {
-            RecordLog.warn("[TokenClientHandler] Client ping failed (target server: {0})", getRemoteAddress(ctx));
+            RecordLog.warn("[TokenClientHandler] Client ping failed (target server: {})", getRemoteAddress(ctx));
         }
         }
     }
     }
 
 
@@ -90,12 +90,12 @@ public class TokenClientHandler extends ChannelInboundHandlerAdapter {
 
 
     @Override
     @Override
     public void channelInactive(ChannelHandlerContext ctx) throws Exception {
     public void channelInactive(ChannelHandlerContext ctx) throws Exception {
-        RecordLog.info("[TokenClientHandler] Client handler inactive, remote address: " + getRemoteAddress(ctx));
+        RecordLog.info("[TokenClientHandler] Client handler inactive, remote address: {}", getRemoteAddress(ctx));
     }
     }
 
 
     @Override
     @Override
     public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
     public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
-        RecordLog.info("[TokenClientHandler] Client channel unregistered, remote address: " + getRemoteAddress(ctx));
+        RecordLog.info("[TokenClientHandler] Client channel unregistered, remote address: {}", getRemoteAddress(ctx));
         currentState.set(ClientConstants.CLIENT_STATUS_OFF);
         currentState.set(ClientConstants.CLIENT_STATUS_OFF);
 
 
         disconnectCallback.run();
         disconnectCallback.run();

+ 7 - 1
sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/cluster/client/init/DefaultClusterClientInitFunc.java

@@ -23,6 +23,7 @@ import com.alibaba.csp.sentinel.cluster.client.codec.data.PingRequestDataWriter;
 import com.alibaba.csp.sentinel.cluster.client.codec.data.PingResponseDataDecoder;
 import com.alibaba.csp.sentinel.cluster.client.codec.data.PingResponseDataDecoder;
 import com.alibaba.csp.sentinel.cluster.client.codec.registry.RequestDataWriterRegistry;
 import com.alibaba.csp.sentinel.cluster.client.codec.registry.RequestDataWriterRegistry;
 import com.alibaba.csp.sentinel.cluster.client.codec.registry.ResponseDataDecodeRegistry;
 import com.alibaba.csp.sentinel.cluster.client.codec.registry.ResponseDataDecodeRegistry;
+import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientStartUpConfig;
 import com.alibaba.csp.sentinel.init.InitFunc;
 import com.alibaba.csp.sentinel.init.InitFunc;
 import com.alibaba.csp.sentinel.init.InitOrder;
 import com.alibaba.csp.sentinel.init.InitOrder;
 
 
@@ -42,7 +43,12 @@ public class DefaultClusterClientInitFunc implements InitFunc {
     private void initDefaultEntityWriters() {
     private void initDefaultEntityWriters() {
         RequestDataWriterRegistry.addWriter(ClientConstants.TYPE_PING, new PingRequestDataWriter());
         RequestDataWriterRegistry.addWriter(ClientConstants.TYPE_PING, new PingRequestDataWriter());
         RequestDataWriterRegistry.addWriter(ClientConstants.TYPE_FLOW, new FlowRequestDataWriter());
         RequestDataWriterRegistry.addWriter(ClientConstants.TYPE_FLOW, new FlowRequestDataWriter());
-        RequestDataWriterRegistry.addWriter(ClientConstants.TYPE_PARAM_FLOW, new ParamFlowRequestDataWriter());
+        Integer maxParamByteSize = ClusterClientStartUpConfig.getMaxParamByteSize();
+        if (maxParamByteSize == null) {
+            RequestDataWriterRegistry.addWriter(ClientConstants.TYPE_PARAM_FLOW, new ParamFlowRequestDataWriter());
+        } else {
+            RequestDataWriterRegistry.addWriter(ClientConstants.TYPE_PARAM_FLOW, new ParamFlowRequestDataWriter(maxParamByteSize));
+        }
     }
     }
 
 
     private void initDefaultEntityDecoders() {
     private void initDefaultEntityDecoders() {

+ 0 - 0
sentinel-cluster/sentinel-cluster-client-default/src/main/java/com/alibaba/csp/sentinel/command/handler/ModifyClusterClientConfigHandler.java


部分文件因为文件数量过多而无法显示