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