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

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

同步最新的代码
This commit is contained in:
pbting 2018-12-12 16:44:50 +08:00 committed by GitHub
commit b1084d1d92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 1119 additions and 129 deletions

View File

@ -24,7 +24,7 @@ jobs:
- ~/.m2
- run:
name: "Running build"
command: ./mvnw -Pspring clean install -U -nsu --batch-mode -Dmaven.test.redirectTestOutputToFile=true -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
command: ./mvnw -Pspring -Pdocs clean install -U -nsu --batch-mode -Dmaven.test.redirectTestOutputToFile=true -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
- run:
name: "Aggregate test results"
when: always

View File

@ -8,28 +8,33 @@ Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。
## 主要功能
* **服务限流降级**:默认支持 Servlet、RestTemplate、Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
* **服务限流降级**:默认支持 Servlet、Feign、RestTemplate、Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
* **服务注册与发现**:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
* **分布式配置管理**:支持分布式系统中的外部化配置,配置更改时自动刷新。
* **消息驱动能力**:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
* **阿里云对象存储**:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
更多功能请参考 [Roadmap](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/Roadmap-zh.md)。
## 组件:
## 组件
**[Sentinel](https://github.com/alibaba/Sentinel)**:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
**[Nacos](https://github.com/alibaba/Nacos)**:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
**[RocketMQ](https://rocketmq.apache.org/)**:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
**[AliCloud ACM](https://www.aliyun.com/product/acm)**:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。
**[AliCloud OSS](https://www.aliyun.com/product/oss)**: 阿里云对象存储服务Object Storage Service简称 OSS是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
更多组件请参考 [Roadmap](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/Roadmap-zh.md)。
## 如何构建
* master 分支对应的是 Spring Boot 2.x,最低支持 JDK 1.8。
* 1.x 分支对应的是 Spring Boot 1.x,最低支持 JDK 1.7。
* master 分支对应的是 Spring Cloud Finchley,最低支持 JDK 1.8。
* 1.x 分支对应的是 Spring Cloud Edgware,最低支持 JDK 1.7。
Spring Cloud 使用 Maven 来构建最快的使用方式是将本项目clone到本地然后执行以下命令
@ -83,6 +88,8 @@ Example 列表:
[Nacos Discovery Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme-zh.md)
[RocketMQ Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/rocketmq-example/readme-zh.md)
[AliCloud OSS Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/oss-example/readme-zh.md)
[AliCloud ANS Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/ans-example/ans-provider-example/readme-zh.md)

View File

@ -6,7 +6,7 @@ See the [中文文档](https://github.com/spring-cloud-incubator/spring-cloud-al
Spring Cloud Alibaba provides a one-stop solution for distributed application development. It contains all the components required to develop distributed applications, making it easy for you to develop your applications using Spring Cloud.
With Spring Cloud Alibabayou only need to add some annotations and a small amount of configurations to connect Spring Cloud applications to the distributed solutions of Alibaba, and build a distributed application system with Alibaba middleware.
With Spring Cloud Alibaba, you only need to add some annotations and a small amount of configurations to connect Spring Cloud applications to the distributed solutions of Alibaba, and build a distributed application system with Alibaba middleware.
## Features
@ -14,6 +14,7 @@ With Spring Cloud Alibabayou only need to add some annotations and a small am
* **Flow control and service degradation**Flow control for HTTP services is supported by default. You can also customize flow control and service degradation rules using annotations. The rules can be changed dynamically.
* **Service registration and discovery**Service can be registered and clients can discover the instances using Spring-managed beans, auto integration Ribbon.
* **Distributed configuration**support for externalized configuration in a distributed system, auto refresh when configuration changes.
* **Event-driven**support for building highly scalable event-driven microservices connected with shared messaging systems.
* **AliCloud Object Storage**massive, secure, low-cost, and highly reliable cloud storage services. Support for storing and accessing any type of data in any application, anytime, anywhere.
For more features, please refer to [Roadmap](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/Roadmap.md).
@ -23,6 +24,10 @@ For more features, please refer to [Roadmap](https://github.com/spring-cloud-inc
**[Nacos](https://github.com/alibaba/Nacos)**: an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications.
**[RocketMQ](https://rocketmq.apache.org/)**a distributed messaging and streaming platform with low latency, high performance and reliability, trillion-level capacity and flexible scalability.
**[AliCloud ACM](https://www.aliyun.com/product/acm)**an application configuration center that enables you to centralize the management of application configurations, and accomplish real-time configuration push in a distributed environment.
**[AliCloud OSS](https://www.aliyun.com/product/oss)**: An encrypted and secure cloud storage service which stores, processes and accesses massive amounts of data from anywhere in the world.
For more features please refer to [Roadmap](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/Roadmap.md).
@ -30,7 +35,7 @@ For more features please refer to [Roadmap](https://github.com/spring-cloud-incu
## How to build
* **master branch**: Corresponds to Spring Boot 2.x. JDK 1.8 or later versions are supported.
* **1.x branch**: Corresponds to Spring Boot 1.xJDK 1.7 or later versions are supported.
* **1.x branch**: Corresponds to Spring Boot 1.x, JDK 1.7 or later versions are supported.
Spring Cloud uses Maven for most build-related activities, and you should be able to get off the ground quite quickly by cloning the project you are interested in and typing:
@ -40,7 +45,7 @@ Spring Cloud uses Maven for most build-related activities, and you should be abl
## How to Use
### Add maven dependency
Version 0.2.0.RELEASE is compatible with the Spring Boot 2.0.x line. Version 0.1.0.RELEASE is compatible with the Spring Boot 1.x line.
Version 0.2.0.RELEASE is compatible with the Spring Cloud Finchley. Version 0.1.0.RELEASE is compatible with the Spring Cloud Edgware.
These artifacts are available from Maven Central and Spring Release repository via BOM:
@ -95,6 +100,8 @@ Examples
[Nacos Discovery Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme.md)
[RocketMQ Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/rocketmq-example/readme.md)
[AliCloud OSS Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/oss-example/readme.md)
## Version control guidelines
@ -104,8 +111,8 @@ As the interfaces and annotations of Spring Boot 1 and Spring Boot 2 have been c
* 0.1.x for Spring Boot 1
* 0.2.x for Spring Boot 2
During the incubation periodthe version management of the project will follow these rules
* Functional updates will be reflected in the 3rd number of the version, for example, the next version of 0.1.0 will be 0.1.1
During the incubation period, the version management of the project will follow these rules
* Functional updates will be reflected in the 3rd number of the version, for example, the next version of 0.1.0 will be 0.1.1.
## Contact Us

View File

@ -19,7 +19,7 @@
<properties>
<sentinel.version>1.3.0-GA</sentinel.version>
<oss.version>3.1.0</oss.version>
<nacos.version>0.5.0</nacos.version>
<nacos.version>0.6.0</nacos.version>
<acm.version>1.0.8</acm.version>
<ans.version>0.1.1</ans.version>
<aliyun.sdk.version>4.0.1</aliyun.sdk.version>
@ -64,6 +64,7 @@
<version>${acm.version}</version>
</dependency>
<!--Nacos-->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
@ -89,6 +90,11 @@
<artifactId>sentinel-core</artifactId>
<version>${sentinel.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
<version>${sentinel.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
@ -170,6 +176,11 @@
<artifactId>spring-cloud-alibaba-nacos-config</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-nacos-config-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alicloud-acm</artifactId>
@ -214,6 +225,11 @@
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>

View File

@ -161,7 +161,7 @@ spring.application.name=acm-config
spring.cloud.nacos.config.file-extension=yaml #显示的声明使用的文件扩展名
----
其中 ${deploy.env}变量的值可以在启动应用时通过-Ddeploy.env=*****来动态指定。比如现在在轻量版配置中心上新增了一个dataid为acm-config-develop.yaml的基础配置如下所示
其中 ${deploy.env}变量的值可以在启动应用时通过-Ddeploy.env=<env>来动态指定。比如现在在轻量版配置中心上新增了一个dataid为acm-config-develop.yaml的基础配置如下所示
[source,subs="normal"]
----

View File

@ -242,7 +242,7 @@ public class ProviderApplication {
[source,subs="normal"]
----
in develop-evn enviroment; user name :nacos-config-yaml-update; age: 68
in develop-env enviroment; user name :nacos-config-yaml-update; age: 68
2018-11-02 15:34:25.013 INFO 33014 --- [ Thread-11] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6f1c29b7: startup date [Fri Nov 02 15:33:57 CST 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@63355449
----
@ -275,12 +275,15 @@ in product-env enviroment; user name :nacos-config-yaml-update; age: 68
----
NOTE: 此案例中我们通过 `spring.profiles.active=****` 的方式写死在配置文件中,而在真正的项目实施过程中这个变量的值是需要不同环境而有不同的值。这个时候通常的做法是通过 `-Dspring.profiles.active=****` 参数指定其配置来达到环境间灵活的切换。
NOTE: 此案例中我们通过 `spring.profiles.active=<profilename>` 的方式写死在配置文件中,而在真正的项目实施过程中这个变量的值是需要不同环境而有不同的值。这个时候通常的做法是通过 `-Dspring.profiles.active=<profile>` 参数指定其配置来达到环境间灵活的切换。
=== 支持自定义 namespace 的配置
首先看一下 Nacos 的 Namespace 的概念, https://nacos.io/zh-cn/docs/concepts.html[Nacos 概念]
在没有明确指定 `${spring.cloud.nacos.config.namespace}` 配置的情况下, 默认使用的是 Nacos 上 Public 这个namespae。在我们日常业务的开发过程中经常需要自定义自己的 namespace那这个时候可以通过以下配置来实现
[quote]
用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
在没有明确指定 `${spring.cloud.nacos.config.namespace}` 配置的情况下, 默认使用的是 Nacos 上 Public 这个namespae。如果需要使用自定义的命名空间可以通过以下配置来实现
[source,properties]
----
spring.cloud.nacos.config.namespace=b3404bc0-d7dc-4855-b519-570ed34b62d7
@ -290,7 +293,7 @@ NOTE: 该配置必须放在 bootstrap.properties 文件中。此外 `spring.clou
=== 支持自定义 Group 的配置
在没有明确指定 `${spring.cloud.nacos.config.group}` 配置的情况下, 默认使用的是 DEFAULT_GROUP 。在我们日常业务的开发过程中,经常需要自定义自己的 Group那这个时候可以通过以下配置来实现:
在没有明确指定 `${spring.cloud.nacos.config.group}` 配置的情况下, 默认使用的是 DEFAULT_GROUP 。如果需要自定义自己的 Group可以通过以下配置来实现:
[source,properties]
----

View File

@ -100,7 +100,53 @@ spring:
### Feign 支持
DOING
Sentinel 适配了 https://github.com/OpenFeign/feign[Feign] 组件。如果想使用,除了引入 `sentinel-starter` 的依赖外还需要 3 个步骤:
* 配置文件打开 sentinel 对 feign 的支持:`feign.sentinel.enabled=true`
* 加入 `feign starter` 依赖触发 `sentinel starter` 的配置类生效:
```xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
```
* `feign` 内部的 `loadbalance` 功能依赖 Netflix 的 `ribbon` 模块(如果不使用 `loadbalance` 功能可不引入)
```xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
```
这是一个 `FeignClient` 对应的接口:
```java
@FeignClient(name = "service-provider", fallback = EchoServiceFallback.class, configuration = FeignConfiguration.class)
public interface EchoService {
@RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
String echo(@PathVariable("str") String str);
}
class FeignConfiguration {
@Bean
public EchoServiceFallback echoServiceFallback() {
return new EchoServiceFallback();
}
}
class EchoServiceFallback implements EchoService {
@Override
public String echo(@PathVariable("str") String str) {
return "echo fallback";
}
}
```
NOTE: Feign 对应的接口中的资源名策略定义httpmethod:http://requesturl
`EchoService` 接口中方法 `echo` 对应的资源名为 `GET:http://service-provider/echo/{str}`。
请注意:`@FeignClient` 注解中的所有属性Sentinel 都做了兼容。
### RestTemplate 支持
@ -124,10 +170,11 @@ public RestTemplate restTemplate() {
NOTE: 以 `https://www.taobao.com/test` 这个 url 为例。对应的资源名有两种粒度,分别是 `https://www.taobao.com:80` 以及 `https://www.taobao.com:80/test`
### 动态数据源支持
*在版本 0.2.0.RELEASE 或 0.1.0.RELEASE 之前*,要在 Spring Cloud Alibaba Sentinel 下使用动态数据源需要3个步骤
#### 在版本 0.2.0.RELEASE 或 0.1.0.RELEASE 之前
需要3个步骤才可完成数据源的配置
* 配置文件中定义数据源信息。比如使用文件:
@ -173,7 +220,9 @@ private ReadableDataSource dataSource;
[Sentinel Starter] load 3 flow rules
```
*在版本 0.2.0.RELEASE 或 0.1.0.RELEASE 之后*,要在 Spring Cloud Alibaba Sentinel 下使用动态数据源只需要1个步骤
#### 在版本 0.2.0.RELEASE 或 0.1.0.RELEASE 之后
只需要1个步骤就可完成数据源的配置
* 直接在 `application.properties` 配置文件中配置数据源信息即可
@ -236,6 +285,16 @@ NOTE: 默认情况下xml 格式是不支持的。需要添加 `jackson-datafo
### More
下表显示当应用的 `ApplicationContext` 中存在对应的Bean的类型时会进行的一些操作
:frame: topbot
[width="60%",options="header"]
|====
^|存在Bean的类型 ^|操作 ^|作用
|`UrlCleaner`|`WebCallbackManager.setUrlCleaner(urlCleaner)`|资源清理(资源(比如将满足 /foo/:id 的 URL 都归到 /foo/* 资源下))
|`UrlBlockHandler`|`WebCallbackManager.setUrlBlockHandler(urlBlockHandler)`|自定义限流处理逻辑
|====
下表显示 Spring Cloud Alibaba Sentinel 的所有配置信息:
:frame: topbot
@ -244,14 +303,16 @@ NOTE: 默认情况下xml 格式是不支持的。需要添加 `jackson-datafo
^|配置项 ^|含义 ^|默认值
|`spring.cloud.sentinel.enabled`|Sentinel自动化配置是否生效|true
|`spring.cloud.sentinel.eager`|取消Sentinel控制台懒加载|false
|`spring.cloud.sentinel.charset`|metric文件字符集|UTF-8
|`spring.cloud.sentinel.transport.port`|应用与Sentinel控制台交互的端口应用本地会起一个该端口占用的HttpServer|8721
|`spring.cloud.sentinel.transport.dashboard`|Sentinel 控制台地址|
|`spring.cloud.sentinel.transport.heartbeatIntervalMs`|应用与Sentinel控制台的心跳间隔时间|
|`spring.cloud.sentinel.filter.order`|Servlet Filter的加载顺序。Starter内部会构造这个filter|Integer.MIN_VALUE
|`spring.cloud.sentinel.filter.spring.url-patterns`|数据类型是数组。表示Servlet Filter的url pattern集合|/*
|`spring.cloud.sentinel.metric.charset`|metric文件字符集|UTF-8
|`spring.cloud.sentinel.metric.fileSingleSize`|Sentinel metric 单个文件的大小|
|`spring.cloud.sentinel.metric.fileTotalCount`|Sentinel metric 总文件数量|
|`spring.cloud.sentinel.log.dir`|Sentinel 日志文件所在的目录|
|`spring.cloud.sentinel.log.switch-pid`|Sentinel 日志文件名是否需要带上pid|false
|`spring.cloud.sentinel.servlet.blockPage`| 自定义的跳转 URL当请求被限流时会自动跳转至设定好的 URL |
|`spring.cloud.sentinel.flow.coldFactor`| https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81---%E5%86%B7%E5%90%AF%E5%8A%A8[冷启动因子] |3
|====

View File

@ -1,4 +1,4 @@
server.port=18083
# The following configuration can be omitted.
spring.cloud.alicloud.ans.server.list=127.0.0.1
spring.cloud.alicloud.ans.server.port=8080
spring.cloud.alicloud.ans.server-list=127.0.0.1
spring.cloud.alicloud.ans.server-port=8080

View File

@ -1,4 +1,4 @@
server.port=18082
# The following configuration can be omitted.
spring.cloud.alicloud.ans.server.list=127.0.0.1
spring.cloud.alicloud.ans.server.port=8080
spring.cloud.alicloud.ans.server-list=127.0.0.1
spring.cloud.alicloud.ans.server-port=8080

View File

@ -1,4 +1,4 @@
spring.application.name=ans-provider
server.port=18081
spring.cloud.alicloud.ans.server.list=127.0.0.1
spring.cloud.alicloud.ans.server.port=8080
spring.cloud.alicloud.ans.server-list=127.0.0.1
spring.cloud.alicloud.ans.server-port=8080

View File

@ -40,6 +40,11 @@
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -2,6 +2,7 @@ package org.springframework.cloud.alibaba.cloud.examples;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.alibaba.cloud.examples.ConsumerApplication.EchoService;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
@ -10,6 +11,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;
/**
@ -30,9 +32,40 @@ public class ConsumerApplication {
SpringApplication.run(ConsumerApplication.class, args);
}
@FeignClient(name = "service-provider")
@FeignClient(name = "service-provider", fallback = EchoServiceFallback.class, configuration = FeignConfiguration.class)
public interface EchoService {
@RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
String echo(@PathVariable("str") String str);
@RequestMapping(value = "/divide", method = RequestMethod.GET)
String divide(@RequestParam("a") Integer a, @RequestParam("b") Integer b);
@RequestMapping(value = "/notFound", method = RequestMethod.GET)
String notFound();
}
}
class FeignConfiguration {
@Bean
public EchoServiceFallback echoServiceFallback() {
return new EchoServiceFallback();
}
}
class EchoServiceFallback implements EchoService {
@Override
public String echo(@PathVariable("str") String str) {
return "echo fallback";
}
@Override
public String divide(@RequestParam Integer a, @RequestParam Integer b) {
return "divide fallback";
}
@Override
public String notFound() {
return "notFound fallback";
}
}

View File

@ -6,6 +6,7 @@ import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@ -29,6 +30,16 @@ public class TestController {
String.class);
}
@RequestMapping(value = "/notFound-feign", method = RequestMethod.GET)
public String notFound() {
return echoService.notFound();
}
@RequestMapping(value = "/divide-feign", method = RequestMethod.GET)
public String divide(@RequestParam Integer a, @RequestParam Integer b) {
return echoService.divide(a, b);
}
@RequestMapping(value = "/echo-feign/{str}", method = RequestMethod.GET)
public String feign(@PathVariable String str) {
return echoService.echo(str);

View File

@ -1,4 +1,12 @@
spring.application.name=service-consumer
server.port=18083
management.endpoints.web.exposure.include=*
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
feign.sentinel.enabled=true
spring.cloud.sentinel.transport.dashboard=localhost:8080
spring.cloud.sentinel.eager=true
spring.cloud.sentinel.datasource.ds1.file.file=classpath: flowrule.json
spring.cloud.sentinel.datasource.ds1.file.data-type=json

View File

@ -0,0 +1,10 @@
[
{
"resource": "GET:http://service-provider/echo/{str}",
"controlBehavior": 0,
"count": 1,
"grade": 1,
"limitApp": "default",
"strategy": 0
}
]

View File

@ -6,6 +6,7 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
@ -25,5 +26,10 @@ public class ProviderApplication {
public String echo(@PathVariable String string) {
return "hello Nacos Discovery " + string;
}
@RequestMapping(value = "/divide", method = RequestMethod.GET)
public String divide(@RequestParam Integer a, @RequestParam Integer b) {
return String.valueOf(a / b);
}
}
}

View File

@ -75,7 +75,7 @@ spring.cloud.stream.bindings.input.group=test-group
### 下载并启动 RocketMQ
在接入 RocketMQ Binder 之前,首先需要启动 RocketMQ 的 Name Server 和 Broker。
**在接入 RocketMQ Binder 之前,首先需要启动 RocketMQ 的 Name Server 和 Broker。**
1. 下载[RocketMQ最新的二进制文件](https://www.apache.org/dyn/closer.cgi?path=rocketmq/4.3.2/rocketmq-all-4.3.2-bin-release.zip),并解压

View File

@ -0,0 +1,271 @@
# RocketMQ Example
## Project Instruction
This example illustrates how to use RocketMQ Binder implement pub/sub messages for Spring Cloud applications.
[RocketMQ](https://rocketmq.apache.org/) is a distributed messaging and streaming platform with low latency, high performance and reliability, trillion-level capacity and flexible scalability.
Before we start the demo, let's look at Spring Cloud Stream.
Spring Cloud Stream is a framework for building message-driven microservice applications. Spring Cloud Stream builds upon Spring Boot to create standalone, production-grade Spring applications and uses Spring Integration to provide connectivity to message brokers. It provides opinionated configuration of middleware from several vendors, introducing the concepts of persistent publish-subscribe semantics, consumer groups, and partitions.
There are two concepts in Spring Cloud Stream: Binder 和 Binding.
* Binder: A strategy interface used to bind an app interface to a logical name.
Binder Implementations includes `KafkaMessageChannelBinder` of kafka, `RabbitMessageChannelBinder` of RabbitMQ and `RocketMQMessageChannelBinder` of `RocketMQ`.
* Binding: Including Input Binding and Output Binding.
Binding is Bridge between the external messaging systems and application provided Producers and Consumers of messages.
This is a overview of Spring Cloud Stream.
![](https://docs.spring.io/spring-cloud-stream/docs/current/reference/htmlsingle/images/SCSt-overview.png)
## Demo
### Integration with RocketMQ Binder
Before we start the demo, let's learn how to Integration with RocketMQ Binder to a Spring Cloud application.
**Note: This section is to show you how to connect to Sentinel. The configurations have been completed in the following example, so you don't need modify the code any more.**
1. Add dependency spring-cloud-starter-stream-rocketmq in the pom.xml file in your Spring Cloud project.
```xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
</dependency>
```
2. Configure Input and Output Binding and cooperate with `@EnableBinding` annotation
```java
@SpringBootApplication
@EnableBinding({ Source.class, Sink.class })
public class RocketMQApplication {
public static void main(String[] args) {
SpringApplication.run(RocketMQApplication.class, args);
}
}
```
Configure Binding:
```properties
# configure the nameserver of rocketmq
spring.cloud.stream.rocketmq.binder.namesrv-addr=127.0.0.1:9876
# configure the output binding named output
spring.cloud.stream.bindings.output.destination=test-topic
spring.cloud.stream.bindings.output.content-type=application/json
# configure the input binding named input
spring.cloud.stream.bindings.input.destination=test-topic
spring.cloud.stream.bindings.input.content-type=application/json
spring.cloud.stream.bindings.input.group=test-group
```
3. pub/sub messages
### Download and Startup RocketMQ
You should startup Name Server and Broker before using RocketMQ Binder.
1. Download [RocketMQ](https://www.apache.org/dyn/closer.cgi?path=rocketmq/4.3.2/rocketmq-all-4.3.2-bin-release.zip) and unzip it.
2. Startup Name Server
```bash
sh bin/mqnamesrv
```
3. Startup Broker
```bash
sh bin/mqbroker -n localhost:9876
```
4. Create topic: test-topic
```bash
sh bin/mqadmin updateTopic -n localhost:9876 -c DefaultCluster -t test-topic
```
### Start Application
1. Add necessary configurations to file `/src/main/resources/application.properties`.
```properties
spring.application.name=rocketmq-example
server.port=28081
```
2. Start the application in IDE or by building a fatjar.
1. Start in IDE: Find main class `RocketMQApplication`, and execute the main method.
2. Build a fatjar: Execute command `mvn clean package` to build a fatjar, and run command `java -jar rocketmq-example.jar` to start the application.
### Message Handle
Using the binding named output and sent messages to `test-topic` topic.
And using two input bindings to subscribe messages.
* input1: subscribe the message of `test-topic` topic and consume ordered messages(all messages should in the same MessageQueue if you want to consuming ordered messages).
* input2: subscribe the message of `test-topic` topic and consume concurrent messages which tags is `tagStr`, the thread number in pool is 20 in Consumer side.
see the configuration below:
```properties
spring.cloud.stream.rocketmq.binder.namesrv-addr=127.0.0.1:9876
spring.cloud.stream.bindings.output.destination=test-topic
spring.cloud.stream.bindings.output.content-type=application/json
spring.cloud.stream.bindings.input1.destination=test-topic
spring.cloud.stream.bindings.input1.content-type=text/plain
spring.cloud.stream.bindings.input1.group=test-group1
spring.cloud.stream.rocketmq.bindings.input1.consumer.orderly=true
spring.cloud.stream.bindings.input2.destination=test-topic
spring.cloud.stream.bindings.input2.content-type=text/plain
spring.cloud.stream.bindings.input2.group=test-group2
spring.cloud.stream.rocketmq.bindings.input2.consumer.orderly=false
spring.cloud.stream.rocketmq.bindings.input2.consumer.tags=tagStr
spring.cloud.stream.bindings.input2.consumer.concurrency=20
```
#### Pub Messages
Using MessageChannel to send messages:
```java
public class ProducerRunner implements CommandLineRunner {
@Autowired
private MessageChannel output;
@Override
public void run(String... args) throws Exception {
Map<String, Object> headers = new HashMap<>();
headers.put(MessageConst.PROPERTY_TAGS, "tagStr");
Message message = MessageBuilder.createMessage(msg, new MessageHeaders(headers));
output.send(message);
}
}
```
Or you can using the native API of RocketMQ to send messages:
```java
public class RocketMQProducer {
DefaultMQProducer producer = new DefaultMQProducer("producer_group");
producer.setNamesrvAddr("127.0.0.1:9876");
producer.start();
Message msg = new Message("test-topic", "tagStr", "message from rocketmq producer".getBytes());
producer.send(msg);
}
```
#### Sub Messages
Using `@StreamListener` to receive messages:
```java
@Service
public class ReceiveService {
@StreamListener("input1")
public void receiveInput1(String receiveMsg) {
System.out.println("input1 receive: " + receiveMsg);
}
@StreamListener("input2")
public void receiveInput2(String receiveMsg) {
System.out.println("input2 receive: " + receiveMsg);
}
}
```
## Endpoint
Add dependency `spring-cloud-starter-stream-rocketmq` to your pom.xml file, and configure your endpoint security strategy.
* Spring Boot1.x: Add configuration `management.security.enabled=false`
* Spring Boot2.x: Add configuration `management.endpoints.web.exposure.include=*`
To view the endpoint information, visit the following URLS:
* Spring Boot1.x: Sentinel Endpoint URL is http://127.0.0.1:18083/rocketmq_binder.
* Spring Boot2.x: Sentinel Endpoint URL is http://127.0.0.1:18083/actuator/rocketmq-binder.
Endpoint will metrics some data like last send timestamp, sending or receive message successfully times or unsuccessfully times.
```json
{
"runtime": {
"lastSend.timestamp": 1542786623915
},
"metrics": {
"scs-rocketmq.consumer.test-topic.totalConsumed": {
"count": 11
},
"scs-rocketmq.consumer.test-topic.totalConsumedFailures": {
"count": 0
},
"scs-rocketmq.producer.test-topic.totalSentFailures": {
"count": 0
},
"scs-rocketmq.consumer.test-topic.consumedPerSecond": {
"count": 11,
"fifteenMinuteRate": 0.012163847780107841,
"fiveMinuteRate": 0.03614605351360527,
"meanRate": 0.3493213353657594,
"oneMinuteRate": 0.17099243039490175
},
"scs-rocketmq.producer.test-topic.totalSent": {
"count": 5
},
"scs-rocketmq.producer.test-topic.sentPerSecond": {
"count": 5,
"fifteenMinuteRate": 0.005540151995103271,
"fiveMinuteRate": 0.01652854617838251,
"meanRate": 0.10697493212602836,
"oneMinuteRate": 0.07995558537067671
},
"scs-rocketmq.producer.test-topic.sentFailuresPerSecond": {
"count": 0,
"fifteenMinuteRate": 0.0,
"fiveMinuteRate": 0.0,
"meanRate": 0.0,
"oneMinuteRate": 0.0
},
"scs-rocketmq.consumer.test-topic.consumedFailuresPerSecond": {
"count": 0,
"fifteenMinuteRate": 0.0,
"fiveMinuteRate": 0.0,
"meanRate": 0.0,
"oneMinuteRate": 0.0
}
}
}
```
Note: You should add [metrics-core dependency](https://mvnrepository.com/artifact/io.dropwizard.metrics/metrics-core) if you want to see metrics data. endpoint will show warning information if you don't add that dependency:
```json
{
"warning": "please add metrics-core dependency, we use it for metrics"
}
```
## More
For more information about RocketMQ, see [RocketMQ Project](https://rocketmq.apache.org).
If you have any ideas or suggestions for Spring Cloud RocketMQ Binder, please don't hesitate to tell us by submitting github issues.

View File

@ -1,12 +1,13 @@
package org.springframework.cloud.alibaba.cloud.examples;
import com.alibaba.csp.sentinel.datasource.Converter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect;
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
import com.alibaba.csp.sentinel.datasource.Converter;
/**
* @author xiaojing
*/
@ -14,7 +15,7 @@ import org.springframework.web.client.RestTemplate;
public class ServiceApplication {
@Bean
@SentinelProtect(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class)
@SentinelRestTemplate(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class)
public RestTemplate restTemplate() {
return new RestTemplate();
}
@ -25,9 +26,9 @@ public class ServiceApplication {
}
@Bean
public Converter myConverter() {
return new JsonFlowRuleListConverter();
}
public Converter myConverter() {
return new JsonFlowRuleListConverter();
}
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);

View File

@ -48,7 +48,7 @@ public class NacosDiscoveryClient implements DiscoveryClient {
public List<ServiceInstance> getInstances(String serviceId) {
try {
List<Instance> instances = discoveryProperties.namingServiceInstance()
.getAllInstances(serviceId);
.selectInstances(serviceId, true);
return hostToServiceInstanceList(instances, serviceId);
}
catch (Exception e) {
@ -77,9 +77,7 @@ public class NacosDiscoveryClient implements DiscoveryClient {
List<Instance> instances, String serviceId) {
List<ServiceInstance> result = new ArrayList<ServiceInstance>(instances.size());
for (Instance instance : instances) {
if (instance.isHealthy()) {
result.add(hostToServiceInstance(instance, serviceId));
}
result.add(hostToServiceInstance(instance, serviceId));
}
return result;
}

View File

@ -306,15 +306,15 @@ public class NacosDiscoveryProperties {
this.secretKey = secretKey;
}
public String getNamingLoadCacheAtStart() {
return namingLoadCacheAtStart;
}
public String getNamingLoadCacheAtStart() {
return namingLoadCacheAtStart;
}
public void setNamingLoadCacheAtStart(String namingLoadCacheAtStart) {
this.namingLoadCacheAtStart = namingLoadCacheAtStart;
}
public void setNamingLoadCacheAtStart(String namingLoadCacheAtStart) {
this.namingLoadCacheAtStart = namingLoadCacheAtStart;
}
@Override
@Override
public String toString() {
return "NacosDiscoveryProperties{" + "serverAddr='" + serverAddr + '\''
+ ", endpoint='" + endpoint + '\'' + ", namespace='" + namespace + '\''
@ -323,7 +323,8 @@ public class NacosDiscoveryProperties {
+ ", metadata=" + metadata + ", registerEnabled=" + registerEnabled
+ ", ip='" + ip + '\'' + ", networkInterface='" + networkInterface + '\''
+ ", port=" + port + ", secure=" + secure + ", accessKey='" + accessKey
+ ", namingLoadCacheAtStart=" + namingLoadCacheAtStart + '\'' + ", secretKey='" + secretKey + '\'' + '}';
+ ", namingLoadCacheAtStart=" + namingLoadCacheAtStart + '\''
+ ", secretKey='" + secretKey + '\'' + '}';
}
public void overrideFromEnv(Environment env) {
@ -372,7 +373,7 @@ public class NacosDiscoveryProperties {
properties.put(ACCESS_KEY, accessKey);
properties.put(SECRET_KEY, secretKey);
properties.put(CLUSTER_NAME, clusterName);
properties.put(NAMING_LOAD_CACHE_AT_START,namingLoadCacheAtStart);
properties.put(NAMING_LOAD_CACHE_AT_START, namingLoadCacheAtStart);
try {
namingService = NacosFactory.createNamingService(properties);

View File

@ -57,7 +57,7 @@ public class NacosServerList extends AbstractServerList<NacosServer> {
private List<NacosServer> getServers() {
try {
List<Instance> instances = discoveryProperties.namingServiceInstance()
.getAllInstances(serviceId);
.selectInstances(serviceId, true);
return instancesToServerList(instances);
}
catch (Exception e) {
@ -70,9 +70,7 @@ public class NacosServerList extends AbstractServerList<NacosServer> {
private List<NacosServer> instancesToServerList(List<Instance> instances) {
List<NacosServer> result = new ArrayList<>(instances.size());
for (Instance instance : instances) {
if (instance.isHealthy()) {
result.add(new NacosServer(instance));
}
result.add(new NacosServer(instance));
}
return result;

View File

@ -18,6 +18,13 @@
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
<optional>true</optional>
</dependency>
<dependency>

View File

@ -18,6 +18,7 @@ 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.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.fasterxml.jackson.core.JsonProcessingException;
@ -32,6 +33,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* @see DegradeRule
* @see SystemRule
* @see AuthorityRule
* @see ParamFlowRule
* @see ObjectMapper
*/
public class JsonConverter implements Converter<String, List<AbstractRule>> {
@ -48,7 +50,7 @@ public class JsonConverter implements Converter<String, List<AbstractRule>> {
public List<AbstractRule> convert(String source) {
List<AbstractRule> ruleList = new ArrayList<>();
if (StringUtils.isEmpty(source)) {
logger.info(
logger.warn(
"Sentinel JsonConverter can not convert rules because source is empty");
return ruleList;
}
@ -68,7 +70,7 @@ public class JsonConverter implements Converter<String, List<AbstractRule>> {
List<AbstractRule> rules = Arrays.asList(convertFlowRule(itemJson),
convertDegradeRule(itemJson), convertSystemRule(itemJson),
convertAuthorityRule(itemJson));
convertAuthorityRule(itemJson), convertParamFlowRule(itemJson));
List<AbstractRule> convertRuleList = rules.stream()
.filter(rule -> !ObjectUtils.isEmpty(rule))
@ -101,8 +103,6 @@ public class JsonConverter implements Converter<String, List<AbstractRule>> {
throw new RuntimeException(
"Sentinel JsonConverter convert error: " + e.getMessage(), e);
}
logger.info("Sentinel JsonConverter convert {} rules: {}", ruleList.size(),
ruleList);
return ruleList;
}
@ -154,4 +154,15 @@ public class JsonConverter implements Converter<String, List<AbstractRule>> {
return rule;
}
private ParamFlowRule convertParamFlowRule(String json) {
ParamFlowRule rule = null;
try {
rule = objectMapper.readValue(json, ParamFlowRule.class);
}
catch (Exception e) {
// ignore
}
return rule;
}
}

View File

@ -18,6 +18,7 @@ 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.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.fasterxml.jackson.core.JsonProcessingException;
@ -32,7 +33,7 @@ import com.fasterxml.jackson.dataformat.xml.XmlMapper;
* @see DegradeRule
* @see SystemRule
* @see AuthorityRule
* @see XmlMapper
* @see ParamFlowRule
*/
public class XmlConverter implements Converter<String, List<AbstractRule>> {
@ -48,7 +49,7 @@ public class XmlConverter implements Converter<String, List<AbstractRule>> {
public List<AbstractRule> convert(String source) {
List<AbstractRule> ruleList = new ArrayList<>();
if (StringUtils.isEmpty(source)) {
logger.info(
logger.warn(
"Sentinel XmlConverter can not convert rules because source is empty");
return ruleList;
}
@ -68,7 +69,7 @@ public class XmlConverter implements Converter<String, List<AbstractRule>> {
List<AbstractRule> rules = Arrays.asList(convertFlowRule(itemXml),
convertDegradeRule(itemXml), convertSystemRule(itemXml),
convertAuthorityRule(itemXml));
convertAuthorityRule(itemXml), convertParamFlowRule(itemXml));
List<AbstractRule> convertRuleList = rules.stream()
.filter(rule -> !ObjectUtils.isEmpty(rule))
@ -101,8 +102,6 @@ public class XmlConverter implements Converter<String, List<AbstractRule>> {
throw new RuntimeException(
"Sentinel XmlConverter convert error: " + e.getMessage(), e);
}
logger.info("Sentinel XmlConverter convert {} rules: {}", ruleList.size(),
ruleList);
return ruleList;
}
@ -154,4 +153,15 @@ public class XmlConverter implements Converter<String, List<AbstractRule>> {
return rule;
}
private ParamFlowRule convertParamFlowRule(String json) {
ParamFlowRule rule = null;
try {
rule = xmlMapper.readValue(json, ParamFlowRule.class);
}
catch (Exception e) {
// ignore
}
return rule;
}
}

View File

@ -35,6 +35,18 @@
<artifactId>sentinel-dubbo-adapter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>

View File

@ -26,6 +26,7 @@ import org.springframework.cloud.alibaba.sentinel.datasource.config.DataSourcePr
import org.springframework.core.Ordered;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.log.LogBase;
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
/**
@ -48,11 +49,6 @@ public class SentinelProperties {
*/
private boolean enabled = true;
/**
* charset when sentinel write or search metric file {@link SentinelConfig#CHARSET}
*/
private String charset = "UTF-8";
/**
* configurations about datasource, like 'nacos', 'apollo', 'file', 'zookeeper'
*/
@ -91,6 +87,12 @@ public class SentinelProperties {
@NestedConfigurationProperty
private Flow flow = new Flow();
/**
* sentinel log configuration {@link LogBase}
*/
@NestedConfigurationProperty
private Log log = new Log();
public boolean isEager() {
return eager;
}
@ -107,14 +109,6 @@ public class SentinelProperties {
this.flow = flow;
}
public String getCharset() {
return charset;
}
public void setCharset(String charset) {
this.charset = charset;
}
public Transport getTransport() {
return transport;
}
@ -163,6 +157,14 @@ public class SentinelProperties {
this.datasource = datasource;
}
public Log getLog() {
return log;
}
public void setLog(Log log) {
this.log = log;
}
public static class Flow {
/**
@ -208,6 +210,12 @@ public class SentinelProperties {
*/
private String fileTotalCount;
/**
* charset when sentinel write or search metric file
* {@link SentinelConfig#CHARSET}
*/
private String charset = "UTF-8";
public String getFileSingleSize() {
return fileSingleSize;
}
@ -223,6 +231,14 @@ public class SentinelProperties {
public void setFileTotalCount(String fileTotalCount) {
this.fileTotalCount = fileTotalCount;
}
public String getCharset() {
return charset;
}
public void setCharset(String charset) {
this.charset = charset;
}
}
public static class Transport {
@ -299,4 +315,34 @@ public class SentinelProperties {
}
}
public static class Log {
/**
* sentinel log base dir
*/
private String dir;
/**
* distinguish the log file by pid number
*/
private boolean switchPid = false;
public String getDir() {
return dir;
}
public void setDir(String dir) {
this.dir = dir;
}
public boolean isSwitchPid() {
return switchPid;
}
public void setSwitchPid(boolean switchPid) {
this.switchPid = switchPid;
}
}
}

View File

@ -16,7 +16,11 @@
package org.springframework.cloud.alibaba.sentinel.annotation;
import java.lang.annotation.*;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author fangjian
@ -24,7 +28,7 @@ import java.lang.annotation.*;
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SentinelProtect {
public @interface SentinelRestTemplate {
String blockHandler() default "";

View File

@ -21,7 +21,6 @@ import java.util.Optional;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -41,6 +40,7 @@ import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig;
import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.init.InitExecutor;
import com.alibaba.csp.sentinel.log.LogBase;
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
import com.alibaba.csp.sentinel.util.AppNameUtil;
@ -71,7 +71,6 @@ public class SentinelAutoConfiguration {
@PostConstruct
private void init() {
if (StringUtils.isEmpty(System.getProperty(AppNameUtil.APP_NAME))
&& StringUtils.hasText(projectName)) {
System.setProperty(AppNameUtil.APP_NAME, projectName);
@ -93,8 +92,9 @@ public class SentinelAutoConfiguration {
properties.getTransport().getHeartbeatIntervalMs());
}
if (StringUtils.isEmpty(System.getProperty(SentinelConfig.CHARSET))
&& StringUtils.hasText(properties.getCharset())) {
System.setProperty(SentinelConfig.CHARSET, properties.getCharset());
&& StringUtils.hasText(properties.getMetric().getCharset())) {
System.setProperty(SentinelConfig.CHARSET,
properties.getMetric().getCharset());
}
if (StringUtils
.isEmpty(System.getProperty(SentinelConfig.SINGLE_METRIC_FILE_SIZE))
@ -113,10 +113,19 @@ public class SentinelAutoConfiguration {
System.setProperty(SentinelConfig.COLD_FACTOR,
properties.getFlow().getColdFactor());
}
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);
@ -146,26 +155,22 @@ public class SentinelAutoConfiguration {
}
@Bean("sentinel-json-converter")
public JsonConverter jsonConverter(
@Qualifier("sentinel-object-mapper") ObjectMapper objectMapper) {
return new JsonConverter(objectMapper);
public JsonConverter jsonConverter() {
return new JsonConverter(objectMapper());
}
@Bean("sentinel-object-mapper")
public ObjectMapper objectMapper() {
private ObjectMapper objectMapper() {
return new ObjectMapper();
}
@ConditionalOnClass(XmlMapper.class)
protected static class SentinelXmlConfiguration {
@Bean("sentinel-xml-converter")
public XmlConverter xmlConverter(
@Qualifier("sentinel-xml-mapper") XmlMapper xmlMapper) {
return new XmlConverter(xmlMapper);
public XmlConverter xmlConverter() {
return new XmlConverter(xmlMapper());
}
@Bean("sentinel-xml-mapper")
public XmlMapper xmlMapper() {
private XmlMapper xmlMapper() {
return new XmlMapper();
}
}

View File

@ -25,17 +25,17 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect;
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.core.type.StandardMethodMetadata;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
/**
* PostProcessor handle @SentinelProtect Annotation, add interceptor for RestTemplate
* PostProcessor handle @SentinelRestTemplate Annotation, add interceptor for RestTemplate
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
* @see SentinelProtect
* @see SentinelRestTemplate
* @see SentinelProtectInterceptor
*/
public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
@ -43,16 +43,16 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces
@Autowired
private ApplicationContext applicationContext;
private ConcurrentHashMap<String, SentinelProtect> cache = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, SentinelRestTemplate> cache = new ConcurrentHashMap<>();
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition,
Class<?> beanType, String beanName) {
if (checkSentinelProtect(beanDefinition, beanType)) {
SentinelProtect sentinelProtect = ((StandardMethodMetadata) beanDefinition
SentinelRestTemplate sentinelRestTemplate = ((StandardMethodMetadata) beanDefinition
.getSource()).getIntrospectedMethod()
.getAnnotation(SentinelProtect.class);
cache.put(beanName, sentinelProtect);
.getAnnotation(SentinelRestTemplate.class);
cache.put(beanName, sentinelRestTemplate);
}
}
@ -61,26 +61,26 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces
return beanType == RestTemplate.class
&& beanDefinition.getSource() instanceof StandardMethodMetadata
&& ((StandardMethodMetadata) beanDefinition.getSource())
.isAnnotated(SentinelProtect.class.getName());
.isAnnotated(SentinelRestTemplate.class.getName());
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (cache.containsKey(beanName)) {
// add interceptor for each RestTemplate with @SentinelProtect annotation
// add interceptor for each RestTemplate with @SentinelRestTemplate annotation
StringBuilder interceptorBeanName = new StringBuilder();
SentinelProtect sentinelProtect = cache.get(beanName);
SentinelRestTemplate sentinelRestTemplate = cache.get(beanName);
interceptorBeanName
.append(StringUtils.uncapitalize(
SentinelProtectInterceptor.class.getSimpleName()))
.append("_")
.append(sentinelProtect.blockHandlerClass().getSimpleName())
.append(sentinelProtect.blockHandler()).append("_")
.append(sentinelProtect.fallbackClass().getSimpleName())
.append(sentinelProtect.fallback());
.append(sentinelRestTemplate.blockHandlerClass().getSimpleName())
.append(sentinelRestTemplate.blockHandler()).append("_")
.append(sentinelRestTemplate.fallbackClass().getSimpleName())
.append(sentinelRestTemplate.fallback());
RestTemplate restTemplate = (RestTemplate) bean;
registerBean(interceptorBeanName.toString(), sentinelProtect);
registerBean(interceptorBeanName.toString(), sentinelRestTemplate);
SentinelProtectInterceptor sentinelProtectInterceptor = applicationContext
.getBean(interceptorBeanName.toString(),
SentinelProtectInterceptor.class);
@ -90,13 +90,13 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces
}
private void registerBean(String interceptorBeanName,
SentinelProtect sentinelProtect) {
SentinelRestTemplate sentinelRestTemplate) {
// register SentinelProtectInterceptor bean
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext
.getAutowireCapableBeanFactory();
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
.genericBeanDefinition(SentinelProtectInterceptor.class);
beanDefinitionBuilder.addConstructorArgValue(sentinelProtect);
beanDefinitionBuilder.addConstructorArgValue(sentinelRestTemplate);
BeanDefinition interceptorBeanDefinition = beanDefinitionBuilder
.getRawBeanDefinition();
beanFactory.registerBeanDefinition(interceptorBeanName,

View File

@ -22,7 +22,7 @@ import java.net.URI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect;
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
@ -37,19 +37,19 @@ import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.util.StringUtil;
/**
* Interceptor using by SentinelProtect and SentinelProtectInterceptor
* Interceptor using by SentinelRestTemplate
*
* @author fangjian
*/
public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor {
private static final Logger logger = LoggerFactory
private static final Logger logger = LoggerFactory
.getLogger(SentinelProtectInterceptor.class);
private SentinelProtect sentinelProtect;
private SentinelRestTemplate sentinelRestTemplate;
public SentinelProtectInterceptor(SentinelProtect sentinelProtect) {
this.sentinelProtect = sentinelProtect;
public SentinelProtectInterceptor(SentinelRestTemplate sentinelRestTemplate) {
this.sentinelRestTemplate = sentinelRestTemplate;
}
@Override
@ -92,15 +92,16 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor
Object[] args = new Object[] { ex };
// handle degrade
if (isDegradeFailure(ex)) {
Method method = extractFallbackMethod(sentinelProtect.fallback(),
sentinelProtect.fallbackClass());
Method method = extractFallbackMethod(sentinelRestTemplate.fallback(),
sentinelRestTemplate.fallbackClass());
if (method != null) {
method.invoke(null, args);
}
}
// handle block
Method blockHandler = extractBlockHandlerMethod(sentinelProtect.blockHandler(),
sentinelProtect.blockHandlerClass());
Method blockHandler = extractBlockHandlerMethod(
sentinelRestTemplate.blockHandler(),
sentinelRestTemplate.blockHandlerClass());
if (blockHandler != null) {
blockHandler.invoke(null, args);
}

View File

@ -32,6 +32,8 @@ 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.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;
@ -58,10 +60,12 @@ public class SentinelEndpoint {
List<FlowRule> flowRules = FlowRuleManager.getRules();
List<DegradeRule> degradeRules = DegradeRuleManager.getRules();
List<SystemRule> systemRules = SystemRuleManager.getRules();
List<ParamFlowRule> paramFlowRules = ParamFlowRuleManager.getRules();
result.put("properties", sentinelProperties);
result.put("FlowRules", flowRules);
result.put("DegradeRules", degradeRules);
result.put("SystemRules", systemRules);
result.put("ParamFlowRule", paramFlowRules);
result.put("datasources", new HashMap<String, Object>());
dataSourceHandler.getDataSourceBeanNameList().forEach(dataSourceBeanName -> {
ReadableDataSource dataSource = applicationContext.getBean(dataSourceBeanName,

View File

@ -0,0 +1,55 @@
/*
* 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.feign;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import feign.Contract;
import feign.MethodMetadata;
/**
*
* Using static field {@link SentinelContractHolder#metadataMap} to hold
* {@link MethodMetadata} data
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
public class SentinelContractHolder implements Contract {
private final Contract delegate;
/**
* map key is constructed by ClassFullName + configKey. configKey is constructed by
* {@link feign.Feign#configKey}
*/
public final static Map<String, MethodMetadata> metadataMap = new HashMap();
public SentinelContractHolder(Contract delegate) {
this.delegate = delegate;
}
@Override
public List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType) {
List<MethodMetadata> metadatas = delegate.parseAndValidatateMetadata(targetType);
metadatas.forEach(metadata -> metadataMap
.put(targetType.getName() + metadata.configKey(), metadata));
return metadatas;
}
}

View File

@ -0,0 +1,149 @@
/*
* 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.feign;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.cloud.openfeign.FeignContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.ReflectionUtils;
import feign.Contract;
import feign.Feign;
import feign.InvocationHandlerFactory;
import feign.Target;
import feign.hystrix.FallbackFactory;
import feign.hystrix.HystrixFeign;
/**
* {@link Feign.Builder} like {@link HystrixFeign.Builder}
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
public class SentinelFeign {
public static Builder builder() {
return new Builder();
}
public static final class Builder extends Feign.Builder
implements ApplicationContextAware {
private Contract contract = new Contract.Default();
private ApplicationContext applicationContext;
private FeignContext feignContext;
@Override
public Feign.Builder invocationHandlerFactory(
InvocationHandlerFactory invocationHandlerFactory) {
throw new UnsupportedOperationException();
}
@Override
public Builder contract(Contract contract) {
this.contract = contract;
return this;
}
@Override
public Feign build() {
super.invocationHandlerFactory(new InvocationHandlerFactory() {
@Override
public InvocationHandler create(Target target,
Map<Method, MethodHandler> dispatch) {
// using reflect get fallback and fallbackFactory properties from
// FeignClientFactoryBean because FeignClientFactoryBean is a package
// level class, we can not use it in our package
Object feignClientFactoryBean = Builder.this.applicationContext
.getBean("&" + target.type().getName());
Class fallback = (Class) getFieldValue(feignClientFactoryBean,
"fallback");
Class fallbackFactory = (Class) getFieldValue(feignClientFactoryBean,
"fallbackFactory");
String name = (String) getFieldValue(feignClientFactoryBean, "name");
Object fallbackInstance;
FallbackFactory fallbackFactoryInstance;
// check fallback and fallbackFactory properties
if (void.class != fallback) {
fallbackInstance = getFromContext(name, "fallback", fallback,
target.type());
return new SentinelInvocationHandler(target, dispatch,
new FallbackFactory.Default(fallbackInstance));
}
if (void.class != fallbackFactory) {
fallbackFactoryInstance = (FallbackFactory) getFromContext(name,
"fallbackFactory", fallbackFactory,
FallbackFactory.class);
return new SentinelInvocationHandler(target, dispatch,
fallbackFactoryInstance);
}
return new SentinelInvocationHandler(target, dispatch);
}
private Object getFromContext(String name, String type,
Class fallbackType, Class targetType) {
Object fallbackInstance = feignContext.getInstance(name,
fallbackType);
if (fallbackInstance == null) {
throw new IllegalStateException(String.format(
"No %s instance of type %s found for feign client %s",
type, fallbackType, name));
}
if (!targetType.isAssignableFrom(fallbackType)) {
throw new IllegalStateException(String.format(
"Incompatible %s instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
type, fallbackType, targetType, name));
}
return fallbackInstance;
}
});
super.contract(new SentinelContractHolder(contract));
return super.build();
}
private Object getFieldValue(Object instance, String fieldName) {
Field field = ReflectionUtils.findField(instance.getClass(), fieldName);
field.setAccessible(true);
try {
return field.get(instance);
}
catch (IllegalAccessException e) {
// ignore
}
return null;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
feignContext = this.applicationContext.getBean(FeignContext.class);
}
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.sentinel.feign;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import com.alibaba.csp.sentinel.SphU;
import feign.Feign;
/**
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
@Configuration
@ConditionalOnClass({ SphU.class, Feign.class })
public class SentinelFeignAutoConfiguration {
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "feign.sentinel.enabled")
public Feign.Builder feignSentinelBuilder() {
return SentinelFeign.builder();
}
}

View File

@ -0,0 +1,172 @@
/*
* 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.feign;
import static feign.Util.checkNotNull;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.LinkedHashMap;
import java.util.Map;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import feign.Feign;
import feign.InvocationHandlerFactory.MethodHandler;
import feign.MethodMetadata;
import feign.Target;
import feign.hystrix.FallbackFactory;
/**
* {@link InvocationHandler} handle invocation that protected by Sentinel
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
public class SentinelInvocationHandler implements InvocationHandler {
private final Target<?> target;
private final Map<Method, MethodHandler> dispatch;
private FallbackFactory fallbackFactory;
private Map<Method, Method> fallbackMethodMap;
SentinelInvocationHandler(Target<?> target, Map<Method, MethodHandler> dispatch,
FallbackFactory fallbackFactory) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch");
this.fallbackFactory = fallbackFactory;
this.fallbackMethodMap = toFallbackMethod(dispatch);
}
SentinelInvocationHandler(Target<?> target, Map<Method, MethodHandler> dispatch) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch");
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args)
throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object otherHandler = args.length > 0 && args[0] != null
? Proxy.getInvocationHandler(args[0])
: null;
return equals(otherHandler);
}
catch (IllegalArgumentException e) {
return false;
}
}
else if ("hashCode".equals(method.getName())) {
return hashCode();
}
else if ("toString".equals(method.getName())) {
return toString();
}
Object result;
MethodHandler methodHandler = this.dispatch.get(method);
// only handle by HardCodedTarget
if (target instanceof Target.HardCodedTarget) {
Target.HardCodedTarget hardCodedTarget = (Target.HardCodedTarget) target;
MethodMetadata methodMetadata = SentinelContractHolder.metadataMap
.get(method.getDeclaringClass().getName()
+ Feign.configKey(method.getDeclaringClass(), method));
// resource default is HttpMethod:protocol://url
String resourceName = methodMetadata.template().method().toUpperCase() + ":"
+ hardCodedTarget.url() + methodMetadata.template().url();
Entry entry = null;
try {
ContextUtil.enter(resourceName);
entry = SphU.entry(resourceName, EntryType.OUT, 1, args);
result = methodHandler.invoke(args);
}
catch (Throwable ex) {
// fallback handle
if (!BlockException.isBlockException(ex)) {
Tracer.trace(ex);
}
if (fallbackFactory != null) {
try {
Object fallbackResult = fallbackMethodMap.get(method)
.invoke(fallbackFactory.create(ex), args);
return fallbackResult;
}
catch (IllegalAccessException e) {
// shouldn't happen as method is public due to being an interface
throw new AssertionError(e);
}
catch (InvocationTargetException e) {
throw new AssertionError(e.getCause());
}
}
else {
// throw exception if fallbackFactory is null
throw ex;
}
}
finally {
if (entry != null) {
entry.exit();
}
ContextUtil.exit();
}
}
else {
// other target type using default strategy
result = methodHandler.invoke(args);
}
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SentinelInvocationHandler) {
SentinelInvocationHandler other = (SentinelInvocationHandler) obj;
return target.equals(other.target);
}
return false;
}
@Override
public int hashCode() {
return target.hashCode();
}
@Override
public String toString() {
return target.toString();
}
static Map<Method, Method> toFallbackMethod(Map<Method, MethodHandler> dispatch) {
Map<Method, Method> result = new LinkedHashMap<>();
for (Method method : dispatch.keySet()) {
method.setAccessible(true);
result.put(method, method);
}
return result;
}
}

View File

@ -12,12 +12,6 @@
"defaultValue": false,
"description": "earlier initialize heart-beat when the spring container starts when the transport dependency is on classpath, the configuration is effective."
},
{
"name": "spring.cloud.sentinel.charset",
"type": "java.lang.String",
"defaultValue": "UTF-8",
"description": "charset when sentinel write or search metric file."
},
{
"name": "spring.cloud.sentinel.transport.port",
"type": "java.lang.String",
@ -40,6 +34,12 @@
"defaultValue": "Integer.MIN_VALUE",
"description": "sentinel filter chain order, will be set to FilterRegistrationBean."
},
{
"name": "spring.cloud.sentinel.metric.charset",
"type": "java.lang.String",
"defaultValue": "UTF-8",
"description": "charset when sentinel write or search metric file."
},
{
"name": "spring.cloud.sentinel.metric.fileSingleSize",
"type": "java.lang.String",
@ -50,6 +50,17 @@
"type": "java.lang.String",
"description": "the total metric file count."
},
{
"name": "spring.cloud.sentinel.log.dir",
"type": "java.lang.String",
"description": "log base directory."
},
{
"name": "spring.cloud.sentinel.log.switch-pid",
"type": "java.lang.Boolean",
"defaultValue": false,
"description": "log file should with pid."
},
{
"name": "spring.cloud.sentinel.servlet.blockPage",
"type": "java.lang.String",

View File

@ -1,4 +1,5 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.alibaba.sentinel.SentinelWebAutoConfiguration,\
org.springframework.cloud.alibaba.sentinel.endpoint.SentinelEndpointAutoConfiguration,\
org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration
org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration,\
org.springframework.cloud.alibaba.sentinel.feign.SentinelFeignAutoConfiguration

View File

@ -22,7 +22,7 @@ import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect;
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;
@ -92,13 +92,13 @@ public class SentinelAutoConfigurationTests {
static class SentinelTestConfiguration {
@Bean
@SentinelProtect
@SentinelRestTemplate
RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
@SentinelProtect(blockHandlerClass = ExceptionUtil.class, blockHandler = "handleException")
@SentinelRestTemplate(blockHandlerClass = ExceptionUtil.class, blockHandler = "handleException")
RestTemplate restTemplateWithBlockClass() {
return new RestTemplate();
}

View File

@ -13,6 +13,7 @@
<description>Spring Cloud Alibaba Starters</description>
<modules>
<module>spring-cloud-starter-alibaba-nacos-config</module>
<module>spring-cloud-starter-alibaba-nacos-config-server</module>
<module>spring-cloud-starter-alibaba-nacos-discovery</module>
<module>spring-cloud-starter-alibaba-sentinel</module>
<module>spring-cloud-starter-stream-rocketmq</module>

View File

@ -0,0 +1,20 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba</artifactId>
<version>0.2.1.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-starter-alibaba-nacos-config-server</artifactId>
<name>Spring Cloud Starter Alibaba Nacos Config Server</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>
</project>