diff --git a/README-zh.md b/README-zh.md
index bd633778..c76e149e 100644
--- a/README-zh.md
+++ b/README-zh.md
@@ -9,9 +9,9 @@ Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。
依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。
-参考文档 请查看 [WIKI](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/wiki)
+参考文档 请查看 [WIKI](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/wiki) 。
-为 Spring Cloud Alibaba 贡献代码请参考 [如何贡献](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/wiki/%E5%A6%82%E4%BD%95%E8%B4%A1%E7%8C%AE%E4%BB%A3%E7%A0%81)
+为 Spring Cloud Alibaba 贡献代码请参考 [如何贡献](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/wiki/%E5%A6%82%E4%BD%95%E8%B4%A1%E7%8C%AE%E4%BB%A3%E7%A0%81) 。
## 主要功能
@@ -46,7 +46,7 @@ Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。
* master 分支对应的是 Spring Cloud Finchley,最低支持 JDK 1.8。
* 1.x 分支对应的是 Spring Cloud Edgware,最低支持 JDK 1.7。
-Spring Cloud 使用 Maven 来构建,最快的使用方式是将本项目clone到本地,然后执行以下命令:
+Spring Cloud 使用 Maven 来构建,最快的使用方式是将本项目 clone 到本地,然后执行以下命令:
./mvnw install
@@ -71,7 +71,7 @@ Spring Cloud 使用 Maven 来构建,最快的使用方式是将本项目clone
-然后再 `dependencies` 中添加自己所需使用的依赖即可使用。
+然后在 `dependencies` 中添加自己所需使用的依赖即可使用。
如果您想体验最新的 BUILD-SNAPSHOT 的新功能,则可以将版本换成最新的版本,但是需要在 pom.xml 中配置 Spring BUILDSNAPSHOT 仓库,**注意: SNAPSHOT 版本随时可能更新**
@@ -109,7 +109,7 @@ Example 列表:
[Alibaba Cloud SchedulerX Example](https://github.com/xiaolongzuo/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/schedulerx-example/schedulerx-simple-task-example/readme-zh.md)
## 版本管理规范
-项目的版本号格式为 x.x.x 的形式,其中 x 的数值类型为数字,从0开始取值,且不限于 0~9 这个范围。项目处于孵化器阶段时,第一位版本号固定使用0,即版本号为 0.x.x 的格式。
+项目的版本号格式为 x.x.x 的形式,其中 x 的数值类型为数字,从 0 开始取值,且不限于 0~9 这个范围。项目处于孵化器阶段时,第一位版本号固定使用 0,即版本号为 0.x.x 的格式。
由于 Spring Boot 1 和 Spring Boot 2 在 Actuator 模块的接口和注解有很大的变更,且 spring-cloud-commons 从 1.x.x 版本升级到 2.0.0 版本也有较大的变更,因此我们使用了两个不同分支来分别支持 Spring Boot 1 和 Spring Boot 2:
* 0.1.x 版本适用于 Spring Boot 1
diff --git a/Roadmap-zh.md b/Roadmap-zh.md
index c28fe972..78e09e0e 100644
--- a/Roadmap-zh.md
+++ b/Roadmap-zh.md
@@ -4,29 +4,42 @@ Spring Cloud Alibaba 致力于提供分布式应用服务开发的一站式解
此项目包含的组件内容,主要选取自阿里巴巴开源中间件和阿里云商业化产品,但也不限定于这些产品。
-如果您对 Roadmap 有任何建议或想法,欢迎在 issue 中或者通过其他社区渠道向我们提出,一起讨论。
+如果您对 Roadmap 有任何建议或想法,欢迎在 issues 中或者通过其他社区渠道向我们提出,一起讨论。
## 已包含的组件
**Sentinel**
-阿里巴巴开源产品,把流量作为切入点,从流量控制,熔断降级,系统负载保护等多个维度保护服务的稳定性。
+
+阿里巴巴开源产品,把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
**Nacos**
+
阿里巴巴开源产品,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
**Alibaba Cloud OSS**
+
阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
-## 即将加入的组件
-**Dubbo**
-Apache Dubbo™ (incubating) 是一款高性能Java RPC框架。
-
**RocketMQ**
-Apache RocketMQ™ 基于Java的高性能、高吞吐量的分布式消息和流计算平台。
-**Alibaba Cloud Schedulerx**
+Apache RocketMQ™ 基于 Java 的高性能、高吞吐量的分布式消息和流计算平台。
+
+**Alibaba Cloud SchedulerX**
+
阿里中间件团队开发的一款分布式任务调度产品,支持周期性的任务与固定时间点触发任务。
+
+## 即将加入的组件
+
+**Dubbo**
+
+Apache Dubbo™ (incubating) 是一款高性能 Java RPC 框架。
+
+**Fescar**
+
+阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
+
**Alibaba Cloud SLS**
+
针对日志类数据的一站式服务,在阿里巴巴集团经历大量大数据场景锤炼而成。您无需开发就能快捷完成日志数据采集、消费、投递以及查询分析等功能,提升运维、运营效率,建立 DT 时代海量日志处理能力。
\ No newline at end of file
diff --git a/Roadmap.md b/Roadmap.md
index 7b392f2a..66cb84f6 100644
--- a/Roadmap.md
+++ b/Roadmap.md
@@ -20,20 +20,28 @@ An open-source project of Alibaba, Sentinel takes "flow" as breakthrough point,
An opensource project of Alibaba, an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications.
+**RocketMQ**
+
+Apache RocketMQ™ is an open source distributed messaging and streaming data platform.
+
**Alibaba Cloud OSS**
Alibaba Cloud Object Storage Service, An encrypted and secure cloud storage service which stores, processes and accesses massive amounts of data from anywhere in the world.
+**Alibaba Cloud Schedulerx**
+
+A distributed task scheduling product developed by Alibaba Middleware team. It supports both periodical tasks and tasks to be triggered at specified time points.
+
More components will be supported by Spring Cloud Alibaba in the future, which may include but are not limited to the following:
**Dubbo**
+
Apache Dubbo™ (incubating) is a high-performance, Java based open source RPC framework.
-**RocketMQ**
-Apache RocketMQ™ is an open source distributed messaging and streaming data platform.
+**Fescar**
-**Alibaba Cloud Schedulerx**
-A distributed task scheduling product developed by Alibaba Middleware team. It supports both periodical tasks and tasks to be triggered at specified time points.
+A distributed transaction solution with high performance and ease of use for microservices architecture.
**Alibaba Cloud SLS**
-Aliyun Log Service is an all-in-one service for log-type data. It helps increase Operations & Management and operational efficiency, as well as build the processing capability to deal with massive logs.
\ No newline at end of file
+
+Aliyun Log Service is an all-in-one service for log-type data. It helps increase Operations & Management and operational efficiency, as well as build the processing capability to deal with massive logs.
diff --git a/pom.xml b/pom.xml
index 8672d824..92bd96cb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -81,6 +81,8 @@
2.8.2
2.21.0
1.6
+ 2.1.1
+ 2.7
@@ -153,11 +155,25 @@
pom
import
-
+
+ net.sourceforge.cobertura
+ cobertura-runtime
+ ${cobertura.version}
+ provided
+ pom
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
org.apache.maven.plugins
@@ -183,7 +199,7 @@
org.codehaus.mojo
cobertura-maven-plugin
- 2.7
+ ${cobertura-maven-plugin.version}
html
@@ -191,26 +207,6 @@
-
-
- org/springframework/cloud/alibaba/cloud/dependencies/**.*.class
- org/springframework/cloud/alibaba/cloud/docs/**.*.class
- org/springframework/cloud/alibaba/cloud/examples/**.*.class
- org/springframework/cloud/alibaba/cloud/test/**.*.class
- org/springframework/cloud/start/alibaba/**.*.class
- org/springframework/cloud/start/alicloud/**.*.class
- org/springframework/cloud/alibaba/nacos/src/test/**.*.class
- org/springframework/cloud/alibaba/nacos/config/server/src/test/**.*.class
- org/springframework/cloud/alibaba/sentinel/src/test/**.*.class
- org/springframework/cloud/alibaba/sentinel/datasource/src/test/**.*.class
- org/springframework/cloud/alicloud/acm/src/test/**.*.class
- org/springframework/cloud/alicloud/ans/src/test/**.*.class
- org/springframework/cloud/alicloud/context/src/test/**.*.class
- org/springframework/cloud/alicloud/oss/src/test/**.*.class
- org/springframework/cloud/alicloud/scx/src/test/**.*.class
- org/springframework/cloud/stream/binder/rocketmq/src/test/**.*.class
-
-
diff --git a/spring-cloud-alibaba-dependencies/pom.xml b/spring-cloud-alibaba-dependencies/pom.xml
index e34b9f72..ef203d31 100644
--- a/spring-cloud-alibaba-dependencies/pom.xml
+++ b/spring-cloud-alibaba-dependencies/pom.xml
@@ -372,4 +372,26 @@
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
+
+
diff --git a/spring-cloud-alibaba-docs/pom.xml b/spring-cloud-alibaba-docs/pom.xml
index 44d6fad1..cb755091 100644
--- a/spring-cloud-alibaba-docs/pom.xml
+++ b/spring-cloud-alibaba-docs/pom.xml
@@ -50,5 +50,27 @@
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
+
+
diff --git a/spring-cloud-alibaba-examples/acm-example/acm-local-example/pom.xml b/spring-cloud-alibaba-examples/acm-example/acm-local-example/pom.xml
index 250a60fb..c7847d51 100644
--- a/spring-cloud-alibaba-examples/acm-example/acm-local-example/pom.xml
+++ b/spring-cloud-alibaba-examples/acm-example/acm-local-example/pom.xml
@@ -23,11 +23,28 @@
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
org.springframework.boot
spring-boot-maven-plugin
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/ans-example/ans-consumer-feign-example/pom.xml b/spring-cloud-alibaba-examples/ans-example/ans-consumer-feign-example/pom.xml
index af0c708c..2e6ddcb8 100644
--- a/spring-cloud-alibaba-examples/ans-example/ans-consumer-feign-example/pom.xml
+++ b/spring-cloud-alibaba-examples/ans-example/ans-consumer-feign-example/pom.xml
@@ -29,4 +29,25 @@
spring-boot-starter-actuator
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/ans-example/ans-consumer-ribbon-example/pom.xml b/spring-cloud-alibaba-examples/ans-example/ans-consumer-ribbon-example/pom.xml
index b66c22ff..6289c404 100644
--- a/spring-cloud-alibaba-examples/ans-example/ans-consumer-ribbon-example/pom.xml
+++ b/spring-cloud-alibaba-examples/ans-example/ans-consumer-ribbon-example/pom.xml
@@ -25,4 +25,25 @@
spring-boot-starter-actuator
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/ans-example/ans-provider-example/pom.xml b/spring-cloud-alibaba-examples/ans-example/ans-provider-example/pom.xml
index fc8f4b67..67ce3b6b 100644
--- a/spring-cloud-alibaba-examples/ans-example/ans-provider-example/pom.xml
+++ b/spring-cloud-alibaba-examples/ans-example/ans-provider-example/pom.xml
@@ -27,11 +27,28 @@
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
org.springframework.boot
spring-boot-maven-plugin
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/pom.xml
index 565a732d..8d964fb7 100644
--- a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/pom.xml
+++ b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/pom.xml
@@ -34,6 +34,14 @@
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
org.springframework.boot
@@ -47,6 +55,15 @@
true
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/pom.xml
index 6a3af32b..aabf1ec1 100644
--- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/pom.xml
+++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/pom.xml
@@ -48,6 +48,14 @@
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
org.springframework.boot
@@ -61,6 +69,15 @@
true
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/pom.xml
index 60fd7050..52bac0cf 100644
--- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/pom.xml
+++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/pom.xml
@@ -33,6 +33,14 @@
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
org.springframework.boot
@@ -46,6 +54,15 @@
true
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/pom.xml
index ac62d9a3..bea5a0b0 100644
--- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/pom.xml
+++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/pom.xml
@@ -21,5 +21,25 @@
nacos-discovery-provider-example
-
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
+
+
diff --git a/spring-cloud-alibaba-examples/oss-example/pom.xml b/spring-cloud-alibaba-examples/oss-example/pom.xml
index 624b3e66..75cbfd2f 100644
--- a/spring-cloud-alibaba-examples/oss-example/pom.xml
+++ b/spring-cloud-alibaba-examples/oss-example/pom.xml
@@ -33,6 +33,14 @@
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
org.springframework.boot
@@ -46,6 +54,15 @@
true
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
diff --git a/spring-cloud-alibaba-examples/pom.xml b/spring-cloud-alibaba-examples/pom.xml
index e742012a..dd03c65a 100644
--- a/spring-cloud-alibaba-examples/pom.xml
+++ b/spring-cloud-alibaba-examples/pom.xml
@@ -35,6 +35,14 @@
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
org.apache.maven.plugins
@@ -44,6 +52,16 @@
true
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
+
diff --git a/spring-cloud-alibaba-examples/rocketmq-example/pom.xml b/spring-cloud-alibaba-examples/rocketmq-example/pom.xml
index 84ef5029..12bfbb4f 100644
--- a/spring-cloud-alibaba-examples/rocketmq-example/pom.xml
+++ b/spring-cloud-alibaba-examples/rocketmq-example/pom.xml
@@ -40,6 +40,14 @@
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
org.springframework.boot
@@ -53,6 +61,15 @@
true
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
diff --git a/spring-cloud-alibaba-examples/schedulerx-example/schedulerx-simple-task-example/pom.xml b/spring-cloud-alibaba-examples/schedulerx-example/schedulerx-simple-task-example/pom.xml
index 57cb2df5..7cb076a9 100644
--- a/spring-cloud-alibaba-examples/schedulerx-example/schedulerx-simple-task-example/pom.xml
+++ b/spring-cloud-alibaba-examples/schedulerx-example/schedulerx-simple-task-example/pom.xml
@@ -27,11 +27,28 @@
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
org.springframework.boot
spring-boot-maven-plugin
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/pom.xml
index 507e8498..25e56597 100644
--- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/pom.xml
+++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/pom.xml
@@ -51,6 +51,14 @@
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
org.springframework.boot
@@ -64,6 +72,15 @@
true
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-api/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-api/pom.xml
index 5e0b8f4a..d594344e 100644
--- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-api/pom.xml
+++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-api/pom.xml
@@ -16,6 +16,14 @@
api for sentinel dubbo example
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
org.apache.maven.plugins
@@ -25,6 +33,15 @@
true
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml
index 5a2cdbf0..868bc10c 100644
--- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml
+++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml
@@ -36,6 +36,14 @@
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
org.springframework.boot
@@ -49,6 +57,15 @@
true
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-provider-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-provider-example/pom.xml
index 8454515c..684a589b 100644
--- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-provider-example/pom.xml
+++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-provider-example/pom.xml
@@ -34,6 +34,14 @@
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
org.springframework.boot
@@ -47,6 +55,15 @@
true
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
diff --git a/spring-cloud-alibaba-examples/sms-example/readme-zh.md b/spring-cloud-alibaba-examples/sms-example/readme-zh.md
new file mode 100644
index 00000000..fb251ecf
--- /dev/null
+++ b/spring-cloud-alibaba-examples/sms-example/readme-zh.md
@@ -0,0 +1,314 @@
+# SMS Example
+
+## 项目说明
+
+如果您的应用是 Spring Cloud 应用,且需要使用阿里云的 SMS 服务来发送短信,例如登录验证码,那么您可以使用 SMS starter 完成 Spring Cloud 应用的短信发送。
+
+短信服务(Short Message Service)是阿里云为用户提供的一种通信服务的能力。支持国内和国际快速发送验证码、短信通知和推广短信,服务范围覆盖全球200多个国家和地区。更多可参考 [官网文档](https://help.aliyun.com/document_detail/60704.html?spm=5176.8195934.1283918.6.18924183bHPct2)
+
+## 示例
+
+### 接入 SMS
+
+在启动示例进行演示之前,我们先了解一下如何接入 SMS。
+
+**注意:本节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您只需修改 accessKey、secretKey 即可。**
+
+1. 修改 pom.xml 文件,引入 alicloud-sms starter。
+
+ ```xml
+
+ org.springframework.cloud
+ spring-cloud-starter-alicloud-sms
+
+ ```
+
+1. 在配置文件中配置 SMS 服务对应的 accessKey、secretKey 。
+
+ ```properties
+ spring.cloud.alicloud.access-key=your-ak
+ spring.cloud.alicloud.secret-key=your-sk
+ ```
+
+ 以阿里云 accessKey、secretKey 为例,获取方式如下。
+
+ i. 在阿里云控制台界面,单击右上角头像,选择 accesskeys,或者直接登录[用户信息管理界面](https://usercenter.console.aliyun.com/):
+
+ 
+
+ ii. 获取 accessKey、secretKey:
+
+ 
+
+ **注意:**如果您使用了阿里云 [STS服务](https://help.aliyun.com/document_detail/28756.html) 进行短期访问权限管理,则除了 accessKey、secretKey 以外,还需配置 securityToken。
+
+1. 注入 ISmsService 实例并进行短信发送等操作。
+
+ ```java
+ @RestController
+ public class SmsController {
+ @Autowired
+ private ISmsService smsService ;
+
+ @RequestMapping("/send.do")
+ public SendSmsResponse sendMessage(String telphone,String code) {
+ // 组装请求对象-具体描述见控制台-文档部分内容
+ SendSmsRequest request = new SendSmsRequest();
+ // 必填:待发送手机号
+ request.setPhoneNumbers(telphone);
+ // 必填:短信签名-可在短信控制台中找到
+ request.setSignName("******");
+ // 必填:短信模板-可在短信控制台中找到
+ request.setTemplateCode("******");
+ // 可选:模板中的变量替换JSON串,如模板内容为"【企业级分布式应用服务】,您的验证码为${code}"时,此处的值为
+ request.setTemplateParam("{\"code\":\"" + code + "\"}");
+ SendSmsResponse sendSmsResponse ;
+ try {
+ sendSmsResponse = smsService.sendSmsRequest(request);
+ }
+ catch (ClientException e) {
+ e.printStackTrace();
+ sendSmsResponse = new SendSmsResponse();
+ }
+ return sendSmsResponse ;
+ }
+ }
+ ```
+
+ **说明:** 直接注入 ISmsService 方式即可。
+
+### 启动应用
+
+
+1. 在应用的 /src/main/resources/application.properties 中添加基本配置信息。
+
+ ```properties
+ spring.application.name=sms-example
+ server.port=18084
+ spring.cloud.alicloud.access-key=your-ak
+ spring.cloud.alicloud.secret-key=your-sk
+ ```
+
+2. 通过 IDE 直接启动或者编译打包后启动应用。
+
+ - IDE直接启动:找到主类 `SMSApplication`,执行 main 方法启动应用。
+ - 打包编译后启动:
+ 1. 执行 `mvn clean package` 将工程编译打包;
+ 2. 执行 `java -jar sms-example.jar`启动应用。
+
+应用启动后访问 http://localhost:18084/send.do?telphone=******&code=6580。查看返回发送的 SendSmsResponse 的 json 串结果。
+
+
+### 批量短信发送
+
+参考以下的 Example ,来快速开发一个具有批量短信发送的功能。在 Controller 中或者新建一个 Controler 新增如下代码:
+
+
+```java
+@RequestMapping("/batch-sms-send.do")
+public SendBatchSmsResponse batchsendCheckCode(
+ @RequestParam(name = "code") String code) {
+ // 组装请求对象
+ SendBatchSmsRequest request = new SendBatchSmsRequest();
+ // 使用 GET 提交
+ request.setMethod(MethodType.GET);
+ // 必填:待发送手机号。支持JSON格式的批量调用,批量上限为100个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式
+ request.setPhoneNumberJson("[\"177********\",\"130********\"]");
+ // 必填:短信签名-支持不同的号码发送不同的短信签名
+ request.setSignNameJson("[\"*******\",\"*******\"]");
+ // 必填:短信模板-可在短信控制台中找到
+ request.setTemplateCode("******");
+ // 必填:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
+ // 友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,比如短信内容中包含\r\n的情况在JSON中需要表示成\\r\\n,否则会导致JSON在服务端解析失败
+ request.setTemplateParamJson(
+ "[{\"code\":\"" + code + "\"},{\"code\":\"" + code + "\"}]");
+ SendBatchSmsResponse sendSmsResponse ;
+ try {
+ sendSmsResponse = smsService
+ .sendSmsBatchRequest(request);
+ return sendSmsResponse;
+ }
+ catch (ClientException e) {
+ e.printStackTrace();
+ sendSmsResponse = new SendBatchSmsResponse();
+ }
+ return sendSmsResponse ;
+}
+```
+
+### 短信查询
+
+参考以下的 Example ,可以快速开发根据某个指定的号码查询短信历史发送状态。在 Controller 中或者新建一个 Controler 新增如下代码:
+
+```java
+/**
+ *
+ * 短信查询 Example
+ * @param telephone
+ * @return
+ */
+@RequestMapping("/query.do")
+public QuerySendDetailsResponse querySendDetailsResponse(
+ @RequestParam(name = "tel") String telephone) {
+ // 组装请求对象
+ QuerySendDetailsRequest request = new QuerySendDetailsRequest();
+ // 必填-号码
+ request.setPhoneNumber(telephone);
+ // 必填-短信发送的日期 支持30天内记录查询(可查其中一天的发送数据),格式yyyyMMdd
+ request.setSendDate("20190103");
+ // 必填-页大小
+ request.setPageSize(10L);
+ // 必填-当前页码从1开始计数
+ request.setCurrentPage(1L);
+ try {
+ QuerySendDetailsResponse response = smsService.querySendDetails(request);
+ return response;
+ }
+ catch (ClientException e) {
+ e.printStackTrace();
+ }
+
+ return new QuerySendDetailsResponse();
+}
+
+```
+
+查询成功后的返回结果如下所示:
+
+ ```plain
+ {
+ "requestId": "0030EE65-25B1-43EE-BA90-D8FDACC45DC7",
+ "code": "OK",
+ "message": "OK",
+ "totalCount": "3",
+ "smsSendDetailDTOs": [
+ {
+ "phoneNum": "152********",
+ "sendStatus": 3,
+ "errCode": "DELIVRD",
+ "templateCode": "SMS_******",
+ "content": "【企业级分布式应用服务】验证码为:1080,您正在注册成为平台会员,感谢您的支持!",
+ "sendDate": "2019-01-03 22:09:09",
+ "receiveDate": "2019-01-03 22:09:21",
+ "outId": "edasTraceId"
+ },
+ {
+ "phoneNum": "152********",
+ "sendStatus": 3,
+ "errCode": "DELIVRD",
+ "templateCode": "SMS_******",
+ "content": "【企业级分布式应用服务】验证码为:1865,您正在注册成为平台会员,感谢您的支持!",
+ "sendDate": "2019-01-03 21:13:30",
+ "receiveDate": "2019-01-03 21:13:37",
+ "outId": "edasTraceId"
+ },
+ {
+ "phoneNum": "152********",
+ "sendStatus": 3,
+ "errCode": "DELIVRD",
+ "templateCode": "SMS_*******",
+ "content": "【企业级分布式应用服务】验证码为:9787,您正在注册成为平台会员,感谢您的支持!",
+ "sendDate": "2019-01-03 17:19:11",
+ "receiveDate": "2019-01-03 17:19:15",
+ "outId": "edasTraceId"
+ }
+ ]
+ }
+ ```
+
+### 短信回执消息
+
+通过订阅 SmsReport 短信状态报告,可以获知每条短信的发送情况,了解短信是否达到终端用户的状态与相关信息。这些工作已经都被 Spring Cloud AliCloud SMS 封装在内部了。你只需要完成以下两步即可。
+
+1. 在 **application.properties** 配置文件中(也可以是 application.yaml)配置 SmsReport 的队列名称。
+
+ ```properties
+ spring.cloud.alicloud.sms.report-queue-name=Alicom-Queue-********-SmsReport
+ ```
+
+2. 实现 SmsReportMessageListener 接口,并初始化一个 Spring Bean .
+
+ ```java
+ /**
+ * 如果需要监听短信是否被对方成功接收,只需实现这个接口并初始化一个 Spring Bean 即可。
+ */
+ @Component
+ public class SmsReportMessageListener
+ implements org.springframework.cloud.alicloud.sms.SmsReportMessageListener {
+
+ @Override
+ public boolean dealMessage(Message message) {
+ // 在这里添加你的处理逻辑
+
+ //do something
+
+ System.err.println(this.getClass().getName() + "; " + message.toString());
+ return true;
+ }
+ }
+ ```
+
+发送状态的回执消息如下所示:
+
+```plain
+org.springframework.cloud.alibaba.cloud.example.SmsReportMessageListener; MessageID:9F3CFCE6BB3B2C8F-2-1682D84D9AD-20000000A,MessageMD5:C6AFEE0EE560BBC3380252337AC36985,RequestID:5C349CCEB8C115CCF344A3EB,MessageBody:"{"send_time":"2019-01-08 20:51:40","report_time":"2019-01-08 20:51:47","success":true,"err_msg":"用户接收成功","err_code":"DELIVERED","phone_number":"152********","sms_size":"1","biz_id":"667618746951900475^0","out_id":"edasTraceId"}",ReceiptHandle:"1-ODU4OTkzNDYwMi0xNTQ2OTUxOTM3LTItOA==",DequeueCount:"1",EnqueueTime:"Tue Jan 08 20:51:47 CST 2019",FirstDequeueTime:"Tue Jan 08 20:51:47 CST 2019",NextVisibleTime:"Tue Jan 08 20:52:17 CST 2019",Priority:"8"
+```
+
+### 上行短信消息
+
+
+通过订阅SmsUp上行短信消息,可以获知终端用户回复短信的内容。这些工作也已经被 Spring Cloud AliCloud SMS 封装好了。你只需要完成以下两步即可。
+
+1. 在 **application.properties** 配置文件中(也可以是 application.yaml)配置 SmsReport 的队列名称。
+
+ ```properties
+ spring.cloud.alicloud.sms.up-queue-name=Alicom-Queue-********-SmsUp
+ ```
+
+1. 实现 SmsUpMessageListener 接口,并初始化一个 Spring Bean 。
+
+ ```java
+ /**
+ * 如果发送的短信需要接收对方回复的状态消息,只需实现该接口并初始化一个 Spring Bean 即可。
+ */
+ @Component
+ public class SmsUpMessageListener
+ implements org.springframework.cloud.alicloud.sms.SmsUpMessageListener {
+
+ @Override
+ public boolean dealMessage(Message message) {
+ // 在这里添加你的处理逻辑
+
+ //do something
+
+ System.err.println(this.getClass().getName() + "; " + message.toString());
+ return true;
+ }
+ }
+ ```
+
+短信成功恢复后,上行短信消息 SmsUpMessageListener 回调后的信息如下所示:
+
+```plain
+org.springframework.cloud.alibaba.cloud.example.SmsUpMessageListener; MessageID:BF030215BA85BB41-1-1682D85425F-400000003,MessageMD5:D1AF5C2D7410EF190532CBF8E17FE2B7,RequestID:5C349CEE36AF628D2A847D50,MessageBody:"{"dest_code":"2493559","send_time":"2019-01-08 20:52:14","sign_name":"【企业级分布式应用服务】","sequence_id":568585703,"phone_number":"152********","content":"5279"}",ReceiptHandle:"1-MTcxNzk4NjkxODctMTU0Njk1MTk2NC0xLTg=",DequeueCount:"1",EnqueueTime:"Tue Jan 08 20:52:14 CST 2019",FirstDequeueTime:"Tue Jan 08 20:52:14 CST 2019",NextVisibleTime:"Tue Jan 08 20:52:44 CST 2019",Priority:"8"
+```
+
+## 查看 Endpoint 信息
+
+Spring Boot 应用支持通过 Endpoint 来暴露相关信息,SMS Starter 也支持这一点。
+
+**前提条件:**
+
+在 maven 中添加 `spring-boot-starter-actuator`依赖,并在配置中允许 Endpoints 的访问。
+
+- Spring Boot1.x 中添加配置 `management.security.enabled=false`
+- Spring Boot2.x 中添加配置 `management.endpoints.web.exposure.include=*`
+
+Spring Boot1.x 可以通过访问 http://127.0.0.1:18084/sms-info 来查看 SMS Endpoint 的信息。
+
+Spring Boot2.x 可以通过访问 http://127.0.0.1:18084/acutator/sms-info 来访问。
+
+Endpoint 内部会显示最近 20 条单个短信发送的记录和批量短信发送的记录,以及当前短信消息的配置信息(包括是**SmsReport** 还是 **SmsUp**,**队列名称**,以及对应的 **MessageListener** )。
+
+
+如果您对 Spring Cloud SMS Starter 有任何建议或想法,欢迎提交 issue 中或者通过其他社区渠道向我们反馈。
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/pom.xml
index 060bb532..6874be2d 100644
--- a/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/pom.xml
+++ b/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/pom.xml
@@ -35,6 +35,14 @@
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
org.springframework.boot
@@ -48,6 +56,15 @@
true
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
diff --git a/spring-cloud-alibaba-nacos-discovery/pom.xml b/spring-cloud-alibaba-nacos-discovery/pom.xml
index d0a5ddd1..87df397d 100644
--- a/spring-cloud-alibaba-nacos-discovery/pom.xml
+++ b/spring-cloud-alibaba-nacos-discovery/pom.xml
@@ -82,5 +82,28 @@
-
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
+ org/springframework/cloud/alibaba/nacos/**.*class
+
+
+
+
+
+
+
diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryClient.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryClient.java
index 178333fa..67e6b441 100644
--- a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryClient.java
+++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryClient.java
@@ -65,7 +65,7 @@ public class NacosDiscoveryClient implements DiscoveryClient {
nacosServiceInstance.setHost(instance.getIp());
nacosServiceInstance.setPort(instance.getPort());
nacosServiceInstance.setServiceId(serviceId);
- Map metadata = new HashMap();
+ Map metadata = new HashMap<>();
metadata.put("instanceId", instance.getInstanceId());
metadata.put("weight", instance.getWeight() + "");
metadata.put("healthy", instance.isHealthy() + "");
@@ -82,7 +82,7 @@ public class NacosDiscoveryClient implements DiscoveryClient {
private static List hostToServiceInstanceList(
List instances, String serviceId) {
- List result = new ArrayList(instances.size());
+ List result = new ArrayList<>(instances.size());
for (Instance instance : instances) {
result.add(hostToServiceInstance(instance, serviceId));
}
diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/NacosServerList.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/NacosServerList.java
index c2b4990f..6b83e83c 100644
--- a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/NacosServerList.java
+++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/NacosServerList.java
@@ -79,4 +79,4 @@ public class NacosServerList extends AbstractServerList {
public void initWithNiwsConfig(IClientConfig iClientConfig) {
this.serviceId = iClientConfig.getClientName();
}
-}
+}
\ No newline at end of file
diff --git a/spring-cloud-alibaba-sentinel/pom.xml b/spring-cloud-alibaba-sentinel/pom.xml
index b5707134..3abdf979 100644
--- a/spring-cloud-alibaba-sentinel/pom.xml
+++ b/spring-cloud-alibaba-sentinel/pom.xml
@@ -42,6 +42,13 @@
true
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-ribbon
+ provided
+ true
+
+
com.alibaba.csp
sentinel-parameter-flow-control
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelConstants.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelConstants.java
index 714cc08b..0c2a9d09 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelConstants.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelConstants.java
@@ -23,6 +23,9 @@ public interface SentinelConstants {
String PROPERTY_PREFIX = "spring.cloud.sentinel";
+ String BLOCK_TYPE = "block";
+ String FALLBACK_TYPE = "fallback";
+
// commercialization
String FLOW_DATASOURCE_NAME = "edas-flow";
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelProperties.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelProperties.java
index 63ce6865..53aea051 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelProperties.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelProperties.java
@@ -21,7 +21,6 @@ import java.util.Map;
import java.util.TreeMap;
import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.cloud.alibaba.sentinel.datasource.config.DataSourcePropertiesConfiguration;
import org.springframework.core.Ordered;
import org.springframework.validation.annotation.Validated;
@@ -31,6 +30,8 @@ import com.alibaba.csp.sentinel.log.LogBase;
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
/**
+ * {@link ConfigurationProperties} for Sentinel.
+ *
* @author xiaojing
* @author hengyunabc
* @author jiashuai.xie
@@ -41,58 +42,51 @@ import com.alibaba.csp.sentinel.transport.config.TransportConfig;
public class SentinelProperties {
/**
- * earlier initialize heart-beat when the spring container starts when the
- * transport dependency is on classpath ,the configuration is effective
+ * Earlier initialize heart-beat when the spring container starts when the transport
+ * dependency is on classpath, the configuration is effective.
*/
private boolean eager = false;
/**
- * enable sentinel auto configure, the default value is true
+ * Enable sentinel auto configure, the default value is true.
*/
private boolean enabled = true;
/**
- * configurations about datasource, like 'nacos', 'apollo', 'file', 'zookeeper'
+ * Configurations about datasource, like 'nacos', 'apollo', 'file', 'zookeeper'.
*/
private Map datasource = new TreeMap<>(
String.CASE_INSENSITIVE_ORDER);
/**
- * transport configuration about dashboard and client
+ * Transport configuration about dashboard and client.
*/
- @NestedConfigurationProperty
private Transport transport = new Transport();
/**
- * metric configuration about resource
+ * Metric configuration about resource.
*/
- @NestedConfigurationProperty
private Metric metric = new Metric();
/**
- * web servlet configuration when the application is web ,the configuration is
- * effective
+ * Web servlet configuration when the application is web, the configuration is
+ * effective.
*/
- @NestedConfigurationProperty
private Servlet servlet = new Servlet();
/**
- * sentinel filter when the application is web ,the configuration is effective
- *
+ * Sentinel filter when the application is web, the configuration is effective.
*/
- @NestedConfigurationProperty
private Filter filter = new Filter();
/**
- * flow configuration
+ * Sentinel Flow configuration.
*/
- @NestedConfigurationProperty
private Flow flow = new Flow();
/**
- * sentinel log configuration {@link LogBase}
+ * Sentinel log configuration {@link LogBase}.
*/
- @NestedConfigurationProperty
private Log log = new Log();
public boolean isEager() {
@@ -170,7 +164,7 @@ public class SentinelProperties {
public static class Flow {
/**
- * the cold factor {@link SentinelConfig#COLD_FACTOR}
+ * The cold factor {@link SentinelConfig#COLD_FACTOR}.
*/
private String coldFactor = "3";
@@ -187,7 +181,7 @@ public class SentinelProperties {
public static class Servlet {
/**
- * The process page when the flow control is triggered
+ * The process page when the flow control is triggered.
*/
private String blockPage;
@@ -203,17 +197,17 @@ public class SentinelProperties {
public static class Metric {
/**
- * the metric file size {@link SentinelConfig#SINGLE_METRIC_FILE_SIZE}
+ * The metric file size {@link SentinelConfig#SINGLE_METRIC_FILE_SIZE}.
*/
private String fileSingleSize;
/**
- * the total metric file count {@link SentinelConfig#TOTAL_METRIC_FILE_COUNT}
+ * The total metric file count {@link SentinelConfig#TOTAL_METRIC_FILE_COUNT}.
*/
private String fileTotalCount;
/**
- * charset when sentinel write or search metric file
+ * Charset when sentinel write or search metric file.
* {@link SentinelConfig#CHARSET}
*/
private String charset = "UTF-8";
@@ -246,22 +240,28 @@ public class SentinelProperties {
public static class Transport {
/**
- * sentinel api port,default value is 8719 {@link TransportConfig#SERVER_PORT}
+ * Sentinel api port, default value is 8719 {@link TransportConfig#SERVER_PORT}.
*/
private String port = "8719";
/**
- * sentinel dashboard address, won't try to connect dashboard when address is
- * empty {@link TransportConfig#CONSOLE_SERVER}
+ * Sentinel dashboard address, won't try to connect dashboard when address is
+ * empty {@link TransportConfig#CONSOLE_SERVER}.
*/
private String dashboard = "";
/**
- * send heartbeat interval millisecond
- * {@link TransportConfig#HEARTBEAT_INTERVAL_MS}
+ * Send heartbeat interval millisecond
+ * {@link TransportConfig#HEARTBEAT_INTERVAL_MS}.
*/
private String heartbeatIntervalMs;
+ /**
+ * Get heartbeat client local ip. If the client ip not configured, it will be the
+ * address of local host.
+ */
+ private String clientIp;
+
public String getHeartbeatIntervalMs() {
return heartbeatIntervalMs;
}
@@ -286,17 +286,24 @@ public class SentinelProperties {
this.dashboard = dashboard;
}
+ public String getClientIp() {
+ return clientIp;
+ }
+
+ public void setClientIp(String clientIp) {
+ this.clientIp = clientIp;
+ }
}
public static class Filter {
/**
- * sentinel filter chain order.
+ * Sentinel filter chain order.
*/
private int order = Ordered.HIGHEST_PRECEDENCE;
/**
- * URL pattern for sentinel filter,default is /*
+ * URL pattern for sentinel filter, default is /*
*/
private List urlPatterns;
@@ -320,12 +327,12 @@ public class SentinelProperties {
public static class Log {
/**
- * sentinel log base dir
+ * Sentinel log base dir.
*/
private String dir;
/**
- * distinguish the log file by pid number
+ * Distinguish the log file by pid number.
*/
private boolean switchPid = false;
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelWebAutoConfiguration.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelWebAutoConfiguration.java
index 8428632f..d70fe785 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelWebAutoConfiguration.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelWebAutoConfiguration.java
@@ -54,11 +54,6 @@ public class SentinelWebAutoConfiguration {
SentinelProperties.Filter filterConfig = properties.getFilter();
- if (null == filterConfig) {
- filterConfig = new SentinelProperties.Filter();
- properties.setFilter(filterConfig);
- }
-
if (filterConfig.getUrlPatterns() == null
|| filterConfig.getUrlPatterns().isEmpty()) {
List defaultPatterns = new ArrayList<>();
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java
index 4349cf70..27739845 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java
@@ -22,6 +22,7 @@ import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -29,10 +30,12 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter;
import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter;
+import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
+import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner;
import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
@@ -74,8 +77,20 @@ public class SentinelAutoConfiguration {
@Autowired
private Optional urlBlockHandlerOptional;
+ @Autowired
+ private Optional requestOriginParserOptional;
+
@PostConstruct
private void init() {
+ if (StringUtils.isEmpty(System.getProperty(LogBase.LOG_DIR))
+ && StringUtils.hasText(properties.getLog().getDir())) {
+ System.setProperty(LogBase.LOG_DIR, properties.getLog().getDir());
+ }
+ if (StringUtils.isEmpty(System.getProperty(LogBase.LOG_NAME_USE_PID))
+ && properties.getLog().isSwitchPid()) {
+ System.setProperty(LogBase.LOG_NAME_USE_PID,
+ String.valueOf(properties.getLog().isSwitchPid()));
+ }
if (StringUtils.isEmpty(System.getProperty(AppNameUtil.APP_NAME))
&& StringUtils.hasText(projectName)) {
System.setProperty(AppNameUtil.APP_NAME, projectName);
@@ -96,6 +111,11 @@ public class SentinelAutoConfiguration {
System.setProperty(TransportConfig.HEARTBEAT_INTERVAL_MS,
properties.getTransport().getHeartbeatIntervalMs());
}
+ if (StringUtils.isEmpty(System.getProperty(TransportConfig.HEARTBEAT_CLIENT_IP))
+ && StringUtils.hasText(properties.getTransport().getClientIp())) {
+ System.setProperty(TransportConfig.HEARTBEAT_CLIENT_IP,
+ properties.getTransport().getClientIp());
+ }
if (StringUtils.isEmpty(System.getProperty(SentinelConfig.CHARSET))
&& StringUtils.hasText(properties.getMetric().getCharset())) {
System.setProperty(SentinelConfig.CHARSET,
@@ -121,18 +141,10 @@ public class SentinelAutoConfiguration {
if (StringUtils.hasText(properties.getServlet().getBlockPage())) {
WebServletConfig.setBlockPage(properties.getServlet().getBlockPage());
}
- if (StringUtils.isEmpty(System.getProperty(LogBase.LOG_DIR))
- && StringUtils.hasText(properties.getLog().getDir())) {
- System.setProperty(LogBase.LOG_DIR, properties.getLog().getDir());
- }
- if (StringUtils.isEmpty(System.getProperty(LogBase.LOG_NAME_USE_PID))
- && properties.getLog().isSwitchPid()) {
- System.setProperty(LogBase.LOG_NAME_USE_PID,
- String.valueOf(properties.getLog().isSwitchPid()));
- }
urlBlockHandlerOptional.ifPresent(WebCallbackManager::setUrlBlockHandler);
urlCleanerOptional.ifPresent(WebCallbackManager::setUrlCleaner);
+ requestOriginParserOptional.ifPresent(WebCallbackManager::setRequestOriginParser);
// earlier initialize
if (properties.isEager()) {
@@ -150,13 +162,15 @@ public class SentinelAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
- public SentinelBeanPostProcessor sentinelBeanPostProcessor() {
- return new SentinelBeanPostProcessor();
+ public SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
}
@Bean
- public SentinelDataSourceHandler sentinelDataSourceHandler() {
- return new SentinelDataSourceHandler();
+ public SentinelDataSourceHandler sentinelDataSourceHandler(
+ DefaultListableBeanFactory beanFactory) {
+ return new SentinelDataSourceHandler(beanFactory);
}
protected static class SentinelConverterConfiguration {
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java
index dd84109e..18580aa5 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java
@@ -16,22 +16,32 @@
package org.springframework.cloud.alibaba.sentinel.custom;
+import java.lang.reflect.Method;
+import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
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.SentinelConstants;
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.core.type.StandardMethodMetadata;
import org.springframework.core.type.classreading.MethodMetadataReadingVisitor;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
/**
* PostProcessor handle @SentinelRestTemplate Annotation, add interceptor for RestTemplate
*
@@ -41,8 +51,14 @@ import org.springframework.web.client.RestTemplate;
*/
public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
- @Autowired
- private ApplicationContext applicationContext;
+ private static final Logger logger = LoggerFactory
+ .getLogger(SentinelBeanPostProcessor.class);
+
+ private final ApplicationContext applicationContext;
+
+ public SentinelBeanPostProcessor(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
private ConcurrentHashMap cache = new ConcurrentHashMap<>();
@@ -60,10 +76,74 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces
sentinelRestTemplate = beanDefinition.getResolvedFactoryMethod()
.getAnnotation(SentinelRestTemplate.class);
}
+ // check class and method validation
+ checkSentinelRestTemplate(sentinelRestTemplate, beanName);
cache.put(beanName, sentinelRestTemplate);
}
}
+ private void checkSentinelRestTemplate(SentinelRestTemplate sentinelRestTemplate,
+ String beanName) {
+ checkBlock4RestTemplate(sentinelRestTemplate.blockHandlerClass(),
+ sentinelRestTemplate.blockHandler(), beanName,
+ SentinelConstants.BLOCK_TYPE);
+ checkBlock4RestTemplate(sentinelRestTemplate.fallbackClass(),
+ sentinelRestTemplate.fallback(), beanName,
+ SentinelConstants.FALLBACK_TYPE);
+ }
+
+ private void checkBlock4RestTemplate(Class> blockClass, String blockMethod,
+ String beanName, String type) {
+ if (blockClass == void.class && StringUtils.isEmpty(blockMethod)) {
+ return;
+ }
+ if (blockClass != void.class && StringUtils.isEmpty(blockMethod)) {
+ logger.error(
+ "{} class attribute exists but {} method attribute is not exists in bean[{}]",
+ type, type, beanName);
+ throw new IllegalArgumentException(type + " class attribute exists but "
+ + type + " method attribute is not exists in bean[" + beanName + "]");
+ }
+ else if (blockClass == void.class && !StringUtils.isEmpty(blockMethod)) {
+ logger.error(
+ "{} method attribute exists but {} class attribute is not exists in bean[{}]",
+ type, type, beanName);
+ throw new IllegalArgumentException(type + " method attribute exists but "
+ + type + " class attribute is not exists in bean[" + beanName + "]");
+ }
+ Class[] args = new Class[] { HttpRequest.class, byte[].class,
+ ClientHttpRequestExecution.class, BlockException.class };
+ String argsStr = Arrays.toString(
+ Arrays.stream(args).map(clazz -> clazz.getSimpleName()).toArray());
+ Method foundMethod = ClassUtils.getStaticMethod(blockClass, blockMethod, args);
+ if (foundMethod == null) {
+ logger.error(
+ "{} static method can not be found in bean[{}]. The right method signature is {}#{}{}, please check your class name, method name and arguments",
+ type, beanName, blockClass.getName(), blockMethod, argsStr);
+ throw new IllegalArgumentException(type
+ + " static method can not be found in bean[" + beanName
+ + "]. The right method signature is " + blockClass.getName() + "#"
+ + blockMethod + argsStr
+ + ", please check your class name, method name and arguments");
+ }
+
+ if (!ClientHttpResponse.class.isAssignableFrom(foundMethod.getReturnType())) {
+ logger.error(
+ "{} method return value in bean[{}] is not ClientHttpResponse: {}#{}{}",
+ type, beanName, blockClass.getName(), blockMethod, argsStr);
+ throw new IllegalArgumentException(type + " method return value in bean["
+ + beanName + "] is not ClientHttpResponse: " + blockClass.getName()
+ + "#" + blockMethod + argsStr);
+ }
+ if (type.equals(SentinelConstants.BLOCK_TYPE)) {
+ BlockClassRegistry.updateBlockHandlerFor(blockClass, blockMethod,
+ foundMethod);
+ }
+ else {
+ BlockClassRegistry.updateFallbackFor(blockClass, blockMethod, foundMethod);
+ }
+ }
+
private boolean checkSentinelProtect(RootBeanDefinition beanDefinition,
Class> beanType) {
return beanType == RestTemplate.class
@@ -103,7 +183,7 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces
SentinelProtectInterceptor sentinelProtectInterceptor = applicationContext
.getBean(interceptorBeanName.toString(),
SentinelProtectInterceptor.class);
- restTemplate.getInterceptors().add(sentinelProtectInterceptor);
+ restTemplate.getInterceptors().add(0, sentinelProtectInterceptor);
}
return bean;
}
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java
index f9776d27..a6cf6dfa 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java
@@ -1,9 +1,7 @@
package org.springframework.cloud.alibaba.sentinel.custom;
import java.lang.reflect.Field;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -12,10 +10,10 @@ import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
-import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.cloud.alibaba.sentinel.SentinelConstants;
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
import org.springframework.cloud.alibaba.sentinel.datasource.SentinelDataSourceConstants;
@@ -24,7 +22,6 @@ import org.springframework.cloud.alibaba.sentinel.datasource.config.DataSourcePr
import org.springframework.cloud.alibaba.sentinel.datasource.config.NacosDataSourceProperties;
import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter;
import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter;
-import org.springframework.context.event.EventListener;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
@@ -44,29 +41,28 @@ import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
* @see JsonConverter
* @see XmlConverter
*/
-public class SentinelDataSourceHandler {
+public class SentinelDataSourceHandler implements SmartInitializingSingleton {
private static final Logger logger = LoggerFactory
.getLogger(SentinelDataSourceHandler.class);
private List dataTypeList = Arrays.asList("json", "xml");
- private List dataSourceBeanNameList = Collections
- .synchronizedList(new ArrayList<>());
+ private final String DATA_TYPE_FIELD = "dataType";
+ private final String CUSTOM_DATA_TYPE = "custom";
+ private final String CONVERTER_CLASS_FIELD = "converterClass";
- private final String DATATYPE_FIELD = "dataType";
- private final String CUSTOM_DATATYPE = "custom";
- private final String CONVERTERCLASS_FIELD = "converterClass";
+ private final DefaultListableBeanFactory beanFactory;
+
+ public SentinelDataSourceHandler(DefaultListableBeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ }
@Autowired
private SentinelProperties sentinelProperties;
- @EventListener(classes = ApplicationStartedEvent.class)
- public void buildDataSource(ApplicationStartedEvent event) throws Exception {
-
- DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) event
- .getApplicationContext().getAutowireCapableBeanFactory();
-
+ @Override
+ public void afterSingletonsInstantiated() {
// commercialization
if (!StringUtils.isEmpty(System.getProperties()
.getProperty(SentinelDataSourceConstants.NACOS_DATASOURCE_ENDPOINT))) {
@@ -101,9 +97,8 @@ public class SentinelDataSourceHandler {
AbstractDataSourceProperties abstractDataSourceProperties = dataSourceProperties
.getValidDataSourceProperties();
abstractDataSourceProperties.preCheck(dataSourceName);
- registerBean(beanFactory, abstractDataSourceProperties,
- dataSourceName + "-sentinel-" + validFields.get(0)
- + "-datasource");
+ registerBean(abstractDataSourceProperties, dataSourceName
+ + "-sentinel-" + validFields.get(0) + "-datasource");
}
catch (Exception e) {
logger.error("[Sentinel Starter] DataSource " + dataSourceName
@@ -112,8 +107,7 @@ public class SentinelDataSourceHandler {
});
}
- private void registerBean(DefaultListableBeanFactory beanFactory,
- final AbstractDataSourceProperties dataSourceProperties,
+ private void registerBean(final AbstractDataSourceProperties dataSourceProperties,
String dataSourceName) {
Map propertyMap = Arrays
@@ -132,8 +126,8 @@ public class SentinelDataSourceHandler {
e);
}
}, HashMap::putAll);
- propertyMap.put(CONVERTERCLASS_FIELD, dataSourceProperties.getConverterClass());
- propertyMap.put(DATATYPE_FIELD, dataSourceProperties.getDataType());
+ propertyMap.put(CONVERTER_CLASS_FIELD, dataSourceProperties.getConverterClass());
+ propertyMap.put(DATA_TYPE_FIELD, dataSourceProperties.getDataType());
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(dataSourceProperties.getFactoryBeanName());
@@ -141,81 +135,78 @@ public class SentinelDataSourceHandler {
propertyMap.forEach((propertyName, propertyValue) -> {
Field field = ReflectionUtils.findField(dataSourceProperties.getClass(),
propertyName);
- if (field != null) {
- if (DATATYPE_FIELD.equals(propertyName)) {
- String dataType = StringUtils
- .trimAllWhitespace(propertyValue.toString());
- if (CUSTOM_DATATYPE.equals(dataType)) {
- try {
- if (StringUtils
- .isEmpty(dataSourceProperties.getConverterClass())) {
- throw new RuntimeException(
- "[Sentinel Starter] DataSource " + dataSourceName
- + "dataType is custom, please set converter-class "
- + "property");
- }
- // construct custom Converter with 'converterClass'
- // configuration and register
- String customConvertBeanName = "sentinel-"
- + dataSourceProperties.getConverterClass();
- if (!beanFactory.containsBean(customConvertBeanName)) {
- beanFactory.registerBeanDefinition(customConvertBeanName,
- BeanDefinitionBuilder
- .genericBeanDefinition(
- Class.forName(dataSourceProperties
- .getConverterClass()))
- .getBeanDefinition());
- }
- builder.addPropertyReference("converter",
- customConvertBeanName);
- }
- catch (ClassNotFoundException e) {
- logger.error("[Sentinel Starter] DataSource " + dataSourceName
- + " handle "
- + dataSourceProperties.getClass().getSimpleName()
- + " error, class name: "
- + dataSourceProperties.getConverterClass());
- throw new RuntimeException(
- "[Sentinel Starter] DataSource " + dataSourceName
- + " handle "
- + dataSourceProperties.getClass()
- .getSimpleName()
- + " error, class name: "
- + dataSourceProperties.getConverterClass(),
- e);
- }
- }
- else {
- if (!dataTypeList.contains(StringUtils
- .trimAllWhitespace(propertyValue.toString()))) {
+ if (null == field) {
+ return;
+ }
+ if (DATA_TYPE_FIELD.equals(propertyName)) {
+ String dataType = StringUtils.trimAllWhitespace(propertyValue.toString());
+ if (CUSTOM_DATA_TYPE.equals(dataType)) {
+ try {
+ if (StringUtils
+ .isEmpty(dataSourceProperties.getConverterClass())) {
throw new RuntimeException("[Sentinel Starter] DataSource "
- + dataSourceName + " dataType: " + propertyValue
- + " is not support now. please using these types: "
- + dataTypeList.toString());
+ + dataSourceName
+ + "dataType is custom, please set converter-class "
+ + "property");
}
- // converter type now support xml or json.
- // The bean name of these converters wrapped by
- // 'sentinel-{converterType}-{ruleType}-converter'
- builder.addPropertyReference("converter",
- "sentinel-" + propertyValue.toString() + "-"
- + dataSourceProperties.getRuleType().getName()
- + "-converter");
+ // construct custom Converter with 'converterClass'
+ // configuration and register
+ String customConvertBeanName = "sentinel-"
+ + dataSourceProperties.getConverterClass();
+ if (!this.beanFactory.containsBean(customConvertBeanName)) {
+ this.beanFactory.registerBeanDefinition(customConvertBeanName,
+ BeanDefinitionBuilder
+ .genericBeanDefinition(
+ Class.forName(dataSourceProperties
+ .getConverterClass()))
+ .getBeanDefinition());
+ }
+ builder.addPropertyReference("converter", customConvertBeanName);
+ }
+ catch (ClassNotFoundException e) {
+ logger.error("[Sentinel Starter] DataSource " + dataSourceName
+ + " handle "
+ + dataSourceProperties.getClass().getSimpleName()
+ + " error, class name: "
+ + dataSourceProperties.getConverterClass());
+ throw new RuntimeException("[Sentinel Starter] DataSource "
+ + dataSourceName + " handle "
+ + dataSourceProperties.getClass().getSimpleName()
+ + " error, class name: "
+ + dataSourceProperties.getConverterClass(), e);
}
- }
- else if (CONVERTERCLASS_FIELD.equals(propertyName)) {
- return;
}
else {
- // wired properties
- Optional.ofNullable(propertyValue)
- .ifPresent(v -> builder.addPropertyValue(propertyName, v));
+ if (!dataTypeList.contains(
+ StringUtils.trimAllWhitespace(propertyValue.toString()))) {
+ throw new RuntimeException("[Sentinel Starter] DataSource "
+ + dataSourceName + " dataType: " + propertyValue
+ + " is not support now. please using these types: "
+ + dataTypeList.toString());
+ }
+ // converter type now support xml or json.
+ // The bean name of these converters wrapped by
+ // 'sentinel-{converterType}-{ruleType}-converter'
+ builder.addPropertyReference("converter",
+ "sentinel-" + propertyValue.toString() + "-"
+ + dataSourceProperties.getRuleType().getName()
+ + "-converter");
}
}
+ else if (CONVERTER_CLASS_FIELD.equals(propertyName)) {
+ return;
+ }
+ else {
+ // wired properties
+ Optional.ofNullable(propertyValue)
+ .ifPresent(v -> builder.addPropertyValue(propertyName, v));
+ }
});
- beanFactory.registerBeanDefinition(dataSourceName, builder.getBeanDefinition());
+ this.beanFactory.registerBeanDefinition(dataSourceName,
+ builder.getBeanDefinition());
// init in Spring
- AbstractDataSource newDataSource = (AbstractDataSource) beanFactory
+ AbstractDataSource newDataSource = (AbstractDataSource) this.beanFactory
.getBean(dataSourceName);
logAndCheckRuleType(newDataSource, dataSourceName,
@@ -234,7 +225,6 @@ public class SentinelDataSourceHandler {
DegradeRuleManager.register2Property(newDataSource.getProperty());
}
}
- dataSourceBeanNameList.add(dataSourceName);
}
private void logAndCheckRuleType(AbstractDataSource dataSource, String dataSourceName,
@@ -282,9 +272,4 @@ public class SentinelDataSourceHandler {
+ ruleClass.getSimpleName() + ">. Class: " + ruleConfig.getClass());
}
}
-
- public List getDataSourceBeanNameList() {
- return dataSourceBeanNameList;
- }
-
}
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java
index a1ab9e22..49bd003f 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java
@@ -17,9 +17,9 @@
package org.springframework.cloud.alibaba.sentinel.custom;
import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
-import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -29,7 +29,6 @@ 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;
@@ -37,19 +36,18 @@ import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
-import com.alibaba.csp.sentinel.util.StringUtil;
/**
* Interceptor using by SentinelRestTemplate
*
- * @author fangjian
+ * @author Jim
*/
public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor {
private static final Logger logger = LoggerFactory
.getLogger(SentinelProtectInterceptor.class);
- private SentinelRestTemplate sentinelRestTemplate;
+ private final SentinelRestTemplate sentinelRestTemplate;
public SentinelProtectInterceptor(SentinelRestTemplate sentinelRestTemplate) {
this.sentinelRestTemplate = sentinelRestTemplate;
@@ -82,18 +80,7 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor
throw new IllegalStateException(e);
}
else {
- try {
- return handleBlockException(request, body, execution,
- (BlockException) e);
- }
- catch (Exception ex) {
- if (ex instanceof IllegalStateException) {
- throw (IllegalStateException) ex;
- }
- throw new IllegalStateException(
- "sentinel handle BlockException error: " + ex.getMessage(),
- ex);
- }
+ return handleBlockException(request, body, execution, (BlockException) e);
}
}
finally {
@@ -109,84 +96,49 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor
}
private ClientHttpResponse handleBlockException(HttpRequest request, byte[] body,
- ClientHttpRequestExecution execution, BlockException ex) throws Exception {
+ ClientHttpRequestExecution execution, BlockException ex) {
Object[] args = new Object[] { request, body, execution, ex };
// handle degrade
if (isDegradeFailure(ex)) {
- Method method = extractFallbackMethod(sentinelRestTemplate.fallback(),
+ Method fallbackMethod = extractFallbackMethod(sentinelRestTemplate.fallback(),
sentinelRestTemplate.fallbackClass());
- if (method != null) {
- return (ClientHttpResponse) method.invoke(null, args);
+ if (fallbackMethod != null) {
+ return methodInvoke(fallbackMethod, args);
}
else {
return new SentinelClientHttpResponse();
}
}
- // handle block
+ // handle flow
Method blockHandler = extractBlockHandlerMethod(
sentinelRestTemplate.blockHandler(),
sentinelRestTemplate.blockHandlerClass());
if (blockHandler != null) {
- return (ClientHttpResponse) blockHandler.invoke(null, args);
+ return methodInvoke(blockHandler, args);
}
else {
return new SentinelClientHttpResponse();
}
}
+ private ClientHttpResponse methodInvoke(Method method, Object... args) {
+ try {
+ return (ClientHttpResponse) method.invoke(null, args);
+ }
+ catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
private Method extractFallbackMethod(String fallback, Class> fallbackClass) {
- if (StringUtil.isBlank(fallback) || fallbackClass == void.class) {
- return null;
- }
- Method cachedMethod = BlockClassRegistry.lookupFallback(fallbackClass, fallback);
- Class[] args = new Class[] { HttpRequest.class, byte[].class,
- ClientHttpRequestExecution.class, BlockException.class };
- if (cachedMethod == null) {
- cachedMethod = ClassUtils.getStaticMethod(fallbackClass, fallback, args);
- if (cachedMethod != null) {
- if (!ClientHttpResponse.class
- .isAssignableFrom(cachedMethod.getReturnType())) {
- throw new IllegalStateException(String.format(
- "the return type of method [%s] in class [%s] is not ClientHttpResponse in degrade",
- cachedMethod.getName(), fallbackClass.getCanonicalName()));
- }
- BlockClassRegistry.updateFallbackFor(fallbackClass, fallback,
- cachedMethod);
- }
- else {
- throw new IllegalStateException(String.format(
- "Cannot find method [%s] in class [%s] with parameters %s in degrade",
- fallback, fallbackClass.getCanonicalName(), Arrays.asList(args)));
- }
- }
- return cachedMethod;
+ return BlockClassRegistry.lookupFallback(fallbackClass, fallback);
}
private Method extractBlockHandlerMethod(String block, Class> blockClass) {
- if (StringUtil.isBlank(block) || blockClass == void.class) {
- return null;
- }
- Method cachedMethod = BlockClassRegistry.lookupBlockHandler(blockClass, block);
- Class[] args = new Class[] { HttpRequest.class, byte[].class,
- ClientHttpRequestExecution.class, BlockException.class };
- if (cachedMethod == null) {
- cachedMethod = ClassUtils.getStaticMethod(blockClass, block, args);
- if (cachedMethod != null) {
- if (!ClientHttpResponse.class
- .isAssignableFrom(cachedMethod.getReturnType())) {
- throw new IllegalStateException(String.format(
- "the return type of method [%s] in class [%s] is not ClientHttpResponse in flow control",
- cachedMethod.getName(), blockClass.getCanonicalName()));
- }
- BlockClassRegistry.updateBlockHandlerFor(blockClass, block, cachedMethod);
- }
- else {
- throw new IllegalStateException(String.format(
- "Cannot find method [%s] in class [%s] with parameters %s in flow control",
- block, blockClass.getCanonicalName(), Arrays.asList(args)));
- }
- }
- return cachedMethod;
+ return BlockClassRegistry.lookupBlockHandler(blockClass, block);
}
private boolean isDegradeFailure(BlockException ex) {
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpoint.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpoint.java
index 03b6c0f8..d2500768 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpoint.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpoint.java
@@ -17,25 +17,20 @@
package org.springframework.cloud.alibaba.sentinel.endpoint;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
-import org.springframework.cloud.alibaba.sentinel.custom.SentinelDataSourceHandler;
-import org.springframework.context.ApplicationContext;
-import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
-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.adapter.servlet.config.WebServletConfig;
+import com.alibaba.csp.sentinel.config.SentinelConfig;
+import com.alibaba.csp.sentinel.log.LogBase;
+import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
-import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
-import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
+import com.alibaba.csp.sentinel.transport.config.TransportConfig;
/**
* Endpoint for Sentinel, contains ans properties and rules
@@ -44,43 +39,39 @@ import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
@Endpoint(id = "sentinel")
public class SentinelEndpoint {
- @Autowired
- private SentinelProperties sentinelProperties;
+ private final SentinelProperties sentinelProperties;
- @Autowired
- private SentinelDataSourceHandler dataSourceHandler;
-
- @Autowired
- private ApplicationContext applicationContext;
+ public SentinelEndpoint(SentinelProperties sentinelProperties) {
+ this.sentinelProperties = sentinelProperties;
+ }
@ReadOperation
public Map invoke() {
final Map result = new HashMap<>();
+ if (sentinelProperties.isEnabled()) {
- List flowRules = FlowRuleManager.getRules();
- List degradeRules = DegradeRuleManager.getRules();
- List systemRules = SystemRuleManager.getRules();
- List paramFlowRules = ParamFlowRuleManager.getRules();
- result.put("properties", sentinelProperties);
- result.put("FlowRules", flowRules);
- result.put("DegradeRules", degradeRules);
- result.put("SystemRules", systemRules);
- result.put("ParamFlowRule", paramFlowRules);
- result.put("datasources", new HashMap());
- dataSourceHandler.getDataSourceBeanNameList().forEach(dataSourceBeanName -> {
- ReadableDataSource dataSource = applicationContext.getBean(dataSourceBeanName,
- ReadableDataSource.class);
- try {
- ((HashMap) result.get("datasources")).put(dataSourceBeanName,
- dataSource.loadConfig());
- }
- catch (Exception e) {
- ((HashMap) result.get("datasources")).put(dataSourceBeanName,
- "load error: " + e.getMessage());
- }
-
- });
+ result.put("logDir", LogBase.getLogBaseDir());
+ result.put("logUsePid", LogBase.isLogNameUsePid());
+ result.put("blockPage", WebServletConfig.getBlockPage());
+ result.put("metricsFileSize", SentinelConfig.singleMetricFileSize());
+ result.put("metricsFileCharset", SentinelConfig.charset());
+ result.put("totalMetricsFileCount", SentinelConfig.totalMetricFileCount());
+ result.put("consoleServer", TransportConfig.getConsoleServer());
+ result.put("clientIp", TransportConfig.getHeartbeatClientIp());
+ result.put("heartbeatIntervalMs", TransportConfig.getHeartbeatIntervalMs());
+ result.put("clientPort", TransportConfig.getPort());
+ result.put("coldFactor", sentinelProperties.getFlow().getColdFactor());
+ result.put("filter", sentinelProperties.getFilter());
+ result.put("datasource", sentinelProperties.getDatasource());
+ final Map rules = new HashMap<>();
+ result.put("rules", rules);
+ rules.put("flowRules", FlowRuleManager.getRules());
+ rules.put("degradeRules", SystemRuleManager.getRules());
+ rules.put("systemRules", SystemRuleManager.getRules());
+ rules.put("authorityRule", AuthorityRuleManager.getRules());
+ rules.put("paramFlowRule", ParamFlowRuleManager.getRules());
+ }
return result;
}
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpointAutoConfiguration.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpointAutoConfiguration.java
index 098822a2..7c9b5d1c 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpointAutoConfiguration.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpointAutoConfiguration.java
@@ -34,8 +34,8 @@ public class SentinelEndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
- public SentinelEndpoint sentinelEndPoint() {
- return new SentinelEndpoint();
+ public SentinelEndpoint sentinelEndPoint(SentinelProperties sentinelProperties) {
+ return new SentinelEndpoint(sentinelProperties);
}
}
diff --git a/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index d05b0863..b445fc6a 100644
--- a/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -18,6 +18,11 @@
"defaultValue": "8719",
"description": "sentinel api port."
},
+ {
+ "name": "spring.cloud.sentinel.transport.clientIp",
+ "type": "java.lang.String",
+ "description": "sentinel client ip connect to dashboard."
+ },
{
"name": "spring.cloud.sentinel.transport.dashboard",
"type": "java.lang.String",
diff --git a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java
index 1ba2c170..2cd3b53a 100644
--- a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java
+++ b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java
@@ -16,76 +16,268 @@
package org.springframework.cloud.alibaba.sentinel;
-import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+import java.util.Arrays;
+import java.util.Map;
+
+import org.junit.Before;
import org.junit.Test;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
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.cloud.alibaba.sentinel.endpoint.SentinelEndpoint;
+import org.springframework.cloud.alibaba.sentinel.rest.SentinelClientHttpResponse;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
+import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig;
+import com.alibaba.csp.sentinel.config.SentinelConfig;
+import com.alibaba.csp.sentinel.log.LogBase;
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 com.alibaba.csp.sentinel.transport.config.TransportConfig;
/**
- * @author fangjian
+ * @author Jim
* @author jiashuai.xie
*/
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = {
+ SentinelAutoConfigurationTests.TestConfig.class }, properties = {
+ "spring.cloud.sentinel.filter.order=123",
+ "spring.cloud.sentinel.filter.urlPatterns=/*,/test",
+ "spring.cloud.sentinel.metric.fileSingleSize=9999",
+ "spring.cloud.sentinel.metric.fileTotalCount=100",
+ "spring.cloud.sentinel.servlet.blockPage=/error",
+ "spring.cloud.sentinel.flow.coldFactor=3",
+ "spring.cloud.sentinel.eager=true",
+ "spring.cloud.sentinel.log.switchPid=true",
+ "spring.cloud.sentinel.transport.dashboard=http://localhost:8080",
+ "spring.cloud.sentinel.transport.port=9999",
+ "spring.cloud.sentinel.transport.clientIp=1.1.1.1",
+ "spring.cloud.sentinel.transport.heartbeatIntervalMs=20000" }, webEnvironment = RANDOM_PORT)
public class SentinelAutoConfigurationTests {
- private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
- .withConfiguration(AutoConfigurations.of(SentinelAutoConfiguration.class,
- SentinelWebAutoConfiguration.class, SentinelTestConfiguration.class))
- .withPropertyValues("spring.cloud.sentinel.transport.port=8888")
- .withPropertyValues("spring.cloud.sentinel.filter.order=123")
- .withPropertyValues("spring.cloud.sentinel.filter.urlPatterns=/*,/test");
+ @Autowired
+ private SentinelProperties sentinelProperties;
+
+ @Autowired
+ private FilterRegistrationBean filterRegistrationBean;
+
+ @Autowired
+ private SentinelBeanPostProcessor sentinelBeanPostProcessor;
+
+ @Autowired
+ private RestTemplate restTemplate;
+
+ @Autowired
+ private RestTemplate restTemplateWithBlockClass;
+
+ @Autowired
+ private RestTemplate restTemplateWithoutBlockClass;
+
+ @Autowired
+ private RestTemplate restTemplateWithFallbackClass;
+
+ @LocalServerPort
+ private int port;
+
+ private String url = "http://localhost:" + port;
+
+ @Before
+ public void setUp() {
+ FlowRule rule = new FlowRule();
+ rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ rule.setCount(0);
+ rule.setResource(url);
+ rule.setLimitApp("default");
+ rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
+ rule.setStrategy(RuleConstant.STRATEGY_DIRECT);
+ FlowRuleManager.loadRules(Arrays.asList(rule));
+
+ DegradeRule degradeRule = new DegradeRule();
+ degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
+ degradeRule.setResource(url + "/test");
+ degradeRule.setCount(0);
+ degradeRule.setTimeWindow(60);
+ DegradeRuleManager.loadRules(Arrays.asList(degradeRule));
+ }
+
+ @Test
+ public void contextLoads() throws Exception {
+ assertNotNull("FilterRegistrationBean was not created", filterRegistrationBean);
+ assertNotNull("SentinelProperties was not created", sentinelProperties);
+ assertNotNull("SentinelBeanPostProcessor was not created",
+ sentinelBeanPostProcessor);
+
+ checkSentinelLog();
+ checkSentinelEager();
+ checkSentinelTransport();
+ checkSentinelColdFactor();
+ checkSentinelMetric();
+ checkSentinelFilter();
+ checkEndpoint();
+ }
+
+ private void checkEndpoint() {
+ SentinelEndpoint sentinelEndpoint = new SentinelEndpoint(sentinelProperties);
+ Map map = sentinelEndpoint.invoke();
+ assertEquals("Endpoint Sentinel log pid was wrong", true, map.get("logUsePid"));
+ assertEquals("Endpoint Sentinel transport console server was wrong",
+ "http://localhost:8080", map.get("consoleServer"));
+ assertEquals("Endpoint Sentinel transport port was wrong", "9999",
+ map.get("clientPort"));
+ assertEquals("Endpoint Sentinel transport heartbeatIntervalMs was wrong", 20000l,
+ map.get("heartbeatIntervalMs"));
+ assertEquals("Endpoint Sentinel transport clientIp was wrong", "1.1.1.1",
+ map.get("clientIp"));
+ assertEquals("Endpoint Sentinel metric file size was wrong", 9999l,
+ map.get("metricsFileSize"));
+ assertEquals("Endpoint Sentinel metric file count was wrong", 100,
+ map.get("totalMetricsFileCount"));
+ assertEquals("Endpoint Sentinel metric file charset was wrong", "UTF-8",
+ map.get("metricsFileCharset"));
+ assertEquals("Endpoint Sentinel block page was wrong", "/error",
+ map.get("blockPage"));
+ }
+
+ private void checkSentinelFilter() {
+ assertEquals("SentinelProperties filter order was wrong", 123,
+ sentinelProperties.getFilter().getOrder());
+ assertEquals("SentinelProperties filter url pattern size was wrong", 2,
+ sentinelProperties.getFilter().getUrlPatterns().size());
+ assertEquals("SentinelProperties filter url pattern item was wrong", "/*",
+ sentinelProperties.getFilter().getUrlPatterns().get(0));
+ assertEquals("SentinelProperties filter url pattern item was wrong", "/test",
+ sentinelProperties.getFilter().getUrlPatterns().get(1));
+ }
+
+ private void checkSentinelMetric() {
+ assertEquals("SentinelProperties metric charset was wrong", "UTF-8",
+ sentinelProperties.getMetric().getCharset());
+ assertEquals("SentinelProperties metric file single size was wrong", "9999",
+ sentinelProperties.getMetric().getFileSingleSize());
+ assertEquals("SentinelProperties metric file total count was wrong", "100",
+ sentinelProperties.getMetric().getFileTotalCount());
+ }
+
+ private void checkSentinelColdFactor() {
+ assertEquals("SentinelProperties coldFactor was wrong", "3",
+ sentinelProperties.getFlow().getColdFactor());
+ }
+
+ private void checkSentinelTransport() {
+ assertEquals("SentinelProperties transport port was wrong", "9999",
+ sentinelProperties.getTransport().getPort());
+ assertEquals("SentinelProperties transport dashboard was wrong",
+ "http://localhost:8080",
+ sentinelProperties.getTransport().getDashboard());
+ assertEquals("SentinelProperties transport clientIp was wrong", "1.1.1.1",
+ sentinelProperties.getTransport().getClientIp());
+ assertEquals("SentinelProperties transport heartbeatIntervalMs was wrong",
+ "20000", sentinelProperties.getTransport().getHeartbeatIntervalMs());
+ }
+
+ private void checkSentinelEager() {
+ assertEquals("SentinelProperties eager was wrong", true,
+ sentinelProperties.isEager());
+ }
+
+ private void checkSentinelLog() {
+ assertEquals("SentinelProperties log file pid was wrong", true,
+ sentinelProperties.getLog().isSwitchPid());
+ }
@Test
public void testFilter() {
- this.contextRunner.run(context -> {
- assertThat(context.getBean("servletRequestListener")
- .getClass() == FilterRegistrationBean.class).isTrue();
+ assertEquals("Sentinel Filter order was wrong", filterRegistrationBean.getOrder(),
+ 123);
+ assertEquals("Sentinel Filter url-pattern was wrong",
+ filterRegistrationBean.getUrlPatterns().size(), 2);
+ }
+
+ @Test
+ public void testSentinelSystemProperties() {
+ assertEquals("Sentinel log pid was wrong", true, LogBase.isLogNameUsePid());
+ assertEquals("Sentinel transport console server was wrong",
+ "http://localhost:8080", TransportConfig.getConsoleServer());
+ assertEquals("Sentinel transport port was wrong", "9999",
+ TransportConfig.getPort());
+ assertEquals("Sentinel transport heartbeatIntervalMs was wrong", 20000l,
+ TransportConfig.getHeartbeatIntervalMs().longValue());
+ assertEquals("Sentinel transport clientIp was wrong", "1.1.1.1",
+ TransportConfig.getHeartbeatClientIp());
+ assertEquals("Sentinel metric file size was wrong", 9999,
+ SentinelConfig.singleMetricFileSize());
+ assertEquals("Sentinel metric file count was wrong", 100,
+ SentinelConfig.totalMetricFileCount());
+ assertEquals("Sentinel metric file charset was wrong", "UTF-8",
+ SentinelConfig.charset());
+ assertEquals("Sentinel block page was wrong", "/error",
+ WebServletConfig.getBlockPage());
+ }
+
+ @Test
+ public void testFlowRestTemplate() {
+ assertEquals("RestTemplate interceptors size was wrong", 2,
+ restTemplate.getInterceptors().size());
+ assertEquals("RestTemplateWithBlockClass interceptors size was wrong", 1,
+ restTemplateWithBlockClass.getInterceptors().size());
+ ResponseEntity responseEntityBlock = restTemplateWithBlockClass.getForEntity(url,
+ String.class);
+ assertEquals("RestTemplateWithBlockClass Sentinel Block Message was wrong",
+ "Oops", responseEntityBlock.getBody());
+ assertEquals(
+ "RestTemplateWithBlockClass Sentinel Block Http Status Code was wrong",
+ HttpStatus.OK, responseEntityBlock.getStatusCode());
+ ResponseEntity responseEntityRaw = restTemplate.getForEntity(url, String.class);
+ assertEquals("RestTemplate Sentinel Block Message was wrong",
+ "RestTemplate request block by sentinel", responseEntityRaw.getBody());
+ assertEquals("RestTemplate Sentinel Block Http Status Code was wrong",
+ HttpStatus.OK, responseEntityRaw.getStatusCode());
+ }
+
+ @Test
+ public void testNormalRestTemplate() {
+ assertEquals("RestTemplateWithoutBlockClass interceptors size was wrong", 0,
+ restTemplateWithoutBlockClass.getInterceptors().size());
+ assertThatExceptionOfType(RestClientException.class).isThrownBy(() -> {
+ restTemplateWithoutBlockClass.getForEntity(url, String.class);
});
}
@Test
- public void testBeanPostProcessor() {
- this.contextRunner.run(context -> {
- assertThat(context.getBean("sentinelBeanPostProcessor")
- .getClass() == SentinelBeanPostProcessor.class).isTrue();
- });
- }
-
- @Test
- public void testProperties() {
- this.contextRunner.run(context -> {
- SentinelProperties sentinelProperties = context
- .getBean(SentinelProperties.class);
- assertThat(sentinelProperties.getTransport().getPort()).isEqualTo("8888");
- assertThat(sentinelProperties.getFilter().getUrlPatterns().size())
- .isEqualTo(2);
- assertThat(sentinelProperties.getFilter().getUrlPatterns().get(0))
- .isEqualTo("/*");
- assertThat(sentinelProperties.getFilter().getUrlPatterns().get(1))
- .isEqualTo("/test");
- });
- }
-
- @Test
- public void testRestTemplate() {
- this.contextRunner.run(context -> {
- 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);
- });
+ public void testFallbackRestTemplate() {
+ ResponseEntity responseEntity = restTemplateWithFallbackClass
+ .getForEntity(url + "/test", String.class);
+ assertEquals("RestTemplateWithFallbackClass Sentinel Message was wrong",
+ "Oops fallback", responseEntity.getBody());
+ assertEquals("RestTemplateWithFallbackClass Sentinel Http Status Code was wrong",
+ HttpStatus.OK, responseEntity.getStatusCode());
}
@Configuration
@@ -94,7 +286,9 @@ public class SentinelAutoConfigurationTests {
@Bean
@SentinelRestTemplate
RestTemplate restTemplate() {
- return new RestTemplate();
+ RestTemplate restTemplate = new RestTemplate();
+ restTemplate.getInterceptors().add(mock(ClientHttpRequestInterceptor.class));
+ return restTemplate;
}
@Bean
@@ -103,12 +297,38 @@ public class SentinelAutoConfigurationTests {
return new RestTemplate();
}
+ @Bean
+ @SentinelRestTemplate(fallbackClass = ExceptionUtil.class, fallback = "fallbackException")
+ RestTemplate restTemplateWithFallbackClass() {
+ return new RestTemplate();
+ }
+
+ @Bean
+ RestTemplate restTemplateWithoutBlockClass() {
+ return new RestTemplate();
+ }
+
}
- static class ExceptionUtil {
- public static void handleException(BlockException ex) {
+ public static class ExceptionUtil {
+ public static SentinelClientHttpResponse handleException(HttpRequest request,
+ byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
System.out.println("Oops: " + ex.getClass().getCanonicalName());
+ return new SentinelClientHttpResponse("Oops");
+ }
+
+ public static SentinelClientHttpResponse fallbackException(HttpRequest request,
+ byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
+ System.out.println("Oops: " + ex.getClass().getCanonicalName());
+ return new SentinelClientHttpResponse("Oops fallback");
}
}
+ @Configuration
+ @EnableAutoConfiguration
+ @ImportAutoConfiguration({ SentinelAutoConfiguration.class,
+ SentinelWebAutoConfiguration.class, SentinelTestConfiguration.class })
+ public static class TestConfig {
+ }
+
}
diff --git a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelBeanAutowiredTests.java b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelBeanAutowiredTests.java
new file mode 100644
index 00000000..b22f17e8
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelBeanAutowiredTests.java
@@ -0,0 +1,134 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
+import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
+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.util.FilterUtil;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/**
+ * @author Jim
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = { SentinelBeanAutowiredTests.TestConfig.class }, properties = {
+ "spring.cloud.sentinel.filter.order=111" })
+public class SentinelBeanAutowiredTests {
+
+ @Autowired
+ private UrlCleaner urlCleaner;
+
+ @Autowired
+ private UrlBlockHandler urlBlockHandler;
+
+ @Autowired
+ private RequestOriginParser requestOriginParser;
+
+ @Autowired
+ private SentinelProperties sentinelProperties;
+
+ @Test
+ public void contextLoads() throws Exception {
+ assertNotNull("UrlCleaner was not created", urlCleaner);
+ assertNotNull("UrlBlockHandler was not created", urlBlockHandler);
+ assertNotNull("RequestOriginParser was not created", requestOriginParser);
+ assertNotNull("SentinelProperties was not created", sentinelProperties);
+
+ checkUrlPattern();
+ }
+
+ private void checkUrlPattern() {
+ assertEquals("SentinelProperties filter order was wrong", 111,
+ sentinelProperties.getFilter().getOrder());
+ assertEquals("SentinelProperties filter url pattern size was wrong", 1,
+ sentinelProperties.getFilter().getUrlPatterns().size());
+ assertEquals("SentinelProperties filter url pattern was wrong", "/*",
+ sentinelProperties.getFilter().getUrlPatterns().get(0));
+ }
+
+ @Test
+ public void testBeanAutowired() {
+ assertEquals("UrlCleaner was not autowired", urlCleaner,
+ WebCallbackManager.getUrlCleaner());
+ assertEquals("UrlBlockHandler was not autowired", urlBlockHandler,
+ WebCallbackManager.getUrlBlockHandler());
+ assertEquals("RequestOriginParser was not autowired", requestOriginParser,
+ WebCallbackManager.getRequestOriginParser());
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ @ImportAutoConfiguration({ SentinelAutoConfiguration.class,
+ SentinelWebAutoConfiguration.class })
+ public static class TestConfig {
+
+ @Bean
+ public UrlCleaner urlCleaner() {
+ return new UrlCleaner() {
+ @Override
+ public String clean(String s) {
+ return s;
+ }
+ };
+ }
+
+ @Bean
+ public RequestOriginParser requestOriginParser() {
+ return new RequestOriginParser() {
+ @Override
+ public String parseOrigin(HttpServletRequest httpServletRequest) {
+ return httpServletRequest.getRemoteAddr();
+ }
+ };
+ }
+
+ @Bean
+ public UrlBlockHandler urlBlockHandler() {
+ return new UrlBlockHandler() {
+ @Override
+ public void blocked(HttpServletRequest httpServletRequest,
+ HttpServletResponse httpServletResponse, BlockException e)
+ throws IOException {
+ FilterUtil.blockRequest(httpServletRequest, httpServletResponse);
+ }
+ };
+ }
+
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelDataSourceTests.java b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelDataSourceTests.java
new file mode 100644
index 00000000..13a6e322
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelDataSourceTests.java
@@ -0,0 +1,106 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration;
+import org.springframework.cloud.alibaba.sentinel.datasource.RuleType;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * @author Jim
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = { SentinelDataSourceTests.TestConfig.class }, properties = {
+ "spring.cloud.sentinel.datasource.ds1.file.file=classpath: flowrule.json",
+ "spring.cloud.sentinel.datasource.ds1.file.data-type=json",
+ "spring.cloud.sentinel.datasource.ds1.file.rule-type=flow",
+
+ "spring.cloud.sentinel.datasource.ds2.file.file=classpath: degraderule.json",
+ "spring.cloud.sentinel.datasource.ds2.file.data-type=json",
+ "spring.cloud.sentinel.datasource.ds2.file.rule-type=degrade",
+
+ "spring.cloud.sentinel.datasource.ds3.file.file=classpath: authority.json",
+ "spring.cloud.sentinel.datasource.ds3.file.rule-type=authority",
+
+ "spring.cloud.sentinel.datasource.ds4.file.file=classpath: system.json",
+ "spring.cloud.sentinel.datasource.ds4.file.rule-type=system",
+
+ "spring.cloud.sentinel.datasource.ds5.file.file=classpath: param-flow.json",
+ "spring.cloud.sentinel.datasource.ds5.file.data-type=custom",
+ "spring.cloud.sentinel.datasource.ds5.file.converter-class=org.springframework.cloud.alibaba.sentinel.TestConverter",
+ "spring.cloud.sentinel.datasource.ds5.file.rule-type=param-flow" })
+public class SentinelDataSourceTests {
+
+ @Autowired
+ private SentinelProperties sentinelProperties;
+
+ @Test
+ public void contextLoads() throws Exception {
+ assertNotNull("SentinelProperties was not created", sentinelProperties);
+
+ checkUrlPattern();
+ }
+
+ private void checkUrlPattern() {
+ assertEquals("SentinelProperties filter order was wrong", Integer.MIN_VALUE,
+ sentinelProperties.getFilter().getOrder());
+ assertEquals("SentinelProperties filter url pattern size was wrong", 1,
+ sentinelProperties.getFilter().getUrlPatterns().size());
+ assertEquals("SentinelProperties filter url pattern was wrong", "/*",
+ sentinelProperties.getFilter().getUrlPatterns().get(0));
+ }
+
+ @Test
+ public void testDataSource() {
+ assertEquals("DataSource size was wrong", 5,
+ sentinelProperties.getDatasource().size());
+ assertNull("DataSource ds1 apollo is not null",
+ sentinelProperties.getDatasource().get("ds1").getApollo());
+ assertNull("DataSource ds1 nacos is not null",
+ sentinelProperties.getDatasource().get("ds1").getNacos());
+ assertNull("DataSource ds1 zk is not null",
+ sentinelProperties.getDatasource().get("ds1").getZk());
+ assertNotNull("DataSource ds1 file is null",
+ sentinelProperties.getDatasource().get("ds1").getFile());
+
+ assertEquals("DataSource ds1 file dataType was wrong", "json",
+ sentinelProperties.getDatasource().get("ds1").getFile().getDataType());
+ assertEquals("DataSource ds1 file ruleType was wrong", RuleType.FLOW,
+ sentinelProperties.getDatasource().get("ds1").getFile().getRuleType());
+
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ @ImportAutoConfiguration({ SentinelAutoConfiguration.class,
+ SentinelWebAutoConfiguration.class })
+ public static class TestConfig {
+
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelFeignTests.java b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelFeignTests.java
new file mode 100644
index 00000000..3f56f700
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelFeignTests.java
@@ -0,0 +1,178 @@
+/*
+ * 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;
+
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Arrays;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.alibaba.sentinel.feign.SentinelFeignAutoConfiguration;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+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;
+
+/**
+ * @author Jim
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = { SentinelFeignTests.TestConfig.class }, properties = {
+ "feign.sentinel.enabled=true" })
+public class SentinelFeignTests {
+
+ @Autowired
+ private EchoService echoService;
+
+ @Autowired
+ private FooService fooService;
+
+ @Autowired
+ private BarService barService;
+
+ @Before
+ public void setUp() {
+ FlowRule rule1 = new FlowRule();
+ rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ rule1.setCount(0);
+ rule1.setResource("GET:http://test-service/echo/{str}");
+ rule1.setLimitApp("default");
+ rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
+ rule1.setStrategy(RuleConstant.STRATEGY_DIRECT);
+ FlowRule rule2 = new FlowRule();
+ rule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ rule2.setCount(0);
+ rule2.setResource("GET:http://foo-service/echo/{str}");
+ rule2.setLimitApp("default");
+ rule2.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
+ rule2.setStrategy(RuleConstant.STRATEGY_DIRECT);
+ FlowRule rule3 = new FlowRule();
+ rule3.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ rule3.setCount(0);
+ rule3.setResource("GET:http://bar-service/bar");
+ rule3.setLimitApp("default");
+ rule3.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
+ rule3.setStrategy(RuleConstant.STRATEGY_DIRECT);
+ FlowRuleManager.loadRules(Arrays.asList(rule1, rule2, rule3));
+ }
+
+ @Test
+ public void contextLoads() throws Exception {
+ assertNotNull("EchoService was not created", echoService);
+ assertNotNull("FooService was not created", fooService);
+ }
+
+ @Test
+ public void testFeignClient() {
+ assertEquals("Sentinel Feign Client fallback success", "echo fallback",
+ echoService.echo("test"));
+ assertEquals("Sentinel Feign Client fallbackFactory success", "foo fallback",
+ fooService.echo("test"));
+ assertThatExceptionOfType(Exception.class).isThrownBy(() -> {
+ barService.bar();
+ });
+
+ assertNotEquals("ToString method invoke was not in SentinelInvocationHandler",
+ echoService.toString(), fooService.toString());
+ assertNotEquals("HashCode method invoke was not in SentinelInvocationHandler",
+ echoService.hashCode(), fooService.hashCode());
+ assertFalse("Equals method invoke was not in SentinelInvocationHandler",
+ echoService.equals(fooService));
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ @ImportAutoConfiguration({ SentinelFeignAutoConfiguration.class })
+ @EnableFeignClients
+ public static class TestConfig {
+
+ @Bean
+ public EchoServiceFallback echoServiceFallback() {
+ return new EchoServiceFallback();
+ }
+
+ @Bean
+ public CustomFallbackFactory customFallbackFactory() {
+ return new CustomFallbackFactory();
+ }
+
+ }
+
+ @FeignClient(value = "test-service", fallback = EchoServiceFallback.class)
+ public interface EchoService {
+ @RequestMapping(path = "echo/{str}")
+ String echo(@RequestParam("str") String param);
+ }
+
+ @FeignClient(value = "foo-service", fallbackFactory = CustomFallbackFactory.class)
+ public interface FooService {
+ @RequestMapping(path = "echo/{str}")
+ String echo(@RequestParam("str") String param);
+ }
+
+ @FeignClient(value = "bar-service")
+ public interface BarService {
+ @RequestMapping(path = "bar")
+ String bar();
+ }
+
+ public static class EchoServiceFallback implements EchoService {
+
+ @Override
+ public String echo(@RequestParam("str") String param) {
+ return "echo fallback";
+ }
+
+ }
+
+ public static class FooServiceFallback implements FooService {
+
+ @Override
+ public String echo(@RequestParam("str") String param) {
+ return "foo fallback";
+ }
+ }
+
+ public static class CustomFallbackFactory
+ implements feign.hystrix.FallbackFactory {
+
+ private FooService fooService = new FooServiceFallback();
+
+ @Override
+ public FooService create(Throwable throwable) {
+ return fooService;
+ }
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelRestTemplateTests.java b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelRestTemplateTests.java
new file mode 100644
index 00000000..f43af925
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelRestTemplateTests.java
@@ -0,0 +1,248 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
+import org.springframework.cloud.alibaba.sentinel.custom.SentinelBeanPostProcessor;
+import org.springframework.cloud.alibaba.sentinel.rest.SentinelClientHttpResponse;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.web.client.RestTemplate;
+
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/**
+ * @author Jim
+ */
+public class SentinelRestTemplateTests {
+
+ @Test(expected = BeanCreationException.class)
+ public void testFbkMethod() {
+ new AnnotationConfigApplicationContext(TestConfig1.class);
+ }
+
+ @Test(expected = BeanCreationException.class)
+ public void testFbkClass() {
+ new AnnotationConfigApplicationContext(TestConfig2.class);
+ }
+
+ @Test(expected = BeanCreationException.class)
+ public void testblkMethod() {
+ new AnnotationConfigApplicationContext(TestConfig3.class);
+ }
+
+ @Test(expected = BeanCreationException.class)
+ public void testblkClass() {
+ new AnnotationConfigApplicationContext(TestConfig4.class);
+ }
+
+ @Test
+ public void testNormal() {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
+ TestConfig5.class);
+ assertEquals("RestTemplate size was wrong", 1,
+ context.getBeansOfType(RestTemplate.class).size());
+ }
+
+ @Test(expected = BeanCreationException.class)
+ public void testBlkMethodExists() {
+ new AnnotationConfigApplicationContext(TestConfig6.class);
+ }
+
+ @Test(expected = BeanCreationException.class)
+ public void testFbkMethodExists() {
+ new AnnotationConfigApplicationContext(TestConfig7.class);
+ }
+
+ @Test(expected = BeanCreationException.class)
+ public void testBlkReturnValue() {
+ new AnnotationConfigApplicationContext(TestConfig8.class);
+ }
+
+ @Test(expected = BeanCreationException.class)
+ public void testFbkReturnValue() {
+ new AnnotationConfigApplicationContext(TestConfig9.class);
+ }
+
+ @Configuration
+ public static class TestConfig1 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(fallback = "fbk")
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ @Configuration
+ public static class TestConfig2 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(fallbackClass = ExceptionUtil.class)
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ @Configuration
+ public static class TestConfig3 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(blockHandler = "blk")
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ @Configuration
+ public static class TestConfig4 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(blockHandlerClass = ExceptionUtil.class)
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ @Configuration
+ public static class TestConfig5 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(blockHandlerClass = SentinelRestTemplateTests.ExceptionUtil.class, blockHandler = "handleException", fallbackClass = SentinelRestTemplateTests.ExceptionUtil.class, fallback = "fallbackException")
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ @Configuration
+ public static class TestConfig6 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(blockHandlerClass = SentinelRestTemplateTests.ExceptionUtil.class, blockHandler = "handleException1")
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ @Configuration
+ public static class TestConfig7 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(fallbackClass = SentinelRestTemplateTests.ExceptionUtil.class, fallback = "fallbackException1")
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ @Configuration
+ public static class TestConfig8 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(blockHandlerClass = SentinelRestTemplateTests.ExceptionUtil.class, blockHandler = "handleException2")
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ @Configuration
+ public static class TestConfig9 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(fallbackClass = SentinelRestTemplateTests.ExceptionUtil.class, fallback = "fallbackException2")
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ public static class ExceptionUtil {
+ public static SentinelClientHttpResponse handleException(HttpRequest request,
+ byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
+ System.out.println("Oops: " + ex.getClass().getCanonicalName());
+ return new SentinelClientHttpResponse("Oops");
+ }
+
+ public static void handleException2(HttpRequest request, byte[] body,
+ ClientHttpRequestExecution execution, BlockException ex) {
+ System.out.println("Oops: " + ex.getClass().getCanonicalName());
+ }
+
+ public static SentinelClientHttpResponse fallbackException(HttpRequest request,
+ byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
+ System.out.println("Oops: " + ex.getClass().getCanonicalName());
+ return new SentinelClientHttpResponse("Oops fallback");
+ }
+
+ public static void fallbackException2(HttpRequest request, byte[] body,
+ ClientHttpRequestExecution execution, BlockException ex) {
+ System.out.println("Oops: " + ex.getClass().getCanonicalName());
+ }
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/TestConverter.java b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/TestConverter.java
new file mode 100644
index 00000000..e7247a41
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/TestConverter.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.util.List;
+
+import com.alibaba.csp.sentinel.datasource.Converter;
+import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * @author Jim
+ */
+public class TestConverter implements Converter> {
+ private ObjectMapper objectMapper = new ObjectMapper();
+
+ @Override
+ public List convert(String s) {
+ try {
+ return objectMapper.readValue(s, new TypeReference>() {
+ });
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+}
diff --git a/spring-cloud-alibaba-sentinel/src/test/resources/authority.json b/spring-cloud-alibaba-sentinel/src/test/resources/authority.json
new file mode 100644
index 00000000..3fb4b249
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/resources/authority.json
@@ -0,0 +1,17 @@
+[
+ {
+ "resource": "good",
+ "limitApp": "abc",
+ "strategy": 0
+ },
+ {
+ "resource": "bad",
+ "limitApp": "bcd",
+ "strategy": 1
+ },
+ {
+ "resource": "terrible",
+ "limitApp": "aaa",
+ "strategy": 1
+ }
+]
diff --git a/spring-cloud-alibaba-sentinel/src/test/resources/degraderule.json b/spring-cloud-alibaba-sentinel/src/test/resources/degraderule.json
new file mode 100644
index 00000000..5977c5fc
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/resources/degraderule.json
@@ -0,0 +1,16 @@
+[
+ {
+ "resource": "abc0",
+ "count": 20.0,
+ "grade": 0,
+ "passCount": 0,
+ "timeWindow": 10
+ },
+ {
+ "resource": "abc1",
+ "count": 15.0,
+ "grade": 0,
+ "passCount": 0,
+ "timeWindow": 10
+ }
+]
\ No newline at end of file
diff --git a/spring-cloud-alibaba-sentinel/src/test/resources/flowrule.json b/spring-cloud-alibaba-sentinel/src/test/resources/flowrule.json
new file mode 100644
index 00000000..d798f805
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/resources/flowrule.json
@@ -0,0 +1,26 @@
+[
+ {
+ "resource": "resource",
+ "controlBehavior": 0,
+ "count": 1,
+ "grade": 1,
+ "limitApp": "default",
+ "strategy": 0
+ },
+ {
+ "resource": "p",
+ "controlBehavior": 0,
+ "count": 1,
+ "grade": 1,
+ "limitApp": "default",
+ "strategy": 0
+ },
+ {
+ "resource": "http://www.taobao.com",
+ "controlBehavior": 0,
+ "count": 0,
+ "grade": 1,
+ "limitApp": "default",
+ "strategy": 0
+ }
+]
diff --git a/spring-cloud-alibaba-sentinel/src/test/resources/param-flow.json b/spring-cloud-alibaba-sentinel/src/test/resources/param-flow.json
new file mode 100644
index 00000000..72e1c2dc
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/resources/param-flow.json
@@ -0,0 +1,16 @@
+[
+ {
+ "resource": "hotResource",
+ "count": 0,
+ "grade": 1,
+ "limitApp": "default",
+ "paramIdx": 0,
+ "paramFlowItemList": [
+ {
+ "object": "2",
+ "classType": "int",
+ "count": 1
+ }
+ ]
+ }
+]
diff --git a/spring-cloud-alibaba-sentinel/src/test/resources/system.json b/spring-cloud-alibaba-sentinel/src/test/resources/system.json
new file mode 100644
index 00000000..7aa623a7
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/resources/system.json
@@ -0,0 +1,8 @@
+[
+ {
+ "highestSystemLoad": -1,
+ "qps": 100,
+ "avgRt": -1,
+ "maxThread": 10
+ }
+]
diff --git a/spring-cloud-starter-alibaba/pom.xml b/spring-cloud-starter-alibaba/pom.xml
index bacdefff..3c91f8d7 100644
--- a/spring-cloud-starter-alibaba/pom.xml
+++ b/spring-cloud-starter-alibaba/pom.xml
@@ -19,4 +19,26 @@
spring-cloud-starter-stream-rocketmq
spring-cloud-starter-bus-rocketmq
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-starter-alicloud/pom.xml b/spring-cloud-starter-alicloud/pom.xml
index df47cfbb..3684464e 100644
--- a/spring-cloud-starter-alicloud/pom.xml
+++ b/spring-cloud-starter-alicloud/pom.xml
@@ -17,4 +17,26 @@
spring-cloud-starter-alicloud-schedulerx
spring-cloud-starter-alicloud-sms
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+
+
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ false
+
+ true
+
+
+
+
+
\ No newline at end of file