1
0
mirror of https://gitee.com/mirrors/Spring-Cloud-Alibaba.git synced 2021-06-26 13:25:11 +08:00

Merge pull request #2 from spring-cloud-incubator/master

同步一次 最新代码
This commit is contained in:
pbting 2019-01-16 10:08:38 +08:00 committed by GitHub
commit a8564140e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 2153 additions and 357 deletions

View File

@ -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)
## 主要功能
@ -71,7 +71,7 @@ Spring Cloud 使用 Maven 来构建最快的使用方式是将本项目clone
</dependencies>
</dependencyManagement>
然后 `dependencies` 中添加自己所需使用的依赖即可使用。
然后 `dependencies` 中添加自己所需使用的依赖即可使用。
如果您想体验最新的 BUILD-SNAPSHOT 的新功能,则可以将版本换成最新的版本,但是需要在 pom.xml 中配置 Spring BUILDSNAPSHOT 仓库,**注意: SNAPSHOT 版本随时可能更新**

View File

@ -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**
**Alibaba Cloud SchedulerX**
阿里中间件团队开发的一款分布式任务调度产品,支持周期性的任务与固定时间点触发任务。
## 即将加入的组件
**Dubbo**
Apache Dubbo™ (incubating) 是一款高性能 Java RPC 框架。
**Fescar**
阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
**Alibaba Cloud SLS**
针对日志类数据的一站式服务,在阿里巴巴集团经历大量大数据场景锤炼而成。您无需开发就能快捷完成日志数据采集、消费、投递以及查询分析等功能,提升运维、运营效率,建立 DT 时代海量日志处理能力。

View File

@ -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.

40
pom.xml
View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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/)
![undefined](https://cdn.nlark.com/lark/0/2018/png/64647/1535371973274-3ebec90a-ebde-4eb7-96ed-5372f6b32fe0.png)
ii. 获取 accessKey、secretKey
![undefined](https://cdn.nlark.com/lark/0/2018/png/64647/1535372168883-b94a3d77-3f81-4938-b409-611945a9e21c.png)
**注意:**如果您使用了阿里云 [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 中或者通过其他社区渠道向我们反馈。

View File

@ -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>

View File

@ -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>

View File

@ -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));
}

View File

@ -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>

View File

@ -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";

View File

@ -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,12 +286,19 @@ 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;
@ -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;

View File

@ -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<>();

View File

@ -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 {

View File

@ -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;
}

View File

@ -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,16 +135,17 @@ 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)) {
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
throw new RuntimeException("[Sentinel Starter] DataSource "
+ dataSourceName
+ "dataType is custom, please set converter-class "
+ "property");
}
@ -158,16 +153,15 @@ public class SentinelDataSourceHandler {
// configuration and register
String customConvertBeanName = "sentinel-"
+ dataSourceProperties.getConverterClass();
if (!beanFactory.containsBean(customConvertBeanName)) {
beanFactory.registerBeanDefinition(customConvertBeanName,
if (!this.beanFactory.containsBean(customConvertBeanName)) {
this.beanFactory.registerBeanDefinition(customConvertBeanName,
BeanDefinitionBuilder
.genericBeanDefinition(
Class.forName(dataSourceProperties
.getConverterClass()))
.getBeanDefinition());
}
builder.addPropertyReference("converter",
customConvertBeanName);
builder.addPropertyReference("converter", customConvertBeanName);
}
catch (ClassNotFoundException e) {
logger.error("[Sentinel Starter] DataSource " + dataSourceName
@ -175,19 +169,16 @@ public class SentinelDataSourceHandler {
+ dataSourceProperties.getClass().getSimpleName()
+ " error, class name: "
+ dataSourceProperties.getConverterClass());
throw new RuntimeException(
"[Sentinel Starter] DataSource " + dataSourceName
+ " handle "
+ dataSourceProperties.getClass()
.getSimpleName()
throw new RuntimeException("[Sentinel Starter] DataSource "
+ dataSourceName + " handle "
+ dataSourceProperties.getClass().getSimpleName()
+ " error, class name: "
+ dataSourceProperties.getConverterClass(),
e);
+ dataSourceProperties.getConverterClass(), e);
}
}
else {
if (!dataTypeList.contains(StringUtils
.trimAllWhitespace(propertyValue.toString()))) {
if (!dataTypeList.contains(
StringUtils.trimAllWhitespace(propertyValue.toString()))) {
throw new RuntimeException("[Sentinel Starter] DataSource "
+ dataSourceName + " dataType: " + propertyValue
+ " is not support now. please using these types: "
@ -202,7 +193,7 @@ public class SentinelDataSourceHandler {
+ "-converter");
}
}
else if (CONVERTERCLASS_FIELD.equals(propertyName)) {
else if (CONVERTER_CLASS_FIELD.equals(propertyName)) {
return;
}
else {
@ -210,12 +201,12 @@ public class SentinelDataSourceHandler {
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;
}
}

View File

@ -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) {

View File

@ -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());
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());
}
catch (Exception e) {
((HashMap) result.get("datasources")).put(dataSourceBeanName,
"load error: " + e.getMessage());
}
});
return result;
}

View File

@ -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);
}
}

View File

@ -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",

View File

@ -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();
}
static class ExceptionUtil {
public static void handleException(BlockException ex) {
@Bean
RestTemplate restTemplateWithoutBlockClass() {
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 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 {
}
}

View File

@ -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);
}
};
}
}
}

View File

@ -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 {
}
}

View File

@ -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;
}
}
}

View File

@ -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());
}
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,17 @@
[
{
"resource": "good",
"limitApp": "abc",
"strategy": 0
},
{
"resource": "bad",
"limitApp": "bcd",
"strategy": 1
},
{
"resource": "terrible",
"limitApp": "aaa",
"strategy": 1
}
]

View File

@ -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
}
]

View File

@ -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
}
]

View File

@ -0,0 +1,16 @@
[
{
"resource": "hotResource",
"count": 0,
"grade": 1,
"limitApp": "default",
"paramIdx": 0,
"paramFlowItemList": [
{
"object": "2",
"classType": "int",
"count": 1
}
]
}
]

View File

@ -0,0 +1,8 @@
[
{
"highestSystemLoad": -1,
"qps": 100,
"avgRt": -1,
"maxThread": 10
}
]

View File

@ -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>

View File

@ -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>