From 058496d625e33c0cf3db6ff6a8efe63c22f3d80b Mon Sep 17 00:00:00 2001 From: YuLuo Date: Fri, 5 Jul 2024 11:06:14 +0800 Subject: [PATCH] example: update sentinel example (#3709) Signed-off-by: yuluo-yx --- .../docs/en/docker-compose-deployment.md | 2 +- .../docs/en/kubernetes-deployment.md | 2 +- .../docs/en/local-deployment.md | 2 +- .../docs/zh/docker-compose-deploy-zh.md | 11 +- .../docs/zh/kubernetes-deployment-zh.md | 11 +- .../docs/zh/local-deployment-zh.md | 11 +- .../sentinel-example/README-zh.md | 319 ++++++++++++++++++ .../sentinel-example/README.md | 316 +++++++++++++++++ .../images/image-20240428171245517.png | Bin 0 -> 40300 bytes .../images/image-20240428171413303.png | Bin 0 -> 30207 bytes .../images/image-20240428171608519.png | Bin 0 -> 41921 bytes .../images/image-20240428171907705.png | Bin 0 -> 11748 bytes .../readme-zh.md | 65 ---- .../sentinel-circuitbreaker-example/readme.md | 63 ---- .../src/main/resources/application.yml | 19 +- .../sentinel-circuitbreaker-rules.yml | 16 + .../sentinel-core-example/readme-zh.md | 232 ------------- .../sentinel-core-example/readme.md | 240 ------------- ...tion.java => SentinelCoreApplication.java} | 4 +- .../src/main/resources/application.properties | 40 --- .../src/main/resources/application.yml | 80 +++++ .../sentinel-openfeign-example/pom.xml | 4 + .../cloud/examples/OpenFeignApplication.java | 4 +- .../SentinelRulesConfiguration.java | 9 +- .../src/main/resources/application.yml | 22 +- .../SentinelRulesConfiguration.java | 3 +- .../src/main/resources/application.yml | 24 +- ...SentinelSpringCloudGatewayApplication.java | 1 + .../src/main/resources/application.yaml | 25 +- .../src/main/resources/application.properties | 8 - .../src/main/resources/application.yml | 40 +++ 31 files changed, 891 insertions(+), 682 deletions(-) create mode 100644 spring-cloud-alibaba-examples/sentinel-example/README-zh.md create mode 100644 spring-cloud-alibaba-examples/sentinel-example/README.md create mode 100644 spring-cloud-alibaba-examples/sentinel-example/images/image-20240428171245517.png create mode 100644 spring-cloud-alibaba-examples/sentinel-example/images/image-20240428171413303.png create mode 100644 spring-cloud-alibaba-examples/sentinel-example/images/image-20240428171608519.png create mode 100644 spring-cloud-alibaba-examples/sentinel-example/images/image-20240428171907705.png delete mode 100644 spring-cloud-alibaba-examples/sentinel-example/sentinel-circuitbreaker-example/readme-zh.md delete mode 100644 spring-cloud-alibaba-examples/sentinel-example/sentinel-circuitbreaker-example/readme.md delete mode 100644 spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md delete mode 100644 spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme.md rename spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/java/com/alibaba/cloud/examples/{ServiceApplication.java => SentinelCoreApplication.java} (95%) delete mode 100644 spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/application.properties create mode 100644 spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/application.yml delete mode 100644 spring-cloud-alibaba-examples/sentinel-example/sentinel-webflux-example/src/main/resources/application.properties create mode 100644 spring-cloud-alibaba-examples/sentinel-example/sentinel-webflux-example/src/main/resources/application.yml diff --git a/spring-cloud-alibaba-examples/integrated-example/docs/en/docker-compose-deployment.md b/spring-cloud-alibaba-examples/integrated-example/docs/en/docker-compose-deployment.md index c643f8b53..d11c2191d 100644 --- a/spring-cloud-alibaba-examples/integrated-example/docs/en/docker-compose-deployment.md +++ b/spring-cloud-alibaba-examples/integrated-example/docs/en/docker-compose-deployment.md @@ -141,6 +141,6 @@ If you are interested or want to go deeper, you are welcome to study the individ - Nacos examples - [Nacos config example](../../../nacos-example/readme.md) - [Nacos discovery example](../../../nacos-example/readme.md) -- [Sentinel core example](../../../sentinel-example/sentinel-core-example/readme.md) +- [Sentinel core example](../../../sentinel-example/README.md) - [Seata example](../../../seata-example/readme.md) - [RocketMQ example](../../../rocketmq-example/readme.md) diff --git a/spring-cloud-alibaba-examples/integrated-example/docs/en/kubernetes-deployment.md b/spring-cloud-alibaba-examples/integrated-example/docs/en/kubernetes-deployment.md index e1ac08ac3..ebd82cf3c 100644 --- a/spring-cloud-alibaba-examples/integrated-example/docs/en/kubernetes-deployment.md +++ b/spring-cloud-alibaba-examples/integrated-example/docs/en/kubernetes-deployment.md @@ -119,6 +119,6 @@ Of course, there is more to each component than just what is demonstrated in the - Nacos examples - [Nacos config example](../../../nacos-example/readme.md) - [Nacos discovery example](../../../nacos-example/readme.md) -- [Sentinel core example](../../../sentinel-example/sentinel-core-example/readme.md) +- [Sentinel core example](../../../sentinel-example/README.md) - [Seata example](../../../seata-example/readme.md) - [RocketMQ example](../../../rocketmq-example/readme.md) \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/integrated-example/docs/en/local-deployment.md b/spring-cloud-alibaba-examples/integrated-example/docs/en/local-deployment.md index 9fbd05e44..e83264ca1 100644 --- a/spring-cloud-alibaba-examples/integrated-example/docs/en/local-deployment.md +++ b/spring-cloud-alibaba-examples/integrated-example/docs/en/local-deployment.md @@ -207,6 +207,6 @@ If you are interested or want to go deeper, you are welcome to study the individ - Nacos examples - [Nacos config example](../../../nacos-example/readme.md) - [Nacos discovery example](../../../nacos-example/readme.md) -- [Sentinel core example](../../../sentinel-example/sentinel-core-example/readme.md) +- [Sentinel core example](../../../sentinel-example/README.md) - [Seata example](../../../seata-example/readme.md) - [RocketMQ example](../../../rocketmq-example/readme.md) diff --git a/spring-cloud-alibaba-examples/integrated-example/docs/zh/docker-compose-deploy-zh.md b/spring-cloud-alibaba-examples/integrated-example/docs/zh/docker-compose-deploy-zh.md index 815937aa1..3786a4e4b 100644 --- a/spring-cloud-alibaba-examples/integrated-example/docs/zh/docker-compose-deploy-zh.md +++ b/spring-cloud-alibaba-examples/integrated-example/docs/zh/docker-compose-deploy-zh.md @@ -140,9 +140,8 @@ docker-compose-env.yml 文件运行成功之后,添加 Nacos 配置: 当然各个组件的功能特性不仅仅只包含最佳实践中演示的这些,如果您感兴趣或是想要深入了解,欢迎学习各个组件的独立 example 相关文档。 - Nacos examples - - [Nacos config example](../../../nacos-example/readme-zh.md) - - [Nacos discovery example](../../../nacos-example/readme-zh.md) -- [Sentinel core example](../../../sentinel-example/sentinel-core-example/readme-zh.md) -- [Seata example](../../../seata-example/readme-zh.md) -- [RocketMQ example](../../../rocketmq-example/readme-zh.md) - + - [Nacos config example](../../../nacos-example/readme.md) + - [Nacos discovery example](../../../nacos-example/readme.md) +- [Sentinel core example](../../../sentinel-example/README-zh.md) +- [Seata example](../../../seata-example/readme.md) +- [RocketMQ example](../../../rocketmq-example/readme.md) diff --git a/spring-cloud-alibaba-examples/integrated-example/docs/zh/kubernetes-deployment-zh.md b/spring-cloud-alibaba-examples/integrated-example/docs/zh/kubernetes-deployment-zh.md index 20f955e61..fde6470a3 100644 --- a/spring-cloud-alibaba-examples/integrated-example/docs/zh/kubernetes-deployment-zh.md +++ b/spring-cloud-alibaba-examples/integrated-example/docs/zh/kubernetes-deployment-zh.md @@ -119,8 +119,9 @@ helm uninstall integrated-example 当然各个组件的功能特性不仅仅只包含最佳实践中演示的这些,如果您对SCA感兴趣或是想要深入了解SCA项目,欢迎阅览各个组件的独立 example 相关文档。 - Nacos examples - - [Nacos config example](../../../nacos-example/readme-zh.md) - - [Nacos discovery example](../../../nacos-example/readme-zh.md) -- [Sentinel core example](../../../sentinel-example/sentinel-core-example/readme-zh.md) -- [Seata example](../../../seata-example/readme-zh.md) -- [RocketMQ example](../../../rocketmq-example/readme-zh.md) \ No newline at end of file + - [Nacos config example](../../../nacos-example/readme.md) + - [Nacos discovery example](../../../nacos-example/readme.md) +- [Sentinel core example](../../../sentinel-example/README-zh.md) +- [Seata example](../../../seata-example/readme.md) +- [RocketMQ example](../../../rocketmq-example/readme.md) +- \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/integrated-example/docs/zh/local-deployment-zh.md b/spring-cloud-alibaba-examples/integrated-example/docs/zh/local-deployment-zh.md index c895c6989..866ec259e 100644 --- a/spring-cloud-alibaba-examples/integrated-example/docs/zh/local-deployment-zh.md +++ b/spring-cloud-alibaba-examples/integrated-example/docs/zh/local-deployment-zh.md @@ -204,8 +204,9 @@ sh bin/mqbroker 当然各个组件的功能特性不仅仅只包含最佳实践中演示的这些,如果您感兴趣或是想要深入了解,欢迎学习各个组件的独立 example 相关文档。 - Nacos examples - - [Nacos config example](../../../nacos-example/readme-zh.md) - - [Nacos discovery example](../../../nacos-example/readme-zh.md) -- [Sentinel core example](../../../sentinel-example/sentinel-core-example/readme-zh.md) -- [Seata example](../../../seata-example/readme-zh.md) -- [RocketMQ example](../../../rocketmq-example/readme-zh.md) \ No newline at end of file + - [Nacos config example](../../../nacos-example/readme.md) + - [Nacos discovery example](../../../nacos-example/readme.md) +- [Sentinel core example](../../../sentinel-example/README-zh.md) +- [Seata example](../../../seata-example/readme.md) +- [RocketMQ example](../../../rocketmq-example/readme.md) +- \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/sentinel-example/README-zh.md b/spring-cloud-alibaba-examples/sentinel-example/README-zh.md new file mode 100644 index 000000000..7d34ddbb9 --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/README-zh.md @@ -0,0 +1,319 @@ +# Spring Cloud Alibaba Sentinel Example + +## 项目说明 + +本 Example 项目演示如何使用 `spring-cloud-starter-alibaba-sentinel` 完成 Spring Cloud 应用中的流量治理功能。 + +[Sentinel](https://github.com/alibaba/Sentinel) 是阿里巴巴开源的分布式系统的流量防卫组件,Sentinel 以流量作为切入点,从流量控制,熔断降级,系统负载保护等多个维度保护服务的稳定性。 + +## Sentinel Example + +在本 Example 项目中,主要演示 Sentinel 断路器,整合 Spring Cloud Gateway 和 OpenFeign、RestTemplate 以及 Webclient 的使用。 + +### 下载并启动 Sentinel Console + +1. 首先需要获取 Sentinel 控制台,Sentinel Console 支持直接下载和源码构建两种方式 + + 1. 直接下载:[下载 Sentinel 控制台](https://github.com/alibaba/Sentinel/releases) + 2. 源码构建:进入 Sentinel [Github 项目页面](https://github.com/alibaba/Sentinel),将代码 clone 到本地自行编译打包,[参考此文档](https://github.com/alibaba/Sentinel/blob/1.8/sentinel-dashboard/README.md)。 + + +2. 启动控制台,执行 Java 命令 `java -jar sentinel-dashboard.jar` 完成 Sentinel 控制台的启动。 + + 控制台默认的监听端口为 `8080`。Sentinel 控制台使用 Spring Boot 编程模型开发,如果需要指定其他端口,请使用 Spring Boot 容器配置的标准方式,详情请参考 [Spring Boot 文档](https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-customizing-embedded-containers)。 + +### Sentinel Core Example + +在此 Example 模块中,主要演示如何使用 Sentinel 的基本功能完成 Spring Cloud 应用的流量管控。 在启动 Example 进行演示之前,先了解一下如何在 Spring Cloud 应用中接入 Sentinel 组件。 + +#### 项目编写 + +> **注意:本文档只是为了便于理解接入方式。本示例代码中已经完成接入工作,您无需再进行修改。** + +1. 首先,修改 `pom.xml` 文件,引入 Sentinel starter。 + + ```xml + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + ``` + +2. 接入限流埋点 + + - HTTP 埋点 + `spring-cloud-starter-alibaba-sentinel` 默认为所有的 HTTP 服务提供了限流埋点,如果只想对 HTTP 服务进行限流,那么只需要引入依赖,无需修改代码。 + + - 自定义埋点 + 如果需要对某个特定的方法进行限流或降级,可以通过 `@SentinelResource` 注解来完成限流的埋点,示例代码如下: + + ```java + @SentinelResource("resource") + public String hello() { + return "Hello"; + } + ``` + + 当然也可以通过原始的 `SphU.entry(xxx)` 方法进行埋点,可以参见 [Sentinel 文档](https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E5%AE%9A%E4%B9%89%E8%B5%84%E6%BA%90)。 + +3. 配置限流规则 + + Sentinel 提供了两种配置限流规则的方式:代码配置 和 控制台配置。本示例使用的方式为通过代码配置。 + + 1. 通过代码来实现限流规则的配置。一个简单的限流规则配置示例代码如下,更多限流规则配置详情请参考 [Sentinel 文档](https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E5%AE%9A%E4%B9%89%E8%A7%84%E5%88%99)。 + + ```java + List rules = new ArrayList(); + FlowRule rule = new FlowRule(); + rule.setResource(str); + // set limit qps to 10 + rule.setCount(10); + rule.setGrade(RuleConstant.FLOW_GRADE_QPS); + rule.setLimitApp("default"); + rules.add(rule); + FlowRuleManager.loadRules(rules); + ``` + + 2. 通过控制台进行限流规则配置请参考文章后面的图文说明。 + +#### 应用启动 + +1. 增加配置,在应用的 `/src/main/resources/application.yml` 中添加基本配置信息 + + ```yaml + server: + port: 18083 + + spring: + application: + name: sentinel-core-example + + cloud: + sentinel: + transport: + dashboard: localhost:8080 + ``` + +2. 启动应用,支持 IDE 直接启动和编译打包后启动。 + + 1. IDE直接启动:找到主类 `SentinelCoreApplication`,执行 main 方法启动应用。 + 2. 打包编译后启动:首先执行 `mvn clean package` 将工程编译打包,然后执行 `java -jar sentinel-core-example.jar` 启动应用。 + +#### 调用服务验证 + +使用 curl 命令分别调用两个 URL,可以看到访问成功。 + +```shell +$ curl http://localhost:18083/test +Blocked by Sentinel (flow limiting) + +$ curl http://localhost:18083/hello +Hello +``` + +#### 配置限流规则并验证 + +1. 访问 http://localhost:8080 页面,进行登陆,默认用户名和密码均为:`sentinel`。 + + 可以在左侧看到 `sentinel-core-example` 应用已经注册到了控制台,单击 **流控规则** ,可以看到目前的流控规则为空。 + + > **注意:如果您在控制台没有找到应用,请调用一下进行了 Sentinel 埋点的 URL 或方法,因为 Sentinel 使用了 lazy load 策略。详细的排查过程请参见 [Sentinel FAQ](https://github.com/alibaba/Sentinel/wiki/FAQ)。** + +image-20240428171413303 + +2. 配置 URL 限流规则:点击新增流控规则,资源名填写需要限流的 URL 相对路径,单机阈值选择需要限流的阈值,点击新增进行确认。(为了便于演示效果,这里将值设置成了 1)。 + +image-20240428171245517 + +3. 配置自定义限流规则:点击新增流控规则,资源名填写 `@SentinelResource` 注解 `value` 字段的值,单机阈值选择需要限流的阈值,点击新增进行确认。(为了便于演示效果,这里将值设置成了 1)。 + +image-20240428171608519 + +4. 访问 URL,当 QPS 超过 1 时,可以看到限流效果如下。 + + ![image-20240428171907705](./images/image-20240428171907705.png) + +#### 自定义限流处理逻辑 + +* 默认限流异常处理 + + URL 限流触发后默认处理逻辑是,直接返回 "Blocked by Sentinel (flow limiting)"。 如果需要自定义处理逻辑,实现的方式如下: + + ```java + public class CustomUrlBlockHandler implements UrlBlockHandler { + @Override + public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException { + // todo add your logic + } + } + + WebCallbackManager.setUrlBlockHandler(new CustomUrlBlockHandler()); + ``` + +* 使用 `@SentinelResource` 注解下的限流异常处理 + + 如果需要自定义处理逻辑,填写 `@SentinelResource` 注解的 `blockHandler` 属性(针对所有类型的 `BlockException`,需自行判断)或 `fallback` 属性(针对熔断降级异常),注意**对应方法的签名和位置有限制**,详情见 [Sentinel 注解支持文档](https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81#sentinelresource-%E6%B3%A8%E8%A7%A3)。示例实现如下: + + ```java + public class TestService { + + // blockHandler 是位于 ExceptionUtil 类下的 handleException 静态方法,需符合对应的类型限制. + @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class}) + public void test() { + System.out.println("Test"); + } + + // blockHandler 是位于当前类下的 exceptionHandler 方法,需符合对应的类型限制. + @SentinelResource(value = "hello", blockHandler = "exceptionHandler") + public String hello(long s) { + return String.format("Hello at %d", s); + } + + public String exceptionHandler(long s, BlockException ex) { + // Do some log here. + ex.printStackTrace(); + return "Oops, error occurred at " + s; + } + } + + public final class ExceptionUtil { + + public static void handleException(BlockException ex) { + System.out.println("Oops: " + ex.getClass().getCanonicalName()); + } + } + ``` + +一个简单的 `@SentinelResource` 示例可以见 [sentinel-demo-annotation-spring-aop](https://github.com/alibaba/Sentinel/tree/2021.x/sentinel-demo/sentinel-demo-annotation-spring-aop)。 + +### Sentinel Circuitbreaker Example + +本 Example 主要演示 OpenFeign 整合 Sentinel 断路器的使用。 + +#### 准备配置文件 + +1. 添加配置到配置中心。dataId 为 `sentinel-circuitbreaker-rules.yml` + + ```yml + feign: + circuitbreaker: + enabled: true # 开启 feign 断路器支持 + sentinel: + default-rule: default # 默认规则名称 + rules: + # 默认规则, 对所有 feign client 生效 + default: + - grade: 2 # 根据异常数目降级 + count: 1 + timeWindow: 15 # 降级后到半开状态的时间 + statIntervalMs: 1000 + minRequestAmount: 1 + # 只对 feign client user 生效 + user: + - grade: 2 + count: 1 + timeWindow: 15 + statIntervalMs: 1000 + minRequestAmount: 1 + # 只对 feign client user 的方法 feignMethod 生效 + # 括号里是参数类型, 多个逗号分割, 比如 user#method(boolean,String,Map) + "[user#feignMethod(boolean)]": + - grade: 2 + count: 1 + timeWindow: 10 + statIntervalMs: 1000 + minRequestAmount: 1 + ``` + +#### 验证配置生效 + +启动项目主类 `FeignCircuitBreakerApplication` + +##### 验证默认 Feign client 生效 + +先访问 http://localhost/test/default/false 2 次 (1秒内) +再访问 http://localhost/test/default/true 断路器处于打开状态 + +##### 验证指定 Feign client 生效 + +先访问 http://localhost/test/feign/false 2 次 (1秒内) +再访问 http://localhost/test/feign/true 断路器处于打开状态 + +##### 验证 Feign client 指定方法生效 + +先访问 http://localhost/test/feignMethod/false 2次 (1秒内) +再访问 http://localhost/test/feignMethod/true 断路器处于打开状态 + +#### 规则动态刷新 + +修改配置中心的规则, 再访问上述接口。 + +### Sentinel OpenFeign Example + +本 Example 演示 OpenFeing 与 Sentinel 的整合。Example 中使用 httpbin 充当后台 API 接口服务。 + +#### 项目编写 + +> **注意:本项目中代码已经完成相对应的功能,不需要再进行任何修改。** + +项目支持两种启动方式:通过主类 `OpenFeignApplication` 启动和 Jar 包启动两种方式。 + +#### 调用测试 + +项目启动完成之后,可以通过访问对应的 URL 访问,查看对应的 Sentinel 流控效果。 + +> **注意:项目中提供的 RestTemplate 和 Webclient Example 同理。** + +## Endpoint 信息查看 + +Spring Boot 应用支持通过 Endpoint 来暴露相关信息,`spring-cloud-starter-alibaba-sentinel` 也支持这一点。 + +在使用之前需要在 Maven 中添加 `spring-boot-starter-actuator`依赖,并在配置中允许 Endpoints 的访问。 +* Spring Boot 1.x 中添加配置 `management.security.enabled=false` +* Spring Boot 2.x 中添加配置 `management.endpoints.web.exposure.include=*` + +Spring Boot 1.x 可以通过访问 http://127.0.0.1:18083/sentinel 来查看 Sentinel Endpoint 的信息。Spring Boot 2.x 可以通过访问 http://127.0.0.1:18083/actuator/sentinel 来访问。 + +

+ +## 查看实时监控 +Sentinel 控制台支持实时监控查看,您可以通过 Sentinel 控制台查看各链路的请求的通过数和被限流数等信息。 +其中 `p_qps` 为通过(pass) 流控的 QPS,`b_qps` 为被限流 (block) 的 QPS。 + +

+ +## ReadableDataSource 支持 + +Sentinel 内部提供了[动态规则的扩展实现 ReadableDataSource](https://github.com/alibaba/Sentinel/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%99%E6%89%A9%E5%B1%95#datasource-%E6%89%A9%E5%B1%95)。 + +Sentinel starter 整合了目前存在的几类 ReadableDataSource。只需要在配置文件中进行相关配置,即可在 Spring 容器中自动注册 DataSource。 + +比如要定义两个ReadableDataSource,分别是 `FileRefreshableDataSource` 和 `NacosDataSource`,配置如下: + +```properties +spring.cloud.sentinel.datasource.ds1.file.file=classpath: degraderule.json +spring.cloud.sentinel.datasource.ds1.file.data-type=json + +spring.cloud.sentinel.datasource.ds2.nacos.server-addr=127.0.0.1:8848 +spring.cloud.sentinel.datasource.ds2.nacos.dataId=sentinel +spring.cloud.sentinel.datasource.ds2.nacos.groupId=DEFAULT_GROUP +spring.cloud.sentinel.datasource.ds2.nacos.data-type=json +``` + +`ds1` 和 `ds2` 表示ReadableDataSource的名称,可随意编写。`ds1` 和 `ds2` 后面的 `file` 和 `nacos` 表示ReadableDataSource的类型。 + +目前支持`file`, `nacos`, `zk`, `apollo`,`redis` 这5种类型。 + +其中`nacos`,`zk`,`apollo`,`redis` 这4种类型的使用需要加上对应的依赖`sentinel-datasource-nacos`, `sentinel-datasource-zookeeper`, `sentinel-datasource-apollo`, `sentinel-datasource-redis`。 + +当 `ReadableDataSource` 加载规则数据成功的时候,控制台会打印出相应的日志信息: + +``` +[Sentinel Starter] DataSource ds1-sentinel-file-datasource load 3 DegradeRule +[Sentinel Starter] DataSource ds2-sentinel-nacos-datasource load 2 FlowRule +``` + +## More +Sentinel 是一款功能强大的中间件,从流量控制,熔断降级,系统负载保护等多个维度保护服务的稳定性。此 Demo 仅演示了 使用 Sentinel 作为限流工具的使用,更多 Sentinel 相关的信息,请参考 [Sentinel 项目](https://github.com/alibaba/Sentinel)。 + +如果您对 `spring-cloud-starter-alibaba-sentinel` 有任何建议或想法,欢迎在 issue 中或者通过其他社区渠道向我们提出。 diff --git a/spring-cloud-alibaba-examples/sentinel-example/README.md b/spring-cloud-alibaba-examples/sentinel-example/README.md new file mode 100644 index 000000000..66c36fb58 --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/README.md @@ -0,0 +1,316 @@ +# Spring Cloud Alibaba Sentinel Example + +## Project description + +This Example project demonstrates how to use `spring-cloud-starter-alibaba-sentinel` to complete the traffic management function in Spring Cloud applications. + +[Sentinel](https://github.com/alibaba/Sentinel) It is the traffic defense component of Alibaba's open source distributed system. Sentinel takes traffic as the entry point to protect the stability of services from multiple dimensions such as traffic control, fuse degradation, system load protection, etc. + +## Sentinel Example + +In this Example project, the Sentinel circuit breaker is mainly demonstrated, and the Spring Cloud Gateway is integrated with the use of OpenFeign, RestTemplate, and Webclient. + +### Download and launch Sentinel Console + +1. First, you need to obtain the Sentinel Console, which supports direct download and source code construction + + 1. Direct download: [Download the Sentinel console](https://github.com/alibaba/Sentinel/releases) + 2. Source code construction: Enter Sentinel [Github project page](https://github.com/alibaba/Sentinel), clone the code to the local compilation and packaging [参考此文档](https://github.com/alibaba/Sentinel/blob/1.8/sentinel-dashboard/README.md). + + +2. Start the console and execute the Java command `java -jar sentinel-dashboard.jar` to finish starting the Sentinel console. + + The default console listening port is `8080`. The Sentinel console is developed using the Spring Boot programming model. If you need to specify other ports, please use the standard method of Spring Boot container configuration. For details, please refer to [Spring Boot documentation](https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-customizing-embedded-containers). + +### Sentinel Core Example + +This Example module mainly demonstrates how to use the basic functions of Sentinel to complete the traffic control of Spring Cloud applications. Before launching Example for a demonstration, let's look at how to access the Sentinel component in a Spring Cloud application. + +#### Project preparation + +> **Note: This document is only for the purpose of understanding the access method. The access work is done in this sample code, and you do not need to modify it.** + +1. First, modify `pom.xml` the file to introduce Sentinel starter. + + + ```xml + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + ``` + +2. Access current-limiting buried point + + - `spring-cloud-starter-alibaba-sentinel` By default, all HTTP services provide the current limiting embedded point. If you only want to limit the current of the HTTP service, you only need to introduce the dependency and do not need to modify the code. + + - Custom Buried Point If you need to limit or degrade a specific method, you can use `@SentinelResource` annotations to complete the current limiting buried point. The example code is as follows: + + + ```java + @SentinelResource("resource") + public String hello() { + return "Hello"; + } + ``` + + Of course, it can also be buried by the original `SphU.entry(xxx)` method, which can be seen [Sentinel documentation](https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E5%AE%9A%E4%B9%89%E8%B5%84%E6%BA%90). + +3. Configure the current limit rule + + Sentinel provides two ways to configure throttling rules: code configuration and console configuration. The method used in this example is configuration through code. + + 1. Configure rate limits by code. Here is a simple code example of rate limit configuration. For more configuration details, see [Sentinel documentation](https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E5%AE%9A%E4%B9%89%E8%A7%84%E5%88%99). + + ```java + List rules = new ArrayList(); + FlowRule rule = new FlowRule(); + rule.setResource(str); + // set limit qps to 10 + rule.setCount(10); + rule.setGrade(RuleConstant.FLOW_GRADE_QPS); + rule.setLimitApp("default"); + rules.add(rule); + FlowRuleManager.loadRules(rules); + ``` + 2. Please refer to the graphic description at the end of the article for current limiting rule configuration through the console. + +#### The application starts + +1. Add configuration, and add basic configuration information in the application `/src/main/resources/application.yml` + + ```yaml + server: + port: 18083 + + spring: + application: + name: sentinel-core-example + + cloud: + sentinel: + transport: + dashboard: localhost:8080 + ``` + +2. Start the application, support IDE direct start and start after compilation and packaging. + + 1. IDE direct startup: find the main class `SentinelCoreApplication` and execute the main method to start the application. + 2. Start after packaging and compiling: First execute `mvn clean package` to package the project compilation, and then execute `java -jar sentinel-core-example.jar` to start the application. + +#### Invoke service validation + +Use the curl command to call the two URLs separately, and you can see that the access is successful. + +```shell +$ curl http://localhost:18083/test +Blocked by Sentinel (flow limiting) + +$ curl http://localhost:18083/hello +Hello +``` + +#### Configure and verify the current limit rule + +1. Visit the http://localhost:8080 page and log in. The default user name and password are: `sentinel`. + + On the left, you can see that the Sentinel-Example application has been registered to the console. Click **Flow control rules** it, and you can see that the current flow control rule is empty. + + > ** Note: If you do not find the application in the console, please call the URL or method with Sentinel buried points, because Sentinel uses lazy load policy. For detailed troubleshooting procedures, see [Sentinel FAQ](https://github.com/alibaba/Sentinel/wiki/FAQ). ** + +

+ +2. Configure URL flow limit rule: click Add Flow Control Rule, fill in the URL relative path to be restricted for the resource name, select the threshold to be restricted for the single machine threshold, and click Add to confirm. (The value is set to 1 for demonstration purposes.). + +

+ +3. Configure custom current limiting rules: click Add a flow control rule, fill in `@SentinelResource` the value of the comment `value` field for the resource name, select the threshold for current limiting for the single machine threshold, and click Add to confirm. (The value is set to 1 for demonstration purposes.). + +

+ +4. Visit the URL. When the QPS exceeds 1, you can see the effect of limiting the current as follows. + +

+ +

+ +#### Custom current limit processing logic + +* Default Current Limit Exception Handling + + After the URL current limiting is triggered, the default processing logic is to directly return to "Blocked by Sentinel". If you need to customize the processing logic, the implementation method is as follows: + + ```java + public class CustomUrlBlockHandler implements UrlBlockHandler { + @Override + public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException { + // todo add your logic + } + } + + WebCallbackManager.setUrlBlockHandler(new CustomUrlBlockHandler()); + ``` + +* Current limit exception handling using `@SentinelResource` annotations + + If you need to customize the processing logic, fill in `@SentinelResource` the attribute of the note `blockHandler` (for all types `BlockException`, you need to make your own judgment) or `fallback` the attribute (for the fuse degradation exception). **There are restrictions on the signature and location of the corresponding method** See for [Sentinel Annotation Support Document](https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81#sentinelresource-%E6%B3%A8%E8%A7%A3) details. An example implementation is as follows: + + ```java + public class TestService { + + // blockHandler 是位于 ExceptionUtil 类下的 handleException 静态方法,需符合对应的类型限制. + @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class}) + public void test() { + System.out.println("Test"); + } + + // blockHandler 是位于当前类下的 exceptionHandler 方法,需符合对应的类型限制. + @SentinelResource(value = "hello", blockHandler = "exceptionHandler") + public String hello(long s) { + return String.format("Hello at %d", s); + } + + public String exceptionHandler(long s, BlockException ex) { + // Do some log here. + ex.printStackTrace(); + return "Oops, error occurred at " + s; + } + } + + public final class ExceptionUtil { + + public static void handleException(BlockException ex) { + System.out.println("Oops: " + ex.getClass().getCanonicalName()); + } + } + ``` + +A simple `@SentinelResource` example can be found in [sentinel-demo-annotation-spring-aop](https://github.com/alibaba/Sentinel/tree/2021.x/sentinel-demo/sentinel-demo-annotation-spring-aop). + +### Sentinel Circuitbreaker Example + +This Example mainly demonstrates the use of OpenFeign integrated Sentinel circuit breaker. + +#### Prepare the configuration file + +1. Add a configuration to the Configuration Center. DataId is `sentinel-circuitbreaker-rules.yml` + + ```yml + feign: + circuitbreaker: + enabled: true # 开启 feign 断路器支持 + sentinel: + default-rule: default # 默认规则名称 + rules: + # 默认规则, 对所有 feign client 生效 + default: + - grade: 2 # 根据异常数目降级 + count: 1 + timeWindow: 15 # 降级后到半开状态的时间 + statIntervalMs: 1000 + minRequestAmount: 1 + # 只对 feign client user 生效 + user: + - grade: 2 + count: 1 + timeWindow: 15 + statIntervalMs: 1000 + minRequestAmount: 1 + # 只对 feign client user 的方法 feignMethod 生效 + # 括号里是参数类型, 多个逗号分割, 比如 user#method(boolean,String,Map) + "[user#feignMethod(boolean)]": + - grade: 2 + count: 1 + timeWindow: 10 + statIntervalMs: 1000 + minRequestAmount: 1 + ``` + +#### Verify that the configuration is in effect + +Start the project main class `FeignCircuitBreakerApplication` + +##### Verify that the default Feign client takes effect + +Access http://localhost/test/default/false 2 times (within 1 second) and then access http://localhost/test/default/true the circuit breaker in the open state + +##### Verify that the specified Feign client is in effect + +Access http://localhost/test/feign/false 2 times (within 1 second) and then access http://localhost/test/feign/true the circuit breaker in the open state + +##### Verify that the Feign client specified method takes effect + +Access http://localhost/test/feignMethod/false 2 times (within 1 second) and then access http://localhost/test/feignMethod/true the circuit breaker in the open state + +#### Rule dynamic refresh + +Modify the rules of the configuration center, and then access the above interface. + +### Sentinel OpenFeign Example + +This Example demonstrates the integration of OpenFeing and Sentinel. In Example, httpbin is used as a background API interface service. + +#### Project preparation + +> **Note: The code in this project has completed the corresponding function and does not need to be modified.** + +The project supports two startup modes: startup through the main class `OpenFeignApplication` and startup through the Jar package. + +#### Call the test + +After the project is started, you can access the corresponding URL to view the corresponding Sentinel flow control effect. + +> **Note: The RestTemplate provided in the project is the same as the Webclient Example.** + +## Endpoint information viewing + +Spring Boot applications support the exposure of relevant information through Endpoints, `spring-cloud-starter-alibaba-sentinel` as well. + +Before using it, you need to add `spring-boot-starter-actuator` dependencies in Maven and allow Endpoints access in the configuration. +* Add configuration in Spring Boot 1.x +* Adding Configuration in Spring Boot 2.x + +Spring Boot 1.x can view Sentinel Endpoint information by visiting http://127.0.0.1:18083/sentinel . Spring Boot 2.x can be accessed by visiting http://127.0.0.1:18083/actuator/sentinel . + +

+ +## View real-time monitoring +The Sentinel console supports real-time monitoring and viewing. You can view information such as the number of requests passed and the number of throttled flows for each link through the Sentinel console. Where `p_qps` is a pass flow controlled QPS and `b_qps` is a blocked QPS. + +

+ +## ReadableData Source support + +The Sentinel provides [Implementation of ReadableDataSource by Extending Dynamic Rule](https://github.com/alibaba/Sentinel/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%99%E6%89%A9%E5%B1%95#datasource-%E6%89%A9%E5%B1%95) internally. + +Sentinel starter incorporates several classes of ReadableDataSources that exist today. The DataSource is automatically registered in the Spring container by simply making the relevant configuration in the configuration file. + + `FileRefreshableDataSource` and `NacosDataSource`, configured as follows: + +```properties +spring.cloud.sentinel.datasource.ds1.file.file=classpath: degraderule.json +spring.cloud.sentinel.datasource.ds1.file.data-type=json + +spring.cloud.sentinel.datasource.ds2.nacos.server-addr=127.0.0.1:8848 +spring.cloud.sentinel.datasource.ds2.nacos.dataId=sentinel +spring.cloud.sentinel.datasource.ds2.nacos.groupId=DEFAULT_GROUP +spring.cloud.sentinel.datasource.ds2.nacos.data-type=json +``` + + `ds1` And `ds2` the name that represents the ReadableDataSource. Feel free to write. The sum `nacos` following `file` the `ds1` and `ds2` represents the type of the ReadableDataSource. + +Currently supports `file`, `nacos`, `zk` `apollo`, `redis` these 5 types. + +Among them `nacos` `zk` `apollo`, the use of `redis` these four types requires the addition of corresponding dependencies `sentinel-datasource-nacos` `sentinel-datasource-zookeeper` `sentinel-datasource-apollo` `sentinel-datasource-redis`. + +When `ReadableDataSource` the rule data is loaded successfully, the console will print out the corresponding log information: + +``` +[Sentinel Starter] DataSource ds1-sentinel-file-datasource load 3 DegradeRule +[Sentinel Starter] DataSource ds2-sentinel-nacos-datasource load 2 FlowRule +``` + +## More +Sentinel is a powerful middleware that protects the stability of services from multiple dimensions such as flow control, fuse degradation, and system load protection. This Demo only demonstrates the use of Sentinel as a current limiting tool. For more information about Sentinel, please refer to [Project Sentinel](https://github.com/alibaba/Sentinel). + +If you `spring-cloud-starter-alibaba-sentinel` have any suggestions or ideas, please feel free to send them to us in the issue or through other community channels. diff --git a/spring-cloud-alibaba-examples/sentinel-example/images/image-20240428171245517.png b/spring-cloud-alibaba-examples/sentinel-example/images/image-20240428171245517.png new file mode 100644 index 0000000000000000000000000000000000000000..c0e390b0f2808b9ff2ffb54ce54a766b60d465cc GIT binary patch literal 40300 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 W!C!?8H8ho8La2dI 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 a=tEvfm#NF6VRE-XvuLQmVTJcCU;Hm@+uCXX literal 0 HcmV?d00001 diff --git a/spring-cloud-alibaba-examples/sentinel-example/images/image-20240428171608519.png b/spring-cloud-alibaba-examples/sentinel-example/images/image-20240428171608519.png new file mode 100644 index 0000000000000000000000000000000000000000..113cb292a520f78db9aab501e211dd9f86a6e1f2 GIT binary patch literal 41921 zcmeFYbySq!_b-m3q9Bd5phJmtOA8F$0@5Mf-7$cO41ypaUD6HG%^*mZbTf4K&;!GL zc*p0iyKa2f{r&#@{mz25p7qRmPVIg6-skn&CsajA8XJ=w6AcXwTUG}A4h`+03L4sd zgU9!QH}mE7OK4~>&}6}4>YgdP^FDfN9%-T|KD-wMvLF8h&_5@8{j{WKeA{OKu_0xg3Z&U&b;Nf-ezmb>zXL{nPx??*H= zDeC*8e?JEhiZ5dP{pcwE`1+p;O3)7Da!UPwen2BNvt0ULRC*jp{dfJ=Cod1t|9*V) z@&V+ZMi@S|;{N@(_zCag-rtWYc>hJu|9_)@FI4LjKmdUlIg@`JmkDfsF-PrLubof( z^cKq>6;(zf_YPYb!z#|d>&6qjw-DaiBo9Qi|1Gb@PgJ3;4*6_1>&eC)B^TpZl={#9 zZZZI)Z5rKQy7FX-&x=Iox&A-W3Bc&>L5Cf$Ml@kXls@>Ucl3?bz}pD0vbh=1xZK|_ z(ZYHANB$$y{lZ{av!sDvjd-DY4KGD_u7*8jV0!usPn}P+I=fv|%N*j;VNID;3KKun zDZHVUDP^~-MzVUows`4g2d|Q~PZVQ9&dI;W<_#GX7Zf?{(Plceh%HZj@**f_OZG|` zC|)sN^Qjov`E5nPNt=RNn;jf+__l645R*)jn{B7XxOC!P)oo_MCR4PNj4cJijqsN_ zDefYq4NS>kjW=Kv)`#TQWYky3K$n|d5qPF*|0T$)<&w5JraJlVbYL^>Q`5;E}8gYz#z&Xq(xLhY|tq%^~s1dwG19Qn!E z+vdT=*NskMJxpB73^>g7jgmT^oB37K-C7<+)%CfjjY*Y~_j{t(tW-rj1a3}`Q*I=` z=p!n(IFS2X6x^^Lz##5NCi z%Jur9Q*z&Umc#q*q-PZ|4`23>!Js7bA;n`iF;#~ufr=Vr8^XlVDfL1N@b-nM=K~`L zkwog;vy@ic+=gXVEe0+%tz4Tu;cJm$pS^!bfcdg9V7)QP{%cf06@`}6vj-kw9l7w% zKPuiAMo!HGbR7B5G^{1ZLqC3q6-|hW?I&Q<*G3(6+6ey}cdp{7B#X>|g{&nWJ=HVpb!p$XtEOQl;lh z%Qp=fNK_Q@F9wsoL>gW}*P7RbEt=}GbVH_3wK?al`89tw{`jow8Y!l)mZqtOx~a$F{{Nld<%5J~5rtxxhL-5#kR74_D$JRBr`k)RutW`?H?LkkXlX8!fL(T<>W}dO6FJyAlriqpJD*^?}3KKaNK|NIZP-x-RCg7gd1(lX>!p6#Ky%iIrPq#DE1V)FEMHajZ*Hd0;&N>&IJ zWjot9>q?LA<{jX}`!TI~elnJ4;^D&e_lLjy{JscZWPVypoEL1dI>Ewzf`fxeAkulT zvot%ahqB|R;qX-Q;iU13gAQfdOSp`S!g?w@LnmT#G%6Ldb}&}RqN{{>L=u1>zg{C# zQnFxFbx~4+GmEa&wjG| z8K&+RYJHj8R|03XC>2L~+eD;j7#T^sow?jQc9#dATDG5>9Kw<3;vSBGdl$cTU?4YfnHTVNhttQM^!U6mO10QPc8>%KX>7?2^6& z5?hf3HiNor#+NRY>+9c|iw~Z#iI})0{pJ=;WSaG#IDY07-uc0LqUM~1)H+8)sxxY+ zR5pTBd8zaG3B=e;N@%D-!)JX&-_4jhUz1VLYLqp#{5aHzI(lVO z;~9nqoEX<>=88x8)1{MwXeNdsQGCsQhbO#6*n5zwjC9-LI5qJoDd${zqQa3EpUgTt z{N+*8MfiA+YOWWtzV`Ju1_RHD^=Ne-oZIL*-5!G_EVbX^)bCE`_m#0qPgoi|N@R#< zKWjPk9P=6Kp4;j;&vE-ZIQ6onvBSURA}>QP#lronpX&!r&hc$st{nyX#xX@P^*Km6 zAOA+pPMYgro*$=I8;L}gJ9S6*urS-r53LONo0r=9%g!`}C-x(&bEOtIy2}+J>i@Q& zM%>{q4qfOkIPM1tIm{ZmQ?r&?+j7ip!7m2Bj)&uPa#?gt4f3w{MkWbYI;hfVVlQxc zuJ67|u0PVdxi6l;n7d}~b1NYg=HK)?H!)u8UZ&Cd@j&tf+W4>*+rcb5hXH|UGO6%K zx+6ckN?{W6B&nP)qKt)P&A&0!6RyQGKlsq-0{ZdWpCfQdn4O_tX0@I*yeXVFz=THbJM6#4V;^k|BVTc=)^~4*}G=-c;!% zn(jk*nfdMqZp$IPqij#3WS+2rbKIEUdCZ3A$xlOT_jp?zTG?$x=w43b1~0I^=r(VB z^Kv5Hs}}j_6DMDhF2=3I(m*=f7GD^xJY{;n@1k0X`aFNC;O0MOW|%!1Rok|C*|HYi zGMM^WJ4)_7IJY)9G%r{xSP4OW;Vvu@_GP%wH!S4&W(ebM0|6Geb%8aPFyk0{Y zbZ}4bt4h~b;`PJJz-RPuZMG%>o5euILd?#0D!W9D2w3IEHsh^1?Y>O8@iC6RE`#*L z(4$d$9IfdY^#Lt{=|);eAb4^BOL$TTo3^1Hi7s7Ys-)gakhuIO;KQZ8s+Fv9V;L7| zer=<~?CY}lagF!t*awdZE^sa%&R^fy)>aCIAUC%0pK%{Dynq;yMqZ&vb?p~u$*l|h zoySt2{G!v+IUhkCBA(+UfyB0J>nr>x$7G_bXGYZfGGSGCms5Vf);uGq zqj(OPbWS`lum6oOjMT#&KeM#Zu1wz3V2FJ79s4+fHA~FET?SHWYu}l~&_C)iZ>u^r zf6Dq47V0wg)q~nxo(-R@3VmUi5A*3rN{N>^@IU|gu z)Fi{sB50}oU5-rlMW*}r|5(`6+olT=6@@f=o4=Y)hOu?`?&nhqjsCrt)5@yTZ}}$C#kd- zgLv%v9Q^oa8zt_keZ-49B^{dUfKyTW5^XD}lBr=%mOjTX49NF1yFUjI!FDgj* z3dc-5AfbHhkC1o#>Vo`)=5xkxvj?~zgnSl#>i4j1z8My8>0-)h3x!3US%td>mE~6= z3?zaIBfR7v9PSHA2bla5(vd!+>9Os^QM8HKZfGja&B)L^ZY$J=+)$C!RVFI-+s9R) z-#3}xET&UQ6;>l-OI!Mu*2XfkGrb24=vj@tYoFe&+<8QdVl>&&mlk z7Uua6XL$y!y&lX<=H!X3eJSnW28|+nyHb96FwtdyvM8 z%|icV#w!u`2>dVYS$SVA?Rg_qm}%?RqPgScDRLPwaBXxRs5-xYf2<-n=hj32d|XnR z6rs`}nUni7?6~-VahG3Hh4Se2%7zUxbb3h4rLuBzyEf04P5qZqc1a_3qM>EKLCtzJt%=#Jr~P}tbro8XS4zCI`sJbV6HKy%ztpUb+8 z!1bBr(CMBocbf3ey!^uIcGq|9r(7X8-?`wkd@RgU*8!iO3vdM;e zRLv5KuSFt`TSz#g59ThcBnAyd@kHCsqJ7J`l)dMTTKvPw&8B~q+%~Tu+9fRt$&*UX z$8Xr`SKcjUgoR^}slAJ)jb1hEcQuLg%&yBg;3Ge1_gzV?xkxr~9-SYvvy7sKH?Igj z`}(j182oKUMoBU>zgAaGw+hL$UUtB%Mj()4_=n@4Lu~lxCBBKvAz#kz-=c-Z)A^@u z9zLv3Q6Gij^<3@DfgkDQWC+r?d^WTrw||q&DxC=a$UXHQ&;~7cltFm3x#hk>SNsy# zqDSKnkgeRwIx5>XY;%(aj}}xB>+wCxT3pUW+zuh!L+;dm8ZLes{9o6b9-%6UyV7sl z$alTR)H3M105O{#M_K)Ob%B%H8=}m|z1!jx|PG!b|*uN+HM*Q$4 z3+e3UZ;L(~H_+~O|NT)!Vn!-qv#)2@RVSMwc}bNr_*(Xtkk8IDyK^6No;XX0lup8I zs>calUQW1y)bV2_Z+cnJE}T6ol&t*nO`>Q^$SZ!6=_;onst;>|H;Lg4``Zl0p=sSR zgt%Fi&AM7M%L!3>(TxCPu;lqmTPmySfRM4WpN~;c$m>aE>qJEp;cJ&Nyy0CsiL^eC z^ z4Q3MKUxk8*g>HA&qVdy>SLr;Mz91l`uFe}BY-t?{t|NDDIYCu#r-z2&`~13MAJ_WK zEfXop_a^g!{l1jB9K*y?x zB%AHmg*m;%iY_*JRzZ1X{;gmqSA~XK|zs;;iTZ6=9_;yUjOF&EV|Ro1=_eXr1)tJm`XIZd?)KGaZJy!6Ndn%8QrZ zrDj&gbbqCJ{59=)Iv&k(9f9gw_DG`jk!pr)>?7reqKk?9bJM?+o0?u`9t<96w&Oeh z`6qBYZ(AEx7IkxdjqiOcQN{&A^(4768B;+O{QyTng_ohSx%v8TZgU>z} zSdUhklG@tyDAiNn?o z4ulo8zoPFvLG!rB@;6@-$*QTTX=rGusX4i@V3709%C7%lZ_m!s z^3TpGcy)PsdHc&E+Tr0ZILqC{T4@G`hS*-c>Q1`kAs^VAt)(C**U{At?Vg|a>;2A1 ziPJSa{vZHf;_rPEAf1|?Zs_4r`>tSjd)wO5a?!b-&wlO&$iu^@7njPc zox8Tt&~S+H@cxXBy4+lymX@-b&!Y6R{lHHHINHp0?!<+L=4q;!@@CkG-LhH-wFFlE7-_h^o6{}4$5fTZ1l zK4xnj8XFsx6WD)sbmU}Zt@Oq+rKYAvl#-*((LI0!gn*SbHHU=b*t<87eg;7Aq==;C zTlpzYf8N+-^5DwxbotK)m85eL2uEJ!_aE2i70i+qqR= zWz{~NIEepTM~EMVnmdae>bP@B48G`VO6Y7_esp#Hy>RUd8qTda+)2v$(hjEN4wkjc%>| z_~c~B=g$>d-8g8tWY@#@!}T9cFZqcUhITvl1?9`9*6tC`6M29L@`^X-%Fv!z#iTVdehGbI$#ikMz?Q%N z`oe8sq!uo>)snf({(NqYbrZxxhKWlVuJojw$SS#trQzu#gz9PshqVASyF%{fJ7e4) zl>wzkZJ=R!`DXL({YFE`(JY3+5~1pBz2CGyOvn*A7GK*TBpb_VMxskZXjl6fkZWBf zFtLe=_i8h^n3%^11XqcNn_DWUS?^k3{Hb^IFQY$}+!Kmuhd+VsPuloYvk$Yy%vAqo z4K%vEe38?}>oQ}3c<4*5>Z+%A(*Nct0OY!H)?7RP?o-8LnctSq<=(`ugVlE5tZ^D~ z_O*1)(yjSkQRxuer*%qK3*);Gw8Dz)EvHKe?0Yf+Hxc@HQp}KA}^T?ZZrx z<2T7C!7lM-q_u)OMtS`xm zbT`MTZ&+1h*ug&*5C)LzFAqTSiwDE6G*Z4BmV!V!H6ppv`X>vW_d&*8#r_+6-_ba` zac?j8K5kp>)SBl!o`dH5V>M}ye6|~UMBa2s$p-=j=_r0&_hVY%aAR6jP?GWT{-UGf zsAOhep1v-@vraQEdC*dG!>Q=!HS2(JY2K-8w@uG;JMqI1aFq?$N9_OR0sym30E}2C zv*NH}1(;w`gXs_h8I5V?u;Ex2{#UX;%|hLj_tJ5Q~16i z$4>nrf@|fhK?_skW|liYShvoKVOX^-1nn=yC9|4N^TJKt`afO$xHTZ}9hD^8>iZ@|SzZ(tzobBkh?x2l{2c+1 zk^vT%qi<@w@I7CmhzHpiIfCJ&O&+A#{AwIIp{p0=e`FhqE@9f$9wD7uD@vQ^?9FoN&iJV@l+uNw|w5jT#<#LkQUN?Qiu2Y1?XKW zlZtkjMjG!B^*v}_&< z{VsW6C#;o43yP*%fY{vBh?=?N+s;(VOS}m`=6@-F`%`%R&#xryqL|AhkYV79fN=8a z+@QJUha!}q2sa^dU0p)_VCVy|=D|?~UJcPs zcxqz89eHNQn>=7%sjqieq>2v9p9WGJ?Q$xrP1f4(wK{Cl+E21$l_T1Om}XhglZU#P z4HJ`?*K74zU%kpMxF;{6ej}my&Ipz!}lUDegArPPxJj~VFm0ig6$+F#$rU@ zPfJdo1yhqKA{}q*eWU>yu&}V`u}+STj*gFycNkaoQuiI^J!W@*k_KT&R&(WgCH4YY z2H$T`00M%a#YJyf5;`CRzwah(*#be)EIzG?Y?g(B&p$*$?hVRp1O%})f zrX2qH&fX0TKqRNgzSpy=@qF7aIQDzh(jJM)75y~~y;?^S?7HvIYrg@585nCb2^%T{ zam+#U;CC~Tk7)OtgzRu_Tbz5|f>A9ZV**WTe=WNZNELIRkc1>&)z{pVn_O2%=fbxo zFOMOt0|?)qmqb%z1{rj9MmKkS|DM=Q2^sbDbvV+5&zo&0soRI(yUC#$ZmcB-FqTIo!r0ts0wXFbBY)?xMMlnpz#HXb!` zSe=nNF6!_(*CF}1D@wGy-g|)~|Kd#CZ!F+CyH@q(1@?C}5G{U6jSy-XQlmL7?_EL@cdmXVZfD zLd(6?4fzJ>tC!~FQMt#y==e{-;vUhb@bZ$9M1lOG4OUpxv2SP+G`Dmx$?yyXXN!Lx zQv!`>IX)jimJ)a=r}ozS)`vp79k*HqZ}oe$&j$BN@}dPpRtj0Cyxz*Lvf??8ZGVj9 z%{PGD?&a~XCSOksf}Ky_p4=si1q9B_&N{$b?BtMVYa{57Kt(x))qAP^c@X3ymn-djc8o*RHTK0@b616<}xG zC*&gMUN+dPL8*`C7NLs%7iq|JI-}<}1A3V!?^R98@5t{1u!9s4#hQ@6p zDwV!ZfG==p+@k_z^#U|Jl%67R_~L_13VQ@%w{YwNuSF0)YKG_n3$CsDW-9dinrMEg z4Eq3Vue)(N7;vq1)$)0qoomR8$I!8OSa&Z7+p8>$S)9T_tjnFF~jw6wA)l z`Yz$<1Pk#ref*)lk?Z<)32sp(bx%6`EA`>u&wAi3hP*U3 z5(Mc`A^!VfXqVv#ybhMHm*zfZAyO->&|9-!b!W|x&woO_LFX6Hx0%VKX-Z^QjS2{- z^ef*psR5W@r(tYA1IRhPc;?V_TChVz{`RzFoa+Kibne>pqeahdJN<=0ET!Tqaqaf_ zYK3U5B2w|Jlh);iKl*gqL6J*A@#%*c9Rl&(OB8gDuISzG_U0O3(a;u88X6m^k36L6 ze6P0h+~4nzQ-l_=43_*kHjeLHcw_5eAPfQ-*p7T`eM>bYU}dP1Rd3N^7Z2&nN7BBo zVR>zNY}zqiG{GV!n@CVnfDSVoY3Y%bXHZXQJkea0tB#4`lptp|xu9UTq88%j@izDRgbp*E ztn68Hba9O_75e)Xs))?UV$u^yWyU=9IJ(;(ppniwj*8&d&xWnp@4L%6np__^ zetB+>{V1jFi*T{{BS81MN$#`}jgJvD9YU>zMhHsez=EcUsh0lR!w$x zoacwo&``d$ydjD=_l%ta>dG2X16XulCqSQLR+CM`(WQD8fx)0&;~$CNug}&9H!o^b z%A#BDpbs7+(WER>P_A`7=5tt=*2Q=T&a;q;c^{W756&)mWe-&x3SYhT2d<}i@^S+GMg1(-z-T!> z(P2I!I%<7NenCM|N`TMy=9~1_t{feZfec~hT^H&8!c9vtE zo7o{uRbU}1<)U!{d>$&eF2}F2TSHy1CUJSwwDK|#U;M}@#-8I}vq!_JwHl=#(b&E9 z>?{Ap3FB{P=&#k6*mAIqc2v0M1LLn{iqxLmd41-8y;S}rNJN@_?efpbuiC=+rgFwO ziEQSX;}21WaIS~uJnr#{E|9zVpCg-FIOCXf>a0e^rDVHmcCBU8O=OV}8@>{v#7Y0U z^aUU@_h}6zfdsFUyE$WYq)GWVR@fT10ph9rQ&Q!7p~j$DVhBRr|6b_5TrX6q+Z0=j zuvMq+QELd!x&6;AOvRf^ua{y1E*3XGw01mwbcNDxPS32Em`ane@DFcnoDzB z#iyK=+b?3RJg|Bus01T#ktHoP;f)DFvTqv!VP2#reDj5!ootVlSIo7mEKuwy-1Gzu zjmf8n*bRgP%VA_OQ($mYE0V_pZp3o^moFnEkeO@E?$KnT&t`aZ1~1I-W}#1`O_e*_ z=MjCrXvO5>lEVv^XBwDis%CyLAO`{^QQDA?J0z9DuXKb{@)z}MRRbGp|T+{R~e$?-0;gVI3qjIx6EGz;9PO{onW+WxmqcaYgE9LsYdNtX5 zJGO@ER~OtqHI*pwFpp<5Z1KIUm{(NGQ%V(Z+eR-Pf<9e1v$yZEFm_*OPzxs+1&lY7 zM8s091e>V7Q%O5#IoyxaG9J|m?rXfL!{S6IV^SnM))vLX=Md@2w&;B7*Lm2IT0hKt zaJW-zxnUR3wCSpbk&5!UyM=+)s+CzRBAKxzfp6U%^mH_`NfvEj5$uL4|N5% zlxz98VFeDKZg2j*?2%Pq3=ffBePY9h&54bAK`>EVm2ETJ#`DCiQCYp;fUHQk#>^vZ zO)Mb87#_#MeenR<{z7h{igB7-dND5Uo(rZ9{VbQ8xC+&5g&Ee0-@VUQ+X7CW4bAI* zX2WrRX;g~}chl+Mm&8lzg(_u566;yd55xEF-EZHruD?||7^PstS&bb_n@#O0)OEYYkP@C3sy{IzI36Y;TA$ zUno{L{%5C-%iYMw_-fNL&WxYCWCpgh_ZL9SV5O*_5)XSdGD`jU#u3+y?Du_G$y9H_ z3(L?ae1qw6a7t5n@-de4;0Zf5h&phJlff_nx13>s-Ot^2hT)$JGNk$r;cxcB+BQ`B zusa|7+Q~n^y!>jcG@-EzuYfJ5r4dzCclS7m-!_#o&s6NFx6LTJkG7l6j8A{_=>b#7`Qba$uFB0~be{p92t`@&~Jnnl++)jueeRGD3YoRa3KUaI$ zllC)V&6Z~by#v(`oY%_d&A1u#z$sIgPu%==u%CHJZ~tEU;DMh}|6ysl0mv-Yyh@~> zO`kug|75A;Z8^Pf5=ZZ6!vg?glOy685TRsrz@j!PO>YgdPkECzo*a+T(QTA@eWp80 z=XPlFbT=ji#__$oxZu_t@fGJRn=Ewro??YgiL#b4P!cw(G1qM_`)#S8aqF{}l+;Zu z5qiQf4hcK|=4o3uXN;2&5zycA3tG?mtb}UJ_~u{U=$Q8j8td#P=lCz>@^sZa)l>d_ zXNRO)%ALEt?va(vt^6+a4m$I@$f(b5NjvMGJa(dGg8#Uo*4d^`LUq#HT+YM^949*< zip9KM*GA;qPsVX}_I|2PZY$a=lSWZzC7IGy+wIDhhX&Cf25g1i8>3|^SL$;}n>1u| zXyUQz+(p|F@!%{@m5S=iONU&&7{WO0?(Ob&bp7j^4b`WV6iR8$dsOz#&CLhTTfUzn z@_m@4GgKZtdeqP$((rtidfwr0Dhy4jzoVmLSi_P1-#b%|;>^TFjg7Oz)sD3P{T&q* z6+mj$I>6msB`F{MOFyQ3i;s{0WmJcN3}XEc@4~|R29o)n>*I$2Q0Ct%zp=WqG77+C zI}zy^Yn%@8#)vad~-jGOFy|yfEzMgcg^@^!F!djxbLzuZIsFR8?1#J$-}| z(r#X<%{)PK+$r*(k(mP{L!nT2xZ>WPcXJDXEaE3NQ2%E%>A(nql4W!B_QTWvi6#!< z$d;Y`b?)rp%93*L-&Ha0%K+D2AdWOAWuF@ccvg~5L1@0LiK!^lT>V01}krCQ$T(WP(T+eLt0 z2B=}+tR2He{#77#zw;}_Unm@6AG7DmYix4@6Kx*{ARMN~M@O?px9F8px|bX&^+|wF zJZU}uTBkb%^6GSlkY1rK-W`JG_$GV_V3PotY%j>-vP~yz4m_J4B$4?1qO>wHJ>Q{C?>8t;0Yoc*PS-cDNiz)o5dP^V5jKFxSE zDqVurN_Ki9vAWjbaVMcWhWF#zCMFtMxLdq>11_Sd(Z_q#{PEVso0C|I#xG^N!o!DB)}Eif?cDu%mz(i)%d^^hWn;rXo~r)(=&kf5fQ@v% z0{ZKLqyT?wkr-6hO`)LMMd+2Db`dTPYBM;?+BkSrcru%4-+;_T2 zUHb1Ix1=r1+LyBa^YNUXzPGo7;D0^_^aKsGh2l69;%PJlo=Qn~Y z0Gog6?FT?fH-dMtQtQo+#sou8)eWHiDI@*{U;>YGvY6P70b8;F{EruD0yO*Wlu6`# ziy!iEsU7ZjuCJ~>#^!%15A3c+X-1+iWi40Ri56*zoMvC-a^4lFOQf_746q0ZrL-?; zO{!~X_#O8!%+JqnZEeZNF_W#8-CZEWjo5z&fy|zS*H7>*52w5c@PU>9roVXJSJGgh^B!mL|*( zaoY5XWyTL&BbjeH+6cMx;BFH@-LjK3i-W7FOZ_Xz$9s>ii-WEe8p1AvD>A5XowC$O zB0J%jz(;xhVb9g>sFz9{_H%XE6uh54eM%q(AaG}AXJF?7^Ny8U8%z~M z?hP$D#xZLzuLXym01MD5)O=iMz^$EptHH=M{t63LT*VsxKAViI4!@w_yJ5kX6q2YU ze6ehSSeliAz*eUyDOBW2`@$XqE;TSn21IhHOF^MSj8e{z z%}aNxUUq-O`HpKfAfeI871(M*cni4j+eF1g&b8A?!`oRns?)qOSbT}Z&^Kdpr@5&~ zNLUz1HW{|$_5(DdMk&w5$?53u(5PI?X0p`H%}v%It5l^)7f!W~B3NHve`6?}S}W%XA-&M)n3@UK;l{=W za2%*_Zoav`M1&Euc(283R_W}m4bbL1pa0*=Dh7wTHU8PBiZ} zv^BjEb>KPZSVD|pTwz*r*V9N37r#%4G`|R`xi+|Fa(738`YW|)M^B_6lY{;5l&7p~ z-K?F=5LN(j`OW}i9)}+t#h{EoK6ZK|#Iek+HshDv(0t;E$XImeKcQ zSQ;}J#K>qhU10{4MvsFCC^SL9?KOV+^lrQ%grA;X3KZpgdvgxv{?PVmYc$Wp)pcxg z^8D;SQbTn4dDDu~88JE(id^87mVLny`*9;o-X^TR?sgkU!z$<1=l`8^+^) zTcn|*b2TE*cGKm5je(yy0x>mR4WWj}fYbfXrVspYkwDy0IlYT3a?v_8bkN5JqXiYH zUmbN(Cok8kzxF$y_fW*2Cps3rSPTM0#i*rn&frSAz1}c2fw`>{9H?Xx~A6&5$toN|F zbPLia88X^%$C{6}v8BZ0mpso5t5fU>EF+gZOUk8wR;b3$X|TP_PZs`?AiQd5_Ygjr zBd&d6mcuUaxqC2&%WZB^Dp!O;7QH}eZL|0Z{Ole!knq>~%N`geU<5`+MkK?Cn{Uo$ zy_-c|yjTS6rDmA{T_&JK6K zv$ZX@Y6gR*%S|xw-Q3+F$h}%%k8$#J`*?ec++HK@Hc(MfQG0uPh{%DOvap#PQm5o> zxg!kN<2)?Ko^=ry0(?L+ujnoM zpKY_Q#gAN%LA%E+1~8bEA=+CpN#tU2Hl?svHnhhRThKbNcS%Iw4)Xb*Hrz!a^*raz z4h88+W7C#f6!LD~Qdv<@atez1xj7*B?Rs3?mR&QUQi;GKGSeV67ny|}y z|Jm7DL3+V}-ix*rbEr8>6|pZS{!aTQMbQ-%50xLgKZx zO5vJnfHUo+bl$s7s7zdXS#o-Bbqql$bpMcz)-O5jabP+WTBrA6by@v&e_0dJKn`JS z14`mtQ4Y)Nj7lc)O7Fp-&wH7;U*!CTi4=#ifSJqjBEw$*6Y(>=g0ZKsKVp2P^4pGE z-#=AJVj%|E3&s)+K;RAb0EZ+g+qpU?z!y;BSaf`OR#H-Oi#oWq*M|X`esOWJxw)At z=&|i8pq6#>H=bJ z#O3wkqEWpbWT`DU`mPXwoSmE!d2HUBo38`R8r7I4^>gV+DxR;2wK`Rn`1sAoy-c7e z%Zc~@-${GtvB&D6VK=@Jn#%qrO7yeF4k9Bx;OH30X-;9oN7S3LK zh`Y&b#7Zx~)0s>pAp9gGY2*NZ<$3)@y$mRGEc`Lj=M|vE1z)O87A7@`A=*9IM5Bg~^p6L~~@rO>aMO;$66o@v6>?+%Td|nU$ z0Zzln5QyS3CBxQ-(hs(_3MO}kQus~SNddn~0$8QBwVZKlz}X)_&KnUML-Db(fV1eL z_D|_BzF8)Lk@DH6=#)UV3-T~AF%ur90V)aD6ZauAJ70Gvr%)&~zqE9Jcb66v-gZRT$yA+OZvU78j8upXvrwBvAx#eh+rTRO3r3SR*(^UcjsY+SHT0X>D zPlDS3Pl2lk;IodR{i%f6mg-t0)aAqwV5aL&PD!v3v)#!%C7Ru)atB^y;-JwWwB|U$ z-JL8U?ZU7$64&aguT0^3*OY)YNTI9>PN(<~Q5F}mwysy$xLRT}ivRNPyxah^qkFkA;#2HdD3Q&v_MU{~zeNtMz?>~vPv z*NLg92A3SA5IzeSX=!N?2n0wD0S^29iI<67d&$6JzK#XL?jG~XhxY^D6;#<=PHFh>407yk4*hF)>|{KX4EGVlZd{|r#2rG3XvOr1dtx$@tg z_tI0dXc0YhAbw%{&wd&z=1zq{(Q?u0V{<)gbcmn{c988_4 zuXfvZr8KJ|p8X=9Cnc#qql)*iBtR-hMf=k$a1Gjrfn(&#+s7}NONS4=ZevprDKQ~I z{KvvW=a(X2^6WWkS{Re@E%bowjuYb@qPMmdmy{@ep0=~I`)}O6cTiO87BAR}3JQpb zfCNE_iYOpiLX(pq0*Xiuk_16=4vHiZk(_A(0RhP%AW>0~EIHHUCdVd+eyctAymzN+ z=C7%G^J=yIYi0IlA`fV29W55P7)fCzXm%t~7*w zU`r@Z7jR{no0#zW93STCl)JmR@jTkEJKkSZOPAwu+5+9C?O1j8LyprYzmXvi-zO&0 zk&|~GNPBsCv8$$3+oHwIxKVD+;|mL4pY`UzG42DC+PYm_T-*>y#>=>0S^$UviK4XK zYn0odt45!~bPo;V{bduy5%Gessex>h;IGA4Ivd*FiMkiR4fQ2fBOO;a)|^d{Jk0H#av6^lKI-Cder$OiWFk4tMRm_Phe* z(v8?AAYw!oc9*;4uEBqod|m_MBR=NlvDrEt7Z(?O^YQG=O#I=8<(v2DB^ht+Lk6#NX`s1y}0`Qw!F<`-ceD7&mTY z7p2;9`who@melXNO0vbo6cO8W#Y;9bU(T=iWs)bOYhlg0*^vLaT4n?92}4Z`b6kK< zcJ`ffCqgFm!`HH{z7h(*3ypsGG=sj?oxbUigFL;w;ven5 z9T*t!+?@!NV{Eiu^~xC&Gy1GumQ#>`=Sft1{?JUs8i>aAiY>HL|NMD2__f_N%j|x| zKjv$@68-Yt>xP=b2A8O*mlQTa2+8z_dHBzbUhP)p-$~hSzEqIiU433pK_y)%e8b&u zXNq3J`DqXwJlEBfM740H;1a}6vYJw`ZA7ZU%1cf zLdFoFnX6Z5C_)?z=t!U_mz$7MX^c%+GcQ6;ufo%4r~2Juk2)?H0aO=I=g*Hyy&Yl9TrcTsurRtqALne#Wl-N$IgDLI64x*FZ2<}_3 z1^%Q;L)uX1|I^q%Fc6Q>JbLs92h}iyBnUBJ7OLBuOpHV331DHSN&??}SH?X^evFE=U^JlU;WcZ$Ebb`;%11{>dj4lo+IjHzaNNf>J)LR{ z+gV?qpPO6T+^q7!d4nYhiem_7=B;r&_S3&NHZ}m5s5@G=y_?+L*~!Dg^15XvU%Tv0 z!$hGLX9HPTadCp6Z3>uK)XXoPlUHxd-l(K&U^mx*gl1ys>%@M|wu|P?2cU zsa97v#23bvp?zj@h}*#2Vdo?wfRAFz8iA0IagYi?{f;IZ0VbqAilPz(!3}Mr#E8pX zu-y@HRFu>0GlgN(QDh^R5Qq<1%)&Fe=S4VvS8$Lb>bqaVRX?cKg%Iy+xo;Y)l~syB z2<11sRV7Qw5FIBWlp_d;s5Wrxn$vqHe-182A9PSZ=Fs8SPOik`uN@c8C-Upo6K^_s zGrP0QE)&$35Wr2v{Ib|yZ1C4NfXL$Z!V&t^czy0zIUsi;YDmm=@rQ;Etgi<8F z=BN5WEesR?_to<90>L=V%c%coZ4kuxA^H2NKY8t6(x*^@=wG`c=LOhL|GfU+|ER8m z%RiSgBvNnwh`FK7IZ0=I6NyT~Fj0k^5)+8tZIpMTQ1erE=Tyx`GZU>2USv>MU9^3X zy%m~&uR|kB=p)9|=DlKv+QZcl^wmg)TvGG)2j1e9JxK3>a<#~fx6=-$XI8z^r_5b< zdUreV4|F+d^YojN{+pXE3mnmTw0Z3#qfnKZ69u->-X8bgyW4S=hJkY0W@RPLz+P@I zl1Pv9oy4VOF0mM0x=~8wgrIE?#vaOP!QUS-DN2HY0+I?*br0RgZiv5WXGc|;RNPBQ z%JNyJxWYK2(p-Eo!*uRr7)(=IRnplXa<&j74v_EZZHn~kpH;XiWEWQ4wU2!Xz!sV+sop>^t@u{@A z7xTjL>hzS1Xs3#K^5cy|Pu9-L>6H_3fdId~S1*dRepD|h)4x~{y{)R)GlhDo>ewOU z7Z?Ed`vHj|zO;Jfx0ivavm%Qe2P|obEi+TkR;QlTv2-0M+~1?~sjFD-B_i2sK*Z_V zFfBw2>ZgS3O38SJ3+cXv!6 ztOhJp6kE-|`oq?EJKfxV37f_V>7x$O@-g0icF3sWYLQX$OJkYTkgl|r6Vo5#ot!ei;B09r}c&v52#rH;%^*e}zK8gATeM;58v9@><cod z7A}`G(G6;3U%bRpqZ_myB#to96trVWNe6gJIzgz5weEBKNR6*~w97-J|6sNG4+tCl z*Kcq(8%DcwaMg5+psPfgpSU0Ks2(yzG;eO+5WSnUvDtE9ky?FQ#f;8K3e?n@q z)ipi6Su^ZHwYGV4{wQ}ng`DgBHB+%s;aJa1=eb;hr-MJt&a`?2oOE<{huG%u=KX4eVnvwT=V2k*Sy-Q|ww+Y@yvNXv>+%z(ya z>(@@Eefs2{jdhQ*YnE{HX7CT0k|m{SYRXWi@>PF=lkw)pA10ZWrFYLKny%=>4&8jy zUw2AaS7qp(^b(RjFKIi6?eUrJrd>Y~ZYbyJ z*^eRq{$u?bDJD|o(s|cQaB>8kOTG_&VjB~aZjZ;1Eh=Rg7ij?+s=GokLe=xEOev#l z-VxqIlEF4Cifw|WH7>k$xq))7=W5H%&ER)}6@|dNyWLMnlAopsT|D{&qgN#QQZnd% zYefxuZ`!1(jG`g>((QNDXK9~ynKU(drO1x2W^-2F6Yor(LAWmwlQ#xFaD%pB!c3|@ z-}Fp(nTotK12(=sOeny4N`65r!8N``l>%n3|v5z@9&#-fy zVYvj5=MR5*uE7y)Te~+)%vEhzvAEEiTjji5Tvn!6Zj)SBha2yBqOQI%XpRD_Y&D?! zkje=Tn$<@N3JNj`y{PZ+cWF-&2AJja>C*tRpvWlC0e05?DMQexA^WBdMr@=AI3*G1 zrGZk*$bte}WLuQd3?OYY_HHsVKY$hShzv72B&%o;hQzD zc6`%~>Jk-|yQ{0{kC%jG44zBLCwv2aI|Y1GrEbUe_xBGE4zBMV?(e%ZO|H9&G87bS z1J?>*D9_MkDyr>81Kd}XYG>HqV)>_s9854Jp`oFzm^)aLdO76KNf^sD<> zEp2VR7JP7FpS>1?xKOAI0A%&L0vA(%Y_6+Q0o+?rks9(OdSKx9k8SYm7Zr7W`<7Z= zokX)Kc(<^u%n4A|03Em|U|iu!@R*-Je|~ztw7i@;J=?0TZV@Ae^MKa z)Ornnva`3}_!&Ztr?YRzE>2JXm|DMk_io&iqbpagsHms}Fy{3>15PWf(IgXFq6L8a zb(m}b=w{Vkg@&pqCFSQ4R?ik>V;NP8{cHdr| z4yaPs(10SmrFyK;NE(uU9i3rzlxIXD2$Jje+HB)O_j$S5jm%DQ-vXLtYH~7856b-f z{QTpiRUh=h;cCs!7>Xev;!cqr)Kx70opwpuzC;w(#6le3)%nW$IhQ2n+aTxFv7`Ac6!2%2C`)R0J3Bil(fqP$|E7=9gm5ml^!N4sdfP;O>C)kL zd7Y`inNz0*hK8D--tppX`0P=nwS>XAY)mUM(ej!pOGronh!@y!_S`w`ua4Qr7b0a3 zK=}Yy)zic47hqb<2LMeuhGMTIGjkR|YXE7Tot28pb?WgM|74dO8=|l4T?bCDu8Xp zoE9Fw{Lt7X0%a!rLSNt_j5hpqPC)^qxaY10?j`>5>Ms_yL5k`+=_;B6Xd^H*etrZ% z%36b&K^)aVQndAz|HT(Tu|;k1`)<)o1k1I4)9L+m_{giZAOG_SIu3% z#e*l_Uy7WDCL5hrAoM_6d+5&}Zs#SfqkLc`fn{mf_kRlz8c!23EXx=Lmi>{xb%Jl8 zA31D0BjAT&6;wH3+M(Yk1p*ob={3Q=Ty8-vv^e3UvI1%qEG0ZVJnGq4z~x}8TRVqwrb2ug8PT29cx%B&+b~Z+ z0#?XM2h7mmU?{!t#?cO%or!H66kq1&NKc(QRei8h)ym>LoBe}X&!0V@DUN$Y-N2y0 z$2FC!uD^o@8+j5-dCooS?+!qSUcd)fDK)iDz%%3eVfyfdG81g@V*+oc1R*=)`}gll zO1_l1N<>2Pv`CBBygjPvDQE@(lM&GF?ST4wZ(|02t#x>Ib{2#e4fRexg2-XIOy6u$ zgolQbMh+P0>Lw*5&|bMR3iHy{Wem}zb(rhs&Fp!YUYuLqIFmJUc?NRVl1X|LQmVR7l{9sTfz8XbWs zq>pQW(QZxPzj^K2OU3UnB%tkZlai8xW6+nduNRs!YF<=>xTdY0a6hZ0q{L%BBQ_~H z84wbqhZ18$Lw7kjIl(k^=Z-xvgs_C+Cd{Q=s6u|4l>n9pU^)#gZFA{aVqz1yjn&aA z7dZ4w%sT}5`FXO8V|E-s*kHleYEc;-9bHnws}V5%#N;1v9)@iigP~A+@`OgCDC}zv zc6p#E{qyIyL2yt*+?Z`owt83;=JxajM4DMUMTqe6yykJT6xP<({}aRyo`bW|kZSX9 zADlTxImnM*`{G}S+p&o$DUc;mkdqt1=Z#C)f&S{7AK{iX7DA4Zkr9xLfC2gJccTWZ zzj#xMF$1JDSYodF=+VyJrt802fM^g;fm$8&MnhbUFd+ zKdRpi3Jt#nQ?SSBL9~?li24O=D=1K|(a|OHTlE0~zb9C?M@kO79Kg>@14UCSqgC*` z1vT44aGHI8deHde$7kKjJ6v2Hb0iQ{)8#_l@jDL#=`+N-2T)}i{_cEM1`iDMme=}E z>Vq3z1)8~J^g=JDevv|`$~8FRxOsCC05P-=t_Fod`T0p3P#yy~1rK3A@%?n3<{~g5 z?pq5vIUHt>B_!(c5*J`*AqlbtvcR8OU#rw29PlxvjIXdrLw;^-ZPmP(I7to?R+wFU z5dH2?T({p_5Tuaa`7tZYpvFDAVdAq^Q5tURbSo>YEml@pob&qR&;5QFg&IFkraNz2 zft-OAPP)U!%F4q96D|(0e1l%=Ap8*b8qAL=pZpMk@B3ZlM_Fx!@Oj0W@ZN8lBaXMYPFV` zsYuJ())oddGYocISHU=aN^UCuj=?-zqWuoa~YI^~_nVsj2b8740{8O^&OMb#|uqK6`F$9i))e&Ckzo z$wvzteay@I9NW=j?LJ)VOd;y0JLGX|OWhVu8n^?;ZCnVyi#&tAkx^=3V52m>>)QCY z#xWX%E74`p4QngQwL|UHYQL^JM?vUg->?GMth{_9dv~@Z&F%3ft3)r|<+cHk*|>Vc zn?Pb$f^q_ctMPjpVuYb~T*2ndq&6N>MWj5A4Y6wQO#UzYRDk{IZm@3Uf9n1JXLvQS ziW%uY!o$7m~?!u0@5!<9Hs zyNu#T>Amq)8gSnv6NOJ2PFHgqUt zi4h&=@Z)tP>~e4YxW95Wr15qzR=~voV6MU^;`!U~zk5tI0MXa2?}RWG1l2#ly|=HG zSRXqv;+(=;Q&%~xA6owaiHN&`p8@QAO!dT3fdkTO`Sjr+saX-gj@|wGuDHb!osBbi z$SvY8Bsl(M)6=B3a`j5RAT^?u?7yHo5!OGwBUfIgS}A>4JeVv3__cME+*Rqta1qJw z?8$oGlkl^?mKd8GfU=z#tY33l^vIouoQkz z+#howT&yLT@uMxSdowptsO#rQ4dN;!Y+WMAAET>S6S+lWw|LF*!1FW#Nn z6TkR<#f-6rg`MemAWm5FomLDP;sY6N-g#n!2Hscy6C$dRuUl4NxyYoXsOY9FMhiWU z87g9+&R|sLg&X*ws+Yd@mRH`Eb|uQCc9mqv;|lAgb9B>QNn00H^s5IV=WMni`uRta z|GNW3W#36UP=Rdl*IcAA*6wI@DP8cioL%jSf~F8Z(RrI~wa627CV!HCCUc3g-_rLK zq`Ps1kPg$#HH9G7_u))#x^J}=e`p}&=G@xOEq_{{kmzAkOU#t#l$K{zBN@{BJm3NS0L{4As=?693)aMgS z7w_x8MNXawcpnfDzy*vS9N@W+3mAm$r$L4RS+2WaNl_7eSprmaY>=p?K~?+iGLP}r zLT`I}yRskPJ0LCnr?Ac0nE)#Yq;`;N0u%uUA}cE^s99;q;Zp5rv_Wof3iPE)Pd9;L zzt!P2DE-pXm?EzLnJQ(3IsAylVj;VH?eBjCU$x{-PfdVGxfrh;6A|$pv)TXoiHUi? zu%sANOv1v0XtWQYCMLh$@>u%-SqV}-FxTMA1u*jhbvE;>;c|c=p&V!e0H^`}5~S*1 z-@SWRyTZ{BKuSzRWT$c>}L2Ztw6UDSQqNsy1R48gKnt$-Jv-m zR1*nR%rUcgY4=787ye-ImFzYrb+nSfAHPsD!mGTrGzlsU(BPrH51@v4zG?<+CZp%t z35qpzg`J<<^+F%qvB%B|4{6*jkZ~P=ZvGRJLtI>+jJ_WfYJb!0~jW3MqQNH*}S{l z&9U-T!jQuC4})YjG(oRdp0GQarmtEvf^xXhLFQ zZcfhmD|bO?3+j6~z-wVJK{T0sAG0i0pRft|mwI-OMY3wuJ@<*xHfioy8_#R|Gl!<~oIzGP1dSc!6?Afyf+ufY7sKocC zBcTj*aQShv1~fje;}{Vsars^D$@_5)s?$OOY?*bEM=$TcP4hxXH-OwLp1cI~*H73Y zq3Y@D>5-9_OdGdr@O+%v_h7KzatSz7)7s3@-zB(;JITcps4?hAgQuG}z zQBl$3wLs+3P+4wu_2G0=WOt@YceyZNjiAd05aV&at}vAPX6bx)55@T&`X)#&hhSdqe z1sO+>BmDRwv*2$0n`M*tTDe@h8b`EppBD~mvH^rQ)I&F}Uk3s{QPj=e#3UU8(B@ui zHKI}%I)UaRb3kKLS{l_b@j4&?Hg)sdao@;DEB1Ym_4DVw&$djyvV>7kEZ@BJ25 z1xVSg;7MV;pFQK)0zw$X09gAFL!b)=8jXYNID(dY!XNg0#fJyi@*|7)buYmDbPx=S_iPdirzXeGxzF zFqZYw<7Bls{4@$=Kfdj-B-fzT^Qmp_b+VYrlB5v0JYba074Sl*2Glo$xS&~s&>Ii!R9C?N-1j+$OawzQ^ z)&xu~0J)-Izwc;o&&1^8g9A*cr?hHT?kwj~=8JB6+Dgo3g(*;8| zrsQE7)WvWnrz!2=kLIFi$`1>A_adyrADGiUiD2P*4uhVTwoZ)}#)VsK)31YrlP5?-~~i zZYJ4wWJK0ZNBuQydG#l{J8H$Nkrp$UBAa3s#ch^`lBR9}8y=!rjE$1p&13#e^zGkXoW_P46_3{@N=r%{tw_dw7B|f#k}5pdV}#+#v=DTV-pa zR6Y9;3{08^2Dmv?*T@KGv{L*Mf(bCabN*>HHDb4JEklL?N6yO1s4BiG6-2Y3-w;n= z?=_Kgiw=V;MisTQD@!7Nkmor0=5G(*d;B zi|tkwS~}jQqVV73_-Ss193+jbdJEfN8qjfa1zqO@kVkq9YCI8me^2%EF|Jjd*VN@O7iP z)K=U%a?@oGK6QRDn>QC)QeR;;RQix(EEc7E zJg8}*3jTKRUFN8&{&h$Bude8~QXHI~^2XeBB5L*tZ&{SO?rvUMVP%bB@jQ+Z(Tj}v zjAaUDQA1nU%6)H-x)pXgGLY@#>x<-@q!(E~I*N0=pZTapp2t6zX*c%mPv5`*P$UX6 zaB{USr+xjJFqo#E4O;wKP##T&+qf^YxKPaiKxxA#=~d8)G7pAJ^gmzuUMmB(r-HTxZ2f_MSO%lWLA zU|FfF^8ppQUA^u5hg`$Vdzu5x94j^clV3j0J*MGJs<%zxoKKE;oqTrXqk@P+OZl%i zlg1Q5U)cuekQaE8Xu_{6GQSxu?GCwqOD)38bF%W+a&XXHM(MZr2ZWfX9Cu2ID3Cwj zKi1W?e)x6@h9qEzR2nZQe0c;*eS6#4YyI#90AEM_K1Z?Q;+S<;ytxUuh%sQ{L_|cO zJ2e12JYgdIv?_9Pa;yAGuU@@kVqzjGKir)Pqk19-)7WY=E_}$Qw1WI?S)~sE!)5J! zgSvcGO%fs^twO^SVU4-j*?Zf=wyPtRB^8TQfXB2nH@h>A9gw*&L&6Eil@VJMjQ{)h z?=8DCc~HAxT!QRM_p9SPG~EWl=j`U5ZT0#u@Pj}~RC~N{m8VH}=@J%fh7jYuVJkp# zN=$tlyR&0{j*trYRZ0d?gK4D=2R5c=etpx=-CIftj51rdK3cO%b|}clM9Mh`#-DQ; zWzG!CRIYp_<0TV+&h2b?-Jj$jw${rvf;V6!MTy5HL2yVXzfn_BQ6W0!_7i@jO>Yd% z9BMUa`SACTi6S};V~-Wi@SE358)wLCq9Zn_D1J3A5TDqN?CL0Gk6>MH%IvnZ)QcvE zl*(V`cqeG&Zq+RlVOj5hYIqYnx{<{!Jh`4WRX^@aS`Msl;q^= zPaV|Jv7BPNE9UNKXw*+|`r#_C z(b^4=Q{Ls})$kcs%YFz4_WH&K6BD!`H7+_satyv;AT{hD8)pK177ij{>|v)sqH+Lp z6(=Cd&;goMm;<>T!3HTWl1yJTnBvzC=k-d%xQn2$(8233_#op0tQEShv$JQxfYG)8 zB0GQH3`RIY+daU4-yOQxB|#3sfG`W9GNSiq=; zHFiHMgW~fE*ll%h!*$r0;M%~E!y%;17P)tD5C%!oP^l%*b08Cjgjrl%Ty;TPlroih z4yk&9yoT@Hc|G2-#T5xVlj}3I{_n0@wKIFRTBg^#LOK_<^4b~Z;6~Apw|c9$KBj#v zi}*;G;BfoWIXI$$tr}BJglzct@85xQRhIq5-PZ-2ZG!vniScf(WgS%`ordMWO01xjcKyq;h>C68pJX> zWOQ9(vA?=tep~CKA3whuwA^RLseBsi?;h9KhYSy!Aq^QGSP5j9)ZE?glAk?w5?Bvl zuDl?*7J&2&w6OgcFd0MGfNZd^5WeR|Zl1vVd>{}y6XWCWqoYj+#tI)Wl9(EJ?`5d9 zhJj`*#Q;A+Nf72U$Gl;rdCSVaesu>b03;PBUrL~@fkUa0w+ketqq=esJNnHl38 zo<)%D33sE5E!rOLPf+h;W*9tNs%7W}mfJH;jcUl8sP|pk8&A!!!9ZJW8XD_xE9#MtxyJP245<{6@Fx{bbS}poHP9uYv^gK&p zFSyP5=<0!Z_KOuZuN&fnDP@YXGRiAbhTk#!uTDV8M_9w;{&ND7xa%+YD9P2dC(jgg zl;2qwby7K46<)l?m$CU!N9k+Il^-^PN;0i7!EZ+nipUAtMJ^kIDuEQ2Y{8cR5&zdO zIa^y>pnV4-;#t0vOz}b|`&bbuS#RFVjftU(WJdzv4*-TY?f^|^1MDwUbw>JC6&2ts z;pz52-iWQc+%6r@XQ{rr;a%hL2n?1Q8k_Y<0Z|-@i8mfN*2S~FrQp=Zr#3zOv_6}& zDrP#B0g?jq~$4&9vpm9_c*0w4euE2OdL4y5r?Q#e&_C}@;s5SKZ=_|KAZrS% z8$k^&Eto(Sc#@%~W3^0zC4UTG?i0>1RB;Me-U~hy4?ZKv2Lgf~Fj~*UoPhgowWhqd z7(@mML#zw82hBOsR!T-j`7PDemNN5JWQtl8BOLP|Q*CXwan`-#>P z%JLwZmx?rxyEwL~e%Z)WtI9w%l8}%98-(<+gN2n9UbzmW>5CVIY8#^DWnaF4uk~Pe z7Yb&)1GDuPRM0@rG12mTcg=h)l?uNBQDy%YzDESuC#7USVh(_rkeyV9`ryC-&`yAP zT+ZiCLy0}bC){(#nGdd~g!meO>jzysI#N@}r~J=;a>4JX7)bvpsi^K)slX8crPtK@ zfVDv65G_5uwY{f)0WQr84rxI8`s(tsvj?kOY^A0BpuG%K&HDN&&_h7Swx(C)mJcvh zkkI$nE$a=2fXL-7)oD+Lnu|)OcPzJ&b0x7Bk63DiiyRI{&uDu^z0+r(SDH*vRzFY4 zekxyOgg4s(d+a^*%O&C*)Fr{K_u+gA^@|TIz!%8S$8Wc;ZcXxHC+o!~U^2nS1Ylb_ z*O7z_@b=)-10e_$olBTC!uZA8_l6<_0On8*leK-`9Am9KT*=y(=TIdXWV0)g$}X6S zWHmKNjH+Sf36*1C8ew8m|LC14;Ntd%TLe1pia9S*eT`vpYrJ|o)jZyBAn(R^v4|fR zy_yK{TbnXmxh^h{P0|15SrpRxh757HYAxATxF{D@2;?=;%mO`9<)T`r#ujNpT%!Uo;oQhEiDb6LdYH`N)#d=l#;!@3VL{AUtn}C zfYF7r2@(MiECDtME*o4JZZ`~83?$2t(jDyWnSzb(Nf{d%W1$7(Cb@m(VNbROplr{| z0DSTWY!Q@60I5NeiaV&oiGb(>DoQ{Hp$(4N-DzSrpTz`P2+wxjwLC zqljg*S>ky=7SD~7An1m+M1W2RgArby(QA5jbrq7BY4<{{&rrvbU0RN0+8Qpcr$LcT zlKWY!8f!y@8?B*a{m7{rp}-=;=c1vs>WG*XCQ|EAJV;Y?xM6qCxc@2PlZa`ZZ4#VH z9c5?P(uzsdV^Y58E_A%)dzKoi#0yHrVpfrdf*Dt~REd}qL{!d=R#_Z17+h55#U)7@mb^a=Y)7z4<3 z<>ck%K}-fhHei^+EjpnFh?H4h!Aho8O_KPNSKv3;LPM9I8$#^ zq@!+Z>JjJKFQL{oM9TNc_#pM{jT?~>ECG=ih+xWt8{UJ{CsbaB59}?%u_>3;-NX znSiYq)n?WKAQJpbhI&Ru26?zzrUa1D;KXYkd9}|m#2&2#!jy6l-VxTGn|&YAWtDvv zn!|nvW|8C;;QYvt6lm>Gsmr9E+Y34fFs68Ukqgr0BHzm0#Z?co&L> zVUNA&>>MzYY<14DKb*st?tt{(RC?jMPn+OUVg+8z!sjd%VCquX( zF2I^ojAY}{0fhzhfQwQxm6s0$4hXbGq_&y?8s1o!6bLYYpaQ`T*$O;vx^bkJDbr=U z7cX2zkzEog-=7n@n=r78o!xzY(s!^31Y@Qv;s}znpqBIaBMp1(48Q(eA^;gUqQPDT zGCS=3{(ej^9O88EmUn!j6ahP{tQ`FHE5I%4Pb`EtV0A#tK(- z@-ivvJieg>c+y6(t>??H5SyGpk9_|wMMf4DC4*OHB zCTiR7F`=d&A-);YH{IyCnS0<<@WIU2f7o$O`!bM-(+c<9yr1YzZXUMD!q-?;FRq3Q z10gRU&vz)3Kk}Edyc>QK#Y}6nqF+xT$bPE)N{BtvV{!IVz&>)b2qHgya5;j~XzN1QncH`_wbeaEx4XaBOb-qh6CBJAwXv}X=3rvmc- z+m!V(3qGO67dTZ>W<1x9y`l9R=PLVS{|ISRPue;OJ{0()S2Yup*6zogpoS7Qt>5Tv z4jYjU!P&&Bnu@=}V$%Eo2#Jtbeim{g!n4Y?BYt#4avYuqmXc(m&6ul_)|%6(A!KSzV`U(Pf9#Qg6ULEvv740S&X(#ZfFVD^BI zkxpt{yABN$y5||Baewb90FhrQ0$ycB2Zzn|sm4bfk8o{=M@L{Vmt_J(?XN!HjGGsb@hK3(}9D5f9TxwP(06CTMIzOEly5P z$aJAS2}BDVoO+9X?6-<9M+T0I8xsI8g#p7ORWso|ZsFOz06Kx0eDNjO6BVK=(Y>)U+3(KT-qf80gq;-MFEsq;%)5 z5y;*W_^kjD#j}}*8!awlZW!n>`4w7lL5rr?gD4waKun;Xj%j-8CAM`Lo(RBY=)$$; zxc_(B)+F#pjqv*V`n7A?TO-E?x2ANufW7L zQc_YMC=lW6B~)Yps{qAXCa7J>_3FIEfNFn1O#TEWVQ|orF9F*2jG@K`26jQI3-Y@Q zYQR}TCKI=e=MUg+euXjOxRHAV5lv6wd*)Wn_kBxB@Vap|HFCUf3)EL<@Ia)!To?mj zVO<^`V5R`>dUY8jV0hHpasHx4U|5@3%ghEqTfl%ry`;br5zZ`Mti_4Czn0cRGlIf@p@>nWB{*85pcCEwxtL*xHttl$bD3ft3Kc!GHNu zs9bmdKrKUH0}qGlU+LP~Tf8zVGrM;IzQzsU@-Kyj4|03|qMjGnZq-5afS<Pg=4P_Om2w(a61zUj40f4S}_`s$FpTARPcoA>T$SN#+Q7-^8ibUJmrqEHyKL?{@*kpruI8#r*1 z4$D%1nhDs&uq9hdJ;hN-=mZB)3t%;n&4M@@if6_2PHz7*V6g$ydZ+#0n?d}chR7~` z8r6XVl{sN6(){8qG%N+E7gE;o`S}}EA%Fq_TdQ^tbmweKIFKf?MmFI={()c6^v~ja z-2~O0->t|O!iWlRLC= z3QF{2Ft?lCSvZ)OoZR?E$S5Q%Ok)E*Td(jxgK#sCk$~@pWJ&<$?*9GriERwLW^ZF- zsY_abE{8S=PuL7$R$(QBxgFHo_=|J9LMhJ47}n?kP2++;efl&9(+X{r`T(u~O=>d( ziW)Fuunqlf-;jx>yl{aV1gEpJW=waI8dPQeQM$N(gjSpQTR8AA#&84JFm#3RVbG>aXU;PLbWS_mq^N{~zzsFr-l+9R5B%@)80x@@QUF zjt;&T3PNczh*U%6q-B`p-@(^`W`d)Q3UdIF%=AutEe~%XDhMJe6u$V+HpH8!13=}I z8ioRqG8`mvU{&dplU-$WPxUYh6*FrE9GxG&#s>JPLD+C07S+rJsY{0?>uwpczx87j*h$qs`?FIF_W!VtGnP$Y~W##3JNbixS z+0}s403icM*zB#q2Iw#MR`)g%Y%2hhXy<_d>+QRD(6yi(9B=^7r%DGxj0CfZ0!sp) zW!LX;X2_hujrH5O=sdm919kOcm(?-28g#l`S`r1(PbS%HLIV;CY`mA|!-o$AN~fSH z13)uy!gwas{GGbXEU*`2RI|Dv@PU@8M2oXXOZV|(7f?XGeM@3y3mSX;Rv!lQRlk7` z{(DDTbwcG#;q@bYS?ED4j zGHYwj*p^V}nB3H~hg%-?v5wxVsb4R6aPE>dsnd%GADEhrRW8TGdIkafutI0+`7wRuCnnjie|SnC}@xzY6FS1J@acW%Zo+P~>5 zrCj*5wQ_7XBxRWzG=N!~*?QwsiV;=q!0g={>)7;4*<7hwWq=+WT>zsA?gnV`m;dS0?gW{S>yX2NG!Z}wU?cN@>DUJ8%5!w> z(E%EF{Q4}#{@Bqk!P<3v!&8XkJ9W6DSg~2+ry!yNCQij25*i`K*xtTAHZcO-iz{Cj z^SQnPT;Bo$Ag&$+kQmoq*P3r7hm4%V;SXETme4si0uclPIs@lHQ}_=fkFktR?8x%I z`JmYcXhgFF5gvFrfO{&*+Mc5hL?F3ni9rgTGegoDP@8y>dWQ44#lRZbmKsn(;1T_+ODY~1&2 z>e?%u3(NOKrU?BnNL|0-cKk9vo41A-!6FZ>uL`XObmHgf5A(LIO}FZE4`q9EZIURY zhzR@5?owx9+RtA5XqK--S*)|eX>&9bQ|oYw)Z-?v(NOl0*Ua2|%dv<0Lk{PzR@n{) znBeT1XaW0rq!;>)_0Nz<-;hrMjA`4>N{TqLK+QMJTk zduB(Atx~oZqT)J{H$p+#c`@auhd8q3Oh}hQ>`pSR#Xwbs<>4loa+@B8t8RWs_}Wux zqUR_xX$AiaFl^FIn*{e+r){VP`Yz12J{ujxx>?v7=+;zWYQ$C_t+*9nFub?hZDAHX ztcDw|Rwe1eSA$PqfkhL1*Tjrbci87SE^*Z{VEet*)<&ZHN)57W-^~K~g_n*hWB>V| zRb|QRdRPfdqM9At4$HnUx`FX~Hp*4KVXV28T38)n=z11zx+lATm{7~Hxx-;4sfral zQi`stLE@}T7SkSD+W4jP?otr$S=rWhm)st|ciAx;iFh9dW5@zu5t?~xv#x&I_SkJF z=Su{Jan9qd&*IB37nI}z#0CX1v9n6zRa*@ztWzk}EgLkw6j3T?*-U#j{$o~7-X}v$ z3&Mja3bpv{tM(u8dklIlCQ1|4+&rnIl;ZP&@AU)mbZg5+kHzvY`H2Pz9{FQww%a4j z@2h5UvnZE>yWQ;DRo8q9oemK7viH^qB&6DB=$1+jVx9~2Z8|(NL-$W@)g1J1zc`A{ zaA?+P2$)rw$v|6M>ksQc6q|dDz7eGm;^Xc3ndj22WJ;;H%2Ml{9Ma0hEz+%l!*mpXiqYFt)I{tN7G;DL z6@+E{Qvxorv5$O++<96nDX>_{UiWc&?~#K>s#AA2DzJD`cdR4+vHIA^m{yFo_}t7< zq&6%%64Pu+)pNQ&rgi2g@<$6jo)e{he(Huc3~i*n_e)j0zu_bzf@Sx__~f6GL+?i& zrnsfP*|KHX-b%vVtx*&@DbI7in|JKSpOPo5`bUlefRXnPV+D2jR-&cRJL>Nb%}V*D!sVr^9sAYrohc|vh9jm4U)sp zBP`}|5d1nOGuNek`VlYehM>_nd}n*RNh7`$+$%DIB*JfG4X{EG)YRX|sH?~o|K^># zuCMTk7ZXem+=JBr58j-H2h%kKLA7KTGXmj;$;r)EazABQan?I16qgJ|#J321ff*D5 zMjC`K5kwJq^Zx(CHSvP9m%ejQfvCX!C+j%Hx42sh*A^1HUDJaazSV<%SHdI zZ3}b9qvI9bFUZ3`%>JetY{)&=3GCtB_xVSJLk6z%Ytmu5dLH))Ps;qlkIKb0nTGxO zpBmo$E~d4;-b6-n>!k*5dcrlX%@m}Ifg`(9S$)(oDtkj6k7sq3123G*Qayk_Q^8;D zW~Y*!3JQa9ym}%r(cqw|e^tT#P01eocVOgj=zXGBgj?QpO11d=2#=Lr7GCU2#5y_T z5iH~Iz;3S3aSwUlWF@yWoch2U$&9MJqMgk@~73CCr4(n4)S^l_1k1q3K9%?5v>DMoW#x zSA<$33B`oOM-mbV$v*ir`)AM0{@8Q&oc;NJoaemn^W5)q-S>6h_w^G0^R*Gru8oxK z_wKr=9&uQ`8lUSc)WzFkE<5U_jsxy&HYiy7bY45Ypbe1@*|wXH_dLe zQs`OH4O3@@OQ@L2y2l3sn2+Y3tMq+!wcXz>m3%IxWnq$CDS7D>E%{!2JhILA18xd& z?+v59It(?gw6mRAj{f;Nu1m1RWCu}yQ;hbea~Tr934at$*+2U|9#QLOw)VQWLl>0y3h~#HxC8zY2g!kYDms!gX?97u19Sv4VH{P>(Uxei2EFB11GA*zec76`+Zp-nN zapZWEO*F|3_XFnT-QbOCm)yGe5c&x6}|=RYj;H2_ZNvC*d#C@rxEW-Tk@Xc25LW?fYO-)12Qso z2Y-Dp{A0Z%0e!p}#@*@pXd~{nzNi1_4xF(5DHAg}&hjz5(3RFN@~q&9=Qwqs#;4j6nvf{O{$cWI$fdY=#Y^#KPpxwu~#VMv`f^2jwu@I>t&L~x(`cj0q zOm{kLJNR z^z#JX?QXiYeZX2=d#Wr1sVP)C0nB-YEk!72I4eb+jAZn3kcAZGB@w8|WshhchIPG* zHUp#KWbjy6VT~nxj6=pEHLUPhqcIQ`M2SlGNDr1M7;k{8-g>u3= zV6zO3d3>TGLR+7u!$z#)Ws}l9Tqo3_@aQ`EZZk-*G4grKnOWQHW{EvmV9X0 zRQeD6cUKg{<|Z#dGy}O)F!f{V7T?ePXu!+KZE0;)s!Qj!S3I{U(WpM?^5H+|_PAfF zv)7(1&=|jjcb}w>kM7tJ^Vzy@{JRE4V`<{5PU;&`+V%Ozjlrq@5=rAF9`VGs{Joqn zYB?A~Ar!9*K`I~YhC=fkPTsP-YY%`pVl0Ei!97ga`3jF$88L9^XAERIBkSfikrVnA z06&N0)~~B80D^tlUkRQkJxk8Ew}2fqu5e=P;DuZ_=V?fl@j_!2R38`MmV0gYoIchO+J3*u%hzQ$rOF3!%Ub7A6$*3;&S3BUq@ijZ>9 zn*VY?60~ml4Ol^L2|&68WCK9S8c5rNvaD*A&$Fr{8aT5QFce(EZV>DP>Im=#r^-zs zAuWI!0?aTt|JmUq4jQ<~D3<|BLZghlgZRjtJy@{+t-tecJuYIxD9|xh0k$FD*RW8y z4c8>xw*_ph6lC_g)Ac;lt>CpDj~qZe0xg2dIunT4WlShErZb!Z%!ZBEq%s*0A?jQ( zha?Jw!l$6IEJ6#%-@XXcSx3E|HO^K>odB{Z_MV9^-n1r#mmVUifE1)n7$5+3s~ip> za{z7cBaJJlbYlP&>)hXfX%d^T8vf6`<6p9C@W%h;I6!5H4p{oB+&nJ{j14&zdN!mb I=*pM>1d&9FZvX%Q literal 0 HcmV?d00001 diff --git a/spring-cloud-alibaba-examples/sentinel-example/images/image-20240428171907705.png b/spring-cloud-alibaba-examples/sentinel-example/images/image-20240428171907705.png new file mode 100644 index 0000000000000000000000000000000000000000..b179e2b15278e43a1a1074b596bb9919ba3585a1 GIT binary patch literal 11748 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 literal 0 HcmV?d00001 diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-circuitbreaker-example/readme-zh.md b/spring-cloud-alibaba-examples/sentinel-example/sentinel-circuitbreaker-example/readme-zh.md deleted file mode 100644 index 0fa2d0cf1..000000000 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-circuitbreaker-example/readme-zh.md +++ /dev/null @@ -1,65 +0,0 @@ -# Sentinel Feign Circuit Breaker Example - -## 项目说明 - -Spring Cloud OpenFeign 整合 Sentinel 断路器实现。 - -## 项目示例 - -1. 添加配置到配置中心 - -dataId 为 `sentinel-circuitbreaker-rules.yml` - -```yaml -feign: - circuitbreaker: - enabled: true # 开启 feign 断路器支持 - sentinel: - default-rule: default # 默认规则名称 - rules: - # 默认规则, 对所有 feign client 生效 - default: - - grade: 2 # 根据异常数目降级 - count: 1 - timeWindow: 15 # 降级后到半开状态的时间 - statIntervalMs: 1000 - minRequestAmount: 1 - # 只对 feign client user 生效 - user: - - grade: 2 - count: 1 - timeWindow: 15 - statIntervalMs: 1000 - minRequestAmount: 1 - # 只对 feign client user 的方法 feignMethod 生效 - # 括号里是参数类型, 多个逗号分割, 比如 user#method(boolean,String,Map) - "[user#feignMethod(boolean)]": - - grade: 2 - count: 1 - timeWindow: 10 - statIntervalMs: 1000 - minRequestAmount: 1 -``` - -## 验证配置生效 - -启动项目主类 `FeignCircuitBreakerApplication` - -### 验证默认 Feign client 生效 - -先访问 http://localhost/test/default/false 2 次 (1秒内) -再访问 http://localhost/test/default/true 断路器处于打开状态 - -### 验证指定 Feign client 生效 - -先访问 http://localhost/test/feign/false 2 次 (1秒内) -再访问 http://localhost/test/feign/true 断路器处于打开状态 - -### 验证 Feign client 指定方法生效 - -先访问 http://localhost/test/feignMethod/false 2次 (1秒内) -再访问 http://localhost/test/feignMethod/true 断路器处于打开状态 - -## 规则动态刷新 - -修改配置中心的规则, 再访问上述接口。 diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-circuitbreaker-example/readme.md b/spring-cloud-alibaba-examples/sentinel-example/sentinel-circuitbreaker-example/readme.md deleted file mode 100644 index 468439542..000000000 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-circuitbreaker-example/readme.md +++ /dev/null @@ -1,63 +0,0 @@ -# Sentinel Feign Circuit Breaker Example - -## Project description - -Spring Cloud OpenFeign integrates Sentinel circuit breaker implementation. - -## sample - -1. add configuration to config center. - -```yaml -feign: - circuitbreaker: - enabled: true # Enable feign circuit breaker support - sentinel: - default-rule: default # Default rule name - rules: - # Default rule, valid for all feign clients - default: - - grade: 2 # Downgrade based on number of exceptions - count: 1 - timeWindow: 15 # Time to half-open state after downgrade - statIntervalMs: 1000 - minRequestAmount: 1 - # Only valid for feign client user - user: - - grade: 2 - count: 1 - timeWindow: 15 - statIntervalMs: 1000 - minRequestAmount: 1 - # Only valid for the method feignMethod of the feign client user - # Parentheses are parameter types, separated by multiple commas, such as user#method(boolean,String,Map) - "[user#feignMethod(boolean)]": - - grade: 2 - count: 1 - timeWindow: 10 - statIntervalMs: 1000 - minRequestAmount: 1 -``` - -## Verify - -Startup project main class `FeignCircuitBreakerApplication` - -### Verify that the default Feign client takes effect. - -First visit http:localhost/test/default/false 2 times (in 1 second) -and then visit http:localhost/test/default/true, the circuit breaker is open - -### Verify that the specified Feign client takes effect. - -First visit http:localhost/test/feign/false 2 times (in 1 second) -and then visit http:localhost/test/feign/true, the circuit breaker is open - -### Verify that the specified method of Feign client takes effect. - -First visit http://localhost/test/feignMethod/false 2 times (in 1 second) -and then visit http://localhost/test/feignMethod/true, the circuit breaker is open - -## Rules are dynamically refreshed - -Modify the rules of the configuration center, and then access the above interface diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-circuitbreaker-example/src/main/resources/application.yml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-circuitbreaker-example/src/main/resources/application.yml index a02a8565a..cdb4417c7 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-circuitbreaker-example/src/main/resources/application.yml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-circuitbreaker-example/src/main/resources/application.yml @@ -1,9 +1,26 @@ +# +# Copyright 2023-2024 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 +# +# https://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. +# + server: port: 80 spring: application: - name: circuit-breaker-app + name: sentinel-circuit-breaker-example + cloud: nacos: config: diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-circuitbreaker-example/src/main/resources/sentinel-circuitbreaker-rules.yml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-circuitbreaker-example/src/main/resources/sentinel-circuitbreaker-rules.yml index d5c867aab..b8343aef4 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-circuitbreaker-example/src/main/resources/sentinel-circuitbreaker-rules.yml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-circuitbreaker-example/src/main/resources/sentinel-circuitbreaker-rules.yml @@ -1,3 +1,19 @@ +# +# Copyright 2023-2024 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 +# +# https://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. +# + feign: circuitbreaker: enabled: true diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md deleted file mode 100644 index c5b151779..000000000 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md +++ /dev/null @@ -1,232 +0,0 @@ -# Sentinel Example - -## 项目说明 - -本项目演示如何使用 spring-cloud-starter-alibaba-sentinel 完成 Spring Cloud 应用的限流管理。 - -[Sentinel](https://github.com/alibaba/Sentinel) 是阿里巴巴开源的分布式系统的流量防卫组件,Sentinel 把流量作为切入点,从流量控制,熔断降级,系统负载保护等多个维度保护服务的稳定性。 - -## 示例 - -### 如何接入 - -在启动示例进行演示之前,我们先了解一下如何接入 Sentinel。 - -> **注意:本章节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您无需再进行修改。** - -1. 首先,修改 `pom.xml` 文件,引入 Sentinel starter。 - -```xml - - com.alibaba.cloud - spring-cloud-starter-alibaba-sentinel - -``` - -2. 接入限流埋点 - - - HTTP 埋点 - `spring-cloud-starter-alibaba-sentinel` 默认为所有的 HTTP 服务提供了限流埋点,如果只想对 HTTP 服务进行限流,那么只需要引入依赖,无需修改代码。 - - - 自定义埋点 - 如果需要对某个特定的方法进行限流或降级,可以通过 `@SentinelResource` 注解来完成限流的埋点,示例代码如下: - - ```java - @SentinelResource("resource") - public String hello() { - return "Hello"; - } - ``` - - 当然也可以通过原始的 `SphU.entry(xxx)` 方法进行埋点,可以参见 [Sentinel 文档](https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E5%AE%9A%E4%B9%89%E8%B5%84%E6%BA%90)。 - -3. 配置限流规则 - - Sentinel 提供了两种配置限流规则的方式:代码配置 和 控制台配置。本示例使用的方式为通过控制台配置。 - - 1. 通过代码来实现限流规则的配置。一个简单的限流规则配置示例代码如下,更多限流规则配置详情请参考 [Sentinel 文档](https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E5%AE%9A%E4%B9%89%E8%A7%84%E5%88%99)。 - - ```java - List rules = new ArrayList(); - FlowRule rule = new FlowRule(); - rule.setResource(str); - // set limit qps to 10 - rule.setCount(10); - rule.setGrade(RuleConstant.FLOW_GRADE_QPS); - rule.setLimitApp("default"); - rules.add(rule); - FlowRuleManager.loadRules(rules); - ``` - - 2. 通过控制台进行限流规则配置请参考文章后面的图文说明。 - -### 启动 Sentinel 控制台 - -1. 首先需要获取 Sentinel 控制台,支持直接下载和源码构建两种方式。 - - 1. 直接下载:[下载 Sentinel 控制台](https://github.com/alibaba/Sentinel/releases) - 2. 源码构建:进入 Sentinel [Github 项目页面](https://github.com/alibaba/Sentinel),将代码 clone 到本地自行编译打包,[参考此文档](https://github.com/alibaba/Sentinel/blob/1.8/sentinel-dashboard/README.md)。 -`` -2. 启动控制台,执行 Java 命令 `java -jar sentinel-dashboard.jar`完成 Sentinel 控制台的启动。 - - 控制台默认的监听端口为 8080。Sentinel 控制台使用 Spring Boot 编程模型开发,如果需要指定其他端口,请使用 Spring Boot 容器配置的标准方式,详情请参考 [Spring Boot 文档](https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-customizing-embedded-containers)。 - -### 应用启动 - -1. 增加配置,在应用的 `/src/main/resources/application.properties` 中添加基本配置信息 - - ```properties - spring.application.name=sentinel-example - server.port=18083 - spring.cloud.sentinel.transport.dashboard=localhost:8080 - ``` - -2. 启动应用,支持 IDE 直接启动和编译打包后启动。 - - 1. IDE直接启动:找到主类 `ServiceApplication`,执行 main 方法启动应用。 - 2. 打包编译后启动:首先执行 `mvn clean package` 将工程编译打包,然后执行 `java -jar sentinel-core-example.jar` 启动应用。 - -### 调用服务 - -使用 curl 命令分别调用两个 URL,可以看到访问成功。 - -```shell -$ curl http://localhost:18083/test -Blocked by Sentinel (flow limiting) - -$ curl http://localhost:18083/hello -Hello -``` - -### 配置限流规则并验证 - -1. 访问 http://localhost:8080 页面,进行登陆,默认用户名和密码均为:`sentinel`。 - -可以在左侧看到 Sentinel-Example 应用已经注册到了控制台,单击 **流控规则** ,可以看到目前的流控规则为空。 - -> **注意:如果您在控制台没有找到应用,请调用一下进行了 Sentinel 埋点的 URL 或方法,因为 Sentinel 使用了 lazy load 策略。详细的排查过程请参见 [Sentinel FAQ](https://github.com/alibaba/Sentinel/wiki/FAQ)。** - -

- -2. 配置 URL 限流规则:点击新增流控规则,资源名填写需要限流的 URL 相对路径,单机阈值选择需要限流的阈值,点击新增进行确认。(为了便于演示效果,这里将值设置成了 1)。 - -

- -3. 配置自定义限流规则:点击新增流控规则,资源名填写 `@SentinelResource` 注解 `value` 字段的值,单机阈值选择需要限流的阈值,点击新增进行确认。(为了便于演示效果,这里将值设置成了 1)。 - -

- -4. 访问 URL,当 QPS 超过 1 时,可以看到限流效果如下。 - -

- -

- -## 自定义限流处理逻辑 - -* 默认限流异常处理 - -URL 限流触发后默认处理逻辑是,直接返回 "Blocked by Sentinel (flow limiting)"。 如果需要自定义处理逻辑,实现的方式如下: - -```java -public class CustomUrlBlockHandler implements UrlBlockHandler { - @Override - public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException { - // todo add your logic - } -} - -WebCallbackManager.setUrlBlockHandler(new CustomUrlBlockHandler()); -``` - -* 使用 `@SentinelResource` 注解下的限流异常处理 - -如果需要自定义处理逻辑,填写 `@SentinelResource` 注解的 `blockHandler` 属性(针对所有类型的 `BlockException`,需自行判断)或 `fallback` 属性(针对熔断降级异常),注意**对应方法的签名和位置有限制**,详情见 [Sentinel 注解支持文档](https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81#sentinelresource-%E6%B3%A8%E8%A7%A3)。示例实现如下: - -```java -public class TestService { - - // blockHandler 是位于 ExceptionUtil 类下的 handleException 静态方法,需符合对应的类型限制. - @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class}) - public void test() { - System.out.println("Test"); - } - - // blockHandler 是位于当前类下的 exceptionHandler 方法,需符合对应的类型限制. - @SentinelResource(value = "hello", blockHandler = "exceptionHandler") - public String hello(long s) { - return String.format("Hello at %d", s); - } - - public String exceptionHandler(long s, BlockException ex) { - // Do some log here. - ex.printStackTrace(); - return "Oops, error occurred at " + s; - } -} -``` - -```java -public final class ExceptionUtil { - - public static void handleException(BlockException ex) { - System.out.println("Oops: " + ex.getClass().getCanonicalName()); - } -} -``` - -一个简单的 `@SentinelResource` 示例可以见 [sentinel-demo-annotation-spring-aop](https://github.com/alibaba/Sentinel/tree/2021.x/sentinel-demo/sentinel-demo-annotation-spring-aop)。 - -## Endpoint 信息查看 - -Spring Boot 应用支持通过 Endpoint 来暴露相关信息,`spring-cloud-starter-alibaba-sentinel` 也支持这一点。 - -在使用之前需要在 Maven 中添加 `spring-boot-starter-actuator`依赖,并在配置中允许 Endpoints 的访问。 -* Spring Boot 1.x 中添加配置 `management.security.enabled=false` -* Spring Boot 2.x 中添加配置 `management.endpoints.web.exposure.include=*` - -Spring Boot 1.x 可以通过访问 http://127.0.0.1:18083/sentinel 来查看 Sentinel Endpoint 的信息。Spring Boot 2.x 可以通过访问 http://127.0.0.1:18083/actuator/sentinel 来访问。 - -

- -## 查看实时监控 -Sentinel 控制台支持实时监控查看,您可以通过 Sentinel 控制台查看各链路的请求的通过数和被限流数等信息。 -其中 `p_qps` 为通过(pass) 流控的 QPS,`b_qps` 为被限流 (block) 的 QPS。 - -

- -## ReadableDataSource 支持 - -Sentinel 内部提供了[动态规则的扩展实现 ReadableDataSource](https://github.com/alibaba/Sentinel/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%99%E6%89%A9%E5%B1%95#datasource-%E6%89%A9%E5%B1%95)。 - -Sentinel starter 整合了目前存在的几类 ReadableDataSource。只需要在配置文件中进行相关配置,即可在 Spring 容器中自动注册 DataSource。 - -比如要定义两个ReadableDataSource,分别是 `FileRefreshableDataSource` 和 `NacosDataSource`,配置如下: - -```properties -spring.cloud.sentinel.datasource.ds1.file.file=classpath: degraderule.json -spring.cloud.sentinel.datasource.ds1.file.data-type=json - -spring.cloud.sentinel.datasource.ds2.nacos.server-addr=127.0.0.1:8848 -spring.cloud.sentinel.datasource.ds2.nacos.dataId=sentinel -spring.cloud.sentinel.datasource.ds2.nacos.groupId=DEFAULT_GROUP -spring.cloud.sentinel.datasource.ds2.nacos.data-type=json -``` - -`ds1` 和 `ds2` 表示ReadableDataSource的名称,可随意编写。`ds1` 和 `ds2` 后面的 `file` 和 `nacos` 表示ReadableDataSource的类型。 - -目前支持`file`, `nacos`, `zk`, `apollo`,`redis` 这5种类型。 - -其中`nacos`,`zk`,`apollo`,`redis` 这4种类型的使用需要加上对应的依赖`sentinel-datasource-nacos`, `sentinel-datasource-zookeeper`, `sentinel-datasource-apollo`, `sentinel-datasource-redis`。 - -当 `ReadableDataSource` 加载规则数据成功的时候,控制台会打印出相应的日志信息: - -``` -[Sentinel Starter] DataSource ds1-sentinel-file-datasource load 3 DegradeRule -[Sentinel Starter] DataSource ds2-sentinel-nacos-datasource load 2 FlowRule -``` - -## More -Sentinel 是一款功能强大的中间件,从流量控制,熔断降级,系统负载保护等多个维度保护服务的稳定性。此 Demo 仅演示了 使用 Sentinel 作为限流工具的使用,更多 Sentinel 相关的信息,请参考 [Sentinel 项目](https://github.com/alibaba/Sentinel)。 - -如果您对 `spring-cloud-starter-alibaba-sentinel` 有任何建议或想法,欢迎在 issue 中或者通过其他社区渠道向我们提出。 diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme.md b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme.md deleted file mode 100644 index e845f807d..000000000 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme.md +++ /dev/null @@ -1,240 +0,0 @@ -# Sentinel Example -## Project Instruction - -This example illustrates how to use Sentinel starter to implement flow control for Spring Cloud applications. - -[Sentinel](https://github.com/alibaba/Sentinel) is an open-source project of Alibaba. Sentinel takes "traffic flow" as the breakthrough point, and provides solutions in areas such as flow control, concurrency, circuit breaking, and load protection to protect service stability. - - -## Demo - -### Connect to Sentinel -Before we start the demo, let's learn how to connect Sentinel 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-alibaba-sentinel in the pom.xml file in your Spring Cloud project. - -```xml - - com.alibaba.cloud - spring-cloud-starter-alibaba-sentinel - -``` - -2. Define Resources - - 1. Define HTTP Resources - `spring-cloud-starter-alibaba-sentinel` defines all HTTP URLS as resources by relative paths. If you only want to add flow control for your HTTP services, you do not need to modify your code. - - 2. Define Custom Resources - If you want to implement flow control or degradation for a specific method, you can add an @SentinelResource annotation to the method, as shown in the code below. - - ```java - @SentinelResource("resource") - public String hello() { - return "Hello"; - } - ``` - -3. Configure flow control rules - - Sentinel provides two ways to configure flow control rules, init from code or configure by dashboard. - - 1. Init rule from code: See the code below for a simple flow rule. See [Sentinel Docs](https://github.com/alibaba/Sentinel/wiki/How-to-Use#define-rules) for more information about flow rules. - - ```java - List rules = new ArrayList(); - FlowRule rule = new FlowRule(); - rule.setResource(str); - // set limit qps to 10 - rule.setCount(10); - rule.setGrade(RuleConstant.FLOW_GRADE_QPS); - rule.setLimitApp("default"); - rules.add(rule); - FlowRuleManager.loadRules(rules); - ``` - - 2. Config by dashboard: See the following section. - -### Start Sentinel Dashboard - -1. Install Sentinel dashboard by downloading a fatjar or build from source code. - - 1. Download: [Download Sentinel Dashboard](https://github.com/alibaba/Sentinel/releases) - 2. Build from source code: Get source code by `git clone git@github.com:alibaba/Sentinel.git` from [Github Sentinel](https://github.com/alibaba/Sentinel) and build your code. See [build reference](https://github.com/alibaba/Sentinel/blob/1.8/sentinel-dashboard/README.md) for details. - -2. Start the dashboard by running the `java -jar sentinel-dashboard.jar` command. - The default port of Sentinel dashboard is 8080. Sentinel dashboard is a Spring Boot project. If you want to use another port, see [Spring Boot Reference](https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-customizing-embedded-containers). - -### Start Application - -1. Add necessary configurations to file `/src/main/resources/application.properties`. - - ```properties - spring.application.name=sentinel-example - server.port=18083 - spring.cloud.sentinel.transport.dashboard=localhost:8080 - ``` - -2. Start the application in IDE or by building a fatjar. - - 1. Start in IDE: Find main class `ServiceApplication`, and execute the main method. - 2. Build a fatjar:Execute command `mvn clean package` to build a fatjar, and run command `java -jar sentinel-core-example.jar` to start the application. - -### Invoke Service - -Use curl command to call two URLs, you can see: - -```shell -$ curl http://localhost:18083/test -Blocked by Sentinel (flow limiting) - -$ curl http://localhost:18083/hello -Hello -``` -### Configure Flow Control - -1. Open http://localhost:8080 in browser. The defaule username and password is `sentienl`. and you can find a Sentinel-Example Application has been registered to the dashboard. - - **Note: If you can't find your application in the dashboard, invoke a method that has been defined as a Sentinel Resource, for Sentinel uses lazy load strategy.** - -

- -2. Configure HTTP Resource Flow Rule:Click **流控规则(Flow Rule)** on the left-side navigation pane and **新增流控规则(Create Flow Rule)**. On the Create Flow Rule dialogbox, type the URL relative path in the **资源名(Resource Name)** field , enter **单机阈值(Threshold)** value, then click **新增(OK)**. Here we set threshold to 1 for demonstration purpose. - -

- -3. Configure Custom Resource Flow Rule:Click **流控规则(Flow Rule)** on the left-side navigation pane and **新增流控规则(Create Flow Rule)**. type the value() of @SentinelResource in the **资源名(Resource Name)** field , enter **单机阈值(Threshold)** value, then click **新增(OK)**.Here we set threshold to 1 for demonstration purpose. - -

- -4. Visit the URL in your browser again. When QPS is more than 1, we can see that flow control takes effect. - -

- -

- -## Customize Flow Control Logic - -* Flow control exception handle by default - -When a URL resource is blocked by Sentinel, the default logic is return HTTP response "Blocked by Sentinel (flow limiting)". - -```java -public class CustomUrlBlockHandler implements UrlBlockHandler { - @Override - public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException { - // todo add your logic - } -} - -WebCallbackManager.setUrlBlockHandler(new CustomUrlBlockHandler()); -``` - - -* Flow control exception handle by using `@SentinelResource` - -When a custom resource is blocked by Sentinel, the default logic is throw BlockException. - -```java -public class TestService { - - @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class}) - public void test() { - System.out.println("Test"); - } - - @SentinelResource(value = "hello", blockHandler = "exceptionHandler") - public String hello(long s) { - return String.format("Hello at %d", s); - } - - public String exceptionHandler(long s, BlockException ex) { - // Do some log here. - ex.printStackTrace(); - return "Oops, error occurred at " + s; - } -} -``` - -```java -public final class ExceptionUtil { - - public static void handleException(BlockException ex) { - System.out.println("Oops: " + ex.getClass().getCanonicalName()); - } -} -``` - -## Endpoint - -Sentinel starter also supports the implementation of Spring Boot actuator endpoints. - -**Prerequisite:** - -Add dependency `spring-boot-starter-actuator` 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/sentinel. -* Spring Boot2.x: Sentinel Endpoint URL is http://127.0.0.1:18083/actuator/sentinel. - -

- -## Metrics -You can view metrics information on Sentinel Dashboard. - -To see the metrics, click **Real time monitoring(Real-time Monitoring)** in the left-side navigation pane. - -`p_qps` stands for passed requests per second, `b_qps` stands for blocked requests per second. - -

- -## ReadableDataSource - -Sentinel provide [ReadableDataSource](https://github.com/alibaba/Sentinel/blob/2021.x/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/ReadableDataSource.java) to manage dynamic rules. - -Sentinel starter integrated 4 DataSources provided by Sentinel. It will be register into Spring Context if you write some configs in `application.properties`. - -If you want to define `FileRefreshableDataSource` and `NacosDataSource`, see the code below: - -```properties -spring.cloud.sentinel.datasource.ds1.file.file=classpath: degraderule.json -spring.cloud.sentinel.datasource.ds1.file.data-type=json - -spring.cloud.sentinel.datasource.ds2.nacos.server-addr=127.0.0.1:8848 -spring.cloud.sentinel.datasource.ds2.nacos.dataId=sentinel -spring.cloud.sentinel.datasource.ds2.nacos.groupId=DEFAULT_GROUP -spring.cloud.sentinel.datasource.ds2.nacos.data-type=json -``` - -`ds1` and `ds2` means the name of ReadableDataSource, you can write whatever you want. The `file` and `nacos` after name `ds1` and `ds2` means the type of ReadableDataSource. - -Now ReadableDataSource type support 5 categories: `file`, `nacos`, `zk`, `apollo` and `redis`. - -If you want to use `nacos`, `zk`, `apollo` or `redis` ReadableDataSource, you could add `sentinel-datasource-nacos`, `sentinel-datasource-zookeeper`,`sentinel-datasource-apollo` or `sentinel-datasource-redis` dependency. - -When ReadableDataSource load rule data successfully, console will print some logs: - -``` -[Sentinel Starter] DataSource ds1-sentinel-file-datasource load 3 DegradeRule -[Sentinel Starter] DataSource ds2-sentinel-nacos-datasource load 2 FlowRule -``` -## Warning -You should use `file` ReadableDataSource in a fatjar carefully or you may get error like this below - -``` -java.lang.RuntimeException: [Sentinel Starter] DataSource ds1 handle file [classpath: flowrule.json] error: class path resource [flowrule.json] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:xxx/xxx.jar!/BOOT-INF/classes!/flowrule.jso -``` - -You could use absolute path when you use File datasource & fat jar. -It is recommended to use Nacos/Apollo/Zookeeper/Redis datasource to store rules. - -https://github.com/alibaba/spring-cloud-alibaba/issues/428 - -## More -For more information about Sentinel, see [Sentinel Project](https://github.com/alibaba/Sentinel). - -If you have any ideas or suggestions for Spring Cloud Sentinel starter, please don't hesitate to tell us by submitting github issues. diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/java/com/alibaba/cloud/examples/ServiceApplication.java b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/java/com/alibaba/cloud/examples/SentinelCoreApplication.java similarity index 95% rename from spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/java/com/alibaba/cloud/examples/ServiceApplication.java rename to spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/java/com/alibaba/cloud/examples/SentinelCoreApplication.java index 173036371..e8fe80d2f 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/java/com/alibaba/cloud/examples/ServiceApplication.java +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/java/com/alibaba/cloud/examples/SentinelCoreApplication.java @@ -35,7 +35,7 @@ import org.springframework.web.client.RestTemplate; * @author xiaojing */ @SpringBootApplication -public class ServiceApplication { +public class SentinelCoreApplication { @Bean @SentinelRestTemplate(blockHandler = "handleException", @@ -67,7 +67,7 @@ public class ServiceApplication { } public static void main(String[] args) { - SpringApplication.run(ServiceApplication.class, args); + SpringApplication.run(SentinelCoreApplication.class, args); } } diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/application.properties b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/application.properties deleted file mode 100644 index e9c085936..000000000 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/application.properties +++ /dev/null @@ -1,40 +0,0 @@ -spring.application.name=sentinel-example -server.port=18083 -management.endpoints.web.exposure.include=* -management.endpoint.health.show-details=always - -# we can disable health check, default is enable -management.health.diskspace.enabled=false -# management.health.sentinel.enabled=false - -spring.cloud.sentinel.transport.dashboard=localhost:8080 -spring.cloud.sentinel.eager=true -spring.cloud.sentinel.web-context-unify=true - -#spring.cloud.sentinel.block-page=/errorPage -#spring.cloud.sentinel.filter.enabled=false -#spring.cloud.sentinel.http-method-specify=false - -#spring.cloud.sentinel.datasource.ds6.nacos.server-addr=127.0.0.1:8848 -#spring.cloud.sentinel.datasource.ds6.nacos.username=nacos -#spring.cloud.sentinel.datasource.ds6.nacos.password=nacos -#spring.cloud.sentinel.datasource.ds6.nacos.dataId=flowrule.json -#spring.cloud.sentinel.datasource.ds6.nacos.data-type=json -#spring.cloud.sentinel.datasource.ds6.nacos.rule-type=flow - -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.rule-type=param_flow diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/application.yml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/application.yml new file mode 100644 index 000000000..6069364d9 --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/application.yml @@ -0,0 +1,80 @@ +# +# Copyright 2023-2024 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 +# +# https://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. +# + +spring: + application: + name: sentinel-core-example + + cloud: + sentinel: + transport: + dashboard: localhost:8080 + datasource: + ds1: + file: + file: classpath:flowrule.json + data-type: json + rule-type: flow + ds2: + file: + file: classpath:degraderule.json + data-type: json + rule-type: degrade + ds3: + file: + file: classpath:authority.json + data-type: json + rule-type: authority + ds4: + file: + file: classpath:system.json + data-type: json + rule-type: system + ds5: + file: + file: classpath:param-flow.json + data-type: json + rule-type: param_flow + ds6: + nacos: + server-addr: 127.0.0.1:8848 + username: nacos + password: nacos + data-id: flowrule.json + data-type: json + rule-type: flow + + block-page: /errorPage + # filter: + # enabled: false + # http-method-specify: false + eager: true + +server: + port: 18083 + +management: + endpoint: + web: + exposure: + include: "*" + health: + show-details: always + diskSpace: + # we can disable health check, default is enable + enabled: false + # sentinel: + # enabled: false diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-openfeign-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-openfeign-example/pom.xml index ecc2d4f56..4b0bbae40 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-openfeign-example/pom.xml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-openfeign-example/pom.xml @@ -30,6 +30,10 @@ com.alibaba.cloud spring-cloud-starter-alibaba-sentinel + + org.springframework.boot + spring-boot-starter-actuator + diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-openfeign-example/src/main/java/com/alibaba/cloud/examples/OpenFeignApplication.java b/spring-cloud-alibaba-examples/sentinel-example/sentinel-openfeign-example/src/main/java/com/alibaba/cloud/examples/OpenFeignApplication.java index ed699c63c..abc023acb 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-openfeign-example/src/main/java/com/alibaba/cloud/examples/OpenFeignApplication.java +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-openfeign-example/src/main/java/com/alibaba/cloud/examples/OpenFeignApplication.java @@ -18,19 +18,19 @@ package com.alibaba.cloud.examples; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; /** * @author raozihao * @author Steve */ + @EnableFeignClients @SpringBootApplication -@EnableDiscoveryClient public class OpenFeignApplication { public static void main(String[] args) { + SpringApplication.run(OpenFeignApplication.class, args); } diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-openfeign-example/src/main/java/com/alibaba/cloud/examples/configuration/SentinelRulesConfiguration.java b/spring-cloud-alibaba-examples/sentinel-example/sentinel-openfeign-example/src/main/java/com/alibaba/cloud/examples/configuration/SentinelRulesConfiguration.java index b011047fc..d2e8833d2 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-openfeign-example/src/main/java/com/alibaba/cloud/examples/configuration/SentinelRulesConfiguration.java +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-openfeign-example/src/main/java/com/alibaba/cloud/examples/configuration/SentinelRulesConfiguration.java @@ -29,16 +29,15 @@ import jakarta.annotation.PostConstruct; import org.springframework.stereotype.Component; /** - * @description - * @author ChengPu raozihao - * @date 2023/2/11 + * @author raozihao + * @author Steve */ @Component public class SentinelRulesConfiguration { - /** - * You can configure sentinel rules by referring https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E6%9F%A5%E8%AF%A2%E6%9B%B4%E6%94%B9%E8%A7%84%E5%88%99. + * You can configure sentinel rules by referring. + * https://sca.aliyun.com/docs/2023/user-guide/sentinel/advanced-guide/#%E6%9B%B4%E5%A4%9A%E9%85%8D%E7%BD%AE%E9%A1%B9 */ @PostConstruct public void init() { diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-openfeign-example/src/main/resources/application.yml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-openfeign-example/src/main/resources/application.yml index 601088869..8aa758c1e 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-openfeign-example/src/main/resources/application.yml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-openfeign-example/src/main/resources/application.yml @@ -1,13 +1,31 @@ +# +# Copyright 2023-2024 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 +# +# https://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. +# + server: port: 18087 spring: application: - name: openfeign-example + name: sentinel-openfeign-example + cloud: sentinel: transport: - dashboard: 127.0.0.1:8081 + dashboard: localhost:8080 + eager: true # Don't support run in jar by using configuration file method now, refer to https://github.com/alibaba/spring-cloud-alibaba/issues/3033 # datasource: # ds1.file: diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-resttemplate-example/src/main/java/com/alibaba/cloud/examples/configuration/SentinelRulesConfiguration.java b/spring-cloud-alibaba-examples/sentinel-example/sentinel-resttemplate-example/src/main/java/com/alibaba/cloud/examples/configuration/SentinelRulesConfiguration.java index b56f72f73..23f796573 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-resttemplate-example/src/main/java/com/alibaba/cloud/examples/configuration/SentinelRulesConfiguration.java +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-resttemplate-example/src/main/java/com/alibaba/cloud/examples/configuration/SentinelRulesConfiguration.java @@ -36,7 +36,8 @@ import org.springframework.stereotype.Component; @Component public class SentinelRulesConfiguration { /** - * You can configure sentinel rules by referring https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E6%9F%A5%E8%AF%A2%E6%9B%B4%E6%94%B9%E8%A7%84%E5%88%99. + * You can configure sentinel rules by referring. + * https://sca.aliyun.com/docs/2023/user-guide/sentinel/advanced-guide/#%E6%9B%B4%E5%A4%9A%E9%85%8D%E7%BD%AE%E9%A1%B9 */ @PostConstruct public void init() { diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-resttemplate-example/src/main/resources/application.yml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-resttemplate-example/src/main/resources/application.yml index 8fea0f9b0..795684fe7 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-resttemplate-example/src/main/resources/application.yml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-resttemplate-example/src/main/resources/application.yml @@ -1,9 +1,32 @@ +# +# Copyright 2023-2024 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 +# +# https://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. +# + server: port: 18088 spring: application: name: sentinel-resttemplate-example + + cloud: + sentinel: + transport: + dashboard: localhost:8080 + eager: true + # Don't support run in jar by using configuration file method now, refer to https://github.com/alibaba/spring-cloud-alibaba/issues/3033 # cloud: # sentinel: @@ -16,4 +39,3 @@ spring: # file: "classpath: flowrule.json" # ruleType: "flow" # dataType: "json" - diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-spring-cloud-gateway-example/src/main/java/com/alibaba/cloud/examples/SentinelSpringCloudGatewayApplication.java b/spring-cloud-alibaba-examples/sentinel-example/sentinel-spring-cloud-gateway-example/src/main/java/com/alibaba/cloud/examples/SentinelSpringCloudGatewayApplication.java index e57c01b9e..bfae95948 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-spring-cloud-gateway-example/src/main/java/com/alibaba/cloud/examples/SentinelSpringCloudGatewayApplication.java +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-spring-cloud-gateway-example/src/main/java/com/alibaba/cloud/examples/SentinelSpringCloudGatewayApplication.java @@ -26,6 +26,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; public class SentinelSpringCloudGatewayApplication { public static void main(String[] args) { + // GatewayCallbackManager.setRequestOriginParser(s -> "123"); SpringApplication.run(SentinelSpringCloudGatewayApplication.class, args); } diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-spring-cloud-gateway-example/src/main/resources/application.yaml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-spring-cloud-gateway-example/src/main/resources/application.yaml index 37181d40a..6d55c65a2 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-spring-cloud-gateway-example/src/main/resources/application.yaml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-spring-cloud-gateway-example/src/main/resources/application.yaml @@ -1,8 +1,26 @@ +# +# Copyright 2023-2024 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 +# +# https://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. +# + server: port: 18085 + spring: application: name: sentinel-spring-cloud-gateway + cloud: gateway: enabled: true @@ -31,6 +49,7 @@ spring: ruleType: gw-api-group transport: dashboard: localhost:8080 + eager: true filter: enabled: true scg.fallback: @@ -40,4 +59,8 @@ spring: scg: order: -100 -management.endpoints.web.exposure.include: "*" \ No newline at end of file +management: + endpoints: + web: + exposure: + include: "*" diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-webflux-example/src/main/resources/application.properties b/spring-cloud-alibaba-examples/sentinel-example/sentinel-webflux-example/src/main/resources/application.properties deleted file mode 100644 index 48bd8f43c..000000000 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-webflux-example/src/main/resources/application.properties +++ /dev/null @@ -1,8 +0,0 @@ -spring.application.name=sentinel-webflux-example -server.port=18084 -management.endpoints.web.exposure.include=* -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 -spring.cloud.sentinel.datasource.ds1.file.rule-type=flow diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-webflux-example/src/main/resources/application.yml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-webflux-example/src/main/resources/application.yml new file mode 100644 index 000000000..a32c172c0 --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-webflux-example/src/main/resources/application.yml @@ -0,0 +1,40 @@ +# +# Copyright 2023-2024 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 +# +# https://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. +# + +server: + port: 18084 + +spring: + application: + name: sentinel-webflux-example + + cloud: + sentinel: + transport: + dashboard: localhost:8080 + eager: true + datasource: + ds1: + file: + file: classpath:flowrule.json + data-type: json + rule-type: flow + +management: + endpoints: + web: + exposure: + include=*: