dengsixing 3 lat temu
rodzic
commit
2b4b9e5c25
100 zmienionych plików z 1932 dodań i 771 usunięć
  1. 3 0
      .gitignore
  2. 0 24
      .travis.yml
  3. 30 18
      README.md
  4. 52 28
      doc/awesome-sentinel.md
  5. BIN
      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
 gen
 
+# Visual Studio Code
+.history/
+
 # Maven
 target/
 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%">
 
-# 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)
 [![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)
@@ -11,13 +11,15 @@
 ## Introduction
 
 As distributed systems become increasingly popular, the reliability between services is becoming more important than ever before.
-Sentinel takes "flow" as breakthrough point, and works on multiple fields including **flow control**, **circuit breaking** and **system adaptive protection**, to guarantee reliability of microservices.
+Sentinel takes "flow" as breakthrough point, and works on multiple fields including **flow control**,
+**traffic shaping**, **circuit breaking** and **system adaptive protection**, to guarantee reliability and resilience for microservices.
 
 Sentinel has the following features:
 
 - **Rich applicable scenarios**: Sentinel has been wildly used in Alibaba, and has covered almost all the core-scenarios in Double-11 (11.11) Shopping Festivals in the past 10 years, such as “Second Kill” which needs to limit burst flow traffic to meet the system capacity, message peak clipping and valley fills, circuit breaking for unreliable downstream services, cluster flow control, etc.
 - **Real-time monitoring**: Sentinel also provides real-time monitoring ability. You can see the runtime information of a single machine in real-time, and the aggregated runtime info of a cluster with less than 500 nodes.
 - **Widespread open-source ecosystem**: Sentinel provides out-of-box integrations with commonly-used frameworks and libraries such as Spring Cloud, Dubbo and gRPC. You can easily use Sentinel by simply add the adapter dependency to your services.
+- **Polyglot support**: Sentinel has provided native support for Java, [Go](https://github.com/alibaba/sentinel-golang) and [C++](https://github.com/alibaba/sentinel-cpp).
 - **Various SPI extensions**: Sentinel provides easy-to-use SPI extension interfaces that allow you to quickly customize your logic, for example, custom rule management, adapting data sources, and so on.
 
 Features overview:
@@ -26,12 +28,15 @@ Features overview:
 
 ## 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 [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.
 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
 
-**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
 <!-- replace here with the latest version -->
 <dependency>
     <groupId>com.alibaba.csp</groupId>
     <artifactId>sentinel-core</artifactId>
-    <version>1.6.2</version>
+    <version>1.8.3</version>
 </dependency>
 ```
 
@@ -74,9 +79,10 @@ try (Entry entry = SphU.entry("HelloWorld")) {
     // Handle rejected request.
     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
 
@@ -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
 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.
@@ -122,23 +128,28 @@ Samples can be found in the [sentinel-demo](https://github.com/alibaba/Sentinel/
 
 ### 5. Start Dashboard
 
+> Note: Java 8 is required for building or running the dashboard.
+
 Sentinel also provides a simple dashboard application, on which you can monitor the clients and configure the rules in real time.
 
+![dashboard](https://user-images.githubusercontent.com/9434884/55449295-84866d80-55fd-11e9-94e5-d3441f4a2b63.png)
+
 For details please refer to [Dashboard](https://github.com/alibaba/Sentinel/wiki/Dashboard).
 
 ## 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
 
 For bug report, questions and discussions please submit [GitHub Issues](https://github.com/alibaba/sentinel/issues).
 
-Contact us: sentinel@linux.alibaba.com
+Contact us via [Gitter](https://gitter.im/alibaba/Sentinel) or [Email](mailto:sentinel@linux.alibaba.com).
 
 ## Contributing
 
-Contributions are always welcomed! Please see [CONTRIBUTING](./CONTRIBUTING.md) for detailed guidelines.
+Contributions are always welcomed! Please refer to [CONTRIBUTING](./CONTRIBUTING.md) for detailed guidelines.
 
 You can start with the issues labeled with [`good first issue`](https://github.com/alibaba/Sentinel/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
 
@@ -150,18 +161,19 @@ And thanks for all [contributors](https://github.com/alibaba/Sentinel/graphs/con
 
 ## Who is using
 
-These are only part of the companies using Sentinel, for reference only. If you are using Sentinel, please [add your company here](https://github.com/alibaba/Sentinel/issues/18) to tell us your scenario to make Sentinel better :)
+These are only part of the companies using Sentinel, for reference only.
+If you are using Sentinel, please [add your company here](https://github.com/alibaba/Sentinel/issues/18) to tell us your scenario to make Sentinel better :)
 
 ![Alibaba Group](https://docs.alibabagroup.com/assets2/images/en/global/logo_header.png)
+![AntFin](https://user-images.githubusercontent.com/9434884/90598732-30961c00-e226-11ea-8c86-0b1d7f7875c7.png)
 ![Taiping Renshou](http://www.cntaiping.com/tplresource/cms/www/taiping/img/home_new/tp_logo_img.png)
+![拼多多](http://cdn.pinduoduo.com/assets/img/pdd_logo_v3.png)
+![爱奇艺](https://user-images.githubusercontent.com/9434884/90598445-a51c8b00-e225-11ea-9327-3543525f3f2a.png)
 ![Shunfeng Technology](https://user-images.githubusercontent.com/9434884/48463502-2f48eb80-e817-11e8-984f-2f9b1b789e2d.png)
-![Mandao](https://user-images.githubusercontent.com/9434884/48463559-6cad7900-e817-11e8-87e4-42952b074837.png)
-![每日优鲜](https://home.missfresh.cn/statics/img/logo.png)
 ![二维火](https://user-images.githubusercontent.com/9434884/49358468-bc43de00-f70d-11e8-97fe-0bf05865f29f.png)
+![Mandao](https://user-images.githubusercontent.com/9434884/48463559-6cad7900-e817-11e8-87e4-42952b074837.png)
 ![文轩在线](http://static.winxuancdn.com/css/v2/images/logo.png)
 ![客如云](https://www.keruyun.com/static/krynew/images/logo.png)
 ![亲宝宝](https://stlib.qbb6.com/wclt/img/home_hd/version1/title_logo.png)
-![杭州光云科技](https://www.raycloud.com/images/logo.png)
 ![金汇金融](https://res.jinhui365.com/r/images/logo2.png?v=1.527)
 ![闪电购](http://cdn.52shangou.com/shandianbang/official-source/3.1.1/build/images/logo.png)
-![拼多多](http://cdn.pinduoduo.com/assets/img/pdd_logo_v3.png)

+ 52 - 28
doc/awesome-sentinel.md

@@ -2,51 +2,75 @@
 
 [![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 also add to [sentinel-group/sentinel-awesome](https://github.com/sentinel-group/sentinel-awesome).
 
 ## Contents
 
+- [Presentations](#presentations)
 - [Tutorials](#tutorials)
-- [Samples / Demos](#samples--demos)
+- [Demos](#demos)
 - [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
 
-## 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
 
 - [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一体化监控解决方案 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)

BIN
doc/image/sentinel-opensource-eco-landscape-en.png


+ 38 - 7
pom.xml

@@ -5,7 +5,7 @@
 
     <groupId>com.alibaba.csp</groupId>
     <artifactId>sentinel-parent</artifactId>
-    <version>1.6.2</version>
+    <version>1.8.3</version>
     <packaging>pom</packaging>
 
     <name>${project.artifactId}</name>
@@ -41,19 +41,20 @@
 
     <properties>
         <!-- 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 -->
         <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>
         <awaitility.version>3.1.5</awaitility.version>
         <powermock.version>2.0.0</powermock.version>
 
         <!-- Build -->
         <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>
         <maven.compiler.version>3.8.0</maven.compiler.version>
         <maven.surefire.version>2.22.1</maven.surefire.version>
@@ -71,11 +72,14 @@
         <module>sentinel-extension</module>
         <module>sentinel-transport</module>
         <module>sentinel-adapter</module>
+        <module>sentinel-cluster</module>
+        <module>sentinel-logging</module>
+
         <module>sentinel-dashboard</module>
 
         <module>sentinel-demo</module>
         <module>sentinel-benchmark</module>
-        <module>sentinel-cluster</module>
+
     </modules>
 
     <dependencyManagement>
@@ -97,6 +101,11 @@
             </dependency>
             <dependency>
                 <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>
                 <version>${project.version}</version>
             </dependency>
@@ -122,6 +131,11 @@
             </dependency>
             <dependency>
                 <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>
                 <version>${project.version}</version>
             </dependency>
@@ -132,6 +146,11 @@
             </dependency>
             <dependency>
                 <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>
                 <version>${project.version}</version>
             </dependency>
@@ -145,7 +164,13 @@
                 <artifactId>sentinel-adapter</artifactId>
                 <version>${project.version}</version>
             </dependency>
-
+            
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-metric-exporter</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            
             <dependency>
                 <groupId>com.alibaba</groupId>
                 <artifactId>fastjson</artifactId>
@@ -258,6 +283,7 @@
                 <configuration>
                     <!-- CircleCI build workaround -->
                     <argLine>@{argLine} -Xms1024m -Xmx2048m</argLine>
+                    <argLine>-Dfile.encoding=UTF-8</argLine>
                     <useSystemClassLoader>false</useSystemClassLoader>
                 </configuration>
             </plugin>
@@ -293,6 +319,11 @@
                     <artifactId>maven-jar-plugin</artifactId>
                     <version>${maven.jar.version}</version>
                 </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-gpg-plugin</artifactId>
+                    <version>${maven.gpg.version}</version>
+                </plugin>
             </plugins>
         </pluginManagement>
     </build>

+ 9 - 1
sentinel-adapter/pom.xml

@@ -7,7 +7,7 @@
     <parent>
         <groupId>com.alibaba.csp</groupId>
         <artifactId>sentinel-parent</artifactId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     <artifactId>sentinel-adapter</artifactId>
     <packaging>pom</packaging>
@@ -18,12 +18,20 @@
         <module>sentinel-web-servlet</module>
         <module>sentinel-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-zuul-adapter</module>
         <module>sentinel-reactor-adapter</module>
         <module>sentinel-spring-webflux-adapter</module>
         <module>sentinel-api-gateway-adapter-common</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>
 
     <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)。
 
@@ -21,7 +21,7 @@ To use Sentinel Dubbo Adapter, you can simply add the following dependency to yo
 The Sentinel filters are **enabled by default**. Once you add the dependency,
 the Dubbo services and methods will become protected resources in Sentinel,
 which can leverage Sentinel's flow control and guard ability when rules are configured.
-Demos can be found in [sentinel-demo-dubbo](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-dubbo).
+Demos can be found in [sentinel-demo-apache-dubbo](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-apache-dubbo).
 
 If you don't want the filters enabled, you can manually disable them. For example:
 
@@ -37,8 +37,8 @@ For more details of Dubbo filter, see [here](http://dubbo.apache.org/en-us/docs/
 
 The resource for Dubbo services has two granularities: service interface and service method.
 
-- Service interface:resourceName format is `interfaceName`,e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService`
-- Service method:resourceName format is `interfaceName:methodSignature`,e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String)`
+- Service interface: resourceName format is `interfaceName`, e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService`
+- Service method: resourceName format is `interfaceName:methodSignature`, e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String)`
 
 ## Flow control based on caller
 
@@ -52,17 +52,21 @@ If `limitApp` of flow rules is not configured (`default`), flow control will tak
 If `limitApp` of a flow rule is configured with a caller, then the corresponding flow rule will only take effect on the specific caller.
 
 > Note: Dubbo consumer does not provide its Dubbo application name when doing RPC,
-so developers should manually put the application name into *attachment* at consumer side,
-then extract it at provider side. Sentinel Dubbo Adapter has implemented a filter (`DubboAppContextFilter`)
-where consumer can carry application name information to provider automatically.
-If the consumer does not use Sentinel Dubbo Adapter but requires flow control based on caller, developers can manually put the application name into attachment with the key `dubboApplication`.
+> so developers should manually put the application name into *attachment* at consumer side,
+> then extract it at provider side. Sentinel Dubbo Adapter has implemented a filter (`DubboAppContextFilter`)
+> where consumer can carry application name information to provider automatically.
+> If the consumer does not use Sentinel Dubbo Adapter but requires flow control based on caller,
+> developers can manually put the application name into attachment with the key `dubboApplication`.
+>
+> Since 1.8.0, the adapter provides support for customizing origin parsing logic. You may register your own `DubboOriginParser`
+> implementation to `DubboAdapterGlobalConfig`.
 
 ## Global fallback
 
 Sentinel Dubbo Adapter supports global fallback configuration.
 The global fallback will handle exceptions and give replacement result when blocked by
 flow control, degrade or system load protection. You can implement your own `DubboFallback` interface
-and then register to `DubboFallbackRegistry`. If no fallback is configured, Sentinel will wrap the `BlockException`
-then directly throw it out.
+and then register to `DubboAdapterGlobalConfig`.
+If no fallback is configured, Sentinel will wrap the `BlockException` as the fallback result.
 
 Besides, we can also leverage [Dubbo mock mechanism](http://dubbo.apache.org/en-us/docs/user/demos/local-mock.html) to provide fallback implementation of degraded Dubbo services.

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

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>sentinel-adapter</artifactId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -15,7 +15,7 @@
     <properties>
         <java.source.version>1.8</java.source.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>
 
     <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;
 
-import org.apache.dubbo.common.Constants;
+import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.rpc.Filter;
 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.RpcException;
 
+import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
+
 /**
  * Puts current consumer's application name in the attachment of each invocation.
  *
  * @author Eric Zhao
  */
-@Activate(group = "consumer")
+@Activate(group = CONSUMER)
 public class DubboAppContextFilter implements Filter {
 
     @Override
     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) {
             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;
 
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
+import com.alibaba.csp.sentinel.util.StringUtil;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 
@@ -32,9 +34,14 @@ public final class DubboUtils {
         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);
-        buf.append(invoker.getInterface().getName())
+        String interfaceResource = useGroupAndVersion ? invoker.getUrl().getColonSeparatedKey() : invoker.getInterface().getName();
+        buf.append(interfaceResource)
             .append(":")
             .append(invocation.getMethodName())
             .append("(");
@@ -50,5 +57,39 @@ public final class DubboUtils {
         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;
 
-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.slots.block.BlockException;
 
-import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
 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>
  * If you want to disable the consumer filter, you can configure:
  * <pre>
  * &lt;dubbo:consumer filter="-sentinel.dubbo.consumer.filter"/&gt;
@@ -40,44 +40,115 @@ import org.apache.dubbo.rpc.RpcException;
  *
  * @author Carpenter Lee
  * @author Eric Zhao
+ * @author Lin Liang
  */
-@Activate(group = "consumer")
-public class SentinelDubboConsumerFilter implements Filter {
+@Activate(group = CONSUMER)
+public class SentinelDubboConsumerFilter extends BaseSentinelDubboFilter {
 
     public SentinelDubboConsumerFilter() {
         RecordLog.info("Sentinel Apache Dubbo consumer filter initialized");
     }
 
     @Override
+    String getMethodName(Invoker invoker, Invocation invocation, String prefix) {
+        return DubboUtils.getMethodResourceName(invoker, invocation, prefix);
+    }
+
+    @Override
+    String getInterfaceName(Invoker invoker, String prefix) {
+        return DubboUtils.getInterfaceName(invoker, prefix);
+    }
+
+    @Override
     public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+        InvokeMode invokeMode = RpcUtils.getInvokeMode(invoker.getUrl(), invocation);
+        if (InvokeMode.SYNC == invokeMode) {
+            return syncInvoke(invoker, invocation);
+        } else {
+            return asyncInvoke(invoker, invocation);
+        }
+    }
+
+    private Result syncInvoke(Invoker<?> invoker, Invocation invocation) {
         Entry interfaceEntry = null;
         Entry methodEntry = null;
+        String prefix = DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey();
+        String interfaceResourceName = getInterfaceName(invoker, prefix);
+        String methodResourceName = getMethodName(invoker, invocation, prefix);
         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);
             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;
         } catch (BlockException e) {
-            return DubboFallbackRegistry.getConsumerFallback().handle(invoker, invocation, e);
+            return DubboAdapterGlobalConfig.getConsumerFallback().handle(invoker, invocation, e);
         } catch (RpcException e) {
             Tracer.traceEntry(e, interfaceEntry);
             Tracer.traceEntry(e, methodEntry);
             throw e;
         } finally {
             if (methodEntry != null) {
-                methodEntry.exit();
+                methodEntry.exit(1, invocation.getArguments());
             }
             if (interfaceEntry != null) {
                 interfaceEntry.exit();
             }
         }
     }
+
+    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;
 
-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.log.RecordLog;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 
 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 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>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:
  * <pre>
  * &lt;dubbo:provider filter="-sentinel.dubbo.provider.filter"/&gt;
@@ -43,39 +41,50 @@ import org.apache.dubbo.rpc.RpcException;
  * @author Carpenter Lee
  * @author Eric Zhao
  */
-@Activate(group = "provider")
-public class SentinelDubboProviderFilter implements Filter {
+@Activate(group = PROVIDER)
+public class SentinelDubboProviderFilter extends BaseSentinelDubboFilter {
 
     public SentinelDubboProviderFilter() {
         RecordLog.info("Sentinel Apache Dubbo provider filter initialized");
     }
 
     @Override
+    String getMethodName(Invoker invoker, Invocation invocation, String prefix) {
+        return DubboUtils.getMethodResourceName(invoker, invocation, prefix);
+    }
+
+    @Override
+    String getInterfaceName(Invoker invoker, String prefix) {
+        return DubboUtils.getInterfaceName(invoker, prefix);
+    }
+
+    @Override
     public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
         // Get origin caller.
-        String application = DubboUtils.getApplication(invocation, "");
-
+        String origin = DubboAdapterGlobalConfig.getOriginParser().parse(invoker, invocation);
+        if (null == origin) {
+            origin = "";
+        }
         Entry interfaceEntry = null;
         Entry methodEntry = null;
+        String prefix = DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey();
+        String interfaceResourceName = getInterfaceName(invoker, prefix);
+        String methodResourceName = getMethodName(invoker, invocation, prefix);
         try {
-            String resourceName = DubboUtils.getResourceName(invoker, invocation);
-            String interfaceName = invoker.getInterface().getName();
             // Only need to create entrance context at provider side, as context will take effect
             // at entrance of invocation chain only (for inbound traffic).
-            ContextUtil.enter(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);
             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;
         } catch (BlockException e) {
-            return DubboFallbackRegistry.getProviderFallback().handle(invoker, invocation, e);
+            return DubboAdapterGlobalConfig.getProviderFallback().handle(invoker, invocation, e);
         } catch (RpcException e) {
             Tracer.traceEntry(e, interfaceEntry);
             Tracer.traceEntry(e, methodEntry);
@@ -90,4 +99,6 @@ public class SentinelDubboProviderFilter implements Filter {
             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;
 
 import com.alibaba.csp.sentinel.slots.block.BlockException;
-import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
 
+import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
@@ -29,7 +29,7 @@ public class DefaultDubboFallback implements DubboFallback {
 
     @Override
     public Result handle(Invoker<?> invoker, Invocation invocation, BlockException ex) {
-        // Just wrap and throw the exception.
-        throw new SentinelRpcException(ex);
+        // Just wrap the exception.
+        return AsyncRpcResult.newDefaultAsyncResult(ex.toRuntimeException(), invocation);
     }
 }

+ 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;
 
-import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
 
 /**
  * <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
+ * @deprecated use {@link DubboAdapterGlobalConfig} instead since 1.8.0.
  */
+@Deprecated
 public final class DubboFallbackRegistry {
 
-    private static volatile DubboFallback consumerFallback = new DefaultDubboFallback();
-    private static volatile DubboFallback providerFallback = new DefaultDubboFallback();
-
     public static DubboFallback getConsumerFallback() {
-        return consumerFallback;
+        return DubboAdapterGlobalConfig.getConsumerFallback();
     }
 
     public static void setConsumerFallback(DubboFallback consumerFallback) {
-        AssertUtil.notNull(consumerFallback, "consumerFallback cannot be null");
-        DubboFallbackRegistry.consumerFallback = consumerFallback;
+        DubboAdapterGlobalConfig.setConsumerFallback(consumerFallback);
     }
 
     public static DubboFallback getProviderFallback() {
-        return providerFallback;
+        return DubboAdapterGlobalConfig.getProviderFallback();
     }
 
     public static void setProviderFallback(DubboFallback providerFallback) {
-        AssertUtil.notNull(providerFallback, "providerFallback cannot be null");
-        DubboFallbackRegistry.providerFallback = providerFallback;
+        DubboAdapterGlobalConfig.setProviderFallback(providerFallback);
     }
 
     private DubboFallbackRegistry() {}

+ 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;
 
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
+import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DefaultDubboFallback;
+import com.alibaba.csp.sentinel.config.SentinelConfig;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
 import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
-
 import org.apache.dubbo.rpc.RpcContext;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
 /**
  * Base test class, provide common methods for subClass
  * The package is same as CtSph, to call CtSph.resetChainMap() method for test
- *
+ * <p>
  * Note: Only for test. DO NOT USE IN PRODUCTION!
  *
  * @author cdfive
+ * @author lianglin
  */
 public class BaseTest {
 
+
     /**
      * 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();
         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);
 
         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;
 
+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.Invoker;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 
 import java.lang.reflect.Method;
 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.fail;
 import static org.mockito.Mockito.*;
@@ -33,12 +39,30 @@ import static org.mockito.Mockito.*;
  */
 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
     public void testGetApplication() {
         Invocation invocation = mock(Invocation.class);
         when(invocation.getAttachments()).thenReturn(new HashMap<>());
         when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, ""))
-            .thenReturn("consumerA");
+                .thenReturn("consumerA");
 
         String application = DubboUtils.getApplication(invocation, "");
         verify(invocation).getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, "");
@@ -51,7 +75,7 @@ public class DubboUtilsTest {
         Invocation invocation = mock(Invocation.class);
         when(invocation.getAttachments()).thenReturn(null);
         when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, ""))
-            .thenReturn("consumerA");
+                .thenReturn("consumerA");
 
         DubboUtils.getApplication(invocation, "");
 
@@ -59,17 +83,125 @@ public class DubboUtilsTest {
     }
 
     @Test
-    public void testGetResourceName() {
+    public void testGetResourceName() throws NoSuchMethodException {
         Invoker invoker = mock(Invoker.class);
         when(invoker.getInterface()).thenReturn(DemoService.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.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);
+
+    }
+
+    @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;
 
 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.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.ContextUtil;
 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.StatisticNode;
 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.Before;
 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.mockito.Mockito.*;
 
 /**
  * @author cdfive
+ * @author lianglin
  */
 public class SentinelDubboConsumerFilterTest extends BaseTest {
 
-    private SentinelDubboConsumerFilter filter = new SentinelDubboConsumerFilter();
+    private final SentinelDubboConsumerFilter consumerFilter = new SentinelDubboConsumerFilter();
 
     @Before
     public void setUp() {
         cleanUpAll();
+        initFallback();
     }
 
     @After
-    public void cleanUp() {
+    public void destroy() {
         cleanUpAll();
     }
 
     @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);
         when(result.hasException()).thenReturn(false);
+        when(result.getException()).thenReturn(new Exception());
         when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> {
             verifyInvocationStructure(invoker, invocation);
             return result;
         });
 
-        filter.invoke(invoker, invocation);
+        consumerFilter.invoke(invoker, invocation);
         verify(invoker).invoke(invocation);
 
         Context context = ContextUtil.getContext();
@@ -92,34 +225,36 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
     private void verifyInvocationStructure(Invoker invoker, Invocation invocation) {
         Context context = ContextUtil.getContext();
         assertNotNull(context);
-
         // As not call ContextUtil.enter(resourceName, application) in SentinelDubboConsumerFilter, use default context
-        // In actual project, a consumer is usually also a provider, the context will be created by SentinelDubboProviderFilter
+        // In actual project, a consumer is usually also a provider, the context will be created by
+        //SentinelDubboProviderFilter
         // If consumer is on the top of Dubbo RPC invocation chain, use default context
-        String resourceName = DubboUtils.getResourceName(invoker, invocation);
-        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());
 
         DefaultNode entranceNode = context.getEntranceNode();
         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);
         Set<Node> childList = entranceNode.getChildList();
         assertEquals(1, childList.size());
-        DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
+        DefaultNode interfaceNode = getNode(DubboUtils.getInterfaceName(invoker), entranceNode);
         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);
         childList = interfaceNode.getChildList();
         assertEquals(1, childList.size());
-        DefaultNode methodNode = (DefaultNode) childList.iterator().next();
+        DefaultNode methodNode = getNode(resourceName, entranceNode);
         ResourceWrapper methodResource = methodNode.getId();
         assertEquals(resourceName, methodResource.getName());
-        assertSame(EntryType.OUT, methodResource.getType());
+        assertSame(EntryType.OUT, methodResource.getEntryType());
 
         // Verify curEntry
         Entry curEntry = context.getCurEntry();
@@ -130,7 +265,60 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
         // Verify clusterNode
         ClusterNode methodClusterNode = methodNode.getClusterNode();
         ClusterNode interfaceClusterNode = interfaceNode.getClusterNode();
-        assertNotSame(methodClusterNode, interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode
+        assertNotSame(methodClusterNode,
+            interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode
+
+        // As context origin is "", the StatisticNode should not be created in originCountMap of ClusterNode
+        Map<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
         Map<String, StatisticNode> methodOriginCountMap = methodClusterNode.getOriginCountMap();
@@ -139,4 +327,68 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
         Map<String, StatisticNode> interfaceOriginCountMap = interfaceClusterNode.getOriginCountMap();
         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;
 
 import com.alibaba.csp.sentinel.BaseTest;
+import com.alibaba.csp.sentinel.DubboTestUtil;
 import com.alibaba.csp.sentinel.Entry;
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
@@ -26,7 +27,8 @@ import com.alibaba.csp.sentinel.node.DefaultNode;
 import com.alibaba.csp.sentinel.node.Node;
 import com.alibaba.csp.sentinel.node.StatisticNode;
 import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
-
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
@@ -34,7 +36,6 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
-import java.lang.reflect.Method;
 import java.util.Map;
 import java.util.Set;
 
@@ -43,37 +44,41 @@ import static org.mockito.Mockito.*;
 
 /**
  * @author cdfive
+ * @author lianglin
  */
 public class SentinelDubboProviderFilterTest extends BaseTest {
 
+
     private SentinelDubboProviderFilter filter = new SentinelDubboProviderFilter();
 
+
     @Before
     public void setUp() {
         cleanUpAll();
     }
 
     @After
-    public void cleanUp() {
+    public void destroy() {
         cleanUpAll();
     }
 
     @Test
     public void testInvoke() {
+
         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, ""))
-            .thenReturn(originApplication);
+                .thenReturn(originApplication);
 
         final Result result = mock(Result.class);
         when(result.hasException()).thenReturn(false);
+        when(result.getException()).thenReturn(new Exception());
+
         when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> {
             verifyInvocationStructure(originApplication, invoker, invocation);
             return result;
@@ -88,39 +93,40 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
 
     /**
      * Simply verify invocation structure in memory:
-     * EntranceNode(resourceName)
+     * EntranceNode(methodResourceName)
      * --InterfaceNode(interfaceName)
-     * ----MethodNode(resourceName)
+     * ----MethodNode(methodResourceName)
      */
     private void verifyInvocationStructure(String originApplication, Invoker invoker, Invocation invocation) {
         Context context = ContextUtil.getContext();
         assertNotNull(context);
 
         // As ContextUtil.enter(resourceName, application) in SentinelDubboProviderFilter
-        String resourceName = DubboUtils.getResourceName(invoker, invocation);
-        assertEquals(resourceName, context.getName());
+        String methodResourceName = filter.getMethodName(invoker, invocation, null);
+        assertEquals(methodResourceName, context.getName());
         assertEquals(originApplication, context.getOrigin());
 
         DefaultNode entranceNode = context.getEntranceNode();
         ResourceWrapper entranceResource = entranceNode.getId();
-        assertEquals(resourceName, entranceResource.getName());
-        assertSame(EntryType.IN, entranceResource.getType());
+        assertEquals(methodResourceName, entranceResource.getName());
+        assertSame(EntryType.IN, entranceResource.getEntryType());
 
         // As SphU.entry(interfaceName, EntryType.IN);
         Set<Node> childList = entranceNode.getChildList();
         assertEquals(1, childList.size());
         DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
         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());
         childList = interfaceNode.getChildList();
         assertEquals(1, childList.size());
         DefaultNode methodNode = (DefaultNode) childList.iterator().next();
         ResourceWrapper methodResource = methodNode.getId();
-        assertEquals(resourceName, methodResource.getName());
-        assertSame(EntryType.IN, methodResource.getType());
+        assertEquals(methodResourceName, methodResource.getName());
+        assertSame(EntryType.IN, methodResource.getEntryType());
 
         // Verify curEntry
         Entry curEntry = context.getCurEntry();
@@ -142,4 +148,6 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
         assertEquals(1, interfaceOriginCountMap.size());
         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;
 
+import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
-import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
 
+import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Result;
-import org.apache.dubbo.rpc.RpcResult;
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 
 /**
@@ -29,20 +31,33 @@ import org.junit.Test;
  */
 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() {
-        // Test for default.
+        // Test for default fallback.
         BlockException ex = new FlowException("xxx");
-        DubboFallbackRegistry.getConsumerFallback()
-            .handle(null, null, ex);
+        Result result = new DefaultDubboFallback().handle(null, null, ex);
+        Assert.assertTrue("The result should carry exception", result.hasException());
+        Assert.assertTrue(BlockException.isBlockException(result.getException()));
+        Assert.assertTrue(result.getException().getMessage().contains(ex.getClass().getSimpleName()));
     }
 
     @Test
     public void testCustomFallback() {
         BlockException ex = new FlowException("xxx");
-        DubboFallbackRegistry.setConsumerFallback(
-            (invoker, invocation, e) -> 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);
         Assert.assertFalse("The invocation should not fail", result.hasException());
         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 {
     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) {
         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>
         <artifactId>sentinel-adapter</artifactId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>sentinel-api-gateway-adapter-common</artifactId>
     <packaging>jar</packaging>
 
-    <properties>
-        <java.source.version>1.7</java.source.version>
-        <java.target.version>1.7</java.target.version>
-    </properties>
-
     <dependencies>
         <dependency>
             <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.SentinelProperty;
 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;
 
 /**
@@ -59,11 +59,11 @@ public final class GatewayApiDefinitionManager {
     }
 
     private static void initializeApiChangeObserverSpi() {
-        List<ApiDefinitionChangeObserver> listeners = SpiLoader.loadInstanceList(ApiDefinitionChangeObserver.class);
+        List<ApiDefinitionChangeObserver> listeners = SpiLoader.of(ApiDefinitionChangeObserver.class).loadInstanceList();
         for (ApiDefinitionChangeObserver e : listeners) {
             API_CHANGE_OBSERVERS.put(e.getClass().getCanonicalName(), e);
-            RecordLog.info("[GatewayApiDefinitionManager] ApiDefinitionChangeObserver added: {0}",
-                e.getClass().getCanonicalName());
+            RecordLog.info("[GatewayApiDefinitionManager] ApiDefinitionChangeObserver added: {}"
+                , e.getClass().getCanonicalName());
         }
     }
 
@@ -103,13 +103,13 @@ public final class GatewayApiDefinitionManager {
         @Override
         public void configUpdate(Set<ApiDefinition> set) {
             applyApiUpdateInternal(set);
-            RecordLog.info("[GatewayApiDefinitionManager] Api definition updated: " + API_MAP);
+            RecordLog.info("[GatewayApiDefinitionManager] Api definition updated: {}", API_MAP);
         }
 
         @Override
         public void configLoad(Set<ApiDefinition> 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) {

+ 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;
 
-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.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.command.CommandHandler;
 import com.alibaba.csp.sentinel.command.CommandRequest;
 import com.alibaba.csp.sentinel.command.CommandResponse;
 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.util.StringUtil;
+import com.alibaba.fastjson.JSON;
 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
@@ -36,6 +41,8 @@ import com.alibaba.fastjson.JSONArray;
 @CommandMapping(name = "gateway/updateApiDefinitions", desc = "")
 public class UpdateGatewayApiDefinitionGroupCommandHandler implements CommandHandler<String> {
 
+    private static WritableDataSource<Set<ApiDefinition>> apiDefinitionWds = null;
+
     @Override
     public CommandResponse<String> handle(CommandRequest request) {
         String data = request.getParam("data");
@@ -49,13 +56,70 @@ public class UpdateGatewayApiDefinitionGroupCommandHandler implements CommandHan
             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;
-        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);
     }
 
     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;
 
-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.GatewayRuleManager;
 import com.alibaba.csp.sentinel.command.CommandHandler;
 import com.alibaba.csp.sentinel.command.CommandRequest;
 import com.alibaba.csp.sentinel.command.CommandResponse;
 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.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
@@ -35,6 +36,7 @@ import com.alibaba.fastjson.JSONArray;
  */
 @CommandMapping(name = "gateway/updateRules", desc = "Update gateway rules")
 public class UpdateGatewayRuleCommandHandler implements CommandHandler<String> {
+    private static WritableDataSource<Set<GatewayFlowRule>> gatewayFlowWds = null;
 
     @Override
     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");
         }
 
-        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;
-        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);
     }
 
+    /**
+     * 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 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) {
         String clientIp = requestItemParser.getRemoteAddress(request);
         String pattern = item.getPattern();
-        if (pattern == null) {
+        if (StringUtil.isEmpty(pattern)) {
             return clientIp;
         }
         return parseWithMatchStrategyInternal(item.getMatchStrategy(), clientIp, pattern);
@@ -114,7 +114,7 @@ public class GatewayParamParser<T> {
         String pattern = item.getPattern();
         // TODO: what if the header has multiple values?
         String headerValue = requestItemParser.getHeader(request, headerKey);
-        if (pattern == null) {
+        if (StringUtil.isEmpty(pattern)) {
             return headerValue;
         }
         // 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) {
         String pattern = item.getPattern();
         String host = requestItemParser.getHeader(request, "Host");
-        if (pattern == null) {
+        if (StringUtil.isEmpty(pattern)) {
             return host;
         }
         // Match value according to regex pattern or exact mode.
@@ -135,7 +135,7 @@ public class GatewayParamParser<T> {
         String paramName = item.getFieldName();
         String pattern = item.getPattern();
         String param = requestItemParser.getUrlParam(request, paramName);
-        if (pattern == null) {
+        if (StringUtil.isEmpty(pattern)) {
             return param;
         }
         // Match value according to regex pattern or exact mode.
@@ -146,7 +146,7 @@ public class GatewayParamParser<T> {
         String cookieName = item.getFieldName();
         String pattern = item.getPattern();
         String param = requestItemParser.getCookieValue(request, cookieName);
-        if (pattern == null) {
+        if (StringUtil.isEmpty(pattern)) {
             return param;
         }
         // 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;
 
-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.param.GatewayRegexCache;
 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.flow.param.ParamFlowRule;
 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.util.AssertUtil;
 import com.alibaba.csp.sentinel.util.StringUtil;
 
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
 /**
  * @author Eric Zhao
  * @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 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<>();
 
     static {
         currentProperty.addListener(LISTENER);
     }
 
+    private GatewayRuleManager() {
+    }
+
     public static void register2Property(SentinelProperty<Set<GatewayFlowRule>> property) {
         AssertUtil.notNull(property, "property cannot be null");
         synchronized (LISTENER) {
@@ -111,18 +114,48 @@ public final class GatewayRuleManager {
         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>> {
 
         @Override
         public void configUpdate(Set<GatewayFlowRule> conf) {
             applyGatewayRuleInternal(conf);
-            RecordLog.info("[GatewayRuleManager] Gateway flow rules received: " + GATEWAY_RULE_MAP);
+            RecordLog.info("[GatewayRuleManager] Gateway flow rules received: {}", GATEWAY_RULE_MAP);
         }
 
         @Override
         public void configLoad(Set<GatewayFlowRule> 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) {
@@ -136,7 +169,7 @@ public final class GatewayRuleManager {
         private void cacheRegexPattern(/*@NonNull*/ GatewayParamFlowItem item) {
             String pattern = item.getPattern();
             if (StringUtil.isNotEmpty(pattern) &&
-                item.getMatchStrategy() == SentinelGatewayConstants.PARAM_MATCH_STRATEGY_REGEX) {
+                    item.getMatchStrategy() == SentinelGatewayConstants.PARAM_MATCH_STRATEGY_REGEX) {
                 if (GatewayRegexCache.getRegexPattern(pattern) == null) {
                     GatewayRegexCache.addRegexPattern(pattern);
                 }
@@ -205,7 +238,7 @@ public final class GatewayRuleManager {
 
         private void applyToConvertedParamMap(Set<ParamFlowRule> paramFlowRules) {
             Map<String, List<ParamFlowRule>> newRuleMap = ParamFlowRuleUtil.buildParamRuleMap(
-                new ArrayList<>(paramFlowRules));
+                    new ArrayList<>(paramFlowRules));
             if (newRuleMap == null || newRuleMap.isEmpty()) {
                 // No parameter flow rules, so clear all the metrics.
                 for (String resource : CONVERTED_PARAM_RULE_MAP.keySet()) {
@@ -217,10 +250,20 @@ public final class GatewayRuleManager {
             }
 
             // 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)) {
                     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.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.ParamFlowRule;
 import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetricStorage;
+import com.alibaba.csp.sentinel.spi.Spi;
 
 /**
  * @author Eric Zhao
  * @since 1.6.1
  */
+@Spi(order = -4000)
 public class GatewayFlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
 
     @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;
 
-import com.alibaba.csp.sentinel.slotchain.DefaultProcessorSlotChain;
-import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain;
-import com.alibaba.csp.sentinel.slotchain.SlotChainBuilder;
-import com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot;
-import com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot;
-import com.alibaba.csp.sentinel.slots.block.flow.FlowSlot;
-import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowSlot;
-import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
-import com.alibaba.csp.sentinel.slots.logger.LogSlot;
-import com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot;
-import com.alibaba.csp.sentinel.slots.statistic.StatisticSlot;
-import com.alibaba.csp.sentinel.slots.system.SystemSlot;
+import com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder;
 
 /**
  * @author Eric Zhao
  * @since 1.6.1
+ *
+ * @deprecated since 1.7.2, we can use @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
+    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() {
         RequestItemParser<Object> itemParser = mock(RequestItemParser.class);
         GatewayParamParser<Object> paramParser = new GatewayParamParser<>(itemParser);

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

@@ -1,6 +1,6 @@
 # Sentinel Dubbo Adapter
 
-> Note: 中文文档请见[此处](https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E6%B5%81%E6%A1%86%E6%9E%B6%E7%9A%84%E9%80%82%E9%85%8D#dubbo)。
+> Note: 中文文档请见[此处](https://github.com/alibaba/Sentinel/wiki/主流框架的适配#dubbo)。
 
 Sentinel Dubbo Adapter provides service consumer filter and provider filter
 for [Dubbo](https://dubbo.apache.org/en-us/) services.
@@ -52,17 +52,21 @@ If `limitApp` of flow rules is not configured (`default`), flow control will tak
 If `limitApp` of a flow rule is configured with a caller, then the corresponding flow rule will only take effect on the specific caller.
 
 > Note: Dubbo consumer does not provide its Dubbo application name when doing RPC,
-so developers should manually put the application name into *attachment* at consumer side,
-then extract it at provider side. Sentinel Dubbo Adapter has implemented a filter (`DubboAppContextFilter`)
-where consumer can carry application name information to provider automatically.
-If the consumer does not use Sentinel Dubbo Adapter but requires flow control based on caller, developers can manually put the application name into attachment with the key `dubboApplication`.
+> so developers should manually put the application name into *attachment* at consumer side,
+> then extract it at provider side. Sentinel Dubbo Adapter has implemented a filter (`DubboAppContextFilter`)
+> where consumer can carry application name information to provider automatically.
+> If the consumer does not use Sentinel Dubbo Adapter but requires flow control based on caller,
+> developers can manually put the application name into attachment with the key `dubboApplication`.
+>
+> Since 1.8.0, the adapter provides support for customizing origin parsing logic. You may register your own `DubboOriginParser`
+> implementation to `DubboAdapterGlobalConfig`.
 
 ## Global fallback
 
 Sentinel Dubbo Adapter supports global fallback configuration.
 The global fallback will handle exceptions and give replacement result when blocked by
 flow control, degrade or system load protection. You can implement your own `DubboFallback` interface
-and then register to `DubboFallbackRegistry`. If no fallback is configured, Sentinel will wrap the `BlockException`
-then directly throw it out.
+and then register to `DubboAdapterGlobalConfig`.
+If no fallback is configured, Sentinel will wrap the `BlockException` as the fallback result.
 
 Besides, we can also leverage [Dubbo mock mechanism](http://dubbo.apache.org/en-us/docs/user/demos/local-mock.html) to provide fallback implementation of degraded Dubbo services.

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

@@ -6,7 +6,7 @@
     <parent>
         <groupId>com.alibaba.csp</groupId>
         <artifactId>sentinel-adapter</artifactId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <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;
 
+import com.alibaba.csp.sentinel.util.StringUtil;
 import com.alibaba.dubbo.rpc.Filter;
 import com.alibaba.dubbo.rpc.Invocation;
 import com.alibaba.dubbo.rpc.Invoker;
@@ -24,7 +25,7 @@ import com.alibaba.dubbo.rpc.Invoker;
  */
 abstract class AbstractDubboFilter implements Filter {
 
-    protected String getResourceName(Invoker<?> invoker, Invocation invocation) {
+    protected String getMethodResourceName(Invoker<?> invoker, Invocation invocation) {
         StringBuilder buf = new StringBuilder(64);
         buf.append(invoker.getInterface().getName())
             .append(":")
@@ -41,4 +42,25 @@ abstract class AbstractDubboFilter implements Filter {
         buf.append(")");
         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.RpcException;
 
+import static com.alibaba.dubbo.common.Constants.CONSUMER;
+
 /**
  * Puts current consumer's application name in the attachment of each invocation.
  *
  * @author Eric Zhao
  */
-@Activate(group = "consumer")
+@Activate(group = CONSUMER)
 public class DubboAppContextFilter implements Filter {
 
     @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.EntryType;
+import com.alibaba.csp.sentinel.ResourceTypeConstants;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.Tracer;
-import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
-import com.alibaba.csp.sentinel.context.ContextUtil;
 import com.alibaba.csp.sentinel.log.RecordLog;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 import com.alibaba.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.RpcException;
 
+import static com.alibaba.dubbo.common.Constants.CONSUMER;
+
 /**
  * <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 Eric Zhao
  */
-@Activate(group = "consumer")
+@Activate(group = CONSUMER)
 public class SentinelDubboConsumerFilter extends AbstractDubboFilter implements Filter {
 
     public SentinelDubboConsumerFilter() {
@@ -53,24 +54,30 @@ public class SentinelDubboConsumerFilter extends AbstractDubboFilter implements
         Entry interfaceEntry = null;
         Entry methodEntry = null;
         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);
             if (result.hasException()) {
+                Throwable e = result.getException();
                 // Record common exception.
-                Tracer.trace(result.getException());
+                Tracer.traceEntry(e, interfaceEntry);
+                Tracer.traceEntry(e, methodEntry);
             }
             return result;
         } catch (BlockException e) {
-            return DubboFallbackRegistry.getConsumerFallback().handle(invoker, invocation, e);
+            return DubboAdapterGlobalConfig.getConsumerFallback().handle(invoker, invocation, e);
         } catch (RpcException e) {
-            Tracer.trace(e);
+            Tracer.traceEntry(e, interfaceEntry);
+            Tracer.traceEntry(e, methodEntry);
             throw e;
         } finally {
             if (methodEntry != null) {
-                methodEntry.exit();
+                methodEntry.exit(1, invocation.getArguments());
             }
             if (interfaceEntry != null) {
                 interfaceEntry.exit();

+ 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.EntryType;
+import com.alibaba.csp.sentinel.ResourceTypeConstants;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.Tracer;
-import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
 import com.alibaba.csp.sentinel.context.ContextUtil;
 import com.alibaba.csp.sentinel.log.RecordLog;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
-import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
 import com.alibaba.dubbo.common.extension.Activate;
 import com.alibaba.dubbo.rpc.Filter;
 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.RpcException;
 
+import static com.alibaba.dubbo.common.Constants.PROVIDER;
+
 /**
  * <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 Eric Zhao
  */
-@Activate(group = "provider")
+@Activate(group = PROVIDER)
 public class SentinelDubboProviderFilter extends AbstractDubboFilter implements Filter {
 
     public SentinelDubboProviderFilter() {
@@ -52,26 +53,35 @@ public class SentinelDubboProviderFilter extends AbstractDubboFilter implements
     @Override
     public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
         // Get origin caller.
-        String application = DubboUtils.getApplication(invocation, "");
+        String origin = DubboAdapterGlobalConfig.getOriginParser().parse(invoker, invocation);
+        if (null == origin) {
+            origin = "";
+        }
 
         Entry interfaceEntry = null;
         Entry methodEntry = null;
         try {
-            String resourceName = getResourceName(invoker, invocation);
-            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);
             if (result.hasException()) {
-                Tracer.trace(result.getException());
+                Throwable e = result.getException();
+                // Record common exception.
+                Tracer.traceEntry(e, interfaceEntry);
+                Tracer.traceEntry(e, methodEntry);
             }
             return result;
         } catch (BlockException e) {
-            return DubboFallbackRegistry.getProviderFallback().handle(invoker, invocation, e);
+            return DubboAdapterGlobalConfig.getProviderFallback().handle(invoker, invocation, e);
         } catch (RpcException e) {
-            Tracer.trace(e);
+            Tracer.traceEntry(e, interfaceEntry);
+            Tracer.traceEntry(e, methodEntry);
             throw e;
         } finally {
             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.Invoker;
 import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcResult;
 
 /**
  * @author Eric Zhao
@@ -28,7 +29,9 @@ public class DefaultDubboFallback implements DubboFallback {
 
     @Override
     public Result handle(Invoker<?> invoker, Invocation invocation, BlockException ex) {
-        // Just wrap and throw the exception.
-        throw new SentinelRpcException(ex);
+        // Just wrap the exception. edit by wzg923 2020/9/23
+        RpcResult result = new RpcResult();
+        result.setException(new SentinelRpcException(ex.toRuntimeException()));
+        return result;
     }
 }

+ 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;
 
+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
+ * @deprecated use {@link DubboAdapterGlobalConfig} instead.
  */
+@Deprecated
 public final class DubboFallbackRegistry {
 
-    private static volatile DubboFallback consumerFallback = new DefaultDubboFallback();
-    private static volatile DubboFallback providerFallback = new DefaultDubboFallback();
-
     public static DubboFallback getConsumerFallback() {
-        return consumerFallback;
+        return DubboAdapterGlobalConfig.getConsumerFallback();
     }
 
     public static void setConsumerFallback(DubboFallback consumerFallback) {
-        DubboFallbackRegistry.consumerFallback = consumerFallback;
+        DubboAdapterGlobalConfig.setConsumerFallback(consumerFallback);
     }
 
     public static DubboFallback getProviderFallback() {
-        return providerFallback;
+        return DubboAdapterGlobalConfig.getProviderFallback();
     }
 
     public static void setProviderFallback(DubboFallback providerFallback) {
-        DubboFallbackRegistry.providerFallback = providerFallback;
+        DubboAdapterGlobalConfig.setProviderFallback(providerFallback);
     }
 
     private DubboFallbackRegistry() {}

+ 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;
 
 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.Invoker;
 import com.alibaba.dubbo.rpc.Result;
 import com.alibaba.dubbo.rpc.RpcException;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 
 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
  */
 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() {
         @Override
         public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
@@ -24,6 +43,7 @@ public class AbstractDubboFilterTest {
         }
     };
 
+
     @Test
     public void testGetResourceName() {
         Invoker invoker = mock(Invoker.class);
@@ -34,8 +54,36 @@ public class AbstractDubboFilterTest {
         when(invocation.getMethodName()).thenReturn(method.getName());
         when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
 
-        String resourceName = filter.getResourceName(invoker, invocation);
+        String resourceName = filter.getMethodResourceName(invoker, invocation);
 
         assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
     }
+
+    @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
         // In actual project, a consumer is usually also a provider, the context will be created by SentinelDubboProviderFilter
         // If consumer is on the top of Dubbo RPC invocation chain, use default context
-        String resourceName = filter.getResourceName(invoker, invocation);
+        String resourceName = filter.getMethodResourceName(invoker, invocation);
         assertEquals(Constants.CONTEXT_DEFAULT_NAME, context.getName());
         assertEquals("", context.getOrigin());
 
         DefaultNode entranceNode = context.getEntranceNode();
         ResourceWrapper entranceResource = entranceNode.getId();
         assertEquals(Constants.CONTEXT_DEFAULT_NAME, entranceResource.getName());
-        assertSame(EntryType.IN, entranceResource.getType());
+        assertSame(EntryType.IN, entranceResource.getEntryType());
 
         // As SphU.entry(interfaceName, EntryType.OUT);
         Set<Node> childList = entranceNode.getChildList();
@@ -100,7 +100,7 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
         DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
         ResourceWrapper interfaceResource = interfaceNode.getId();
         assertEquals(DemoService.class.getName(), interfaceResource.getName());
-        assertSame(EntryType.OUT, interfaceResource.getType());
+        assertSame(EntryType.OUT, interfaceResource.getEntryType());
 
         // As SphU.entry(resourceName, EntryType.OUT);
         childList = interfaceNode.getChildList();
@@ -108,7 +108,7 @@ public class SentinelDubboConsumerFilterTest extends BaseTest {
         DefaultNode methodNode = (DefaultNode) childList.iterator().next();
         ResourceWrapper methodResource = methodNode.getId();
         assertEquals(resourceName, methodResource.getName());
-        assertSame(EntryType.OUT, methodResource.getType());
+        assertSame(EntryType.OUT, methodResource.getEntryType());
 
         // Verify curEntry
         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);
 
         // As ContextUtil.enter(resourceName, application) in SentinelDubboProviderFilter
-        String resourceName = filter.getResourceName(invoker, invocation);
+        String resourceName = filter.getMethodResourceName(invoker, invocation);
         assertEquals(resourceName, context.getName());
         assertEquals(originApplication, context.getOrigin());
 
         DefaultNode entranceNode = context.getEntranceNode();
         ResourceWrapper entranceResource = entranceNode.getId();
         assertEquals(resourceName, entranceResource.getName());
-        assertSame(EntryType.IN, entranceResource.getType());
+        assertSame(EntryType.IN, entranceResource.getEntryType());
 
         // As SphU.entry(interfaceName, EntryType.IN);
         Set<Node> childList = entranceNode.getChildList();
@@ -100,7 +100,7 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
         DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
         ResourceWrapper interfaceResource = interfaceNode.getId();
         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());
         childList = interfaceNode.getChildList();
@@ -108,7 +108,7 @@ public class SentinelDubboProviderFilterTest extends BaseTest {
         DefaultNode methodNode = (DefaultNode) childList.iterator().next();
         ResourceWrapper methodResource = methodNode.getId();
         assertEquals(resourceName, methodResource.getName());
-        assertSame(EntryType.IN, methodResource.getType());
+        assertSame(EntryType.IN, methodResource.getEntryType());
 
         // Verify curEntry
         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;
 
+import com.alibaba.csp.sentinel.adapter.dubbo.DubboAdapterGlobalConfig;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
 import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
@@ -22,7 +23,6 @@ import com.alibaba.dubbo.rpc.Invocation;
 import com.alibaba.dubbo.rpc.Invoker;
 import com.alibaba.dubbo.rpc.Result;
 import com.alibaba.dubbo.rpc.RpcResult;
-
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -31,24 +31,25 @@ import org.junit.Test;
  */
 public class DubboFallbackRegistryTest {
 
-    @Test(expected = SentinelRpcException.class)
+    @Test
     public void testDefaultFallback() {
-        // Test for default.
+        // Test for default fallback.
         BlockException ex = new FlowException("xxx");
-        DubboFallbackRegistry.getConsumerFallback()
-            .handle(null, null, ex);
+        Result result = new DefaultDubboFallback().handle(null, null, ex);
+        Assert.assertTrue(result.hasException());
+        Assert.assertEquals(SentinelRpcException.class, result.getException().getClass());
     }
 
     @Test
     public void testCustomFallback() {
         BlockException ex = new FlowException("xxx");
-        DubboFallbackRegistry.setConsumerFallback(new DubboFallback() {
+        DubboAdapterGlobalConfig.setConsumerFallback(new DubboFallback() {
             @Override
             public Result handle(Invoker<?> invoker, Invocation invocation, BlockException e) {
                 return new RpcResult("Error: " + e.getClass().getName());
             }
         });
-        Result result = DubboFallbackRegistry.getConsumerFallback()
+        Result result = DubboAdapterGlobalConfig.getConsumerFallback()
             .handle(null, null, ex);
         Assert.assertFalse("The invocation should not fail", result.hasException());
         Assert.assertEquals("Error: " + ex.getClass().getName(), result.getValue());

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

@@ -3,7 +3,6 @@
 Sentinel gRPC Adapter provides client and server interceptor for gRPC services.
 
 > 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
 
@@ -35,4 +34,3 @@ Server server = ServerBuilder.forPort(port)
      .intercept(new SentinelGrpcServerInterceptor()) // Add the server interceptor.
      .build();
 ```
