mirror of
https://gitee.com/mirrors/Spring-Cloud-Alibaba.git
synced 2021-06-26 13:25:11 +08:00
commit
a8564140e2
10
README-zh.md
10
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>
|
||||
</dependencyManagement>
|
||||
|
||||
然后再 `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
|
||||
|
@ -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 时代海量日志处理能力。
|
18
Roadmap.md
18
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.
|
||||
|
||||
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.
|
||||
|
40
pom.xml
40
pom.xml
@ -81,6 +81,8 @@
|
||||
<maven-deploy-plugin.version>2.8.2</maven-deploy-plugin.version>
|
||||
<maven-surefire-plugin.version>2.21.0</maven-surefire-plugin.version>
|
||||
<gmavenplus-plugin.version>1.6</gmavenplus-plugin.version>
|
||||
<cobertura.version>2.1.1</cobertura.version>
|
||||
<cobertura-maven-plugin.version>2.7</cobertura-maven-plugin.version>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
@ -153,11 +155,25 @@
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.cobertura</groupId>
|
||||
<artifactId>cobertura-runtime</artifactId>
|
||||
<version>${cobertura.version}</version>
|
||||
<scope>provided</scope>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
@ -183,7 +199,7 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>2.7</version>
|
||||
<version>${cobertura-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<formats>
|
||||
<format>html</format>
|
||||
@ -191,26 +207,6 @@
|
||||
</formats>
|
||||
<check/>
|
||||
<check />
|
||||
<instrumentation>
|
||||
<excludes>
|
||||
<exclude>org/springframework/cloud/alibaba/cloud/dependencies/**.*.class</exclude>
|
||||
<exclude>org/springframework/cloud/alibaba/cloud/docs/**.*.class</exclude>
|
||||
<exclude>org/springframework/cloud/alibaba/cloud/examples/**.*.class</exclude>
|
||||
<exclude>org/springframework/cloud/alibaba/cloud/test/**.*.class</exclude>
|
||||
<exclude>org/springframework/cloud/start/alibaba/**.*.class</exclude>
|
||||
<exclude>org/springframework/cloud/start/alicloud/**.*.class</exclude>
|
||||
<exclude>org/springframework/cloud/alibaba/nacos/src/test/**.*.class</exclude>
|
||||
<exclude>org/springframework/cloud/alibaba/nacos/config/server/src/test/**.*.class</exclude>
|
||||
<exclude>org/springframework/cloud/alibaba/sentinel/src/test/**.*.class</exclude>
|
||||
<exclude>org/springframework/cloud/alibaba/sentinel/datasource/src/test/**.*.class</exclude>
|
||||
<exclude>org/springframework/cloud/alicloud/acm/src/test/**.*.class</exclude>
|
||||
<exclude>org/springframework/cloud/alicloud/ans/src/test/**.*.class</exclude>
|
||||
<exclude>org/springframework/cloud/alicloud/context/src/test/**.*.class</exclude>
|
||||
<exclude>org/springframework/cloud/alicloud/oss/src/test/**.*.class</exclude>
|
||||
<exclude>org/springframework/cloud/alicloud/scx/src/test/**.*.class</exclude>
|
||||
<exclude>org/springframework/cloud/stream/binder/rocketmq/src/test/**.*.class</exclude>
|
||||
</excludes>
|
||||
</instrumentation>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
|
@ -372,4 +372,26 @@
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
@ -50,5 +50,27 @@
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<!-- put here your original plugin configuration for the children -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
|
@ -23,11 +23,28 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -29,4 +29,25 @@
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -25,4 +25,25 @@
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -27,11 +27,28 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -34,6 +34,14 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@ -47,6 +55,15 @@
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
@ -48,6 +48,14 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@ -61,6 +69,15 @@
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
@ -33,6 +33,14 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@ -46,6 +54,15 @@
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
@ -21,5 +21,25 @@
|
||||
<module>nacos-discovery-provider-example</module>
|
||||
</modules>
|
||||
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
@ -33,6 +33,14 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@ -46,6 +54,15 @@
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
@ -35,6 +35,14 @@
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
@ -44,6 +52,16 @@
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
@ -40,6 +40,14 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@ -53,6 +61,15 @@
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
@ -27,11 +27,28 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -51,6 +51,14 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@ -64,6 +72,15 @@
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
@ -16,6 +16,14 @@
|
||||
<description>api for sentinel dubbo example</description>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
@ -25,6 +33,15 @@
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
@ -36,6 +36,14 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@ -49,6 +57,15 @@
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
@ -34,6 +34,14 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@ -47,6 +55,15 @@
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
314
spring-cloud-alibaba-examples/sms-example/readme-zh.md
Normal file
314
spring-cloud-alibaba-examples/sms-example/readme-zh.md
Normal file
@ -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
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alicloud-sms</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
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 中或者通过其他社区渠道向我们反馈。
|
@ -35,6 +35,14 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@ -48,6 +56,15 @@
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
@ -82,5 +82,28 @@
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<instrumentation>
|
||||
<excludes>
|
||||
<exclude>org/springframework/cloud/alibaba/nacos/**.*class</exclude>
|
||||
</excludes>
|
||||
</instrumentation>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
@ -65,7 +65,7 @@ public class NacosDiscoveryClient implements DiscoveryClient {
|
||||
nacosServiceInstance.setHost(instance.getIp());
|
||||
nacosServiceInstance.setPort(instance.getPort());
|
||||
nacosServiceInstance.setServiceId(serviceId);
|
||||
Map<String, String> metadata = new HashMap<String, String>();
|
||||
Map<String, String> 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<ServiceInstance> hostToServiceInstanceList(
|
||||
List<Instance> instances, String serviceId) {
|
||||
List<ServiceInstance> result = new ArrayList<ServiceInstance>(instances.size());
|
||||
List<ServiceInstance> result = new ArrayList<>(instances.size());
|
||||
for (Instance instance : instances) {
|
||||
result.add(hostToServiceInstance(instance, serviceId));
|
||||
}
|
||||
|
@ -79,4 +79,4 @@ public class NacosServerList extends AbstractServerList<NacosServer> {
|
||||
public void initWithNiwsConfig(IClientConfig iClientConfig) {
|
||||
this.serviceId = iClientConfig.getClientName();
|
||||
}
|
||||
}
|
||||
}
|
@ -42,6 +42,13 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-parameter-flow-control</artifactId>
|
||||
|
@ -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";
|
||||
|
@ -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 <note> when the
|
||||
* transport dependency is on classpath ,the configuration is effective </note>
|
||||
* 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<String, DataSourcePropertiesConfiguration> 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 <note> when the application is web ,the configuration is
|
||||
* effective </note>
|
||||
* Web servlet configuration when the application is web, the configuration is
|
||||
* effective.
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
private Servlet servlet = new Servlet();
|
||||
|
||||
/**
|
||||
* sentinel filter <note> when the application is web ,the configuration is effective
|
||||
* </note>
|
||||
* 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<String> 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;
|
||||
|
||||
|
@ -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<String> defaultPatterns = new ArrayList<>();
|
||||
|
@ -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<UrlBlockHandler> urlBlockHandlerOptional;
|
||||
|
||||
@Autowired
|
||||
private Optional<RequestOriginParser> 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 {
|
||||
|
@ -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<String, SentinelRestTemplate> 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;
|
||||
}
|
||||
|
@ -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<String> dataTypeList = Arrays.asList("json", "xml");
|
||||
|
||||
private List<String> 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<String, Object> 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<String> getDataSourceBeanNameList() {
|
||||
return dataSourceBeanNameList;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
*/
|
||||
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) {
|
||||
|
@ -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<String, Object> invoke() {
|
||||
final Map<String, Object> result = new HashMap<>();
|
||||
if (sentinelProperties.isEnabled()) {
|
||||
|
||||
List<FlowRule> flowRules = FlowRuleManager.getRules();
|
||||
List<DegradeRule> degradeRules = DegradeRuleManager.getRules();
|
||||
List<SystemRule> systemRules = SystemRuleManager.getRules();
|
||||
List<ParamFlowRule> 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<String, Object>());
|
||||
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<String, Object> 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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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 <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
* @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<String, Object> 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 {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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 <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
*/
|
||||
@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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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 <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
*/
|
||||
@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<FooService> {
|
||||
|
||||
private FooService fooService = new FooServiceFallback();
|
||||
|
||||
@Override
|
||||
public FooService create(Throwable throwable) {
|
||||
return fooService;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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 <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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 <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
*/
|
||||
public class TestConverter implements Converter<String, List<ParamFlowRule>> {
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Override
|
||||
public List<ParamFlowRule> convert(String s) {
|
||||
try {
|
||||
return objectMapper.readValue(s, new TypeReference<List<ParamFlowRule>>() {
|
||||
});
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
[
|
||||
{
|
||||
"resource": "good",
|
||||
"limitApp": "abc",
|
||||
"strategy": 0
|
||||
},
|
||||
{
|
||||
"resource": "bad",
|
||||
"limitApp": "bcd",
|
||||
"strategy": 1
|
||||
},
|
||||
{
|
||||
"resource": "terrible",
|
||||
"limitApp": "aaa",
|
||||
"strategy": 1
|
||||
}
|
||||
]
|
@ -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
|
||||
}
|
||||
]
|
@ -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
|
||||
}
|
||||
]
|
@ -0,0 +1,16 @@
|
||||
[
|
||||
{
|
||||
"resource": "hotResource",
|
||||
"count": 0,
|
||||
"grade": 1,
|
||||
"limitApp": "default",
|
||||
"paramIdx": 0,
|
||||
"paramFlowItemList": [
|
||||
{
|
||||
"object": "2",
|
||||
"classType": "int",
|
||||
"count": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"highestSystemLoad": -1,
|
||||
"qps": 100,
|
||||
"avgRt": -1,
|
||||
"maxThread": 10
|
||||
}
|
||||
]
|
@ -19,4 +19,26 @@
|
||||
<module>spring-cloud-starter-stream-rocketmq</module>
|
||||
<module>spring-cloud-starter-bus-rocketmq</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -17,4 +17,26 @@
|
||||
<module>spring-cloud-starter-alicloud-schedulerx</module>
|
||||
<module>spring-cloud-starter-alicloud-sms</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
Loading…
x
Reference in New Issue
Block a user