diff --git a/spring-cloud-alibaba-dependencies/pom.xml b/spring-cloud-alibaba-dependencies/pom.xml
index dbde166cc..bd1bb5db1 100644
--- a/spring-cloud-alibaba-dependencies/pom.xml
+++ b/spring-cloud-alibaba-dependencies/pom.xml
@@ -16,7 +16,7 @@
Spring Cloud Alibaba Dependencies
- 0.1.0
+ 0.1.1
@@ -42,6 +42,18 @@
${sentinel.version}
+
+ com.alibaba.csp
+ sentinel-annotation-aspectj
+ ${sentinel.version}
+
+
+
+ com.alibaba.csp
+ sentinel-dubbo-adapter
+ ${sentinel.version}
+
+
diff --git a/spring-cloud-alibaba-examples/sentinel-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/pom.xml
index f346de9de..30e170cbb 100644
--- a/spring-cloud-alibaba-examples/sentinel-example/pom.xml
+++ b/spring-cloud-alibaba-examples/sentinel-example/pom.xml
@@ -30,6 +30,12 @@
spring-boot-starter-actuator
+
+ com.alibaba.boot
+ dubbo-spring-boot-starter
+ 0.1.1
+
+
diff --git a/spring-cloud-alibaba-examples/sentinel-example/readme-zh.md b/spring-cloud-alibaba-examples/sentinel-example/readme-zh.md
index 271f2efc0..f496244ca 100644
--- a/spring-cloud-alibaba-examples/sentinel-example/readme-zh.md
+++ b/spring-cloud-alibaba-examples/sentinel-example/readme-zh.md
@@ -20,20 +20,20 @@
spring-cloud-starter-sentinel
-1. 接入限流埋点
+2. 接入限流埋点
1. HTTP埋点
Sentinel starter 默认为所有的 HTTP 服务提供了限流埋点,如果只想对 HTTP 服务进行限流,那么只需要引入依赖,无需修改代码。
- 1. 自定义埋点
- 如果需要对某个特定的方法进行限流或降级,可以通过 @EnableSentinel 来完成限流的埋点,示例代码如下
+ 2. 自定义埋点
+ 如果需要对某个特定的方法进行限流或降级,可以通过 @SentinelResource 来完成限流的埋点,示例代码如下
- @EnableSentinel("resource")
+ @SentinelResource("resource")
public String hello() {
return "Hello";
}
-1. 配置限流规则
+3. 配置限流规则
Sentinel提供了两种配置限流规则的方式,代码配置 和 控制台配置,本示例使用的方式为通过控制台配置。
@@ -58,7 +58,7 @@
1. 直接下载:[下载 Sentinel 控制台](http://edas-public.oss-cn-hangzhou.aliyuncs.com/install_package/demo/sentinel-dashboard.jar)
2. 源码构建:进入 Sentinel [Github 项目页面](https://github.com/alibaba/Sentinel),将代码 git clone 到本地自行编译打包,[参考此文档](https://github.com/alibaba/Sentinel/tree/master/sentinel-dashboard)。
-1. 启动控制台,执行 Java 命令 `java -jar sentinel-dashboard.jar`完成 Sentinel 控制台的启动。
+2. 启动控制台,执行 Java 命令 `java -jar sentinel-dashboard.jar`完成 Sentinel 控制台的启动。
控制台默认的监听端口为 8080。Sentinel 控制台使用 Spring Boot 编程模型开发,如果需要指定其他端口,请使用 Spring Boot 容器配置的标准方式,详情请参考 [Spring Boot 文档](https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-customizing-embedded-containers)。
### 应用启动
@@ -69,10 +69,10 @@
server.port=18083
spring.cloud.sentinel.dashboard=localhost:8080
-1. 启动应用,支持 IDE 直接启动和编译打包后启动。
+2. 启动应用,支持 IDE 直接启动和编译打包后启动。
1. IDE直接启动:找到主类 `ServiceApplication`,执行 main 方法启动应用。
- 1. 打包编译后启动:首先执行 `mvn clean package` 将工程编译打包,然后执行 `java -jar sentinel-example.jar`启动应用。
+ 2. 打包编译后启动:首先执行 `mvn clean package` 将工程编译打包,然后执行 `java -jar sentinel-example.jar`启动应用。
### 调用服务
@@ -89,18 +89,18 @@

-1. 配置 URL 限流规则:点击新增流控规则,资源名填写需要限流的 URL 相对路径,单机阈值选择需要限流的阈值,点击新增进行确认。(为了便于演示效果,这里将值设置成了 1)。
+2. 配置 URL 限流规则:点击新增流控规则,资源名填写需要限流的 URL 相对路径,单机阈值选择需要限流的阈值,点击新增进行确认。(为了便于演示效果,这里将值设置成了 1)。

-1. 配置自定义限流规则:点击新增流控规则,资源名填写 @EnableSentinel 注解 value 字段的值,单机阈值选择需要限流的阈值,点击新增进行确认。(为了便于演示效果,这里将值设置成了 1)。
+3. 配置自定义限流规则:点击新增流控规则,资源名填写 @SentinelResource 注解 value 字段的值,单机阈值选择需要限流的阈值,点击新增进行确认。(为了便于演示效果,这里将值设置成了 1)。

-1. 访问 URL,当 QPS 超过 1 时,可以看到限流效果如下。
+4. 访问 URL,当 QPS 超过 1 时,可以看到限流效果如下。

@@ -124,24 +124,20 @@
-1. 自定义限流触发后,默认的处理逻辑是抛出异常。
- 如果需要自定义处理逻辑,需要实现 SentinelExceptionHandler 接口,将其注册到 HandlerUtil 中,并在 @EnableSentinel 注解中 handler 字段进行指定。示例实现如下
-
- public class CustomBlockHandler implements SentinelBlockHandler {
- @Override
- public Object handler(BlockException e) {
- //todo add your logic
- return null;
- }
- }
-
- HandlerUtil.addHandler("myhandler", new CustomBlockHandler());
-
-
- @EnableSentinel(value = "resource",handler = "myhandler")
- public String hello() {
- return "Hello";
- }
+2. 自定义限流触发后,默认的处理逻辑是抛出异常。
+ 如果需要自定义处理逻辑,填写@SentinelResource注解的blockHandler和blockHandlerClass属性,指定后会去blockHandlerClass类里找对应的blockHandler静态方法。示例实现如下
+
+ @SentinelResource(value = "resource", blockHandler = "", blockHandlerClass = ExceptionUtil.class)
+ public String hello() {
+ return "Hello";
+ }
+
+ // ExceptionUtil.java
+ public class ExceptionUtil {
+ public static void handleException(BlockException ex) {
+ System.out.println("Oops: " + ex.getClass().getCanonicalName());
+ }
+ }
## Endpoint 信息查看
@@ -157,10 +153,47 @@ Spring Boot1.x 可以通过访问 http://127.0.0.1:18083/sentinel 来查看 Sent
## 查看实时监控
Sentinel 控制台支持实时监控查看,您可以通过 Sentinel 控制台查看各链路的请求的通过数和被限流数等信息。
-其中 p_qps 为通过(pase) 流控的 QPS,b_qps 为被限流 (block) 的 QPS。
+其中 p_qps 为通过(pass) 流控的 QPS,b_qps 为被限流 (block) 的 QPS。

+## Dubbo支持
+
+[Dubbo](http://dubbo.apache.org/)是一款高性能Java RPC框架。
+
+Sentinel提供了[sentinel-dubbo-adapter](https://github.com/alibaba/Sentinel/tree/master/sentinel-adapter/sentinel-dubbo-adapter)模块用来支持Dubbo服务调用的限流降级。sentinel-starter默认也集成了该功能。
+
+比如有个FooService服务,定义如下:
+
+ package org.springframework.cloud.alibaba.cloud.examples.dubbo.FooService;
+ public interface FooService {
+ String hello(String name);
+ }
+
+该服务在Sentinel下对应的资源名是 `org.springframework.cloud.alibaba.cloud.examples.dubbo.FooService:hello(java.lang.String)` 。
+
+在Consumer端进行限流的话,需要处理SentinelRpcException。
+
+ FooService service = applicationContext.getBean(FooService.class);
+
+ for (int i = 0; i < 15; i++) {
+ try {
+ String message = service.hello("Jim");
+ } catch (SentinelRpcException ex) {
+ System.out.println("Blocked");
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+在Provider端进行限流的话,Consumer端调用的话会抛出RpcException。因为Provider端被限流抛出了SentinelRpcException。
+
+### Dubbo 应用启动
+
+在启动ServiceApplication的前提下,再启动ConsumerApplication。
+
+ConsumerApplication在Consumer端设置了限流规则,所以启动完成后查看控制台的打印信息,会发现部分调用被Block。
+
## More
Sentinel 是一款功能强大的中间件,从流量控制,熔断降级,系统负载保护等多个维度保护服务的稳定性。此 Demo 仅演示了 使用 Sentinel 作为限流工具的使用,更多 Sentinel 相关的信息,请参考 [Sentinel 项目](https://github.com/alibaba/Sentinel)。
diff --git a/spring-cloud-alibaba-examples/sentinel-example/readme.md b/spring-cloud-alibaba-examples/sentinel-example/readme.md
index 6edcada68..afef012d5 100644
--- a/spring-cloud-alibaba-examples/sentinel-example/readme.md
+++ b/spring-cloud-alibaba-examples/sentinel-example/readme.md
@@ -24,10 +24,10 @@ Before we start the demo, let's learn how to connect Sentinel to a Spring Cloud
1. Define HTTP Resources
Sentinel starter defines all HTTP URLS as resources by relative paths. If you only want to add flow control for your HTTP services, you do not need to modify your code.
- 1. Define Custom Resources
- If you want to implement flow control or degradation for a specific method, you can add an @EnableSentinel annotation to the method, as shown in the code below.
+ 2. Define Custom Resources
+ If you want to implement flow control or degradation for a specific method, you can add an @SentinelResource annotation to the method, as shown in the code below.
- @EnableSentinel("resource")
+ @SentinelResource("resource")
public String hello() {
return "Hello";
}
@@ -95,11 +95,11 @@ The screenshot belows shows invoke success:

-4. Configure Custom Resource Flow Rule:Click **流控规则(Flow Rule)** on the left-side navigation pane and **新增流控规则(Create Flow Rule)**. type the value() of @EnableSentinel in the **资源名(Resource Name)** field , enter **单机阈值(Threshold)** value, then click **新增(OK)**.Here we set threshold to 1 for demonstration purpose.
+3. Configure Custom Resource Flow Rule:Click **流控规则(Flow Rule)** on the left-side navigation pane and **新增流控规则(Create Flow Rule)**. type the value() of @SentinelResource in the **资源名(Resource Name)** field , enter **单机阈值(Threshold)** value, then click **新增(OK)**.Here we set threshold to 1 for demonstration purpose.

-5. Visit the URL in your browser again. When QPS is more than 1, we can see that flow control takes effect.
+4. Visit the URL in your browser again. When QPS is more than 1, we can see that flow control takes effect.

@@ -125,23 +125,19 @@ The screenshot belows shows invoke success:
2. When a custom resource is blocked by Sentinel, the default logic is throw BlockException.
- If you want to customize your flow control logic, implement interface `SentinelExceptionHandler`, add it to HandlerUtil, and set @EnableSentinel's handler(). See the code below:
-
- public class CustomBlockHandler implements SentinelBlockHandler {
- @Override
- public Object handler(BlockException e) {
- //todo add your logic
- return null;
- }
- }
-
- HandlerUtil.addHandler("myhandler", new CustomBlockHandler());
-
-
- @EnableSentinel(value = "resource",handler = "myhandler")
- public String hello() {
- return "Hello";
- }
+ If you want to customize your flow control logic, implement interface `SentinelExceptionHandler`, set @SentinelResource's blockHandler() and blockHandlerClass(). See the code below:
+
+ @SentinelResource(value = "resource", blockHandler = "", blockHandlerClass = ExceptionUtil.class)
+ public String hello() {
+ return "Hello";
+ }
+
+ // ExceptionUtil.java
+ public class ExceptionUtil {
+ public static void handleException(BlockException ex) {
+ System.out.println("Oops: " + ex.getClass().getCanonicalName());
+ }
+ }
## Endpoint
@@ -169,6 +165,43 @@ To see the metrics, click **实时监控(Real-time Monitoring)** in the left-sid

+## Dubbo
+
+[Dubbo](http://dubbo.apache.org/) is a high-performance, java based open source RPC framework.
+
+Sentinel provide a module named [sentinel-dubbo-adapter](https://github.com/alibaba/Sentinel/tree/master/sentinel-adapter/sentinel-dubbo-adapter) to support dubbo.sentinel-starter integrates this feature by default.
+
+For example, a service named FooService, see the code below:
+
+ package org.springframework.cloud.alibaba.cloud.examples.dubbo.FooService;
+ public interface FooService {
+ String hello(String name);
+ }
+
+The resource name of this service is `org.springframework.cloud.alibaba.cloud.examples.dubbo.FooService:hello(java.lang.String)` .
+
+You should handle SentinelRpcException if rpc invocation was be limited on Consumer side:
+
+ FooService service = applicationContext.getBean(FooService.class);
+
+ for (int i = 0; i < 15; i++) {
+ try {
+ String message = service.hello("Jim");
+ } catch (SentinelRpcException ex) {
+ System.out.println("Blocked");
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+It will throw RpcException on Consumer side if it was be limited on Provider side, because Provider side throw SentinelRpcException in this scene.
+
+### Dubbo Start Application
+
+You can startup ConsumerApplication after ServiceApplication startup.
+
+ConsumerApplication init flow control rules after startup, so you will find some invocations have been blocked in console.
+
## More
For more information about Sentinel, see [Sentinel Project](https://github.com/alibaba/Sentinel).
diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ExceptionUtil.java b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ExceptionUtil.java
new file mode 100644
index 000000000..3b8349fc6
--- /dev/null
+++ b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ExceptionUtil.java
@@ -0,0 +1,14 @@
+package org.springframework.cloud.alibaba.cloud.examples;
+
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/**
+ * @author fangjian
+ */
+public class ExceptionUtil {
+
+ public static void handleException(BlockException ex) {
+ System.out.println("Oops: " + ex.getClass().getCanonicalName());
+ }
+
+}
diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ServiceApplication.java b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ServiceApplication.java
index 543612167..9d1696a94 100644
--- a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ServiceApplication.java
+++ b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ServiceApplication.java
@@ -2,6 +2,9 @@ package org.springframework.cloud.alibaba.cloud.examples;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.client.RestTemplate;
/**
* @author xiaojing
@@ -9,6 +12,17 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ServiceApplication {
+ @Bean
+ @SentinelProtect(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class)
+ public RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+
+ @Bean
+ public RestTemplate restTemplate2() {
+ return new RestTemplate();
+ }
+
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/TestController.java b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/TestController.java
index 773a40d71..4f4d2e084 100644
--- a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/TestController.java
+++ b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/TestController.java
@@ -1,9 +1,11 @@
package org.springframework.cloud.alibaba.cloud.examples;
-import org.springframework.cloud.alibaba.sentinel.custom.EnableSentinel;
+import com.alibaba.csp.sentinel.annotation.SentinelResource;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
/**
* @author xiaojing
@@ -11,8 +13,11 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
+ @Autowired
+ private RestTemplate restTemplate;
+
@RequestMapping(value = "/hello", method = RequestMethod.GET)
- @EnableSentinel("resource")
+ @SentinelResource("resource")
public String hello() {
return "Hello";
}
@@ -22,4 +27,9 @@ public class TestController {
return "Hello test";
}
+ @RequestMapping(value = "/template", method = RequestMethod.GET)
+ public String client() {
+ return restTemplate.getForObject("http://www.taobao.com/test", String.class);
+ }
+
}
diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/DubboProviderRunner.java b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/DubboProviderRunner.java
new file mode 100644
index 000000000..4c683cddf
--- /dev/null
+++ b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/DubboProviderRunner.java
@@ -0,0 +1,23 @@
+package org.springframework.cloud.alibaba.cloud.examples.dubbo;
+
+import org.springframework.boot.Banner;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.cloud.alibaba.cloud.examples.dubbo.provider.ProviderApplication;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author fangjian
+ */
+@Component
+public class DubboProviderRunner implements CommandLineRunner {
+
+ @Override
+ public void run(String... args) throws Exception {
+ SpringApplicationBuilder providerBuilder = new SpringApplicationBuilder()
+ .bannerMode(Banner.Mode.OFF).registerShutdownHook(false)
+ .logStartupInfo(false).web(false);
+ providerBuilder.sources(ProviderApplication.class).run(args);
+ }
+
+}
diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/FooService.java b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/FooService.java
new file mode 100644
index 000000000..d44b5c996
--- /dev/null
+++ b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/FooService.java
@@ -0,0 +1,10 @@
+package org.springframework.cloud.alibaba.cloud.examples.dubbo;
+
+/**
+ * @author fangjian
+ */
+public interface FooService {
+
+ String hello(String name);
+
+}
diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/consumer/ConsumerApplication.java b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/consumer/ConsumerApplication.java
new file mode 100644
index 000000000..05e3d4b2d
--- /dev/null
+++ b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/consumer/ConsumerApplication.java
@@ -0,0 +1,82 @@
+package org.springframework.cloud.alibaba.cloud.examples.dubbo.consumer;
+
+import java.util.Collections;
+
+import org.springframework.boot.Banner;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
+import com.alibaba.dubbo.config.ApplicationConfig;
+import com.alibaba.dubbo.config.ConsumerConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScan;
+
+/**
+ * @author fangjian
+ */
+@DubboComponentScan("org.springframework.cloud.alibaba.cloud.examples.dubbo.provider")
+public class ConsumerApplication {
+
+ @Bean
+ public ApplicationConfig applicationConfig() {
+ ApplicationConfig applicationConfig = new ApplicationConfig();
+ applicationConfig.setName("demo-consumer");
+ return applicationConfig;
+ }
+
+ @Bean
+ public RegistryConfig registryConfig() {
+ RegistryConfig registryConfig = new RegistryConfig();
+ registryConfig.setAddress("multicast://224.5.6.7:1234");
+ return registryConfig;
+ }
+
+ @Bean
+ public ConsumerConfig consumerConfig() {
+ ConsumerConfig consumerConfig = new ConsumerConfig();
+ return consumerConfig;
+ }
+
+ @Bean
+ public FooServiceConsumer annotationDemoServiceConsumer() {
+ return new FooServiceConsumer();
+ }
+
+ public static void main(String[] args) {
+
+ SpringApplicationBuilder consumerBuilder = new SpringApplicationBuilder()
+ .bannerMode(Banner.Mode.OFF).registerShutdownHook(false)
+ .logStartupInfo(false).web(false);
+ ApplicationContext applicationContext = consumerBuilder
+ .sources(ConsumerApplication.class).run(args);
+
+ FlowRule flowRule = new FlowRule();
+ flowRule.setResource(
+ "org.springframework.cloud.alibaba.cloud.examples.dubbo.FooService:hello(java.lang.String)");
+ flowRule.setCount(10);
+ flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ flowRule.setLimitApp("default");
+ FlowRuleManager.loadRules(Collections.singletonList(flowRule));
+
+ FooServiceConsumer service = applicationContext.getBean(FooServiceConsumer.class);
+
+ for (int i = 0; i < 15; i++) {
+ try {
+ String message = service.hello("Jim");
+ System.out.println((i + 1) + " -> Success: " + message);
+ }
+ catch (SentinelRpcException ex) {
+ System.out.println("Blocked");
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+}
diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/consumer/FooServiceConsumer.java b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/consumer/FooServiceConsumer.java
new file mode 100644
index 000000000..2fe62bf5a
--- /dev/null
+++ b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/consumer/FooServiceConsumer.java
@@ -0,0 +1,19 @@
+package org.springframework.cloud.alibaba.cloud.examples.dubbo.consumer;
+
+import org.springframework.cloud.alibaba.cloud.examples.dubbo.FooService;
+
+import com.alibaba.dubbo.config.annotation.Reference;
+
+/**
+ * @author fangjian
+ */
+public class FooServiceConsumer {
+
+ @Reference(url = "dubbo://127.0.0.1:25758", timeout = 3000)
+ private FooService fooService;
+
+ public String hello(String name) {
+ return fooService.hello(name);
+ }
+
+}
diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/provider/FooServiceImpl.java b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/provider/FooServiceImpl.java
new file mode 100644
index 000000000..bb9e81d66
--- /dev/null
+++ b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/provider/FooServiceImpl.java
@@ -0,0 +1,17 @@
+package org.springframework.cloud.alibaba.cloud.examples.dubbo.provider;
+
+import org.springframework.cloud.alibaba.cloud.examples.dubbo.FooService;
+
+import com.alibaba.dubbo.config.annotation.Service;
+
+/**
+ * @author fangjian
+ */
+@Service
+public class FooServiceImpl implements FooService {
+
+ @Override
+ public String hello(String name) {
+ return "hello, " + name;
+ }
+}
diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/provider/ProviderApplication.java b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/provider/ProviderApplication.java
new file mode 100644
index 000000000..6f793bb6e
--- /dev/null
+++ b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/provider/ProviderApplication.java
@@ -0,0 +1,38 @@
+package org.springframework.cloud.alibaba.cloud.examples.dubbo.provider;
+
+import org.springframework.context.annotation.Bean;
+
+import com.alibaba.dubbo.config.ApplicationConfig;
+import com.alibaba.dubbo.config.ProtocolConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScan;
+
+/**
+ * @author fangjian
+ */
+@DubboComponentScan("org.springframework.cloud.alibaba.cloud.examples.dubbo.provider")
+public class ProviderApplication {
+
+ @Bean
+ public ApplicationConfig applicationConfig() {
+ ApplicationConfig applicationConfig = new ApplicationConfig();
+ applicationConfig.setName("demo-provider");
+ return applicationConfig;
+ }
+
+ @Bean
+ public RegistryConfig registryConfig() {
+ RegistryConfig registryConfig = new RegistryConfig();
+ registryConfig.setAddress("multicast://224.5.6.7:1234");
+ return registryConfig;
+ }
+
+ @Bean
+ public ProtocolConfig protocolConfig() {
+ ProtocolConfig protocolConfig = new ProtocolConfig();
+ protocolConfig.setName("dubbo");
+ protocolConfig.setPort(25758);
+ return protocolConfig;
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel-autoconfigure/pom.xml b/spring-cloud-alibaba-sentinel-autoconfigure/pom.xml
index 33924b8ad..82bc75764 100644
--- a/spring-cloud-alibaba-sentinel-autoconfigure/pom.xml
+++ b/spring-cloud-alibaba-sentinel-autoconfigure/pom.xml
@@ -25,6 +25,16 @@
sentinel-transport-simple-http
+
+ com.alibaba.csp
+ sentinel-annotation-aspectj
+
+
+
+ com.alibaba.csp
+ sentinel-dubbo-adapter
+
+
diff --git a/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/EnableSentinel.java b/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/annotation/SentinelProtect.java
similarity index 57%
rename from spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/EnableSentinel.java
rename to spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/annotation/SentinelProtect.java
index c85bb274c..5e7f36824 100644
--- a/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/EnableSentinel.java
+++ b/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/annotation/SentinelProtect.java
@@ -14,28 +14,27 @@
* limitations under the License.
*/
-package org.springframework.cloud.alibaba.sentinel.custom;
+package org.springframework.cloud.alibaba.sentinel.annotation;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import org.springframework.beans.factory.annotation.Qualifier;
+
+import java.lang.annotation.*;
/**
- * Annotation to add Sentinel to custom method
- * @author xiaojing
+ * @author fangjian
*/
-@Target(ElementType.METHOD)
+@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
-public @interface EnableSentinel {
+@Documented
+@Qualifier
+public @interface SentinelProtect {
+
+ String blockHandler() default "";
+
+ Class> blockHandlerClass() default void.class;
+
+ String fallback() default "";
- /**
- * @return sentinel resource value
- */
- String value();
+ Class> fallbackClass() default void.class;
- /**
- * @return Sentinel BlockException Handler name
- */
- String handler() default "";
}
diff --git a/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/BlockClassRegistry.java b/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/BlockClassRegistry.java
new file mode 100644
index 000000000..3a3b967f1
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/BlockClassRegistry.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.alibaba.sentinel.custom;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.csp.sentinel.util.StringUtil;
+
+/**
+ * @author fangjian
+ */
+final class BlockClassRegistry {
+
+ private static final Map FALLBACK_MAP = new ConcurrentHashMap<>();
+ private static final Map BLOCK_HANDLER_MAP = new ConcurrentHashMap<>();
+
+ static Method lookupFallback(Class> clazz, String name) {
+ return FALLBACK_MAP.get(getKey(clazz, name));
+ }
+
+ static Method lookupBlockHandler(Class> clazz, String name) {
+ return BLOCK_HANDLER_MAP.get(getKey(clazz, name));
+ }
+
+ static void updateFallbackFor(Class> clazz, String name, Method method) {
+ if (clazz == null || StringUtil.isBlank(name)) {
+ throw new IllegalArgumentException("Bad argument");
+ }
+ FALLBACK_MAP.put(getKey(clazz, name), method);
+ }
+
+ static void updateBlockHandlerFor(Class> clazz, String name, Method method) {
+ if (clazz == null || StringUtil.isBlank(name)) {
+ throw new IllegalArgumentException("Bad argument");
+ }
+ BLOCK_HANDLER_MAP.put(getKey(clazz, name), method);
+ }
+
+ private static String getKey(Class> clazz, String name) {
+ return String.format("%s:%s", clazz.getCanonicalName(), name);
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/HandlerUtil.java b/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/HandlerUtil.java
deleted file mode 100644
index 167af1917..000000000
--- a/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/HandlerUtil.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2018 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.cloud.alibaba.sentinel.custom;
-
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * @author xiaojing
- */
-public class HandlerUtil {
-
- private static final ConcurrentHashMap map = new ConcurrentHashMap<>(
- 16);
-
- /**
- * you should add your custom handler before use it
- * @param name see {@link EnableSentinel#handler()}
- * @param handler you custom handler
- */
- public static void addHandler(String name, SentinelBlockHandler handler) {
- map.put(name, handler);
- }
-
- public static SentinelBlockHandler getHandler(String name) {
- SentinelBlockHandler handler = map.get(name);
- if (null == handler) {
- throw new RuntimeException("cannot find handler name=<" + name
- + ",> did you forgot to invoke HandlerUtil.addHandler(name, handler) ?");
- }
- return handler;
- }
-}
diff --git a/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAspect.java b/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAspect.java
deleted file mode 100644
index fb923c5d5..000000000
--- a/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAspect.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2018 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.cloud.alibaba.sentinel.custom;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-
-import com.alibaba.csp.sentinel.Entry;
-import com.alibaba.csp.sentinel.SphU;
-import com.alibaba.csp.sentinel.context.ContextUtil;
-import com.alibaba.csp.sentinel.slots.block.BlockException;
-
-import org.aspectj.lang.ProceedingJoinPoint;
-import org.aspectj.lang.annotation.Around;
-import org.aspectj.lang.annotation.Aspect;
-import org.aspectj.lang.reflect.MethodSignature;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * @author xiaojing
- */
-@Aspect
-public class SentinelAspect {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(SentinelAspect.class);
-
- @Around("@annotation(org.springframework.cloud.alibaba.sentinel.custom.EnableSentinel)")
- public Object customBlock(ProceedingJoinPoint pjp) throws Throwable {
- SentinelEntry sentinelEntry = new SentinelEntry();
- try {
- beforeProceed(sentinelEntry, pjp);
- return pjp.proceed();
- }
- catch (BlockException e) {
- LOGGER.error(e.getMessage(), e);
- if (null != sentinelEntry.getHandler()
- && sentinelEntry.getHandler().length() > 0) {
- return HandlerUtil.getHandler(sentinelEntry.getHandler()).handler(e);
- }
- else {
- throw e;
- }
- }
- finally {
- releaseContextResources(sentinelEntry);
- }
- }
-
- private void beforeProceed(SentinelEntry sentinelEntry, ProceedingJoinPoint pjp)
- throws BlockException {
- Method method = getMethod(pjp);
-
- int modifiers = method.getModifiers();
-
- if (!Modifier.isPublic(modifiers) || "toString".equals(method.getName())
- || "hashCode".equals(method.getName())
- || "equals".equals(method.getName())
- || "finalize".equals(method.getName())) {
- return;
- }
-
- Annotation[] annotations = method.getDeclaredAnnotations();
- for (Annotation annotation : annotations) {
- if (annotation instanceof EnableSentinel) {
- sentinelEntry.setKey(((EnableSentinel) annotation).value());
- sentinelEntry.setHandler(((EnableSentinel) annotation).handler());
- }
- }
- if (null == sentinelEntry.getKey() || sentinelEntry.getKey().length() == 0) {
- return;
- }
-
- ContextUtil.enter(sentinelEntry.getKey());
- sentinelEntry.setEntry(SphU.entry(sentinelEntry.getKey()));
- }
-
- private void releaseContextResources(SentinelEntry sentinelEntry) {
-
- if (null == sentinelEntry.getEntry()) {
- return;
- }
-
- Entry entry = sentinelEntry.getEntry();
- entry.exit();
- ContextUtil.exit();
- }
-
- private Method getMethod(ProceedingJoinPoint joinPoint) {
- MethodSignature signature = (MethodSignature) joinPoint.getSignature();
- return signature.getMethod();
- }
-
-}
diff --git a/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java b/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java
new file mode 100644
index 000000000..c473d94b4
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.alibaba.sentinel.custom;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
+
+/**
+ * @author xiaojing
+ */
+@Configuration
+public class SentinelAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ public SentinelResourceAspect sentinelResourceAspect() {
+ return new SentinelResourceAspect();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnClass(value = RestTemplate.class)
+ public SentinelBeanPostProcessor sentinelBeanPostProcessor() {
+ return new SentinelBeanPostProcessor();
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java b/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java
new file mode 100644
index 000000000..609cc8d24
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.alibaba.sentinel.custom;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.type.StandardMethodMetadata;
+import org.springframework.util.StringUtils;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * PostProcessor handle @SentinelProtect Annotation, add interceptor for RestTemplate
+ *
+ * @author fangjian
+ * @see SentinelProtect
+ * @see SentinelProtectInterceptor
+ */
+public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
+
+ @Autowired
+ private ApplicationContext applicationContext;
+
+ private ConcurrentHashMap cache = new ConcurrentHashMap<>();
+
+ @Override
+ public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition,
+ Class> beanType, String beanName) {
+ if (checkSentinelProtect(beanDefinition, beanType)) {
+ SentinelProtect sentinelProtect = ((StandardMethodMetadata) beanDefinition
+ .getSource()).getIntrospectedMethod()
+ .getAnnotation(SentinelProtect.class);
+ cache.put(beanName, sentinelProtect);
+ }
+ }
+
+ private boolean checkSentinelProtect(RootBeanDefinition beanDefinition,
+ Class> beanType) {
+ return beanType == RestTemplate.class
+ && beanDefinition.getSource() instanceof StandardMethodMetadata
+ && ((StandardMethodMetadata) beanDefinition.getSource())
+ .isAnnotated(SentinelProtect.class.getName());
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName)
+ throws BeansException {
+ if (cache.containsKey(beanName)) {
+ // add interceptor for each RestTemplate with @SentinelProtect annotation
+ StringBuilder interceptorBeanName = new StringBuilder();
+ SentinelProtect sentinelProtect = cache.get(beanName);
+ interceptorBeanName
+ .append(StringUtils.uncapitalize(
+ SentinelProtectInterceptor.class.getSimpleName()))
+ .append("_")
+ .append(sentinelProtect.blockHandlerClass().getSimpleName())
+ .append(sentinelProtect.blockHandler()).append("_")
+ .append(sentinelProtect.fallbackClass().getSimpleName())
+ .append(sentinelProtect.fallback());
+ RestTemplate restTemplate = (RestTemplate) bean;
+ registerBean(interceptorBeanName.toString(), sentinelProtect);
+ SentinelProtectInterceptor sentinelProtectInterceptor = applicationContext
+ .getBean(interceptorBeanName.toString(),
+ SentinelProtectInterceptor.class);
+ restTemplate.getInterceptors().add(sentinelProtectInterceptor);
+ }
+ return bean;
+ }
+
+ private void registerBean(String interceptorBeanName,
+ SentinelProtect sentinelProtect) {
+ // register SentinelProtectInterceptor bean
+ DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext
+ .getAutowireCapableBeanFactory();
+ BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
+ .genericBeanDefinition(SentinelProtectInterceptor.class);
+ beanDefinitionBuilder.addConstructorArgValue(sentinelProtect);
+ BeanDefinition interceptorBeanDefinition = beanDefinitionBuilder
+ .getRawBeanDefinition();
+ beanFactory.registerBeanDefinition(interceptorBeanName,
+ interceptorBeanDefinition);
+ }
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ return bean;
+ }
+
+}
\ No newline at end of file
diff --git a/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBlockHandler.java b/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBlockHandler.java
deleted file mode 100644
index 3b7201fed..000000000
--- a/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBlockHandler.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2018 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.cloud.alibaba.sentinel.custom;
-
-import com.alibaba.csp.sentinel.slots.block.BlockException;
-
-/**
- * Sentinel Block Handler
- * @author xiaojing
- */
-public interface SentinelBlockHandler {
-
- /**
- * custom method to process BlockException
- * @param e block exception when blocked by sentinel
- * @return Object result processed by the handler
- */
- Object handler(BlockException e);
-
-}
diff --git a/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelCustomAspectAutoConfiguration.java b/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelCustomAspectAutoConfiguration.java
deleted file mode 100644
index 77cde741c..000000000
--- a/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelCustomAspectAutoConfiguration.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2018 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.cloud.alibaba.sentinel.custom;
-
-import org.springframework.context.annotation.Bean;
-
-/**
- * @author xiaojing
- */
-public class SentinelCustomAspectAutoConfiguration {
-
- @Bean
- public SentinelAspect sentinelAspect() {
- return new SentinelAspect();
- }
-}
diff --git a/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java b/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java
new file mode 100644
index 000000000..e48616489
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel-autoconfigure/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.alibaba.sentinel.custom;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URI;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.util.ClassUtils;
+
+import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.SphU;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
+import com.alibaba.csp.sentinel.util.StringUtil;
+
+/**
+ * Interceptor using by SentinelProtect and SentinelProtectInterceptor
+ *
+ * @author fangjian
+ */
+public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor {
+
+ private static final Logger LOGGER = LoggerFactory
+ .getLogger(SentinelProtectInterceptor.class);
+
+ private SentinelProtect sentinelProtect;
+
+ public SentinelProtectInterceptor(SentinelProtect sentinelProtect) {
+ this.sentinelProtect = sentinelProtect;
+ }
+
+ @Override
+ public ClientHttpResponse intercept(HttpRequest request, byte[] body,
+ ClientHttpRequestExecution execution) throws IOException {
+ URI uri = request.getURI();
+ String hostResource = uri.getScheme() + "://" + uri.getHost() + ":"
+ + (uri.getPort() == -1 ? 80 : uri.getPort());
+ String hostWithPathResource = hostResource + uri.getPath();
+ Entry hostEntry = null, hostWithPathEntry = null;
+ ClientHttpResponse response = null;
+ try {
+ ContextUtil.enter(hostWithPathResource);
+ hostWithPathEntry = SphU.entry(hostWithPathResource);
+ hostEntry = SphU.entry(hostResource);
+ response = execution.execute(request, body);
+ }
+ catch (BlockException e) {
+ LOGGER.error("RestTemplate block", e);
+ try {
+ handleBlockException(e);
+ }
+ catch (Exception ex) {
+ LOGGER.error("sentinel handle BlockException error.", e);
+ }
+ }
+ finally {
+ if (hostEntry != null) {
+ hostEntry.exit();
+ }
+ if (hostWithPathEntry != null) {
+ hostWithPathEntry.exit();
+ }
+ ContextUtil.exit();
+ }
+ return response;
+ }
+
+ private void handleBlockException(BlockException ex) throws Exception {
+ Object[] args = new Object[] { ex };
+ // handle degrade
+ if (isDegradeFailure(ex)) {
+ Method method = extractFallbackMethod(sentinelProtect.fallback(),
+ sentinelProtect.fallbackClass());
+ if (method != null) {
+ method.invoke(null, args);
+ }
+ }
+ // handle block
+ Method blockHandler = extractBlockHandlerMethod(sentinelProtect.blockHandler(),
+ sentinelProtect.blockHandlerClass());
+ if (blockHandler != null) {
+ blockHandler.invoke(null, args);
+ }
+ }
+
+ private Method extractFallbackMethod(String fallback, Class> fallbackClass) {
+ if (StringUtil.isBlank(fallback) || fallbackClass == void.class) {
+ return null;
+ }
+ Method cachedMethod = BlockClassRegistry.lookupFallback(fallbackClass, fallback);
+ if (cachedMethod == null) {
+ cachedMethod = ClassUtils.getStaticMethod(fallbackClass, fallback,
+ BlockException.class);
+ BlockClassRegistry.updateFallbackFor(fallbackClass, fallback, cachedMethod);
+ }
+ return cachedMethod;
+ }
+
+ private Method extractBlockHandlerMethod(String block, Class> blockClass) {
+ if (StringUtil.isBlank(block) || blockClass == void.class) {
+ return null;
+ }
+ Method cachedMethod = BlockClassRegistry.lookupBlockHandler(blockClass, block);
+ if (cachedMethod == null) {
+ cachedMethod = ClassUtils.getStaticMethod(blockClass, block,
+ BlockException.class);
+ BlockClassRegistry.updateBlockHandlerFor(blockClass, block, cachedMethod);
+ }
+ return cachedMethod;
+ }
+
+ private boolean isDegradeFailure(BlockException ex) {
+ return ex instanceof DegradeException;
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-cloud-alibaba-sentinel-autoconfigure/src/main/resources/META-INF/spring.factories
index f9b510b64..ae146be44 100644
--- a/spring-cloud-alibaba-sentinel-autoconfigure/src/main/resources/META-INF/spring.factories
+++ b/spring-cloud-alibaba-sentinel-autoconfigure/src/main/resources/META-INF/spring.factories
@@ -1,4 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.alibaba.sentinel.SentinelWebAutoConfiguration,\
org.springframework.cloud.alibaba.sentinel.endpoint.SentinelEndpointAutoConfiguration,\
-org.springframework.cloud.alibaba.sentinel.custom.SentinelCustomAspectAutoConfiguration
+org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration
diff --git a/spring-cloud-alibaba-sentinel-autoconfigure/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java b/spring-cloud-alibaba-sentinel-autoconfigure/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java
index 46284a699..0494216bf 100644
--- a/spring-cloud-alibaba-sentinel-autoconfigure/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java
+++ b/spring-cloud-alibaba-sentinel-autoconfigure/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java
@@ -16,13 +16,19 @@
package org.springframework.cloud.alibaba.sentinel;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
-import org.springframework.cloud.alibaba.sentinel.custom.SentinelAspect;
-import org.springframework.cloud.alibaba.sentinel.custom.SentinelCustomAspectAutoConfiguration;
+import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect;
+import org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration;
+import org.springframework.cloud.alibaba.sentinel.custom.SentinelBeanPostProcessor;
+import org.springframework.cloud.alibaba.sentinel.custom.SentinelProtectInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
@@ -37,7 +43,7 @@ public class SentinelAutoConfigurationTests {
@Before
public void init() {
- context.register(SentinelCustomAspectAutoConfiguration.class, SentinelWebAutoConfiguration.class);
+ context.register(SentinelAutoConfiguration.class, SentinelWebAutoConfiguration.class, SentinelTestConfiguration.class);
EnvironmentTestUtils.addEnvironment(this.context,
"spring.cloud.sentinel.port=8888",
"spring.cloud.sentinel.filter.order=123",
@@ -50,17 +56,18 @@ public class SentinelAutoConfigurationTests {
this.context.close();
}
- @Test
- public void testSentinelAspect() {
- assertThat(context.getBean(SentinelAspect.class)).isNotNull();
- }
-
@Test
public void testFilter() {
assertThat(context.getBean(
"servletRequestListener").getClass() == FilterRegistrationBean.class).isTrue();
}
+ @Test
+ public void testBeanPostProcessor() {
+ assertThat(context.getBean("sentinelBeanPostProcessor")
+ .getClass() == SentinelBeanPostProcessor.class).isTrue();
+ }
+
@Test
public void testProperties() {
SentinelProperties sentinelProperties = context.getBean(SentinelProperties.class);
@@ -71,5 +78,38 @@ public class SentinelAutoConfigurationTests {
assertThat(sentinelProperties.getFilter().getUrlPatterns().get(1)).isEqualTo("/test");
}
+ @Test
+ public void testRestTemplate() {
+ assertThat(context.getBeansOfType(RestTemplate.class).size()).isEqualTo(2);
+ RestTemplate restTemplate = context.getBean("restTemplateWithBlockClass",
+ RestTemplate.class);
+ assertThat(restTemplate.getInterceptors().size()).isEqualTo(1);
+ assertThat(restTemplate.getInterceptors().get(0).getClass())
+ .isEqualTo(SentinelProtectInterceptor.class);
+ }
+
+ @Configuration
+ static class SentinelTestConfiguration {
+
+ @Bean
+ @SentinelProtect
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+
+ @Bean
+ @SentinelProtect(blockHandlerClass = ExceptionUtil.class, blockHandler = "handleException")
+ RestTemplate restTemplateWithBlockClass() {
+ return new RestTemplate();
+ }
+
+ }
+
+ static class ExceptionUtil {
+ public static void handleException(BlockException ex) {
+ System.out.println("Oops: " + ex.getClass().getCanonicalName());
+ }
+ }
+
}