-

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

@@ -5,14 +5,14 @@
     <parent>
         <artifactId>sentinel-adapter</artifactId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>sentinel-grpc-adapter</artifactId>
     <packaging>jar</packaging>
 
     <properties>
-        <grpc.version>1.13.1</grpc.version>
+        <grpc.version>1.30.2</grpc.version>
     </properties>
 
     <dependencies>
@@ -43,7 +43,7 @@
         <dependency>
             <groupId>javax.annotation</groupId>
             <artifactId>javax.annotation-api</artifactId>
-            <version>1.2</version>
+            <version>${javax.annotation-api.version}</version>
         </dependency>
 
 
@@ -95,4 +95,4 @@
             </plugin>
         </plugins>
     </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;
 
-import javax.annotation.Nullable;
-
 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.context.ContextUtil;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
-
 import io.grpc.CallOptions;
 import io.grpc.Channel;
 import io.grpc.ClientCall;
 import io.grpc.ClientInterceptor;
 import io.grpc.ForwardingClientCall;
-import io.grpc.ForwardingClientCallListener.SimpleForwardingClientCallListener;
+import io.grpc.ForwardingClientCallListener;
 import io.grpc.Metadata;
 import io.grpc.MethodDescriptor;
 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>
  * Example code:
  * <pre>
  * public class ServiceClient {
@@ -52,50 +51,59 @@ import io.grpc.Status;
  *
  * }
  * </pre>
- *
+ * <p>
  * For server interceptor, see {@link SentinelGrpcServerInterceptor}.
  *
  * @author Eric Zhao
  */
 public class SentinelGrpcClientInterceptor implements ClientInterceptor {
-
     private static final Status FLOW_CONTROL_BLOCK = Status.UNAVAILABLE.withDescription(
-        "Flow control limit exceeded (client side)");
+            "Flow control limit exceeded (client side)");
 
     @Override
     public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> methodDescriptor,
                                                                CallOptions callOptions, Channel channel) {
-        String resourceName = methodDescriptor.getFullMethodName();
+        String fullMethodName = methodDescriptor.getFullMethodName();
         Entry entry = null;
         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.
             return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(
-                channel.newCall(methodDescriptor, callOptions)) {
+                    channel.newCall(methodDescriptor, callOptions)) {
                 @Override
                 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
                         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);
                 }
 
+                /**
+                 * Some Exceptions will only call cancel.
+                 */
                 @Override
                 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);
-                    // Record the exception metrics.
-                    recordException(cause);
                 }
             };
         } catch (BlockException e) {
@@ -108,32 +116,27 @@ public class SentinelGrpcClientInterceptor implements ClientInterceptor {
 
                 @Override
                 public void request(int numMessages) {
-
                 }
 
                 @Override
                 public void cancel(@Nullable String message, @Nullable Throwable cause) {
-
                 }
 
                 @Override
                 public void halfClose() {
-
                 }
 
                 @Override
                 public void sendMessage(ReqT message) {
-
                 }
             };
-        } finally {
+        } catch (RuntimeException e) {
+            // Catch the RuntimeException newCall throws, entry is guaranteed to exit.
             if (entry != null) {
+                Tracer.traceEntry(e, entry);
                 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.SphU;
 import com.alibaba.csp.sentinel.Tracer;
-import com.alibaba.csp.sentinel.context.ContextUtil;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
-
 import io.grpc.ForwardingServerCall;
 import io.grpc.ForwardingServerCallListener;
 import io.grpc.Metadata;
 import io.grpc.ServerCall;
-import io.grpc.ServerCall.Listener;
 import io.grpc.ServerCallHandler;
 import io.grpc.ServerInterceptor;
 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>
  * Example code:
  * <pre>
  * Server server = ServerBuilder.forPort(port)
@@ -41,50 +41,69 @@ import io.grpc.Status;
  *      .intercept(new SentinelGrpcServerInterceptor()) // Add the server interceptor.
  *      .build();
  * </pre>
- *
+ * <p>
  * For client interceptor, see {@link SentinelGrpcClientInterceptor}.
  *
  * @author Eric Zhao
  */
 public class SentinelGrpcServerInterceptor implements ServerInterceptor {
-
     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
-    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);
         Entry entry = null;
         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.
             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) {
-            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) {
+                Tracer.traceEntry(e, entry);
                 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;
 
-import java.util.concurrent.TimeUnit;
-
 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.FooServiceGrpc;
-
 import io.grpc.ClientInterceptor;
 import io.grpc.ManagedChannel;
 import io.grpc.ManagedChannelBuilder;
 
+import java.util.concurrent.TimeUnit;
+
 /**
  * A simple wrapped gRPC client for FooService.
  *
  * @author Eric Zhao
  */
 final class FooServiceClient {
-
     private final ManagedChannel channel;
     private final FooServiceGrpc.FooServiceBlockingStub blockingStub;
 
     FooServiceClient(String host, int port) {
         this.channel = ManagedChannelBuilder.forAddress(host, port)
-            .usePlaintext()
-            .build();
+                .usePlaintext()
+                .build();
         this.blockingStub = FooServiceGrpc.newBlockingStub(this.channel);
     }
 
     FooServiceClient(String host, int port, ClientInterceptor interceptor) {
         this.channel = ManagedChannelBuilder.forAddress(host, port)
-            .usePlaintext()
-            .intercept(interceptor)
-            .build();
+                .usePlaintext()
+                .intercept(interceptor)
+                .build();
         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.FooResponse;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooServiceGrpc;
-
 import io.grpc.stub.StreamObserver;
 
 /**
  * Implementation of FooService defined in proto.
  */
 class FooServiceImpl extends FooServiceGrpc.FooServiceImplBase {
-
     @Override
     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
     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 java.io.IOException;
+import java.util.concurrent.Executors;
 
 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;
 
-import java.util.Collections;
-
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
 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.FlowRuleManager;
 import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
-
 import io.grpc.StatusRuntimeException;
 import org.junit.After;
+import org.junit.Before;
 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}.
@@ -38,48 +41,52 @@ import static org.junit.Assert.*;
  * @author Eric Zhao
  */
 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 FooServiceClient client;
 
     private void configureFlowRule(int count) {
         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));
     }
 
     @Test
     public void testGrpcClientInterceptor() throws Exception {
         final int port = 19328;
-
-        configureFlowRule(threshold);
         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);
-        assertEquals(1, clusterNode.totalRequest() - clusterNode.blockRequest());
+        assertEquals(1, clusterNode.totalPass());
 
         // Not allowed to pass.
         configureFlowRule(0);
-
         // The second request will be blocked.
-        assertFalse(sendRequest(client));
+        assertFalse(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(666).build()));
         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();
     }
 
-    private boolean sendRequest(FooServiceClient client) {
+    private boolean sendRequest(FooRequest request) {
         try {
-            FooResponse response = client.sayHello(FooRequest.newBuilder().setName("Sentinel").setId(666).build());
+            FooResponse response = client.sayHello(request);
             System.out.println("Response: " + response);
             return true;
         } catch (StatusRuntimeException ex) {
@@ -88,9 +95,15 @@ public class SentinelGrpcClientInterceptorTest {
         }
     }
 
+    @Before
+    public void cleanUpBefore() {
+        FlowRuleManager.loadRules(null);
+        ClusterBuilderSlot.getClusterNodeMap().clear();
+    }
+
     @After
-    public void cleanUp() {
+    public void cleanUpAfter() {
         FlowRuleManager.loadRules(null);
         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;
 
-import java.util.Collections;
-
 import com.alibaba.csp.sentinel.EntryType;
 import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
 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.FlowRuleManager;
 import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
-
 import io.grpc.StatusRuntimeException;
 import org.junit.After;
+import org.junit.Before;
 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}.
@@ -38,49 +41,52 @@ import static org.junit.Assert.*;
  * @author Eric Zhao
  */
 public class SentinelGrpcServerInterceptorTest {
-
     private final String resourceName = "com.alibaba.sentinel.examples.FooService/anotherHello";
-    private final int threshold = 4;
     private final GrpcTestServer server = new GrpcTestServer();
-
     private FooServiceClient client;
 
     private void configureFlowRule(int count) {
         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));
     }
 
     @Test
     public void testGrpcServerInterceptor() throws Exception {
         final int port = 19329;
-        client = new FooServiceClient("localhost", port);
-
-        configureFlowRule(threshold);
         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);
         assertNotNull(clusterNode);
-        assertEquals(1, clusterNode.totalRequest() - clusterNode.blockRequest());
+        assertEquals(1, clusterNode.totalPass());
 
         // Not allowed to pass.
         configureFlowRule(0);
-
         // The second request will be blocked.
-        assertFalse(sendRequest());
+        assertFalse(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(666).build()));
         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();
     }
 
-    private boolean sendRequest() {
+    private boolean sendRequest(FooRequest request) {
         try {
-            FooResponse response = client.anotherHello(FooRequest.newBuilder().setName("Sentinel").setId(666).build());
+            FooResponse response = client.anotherHello(request);
             System.out.println("Response: " + response);
             return true;
         } catch (StatusRuntimeException ex) {
@@ -89,10 +95,15 @@ public class SentinelGrpcServerInterceptorTest {
         }
     }
 
+    @Before
+    public void cleanUpBefore() {
+        FlowRuleManager.loadRules(null);
+        ClusterBuilderSlot.getClusterNodeMap().clear();
+    }
 
     @After
-    public void cleanUp() {
+    public void cleanUpAfter() {
         FlowRuleManager.loadRules(null);
         ClusterBuilderSlot.getClusterNodeMap().clear();
     }
-}
+}

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

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

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

@@ -1,7 +1,5 @@
 # Sentinel Reactor Adapter
 
-> Note: this module requires Java 8 or later version.
-
 Sentinel provides integration module for [Reactor](https://projectreactor.io/).
 
 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>
         <artifactId>sentinel-adapter</artifactId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     <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 com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.ResourceTypeConstants;
 import com.alibaba.csp.sentinel.util.AssertUtil;
 
 /**
@@ -28,6 +29,8 @@ public class EntryConfig {
 
     private final String resourceName;
     private final EntryType entryType;
+    private final int resourceType;
+
     private final int acquireCount;
     private final Object[] args;
     private final ContextConfig contextConfig;
@@ -44,17 +47,31 @@ public class EntryConfig {
         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) {
         this(resourceName, entryType, acquireCount, args, null);
     }
 
     public EntryConfig(String resourceName, EntryType entryType, int acquireCount, Object[] args,
                        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.notNull(entryType, "entryType cannot be null");
         AssertUtil.isTrue(acquireCount > 0, "acquireCount should be positive");
         this.resourceName = resourceName;
         this.entryType = entryType;
+        this.resourceType = resourceType;
         this.acquireCount = acquireCount;
         this.args = args;
         // Constructed ContextConfig should be valid here. Null is allowed here.
@@ -81,11 +98,19 @@ public class EntryConfig {
         return contextConfig;
     }
 
+    /**
+     * @since 1.7.0
+     */
+    public int getResourceType() {
+        return resourceType;
+    }
+
     @Override
     public String toString() {
         return "EntryConfig{" +
             "resourceName='" + resourceName + '\'' +
             ", entryType=" + entryType +
+            ", resourceType=" + resourceType +
             ", acquireCount=" + acquireCount +
             ", args=" + Arrays.toString(args) +
             ", 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());
         }
         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;
             actual.onSubscribe(this);
         } catch (BlockException ex) {
@@ -155,7 +155,7 @@ public class SentinelReactorSubscriber<T> extends InheritableBaseSubscriber<T> {
 
     @Override
     protected void hookOnCancel() {
-
+        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());
     }
 
+    @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) {
         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
 
-> Note: this module requires Java 8 or later version.
-
 Sentinel provides integration module with Spring Cloud Gateway.
 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>
         <artifactId>sentinel-adapter</artifactId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     <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 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.param.GatewayParamParser;
 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.route.Route;
 import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
+import org.springframework.core.Ordered;
 import org.springframework.web.server.ServerWebExchange;
 import reactor.core.publisher.Mono;
 
@@ -41,7 +43,17 @@ import reactor.core.publisher.Mono;
  * @author Eric Zhao
  * @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<>(
         new ServerWebExchangeItemParser());
@@ -59,8 +71,8 @@ public class SentinelGatewayFilter implements GatewayFilter, GlobalFilter {
                 .map(f -> f.apply(exchange))
                 .orElse("");
             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,
                 r -> r.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME);
             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)
             .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
     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)
             .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;
 
+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.HashMap;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
 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
  * @since 1.6.0
  */
 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() {
         return Collections.unmodifiableMap(API_MATCHER_MAP);
@@ -50,15 +50,12 @@ public final class GatewayApiMatcherManager {
     }
 
     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() {}

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

@@ -1,7 +1,5 @@
 # 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
 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`:
 
 - `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`.
 
 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>
         <artifactId>sentinel-adapter</artifactId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     <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 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.EntryConfig;
 import com.alibaba.csp.sentinel.adapter.reactor.SentinelReactorTransformer;
 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.WebFilter;
@@ -36,24 +38,25 @@ public class SentinelWebFluxFilter implements WebFilter {
 
     @Override
     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:
         // exchange.getAttributeOrDefault(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, path)
-
         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())
             .map(f -> f.apply(exchange))
             .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 = "";

+ 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
+    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 {
         final String fooPrefix = "/foo/";
         String url1 = fooPrefix + 1;
@@ -102,6 +126,26 @@ public class SentinelWebFluxIntegrationTest {
     }
 
     @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 {
         String url = "/error";
         String prefix = "blocked: ";
@@ -166,4 +210,4 @@ public class SentinelWebFluxIntegrationTest {
         FlowRuleManager.loadRules(new ArrayList<>());
         ClusterBuilderSlot.resetClusterNodes();
     }
-}
+}

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

@@ -1,6 +1,7 @@
 # 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
 <dependency>
@@ -10,7 +11,7 @@ Sentinel provides Servlet filter integration to enable flow control for web requ
 </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
 <filter>
@@ -34,16 +35,21 @@ public class FilterConfig {
     public FilterRegistrationBean sentinelFilterRegistration() {
         FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
         registration.setFilter(new CommonFilter());
+        // Set the matching URL pattern for the filter.
         registration.addUrlPatterns("/*");
-        registration.setName("sentinelFilter");
+        registration.setName("sentinelCommonFilter");
         registration.setOrder(1);
-
+        // Set whether to support the specified HTTP method prefix for the filter.
+        registration.addInitParameter(CommonFilter.HTTP_METHOD_SPECIFY, "false");
         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),
 the filter will redirect the request to provided URL. You can also implement your own
 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
 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>
         <groupId>com.alibaba.csp</groupId>
         <artifactId>sentinel-adapter</artifactId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
 
     <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.EntryType;
+import com.alibaba.csp.sentinel.ResourceTypeConstants;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.Tracer;
 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.WebCallbackManager;
+import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig;
 import com.alibaba.csp.sentinel.adapter.servlet.util.FilterUtil;
 import com.alibaba.csp.sentinel.context.ContextUtil;
 import com.alibaba.csp.sentinel.slots.block.BlockException;
 import com.alibaba.csp.sentinel.util.StringUtil;
 
-/***
+/**
  * Servlet filter that integrates with Sentinel.
  *
  * @author youji.zj
  * @author Eric Zhao
+ * @author zhaoyuguang
  */
 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 boolean httpMethodSpecify = false;
+    private boolean webContextUnify = true;
 
     @Override
     public void init(FilterConfig filterConfig) {
         httpMethodSpecify = Boolean.parseBoolean(filterConfig.getInitParameter(HTTP_METHOD_SPECIFY));
+        if (filterConfig.getInitParameter(WEB_CONTEXT_UNIFY) != null) {
+            webContextUnify = Boolean.parseBoolean(filterConfig.getInitParameter(WEB_CONTEXT_UNIFY));
+        }
     }
 
     @Override
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
             throws IOException, ServletException {
         HttpServletRequest sRequest = (HttpServletRequest) request;
-        Entry entry = null;
-
-        Entry methodEntry = null;
+        Entry urlEntry = null;
 
         try {
             String target = FilterUtil.filterTarget(sRequest);
@@ -73,39 +91,33 @@ public class CommonFilter implements Filter {
                 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);
         } catch (BlockException e) {
             HttpServletResponse sResponse = (HttpServletResponse) response;
             // Return the block page, or redirect to another URL.
             WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse, e);
-        } catch (IOException e2) {
-            Tracer.trace(e2);
+        } catch (IOException | ServletException | RuntimeException e2) {
+            Tracer.traceEntry(e2, urlEntry);
             throw e2;
-        } catch (ServletException e3) {
-            Tracer.trace(e3);
-            throw e3;
-        } catch (RuntimeException e4) {
-            Tracer.trace(e4);
-            throw e4;
         } finally {
-            if (methodEntry != null) {
-                methodEntry.exit();
-            }
-            if (entry != null) {
-                entry.exit();
+            if (urlEntry != null) {
+                urlEntry.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 com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.ResourceTypeConstants;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.Tracer;
 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.slots.block.BlockException;
 
@@ -52,26 +53,18 @@ public class CommonTotalFilter implements Filter {
     public void doFilter(ServletRequest request, ServletResponse response,
                          FilterChain chain) throws IOException, ServletException {
         HttpServletRequest sRequest = (HttpServletRequest)request;
-        String target = FilterUtil.filterTarget(sRequest);
-        target = WebCallbackManager.getUrlCleaner().clean(target);
 
         Entry entry = null;
         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);
         } catch (BlockException e) {
             HttpServletResponse sResponse = (HttpServletResponse)response;
             WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse, e);
-        } catch (IOException e2) {
+        } catch (IOException | ServletException | RuntimeException e2) {
             Tracer.trace(e2);
             throw e2;
-        } catch (ServletException e3) {
-            Tracer.trace(e3);
-            throw e3;
-        } catch (RuntimeException e4) {
-            Tracer.trace(e4);
-            throw e4;
         } finally {
             if (entry != null) {
                 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.CommonTotalFilter;
 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 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
@@ -33,10 +43,52 @@ public class WebServletConfig {
      * @return the block page URL, maybe null if not configured.
      */
     public static String getBlockPage() {
-        return SentinelConfig.getConfig(BLOCK_PAGE);
+        return SentinelConfig.getConfig(BLOCK_PAGE_URL_CONF_KEY);
     }
 
     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.
  *
+ * @author zhaoyuguang
  * @author youji.zj
  * @author Eric Zhao
  */
@@ -65,7 +66,7 @@ public final class FilterUtil {
         }
 
         if (StringUtil.isBlank(WebServletConfig.getBlockPage())) {
-            writeDefaultBlockedPage(response);
+            writeDefaultBlockedPage(response, WebServletConfig.getBlockPageHttpStatus());
         } else {
             String redirectUrl = WebServletConfig.getBlockPage() + "?http_referer=" + url.toString();
             // 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();
         out.print(DEFAULT_BLOCK_MSG);
         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 com.alibaba.csp.sentinel.Constants;
 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.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.util.FilterUtil;
 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.flow.FlowRule;
 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.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.test.context.junit4.SpringRunner;
 import org.springframework.test.web.servlet.MockMvc;
 
 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.*;
 
 /**
+ * @author zhaoyuguang
  * @author Eric Zhao
  */
 @RunWith(SpringRunner.class)
@@ -76,6 +81,7 @@ public class CommonFilterTest {
 
     @Test
     public void testCommonFilterMiscellaneous() throws Exception {
+        Constants.ROOT.removeChildList();
         String url = "/hello";
         this.mvc.perform(get(url))
             .andExpect(status().isOk())
@@ -85,22 +91,39 @@ public class CommonFilterTest {
         assertNotNull(cn);
         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);
 
         // Test for url cleaner.
         testUrlCleaner();
-
+        testUrlExclusion();
         testCustomOriginParser();
     }
 
     private void testCommonBlockAndRedirectBlockPage(String url, ClusterNode cn) throws Exception {
         configureRulesFor(url, 0);
         // 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))
             .andExpect(status().isOk())
             .andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG));
         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.
         String redirectUrl = "http://some-location.com";
         WebServletConfig.setBlockPage(redirectUrl);
@@ -139,6 +162,25 @@ public class CommonFilterTest {
         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 {
         String url = "/hello";
         String limitOrigin = "userA";
@@ -158,7 +200,7 @@ public class CommonFilterTest {
             .andExpect(content().string(HELLO_STR));
         // This will be blocked.
         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));
         this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN))
             .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) {
         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.*;
 
 /**
+ * @author zhaoyuguang
  * @author Roger Law
  */
 @RunWith(SpringRunner.class)
@@ -107,7 +108,7 @@ public class CommonFilterMethodTest {
         configureRulesFor(GET + ":" + url, 0);
         // The request will be blocked and response is default block message.
         this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN))
-                .andExpect(status().isOk())
+                .andExpect(status().isTooManyRequests())
                 .andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG));
         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();
         registration.setFilter(new CommonFilter());
         registration.addUrlPatterns("/*");
-        registration.addInitParameter("HTTP_METHOD_SPECIFY", "true");
+        registration.addInitParameter(CommonFilter.HTTP_METHOD_SPECIFY, "true");
         registration.setName("sentinelFilter");
         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.
 - `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">
 

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

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>sentinel-adapter</artifactId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>sentinel-zuul-adapter</artifactId>
@@ -69,6 +69,12 @@
             <artifactId>mockito-core</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-test</artifactId>
+            <version>4.3.20.RELEASE</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </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.function.Predicate;
-
 import com.netflix.zuul.context.RequestContext;
 import org.springframework.util.AntPathMatcher;
 import org.springframework.util.PathMatcher;
 
+import javax.servlet.http.HttpServletRequest;
+
 /**
  * @author Eric Zhao
  * @since 1.6.0
@@ -42,7 +43,12 @@ public class PrefixRoutePathMatcher implements Predicate<RequestContext> {
 
     @Override
     public boolean test(RequestContext context) {
-        String path = context.getRequest().getServletPath();
+        //Solve the problem of prefix matching
+        HttpServletRequest request = context.getRequest();
+        String path = request.getRequestURI();
+        if (path == null) {
+            AssertUtil.assertNotBlank(pattern, "requesturi cannot be blank");
+        }
         if (canMatch) {
             return pathMatcher.match(pattern, path);
         }

+ 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;
 
-import java.util.regex.Pattern;
-
 import com.alibaba.csp.sentinel.util.AssertUtil;
 import com.alibaba.csp.sentinel.util.function.Predicate;
-
 import com.netflix.zuul.context.RequestContext;
 
+import javax.servlet.http.HttpServletRequest;
+import java.util.regex.Pattern;
+
 /**
  * @author Eric Zhao
  * @since 1.6.0
@@ -39,7 +39,12 @@ public class RegexRoutePathMatcher implements Predicate<RequestContext> {
 
     @Override
     public boolean test(RequestContext context) {
-        String path = context.getRequest().getServletPath();
+        //Solve the problem of route matching
+        HttpServletRequest request = context.getRequest();
+        String path = request.getRequestURI();
+        if (path == null) {
+            AssertUtil.assertNotBlank(pattern, "requesturi cannot be blank");
+        }
         return regex.matcher(path).matches();
     }
 

+ 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 com.alibaba.csp.sentinel.AsyncEntry;
+import com.alibaba.csp.sentinel.Entry;
 import com.alibaba.csp.sentinel.Tracer;
 import com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant;
 import com.alibaba.csp.sentinel.context.ContextUtil;
@@ -34,11 +34,11 @@ final class SentinelEntryUtils {
     static void tryExitFromCurrentContext() {
         RequestContext ctx = RequestContext.getCurrentContext();
         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);
         }
@@ -50,17 +50,22 @@ final class SentinelEntryUtils {
     static void tryTraceExceptionThenExitFromCurrentContext(Throwable t) {
         RequestContext ctx = RequestContext.getCurrentContext();
         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);
         }
         ContextUtil.exit();
     }
 
+    static void exit(EntryHolder holder) {
+        Entry entry = holder.getEntry();
+        entry.exit(1, holder.getParams());
+    }
+
     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.EntryType;
+import com.alibaba.csp.sentinel.ResourceTypeConstants;
 import com.alibaba.csp.sentinel.SphU;
 import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser;
 import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
@@ -88,7 +89,7 @@ public class SentinelZuulPreFilter extends ZuulFilter {
     }
 
     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,
             new Predicate<GatewayFlowRule>() {
                 @Override
@@ -96,8 +97,10 @@ public class SentinelZuulPreFilter extends ZuulFilter {
                     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
@@ -106,12 +109,12 @@ public class SentinelZuulPreFilter extends ZuulFilter {
         String origin = parseOrigin(ctx.getRequest());
         String routeId = (String)ctx.get(ZuulConstant.PROXY_ID_KEY);
 
-        Deque<AsyncEntry> asyncEntries = new ArrayDeque<>();
+        Deque<EntryHolder> holders = new ArrayDeque<>();
         String fallBackRoute = routeId;
         try {
             if (StringUtil.isNotBlank(routeId)) {
                 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);
@@ -120,7 +123,7 @@ public class SentinelZuulPreFilter extends ZuulFilter {
             }
             for (String apiName : matchingApis) {
                 fallBackRoute = apiName;
-                doSentinelEntry(apiName, RESOURCE_MODE_CUSTOM_API_NAME, ctx, asyncEntries);
+                doSentinelEntry(apiName, RESOURCE_MODE_CUSTOM_API_NAME, ctx, holders);
             }
         } catch (BlockException ex) {
             ZuulBlockFallbackProvider zuulBlockFallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(
@@ -138,8 +141,8 @@ public class SentinelZuulPreFilter extends ZuulFilter {
         } finally {
             // 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.
-            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;

+ 1 - 12
sentinel-benchmark/pom.xml

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

+ 3 - 5
sentinel-cluster/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>sentinel-parent</artifactId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <packaging>pom</packaging>
@@ -13,16 +13,14 @@
     <description>The parent module of Sentinel cluster server</description>
 
     <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>
 
     <modules>
         <module>sentinel-cluster-client-default</module>
         <module>sentinel-cluster-server-default</module>
         <module>sentinel-cluster-common-default</module>
+        <module>sentinel-cluster-server-envoy-rls</module>
     </modules>
 
     <dependencyManagement>

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

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>sentinel-cluster</artifactId>
         <groupId>com.alibaba.csp</groupId>
-        <version>1.6.2</version>
+        <version>1.8.3</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -53,5 +53,10 @@
             <artifactId>mockito-core</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </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 {
             this.transportClient = new NettyTransportClient(host, port);
             this.serverDescriptor = new TokenServerDescriptor(host, port);
-            RecordLog.info("[DefaultClusterTokenClient] New client created: " + serverDescriptor);
+            RecordLog.info("[DefaultClusterTokenClient] New client created: {}", serverDescriptor);
         } catch (Exception ex) {
             RecordLog.warn("[DefaultClusterTokenClient] Failed to initialize new token client", ex);
         }
@@ -97,7 +97,7 @@ public class DefaultClusterTokenClient implements ClusterTokenClient {
             this.transportClient = new NettyTransportClient(config.getServerHost(), config.getServerPort());
             this.serverDescriptor = new TokenServerDescriptor(config.getServerHost(), config.getServerPort());
             startClientIfScheduled();
-            RecordLog.info("[DefaultClusterTokenClient] New client created: " + serverDescriptor);
+            RecordLog.info("[DefaultClusterTokenClient] New client created: {}", serverDescriptor);
         } catch (Exception ex) {
             RecordLog.warn("[DefaultClusterTokenClient] Failed to change remote token server", ex);
         }
@@ -182,6 +182,15 @@ public 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) {
         switch (result.getStatus()) {
             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")
     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;
 
@@ -126,8 +126,7 @@ public class NettyTransportClient implements ClusterTransportClient {
                     } else {
                         failConnectedTime.set(0);
                         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
                 public void run() {
                     if (shouldRetry.get()) {
-                        RecordLog.info("[NettyTransportClient] Reconnecting to server <" + host + ":" + port + ">");
+                        RecordLog.info("[NettyTransportClient] Reconnecting to server <{}:{}>", host, port);
                         try {
                             startInternal();
                         } catch (Exception e) {
@@ -238,10 +237,12 @@ public class NettyTransportClient implements ClusterTransportClient {
     }
 
     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) {
@@ -266,5 +267,6 @@ public class NettyTransportClient implements ClusterTransportClient {
         return future;
     }*/
 
+    private static final int MIN_ID = 1;
     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;
 
-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.response.ResponseEntityDecoder;
 import com.alibaba.csp.sentinel.log.RecordLog;
@@ -34,19 +34,21 @@ public final class ClientEntityCodecProvider {
     }
 
     private static void resolveInstance() {
-        RequestEntityWriter writer = SpiLoader.loadFirstInstance(RequestEntityWriter.class);
+        RequestEntityWriter writer = SpiLoader.of(RequestEntityWriter.class).loadFirstInstance();
         if (writer == null) {
             RecordLog.warn("[ClientEntityCodecProvider] No existing request entity writer, resolve failed");
         } else {
             requestEntityWriter = writer;
-            RecordLog.info("[ClientEntityCodecProvider] Request entity writer resolved: " + requestEntityWriter.getClass().getCanonicalName());
+            RecordLog.info("[ClientEntityCodecProvider] Request entity writer resolved: {}",
+                requestEntityWriter.getClass().getCanonicalName());
         }
-        ResponseEntityDecoder decoder = SpiLoader.loadFirstInstance(ResponseEntityDecoder.class);
+        ResponseEntityDecoder decoder = SpiLoader.of(ResponseEntityDecoder.class).loadFirstInstance();
         if (decoder == null) {
             RecordLog.warn("[ClientEntityCodecProvider] No existing response entity decoder, resolve failed");
         } else {
             responseEntityDecoder = decoder;
-            RecordLog.info("[ClientEntityCodecProvider] Response entity decoder resolved: " + responseEntityDecoder.getClass().getCanonicalName());
+            RecordLog.info("[ClientEntityCodecProvider] Response entity decoder resolved: {}",
+                responseEntityDecoder.getClass().getCanonicalName());
         }
     }
 

+ 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);
 
         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);
             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);
             if (decoder == null) {
-                RecordLog.warn("Unknown type of response data decoder: {0}", type);
+                RecordLog.warn("Unknown type of response data decoder: {}", type);
                 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

+ 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;
 
-import java.util.Collection;
-
 import com.alibaba.csp.sentinel.cluster.ClusterConstants;
 import com.alibaba.csp.sentinel.cluster.codec.EntityWriter;
 import com.alibaba.csp.sentinel.cluster.request.data.ParamFlowRequestData;
 import com.alibaba.csp.sentinel.log.RecordLog;
 import com.alibaba.csp.sentinel.util.AssertUtil;
-
 import io.netty.buffer.ByteBuf;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
 /**
  * @author jialiang.linjl
  * @author Eric Zhao
@@ -50,41 +51,67 @@ public class ParamFlowRequestDataWriter implements EntityWriter<ParamFlowRequest
 
         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.
-        for (Object param : entity.getParams()) {
+        for (Object param : params) {
             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) {
         // Handle primitive type.
         if (param instanceof Integer || int.class.isInstance(param)) {
             target.writeByte(ClusterConstants.PARAM_TYPE_INTEGER);
-            target.writeInt((Integer)param);
+            target.writeInt((Integer) param);
         } else if (param instanceof String) {
-            encodeString((String)param, target);
+            encodeString((String) param, target);
         } else if (boolean.class.isInstance(param) || param instanceof Boolean) {
             target.writeByte(ClusterConstants.PARAM_TYPE_BOOLEAN);
-            target.writeBoolean((Boolean)param);
+            target.writeBoolean((Boolean) param);
         } else if (long.class.isInstance(param) || param instanceof Long) {
             target.writeByte(ClusterConstants.PARAM_TYPE_LONG);
-            target.writeLong((Long)param);
+            target.writeLong((Long) param);
         } else if (double.class.isInstance(param) || param instanceof Double) {
             target.writeByte(ClusterConstants.PARAM_TYPE_DOUBLE);
-            target.writeDouble((Double)param);
+            target.writeDouble((Double) param);
         } else if (float.class.isInstance(param) || param instanceof Float) {
             target.writeByte(ClusterConstants.PARAM_TYPE_FLOAT);
-            target.writeFloat((Float)param);
+            target.writeFloat((Float) param);
         } else if (byte.class.isInstance(param) || param instanceof Byte) {
             target.writeByte(ClusterConstants.PARAM_TYPE_BYTE);
-            target.writeByte((Byte)param);
+            target.writeByte((Byte) param);
         } else if (short.class.isInstance(param) || param instanceof Short) {
             target.writeByte(ClusterConstants.PARAM_TYPE_SHORT);
-            target.writeShort((Short)param);
+            target.writeShort((Short) param);
         } else {
             // Unexpected type, drop.
         }
@@ -97,30 +124,6 @@ public class ParamFlowRequestDataWriter implements EntityWriter<ParamFlowRequest
         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) {
         if (value == null) {
@@ -132,7 +135,7 @@ public class ParamFlowRequestDataWriter implements EntityWriter<ParamFlowRequest
             return 5;
         } else if (value instanceof String) {
             // Layout for string: |type flag(1)|length(4)|string content|
-            String tmpValue = (String)value;
+            String tmpValue = (String) value;
             byte[] tmpChars = tmpValue.getBytes();
             return 1 + 4 + tmpChars.length;
         } 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
     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();
         }
+        if (size >= 4) {
+            return source.readInt();
+        }
         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;
             }
 
-            RecordLog.info("[ClusterClientConfigManager] Assign to new target token server: " + config);
+            RecordLog.info("[ClusterClientConfigManager] Assign to new target token server: {}", config);
 
             updateServerAssignment(config);
         }
@@ -156,11 +156,11 @@ public final class ClusterClientConfigManager {
         private synchronized void applyConfig(ClusterClientConfig config) {
             if (!isValidClientConfig(config)) {
                 RecordLog.warn(
-                    "[ClusterClientConfigManager] Invalid cluster client config, ignoring: " + config);
+                    "[ClusterClientConfigManager] Invalid cluster client config, ignoring: {}", config);
                 return;
             }
 
-            RecordLog.info("[ClusterClientConfigManager] Updating to new client config: " + config);
+            RecordLog.info("[ClusterClientConfigManager] Updating to new client config: {}", 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 {
         currentState.set(ClientConstants.CLIENT_STATUS_STARTED);
         fireClientPing(ctx);
-        RecordLog.info("[TokenClientHandler] Client handler active, remote address: " + getRemoteAddress(ctx));
+        RecordLog.info("[TokenClientHandler] Client handler active, remote address: {}", getRemoteAddress(ctx));
     }
 
     @Override
@@ -76,10 +76,10 @@ public class TokenClientHandler extends ChannelInboundHandlerAdapter {
     private void handlePingResponse(ChannelHandlerContext ctx, ClusterResponse response) {
         if (response.getStatus() == ClusterConstants.RESPONSE_STATUS_OK) {
             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);
         } 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
     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
     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);
 
         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.registry.RequestDataWriterRegistry;
 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.InitOrder;
 
@@ -42,7 +43,12 @@ public class DefaultClusterClientInitFunc implements InitFunc {
     private void initDefaultEntityWriters() {
         RequestDataWriterRegistry.addWriter(ClientConstants.TYPE_PING, new PingRequestDataWriter());
         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() {

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


Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików