# study **Repository Path**: jiangbogu/study ## Basic Information - **Project Name**: study - **Description**: this is a study project - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2020-09-05 - **Last Updated**: 2022-08-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README --- ![]()typora-root-url: ./images --- ### study this is a study project # Sentinel流量控制 ### 1.Sentinel 导学 > ​ 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的**轻量级**流量控制框架,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助您保护服务的稳定性。 #### 1.1 Sentinel 的历史 > - 2012 年,Sentinel 诞生,主要功能为入口流量控制。 > - 2013-2017 年,Sentinel 在阿里巴巴集团内部迅速发展,成为基础技术模块,覆盖了所有的核心场景。Sentinel 也因此积累了大量的流量归整场景以及生产实践。 > - 2018 年,Sentinel 开源。 #### 1.2 Sentinel 基本概念 **资源** > ​ 资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。 > > 只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。 **规则** > 围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。 #### 1.3 Sentinel 组成 > Sentinel 的使用主要分为两个部分: > > - **核心库**:主要指Java客户端,不依赖任何框架,能够运行于Java7及其以上的版本的运行环境,同时对Dubbo/Cloud等框架有较好的支持。 > - **控制台**:控制台主要负责管理推送规则,监控,集群限流分配管理,机器发现等。 #### 1.4 Sentinel特征 - **丰富的应用场景**:Sentinel承接了阿里巴巴近10年的双十一流量的核心业务场景。例如秒杀、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。 - **完备的实时监控**:Sentinel同时提供实时监控功能。可以在控制台看到接入应用的单台机器秒级数据,深知500台一下的规模的集群汇总运行情况。 - **广泛的开源生态**:Sentinel提供开箱即用的与其他开源框架的整合模块。例如:SpringCloud、Dubbo、gRPC的整合,只需要引入相关的依赖包并进行简单的配置即可。 - **完善的SPI扩展点**:Sentinel提供简单易用、完善的SPI扩展接口,可以通过实现扩展接口快速的定制逻辑。例如,定制规则管理,适配动态数据源等。 ### 2. Sentinel 入门 #### 2.1 流量控制&熔断降级介绍 ##### 2.1.1 流量控制简介 > 流量控制在网络传输中是一个常用的概念,他用于调整网络包的发送数据。在网络传输中, 任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。 ##### 2.1.2 熔断降级简介 > 在调动系统的时候,如果调用链路中的某个资源出现了不稳定,最终会导致请求发生堆积。 > > 而熔断降级就可以解决这个问题,所谓的熔断器就是当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或者异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败(fast-fail),避免影响到其他的资源而导致级联故障。 ##### 2.1.3 流量控制&熔断降级实现方案 **Hystrix** **Resilience4j** **Sentinel** 三种产品对比 | | Sentinel | Hystrix | Resilience4j | | -------------- | :--------------------------------------------------------- | :-------------------- | -------------------------------- | | 隔离策略 | 信号量隔离(并发线程数隔离) | 线程池隔离/信号量隔离 | 信号量隔离 | | 熔断降级策略 | 基于响应时间、异常比率、异常数 | 基于异常比率 | 基于异常比率、响应时间 | | 实时统计实现 | 滑动窗口 | 滑动窗口 | Ring Bit Buffer | | 动态规则配置 | 支持多种数据源 | 支持多种数据源 | 有限支持 | | 扩展性 | 多个扩展点 | 插件的形式 | 接口的形式 | | 基于注释的支持 | 支持 | 支持 | 支持 | | 限流 | 基于QPS,支持基于调用关系的限流 | 有限的支持 | Rate Limiter | | 流量整形 | 支持预热模式、匀速器模式、预热排队模式 | 不支持 | 简单的Rate Limiter模式 | | 系统自适应保护 | 支持 | 不支持 | 不支持 | | 控制台 | 提供开箱即用的控制台,可配置规则,查看秒级监控,机器发现等 | 简单的监控查看 | 不提供控制台,可对接其他监控系统 | ##### 2.1.4 公网Demo 公网Demo的操作主要分为三部分 - 下载公网Demo - 公网Demo接入阿里AHAS控制台 - 定义公网Demo的流控规则 > 第一步:下载Demo ![](/xinyongyong.png) ![](/daima.png) 第二步 本地代码演练 wget https://ahasoss-cn-hangzhou.oss-cn-hangzhou.aliyuncs.com/sdk/latest/ahas-sentinel-sdk-demo.jar java -Dahas.namespace=default -Dproject.name=AppName -Dahas.license=669f0a77e53d48778f492bf644e659e2 -jar ahas-sentinel-sdk-demo.jar > 第三步 到应用高可用服务控制台查看具体信息 ​ ![jiankong](/jiankong.png) ### 3. Sentinel流量控制降级入门 Sentinel本地应用流控降级实现分为三步: 1. 创建本地应用 2. 搭建本地Sentinel控制台 3. 本地应用接入本地Sentinel控制台 ##### 3.1 本地应用搭建 **整体流程分析** 1. 创建SpringBoot项目 2. 在项目的pom.xml文件中引入sentinel-core依赖坐标 3. 创建TestController,定义使用流量规则 4. 运行测试 **具体流程** **1. 创建SpringBoot项目,名称为sentinel-quick-start** **2. 在项目中引入sentinel-core的依赖坐标** ```xml study com.gujiangbo 1.0-SNAPSHOT ../pom.xml 4.0.0 study-sentinel-quick-start 1.0-SNAPSHOT jar 1.8 com.alibaba.csp sentinel-core 1.7.2 org.springframework.boot spring-boot-starter-web RELEASE compile ``` 2. **创建Controller** ```java package com.gujiangbo.sentinel.controller; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.slots.block.RuleConstant; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; @RestController public class HelloController { @GetMapping("sayHello") public String sayHello() { /** * 使用限流规则 */ try { //限流入口 Entry hello = SphU.entry("Hello"); return "hello Sentinel !"; } catch (BlockException e) { e.printStackTrace(); return "系统繁忙,请稍后重试";//限流或者降级处理 } } /** * 自定义限流规则 */ @PostConstruct//当前类的构造函数执行完毕之后再执行的方法 public void initFlowRules() { //1.创建限流规则集合 List rules = new ArrayList(); //2.创建限流规则 FlowRule flowRule = new FlowRule(); //定义资源,标识Sentinel对哪个资源生效 flowRule.setResource("Hello"); //定义限流规则类型,QPS flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS); //QPS设置限流值,每秒通过的请求个数 flowRule.setCount(2); //3.将限流规则存放到集合中 rules.add(flowRule); //4.加载限流规则 FlowRuleManager.loadRules(rules); } } ``` 3. **启动服务** ```java @SpringBootApplication public class SentinelApplication { public static void main(String[] args) { SpringApplication.run(SentinelApplication.class, args); } } ``` ```clojure . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.4.2) 2021-01-26 21:12:39.227 INFO 11516 --- [ main] c.g.sentinel.SentinelApplication : Starting SentinelApplication using Java 1.8.0_151 on gujiangbodeMacBook-Pro.local with PID 11516 (/Users/gujiangbo/IdeaProjects/study/study-sentinel-quick-start/target/classes started by gujiangbo in /Users/gujiangbo/IdeaProjects/study) 2021-01-26 21:12:39.233 INFO 11516 --- [ main] c.g.sentinel.SentinelApplication : No active profile set, falling back to default profiles: default 2021-01-26 21:12:41.713 INFO 11516 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2021-01-26 21:12:41.723 INFO 11516 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2021-01-26 21:12:41.724 INFO 11516 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.41] 2021-01-26 21:12:41.818 INFO 11516 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2021-01-26 21:12:41.820 INFO 11516 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2410 ms INFO: Sentinel log output type is: file INFO: Sentinel log charset is: utf-8 INFO: Sentinel log base directory is: /Users/gujiangbo/logs/csp/ INFO: Sentinel log name use pid is: false 2021-01-26 21:12:42.202 INFO 11516 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2021-01-26 21:12:42.486 INFO 11516 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2021-01-26 21:12:42.503 INFO 11516 --- [ main] c.g.sentinel.SentinelApplication : Started SentinelApplication in 5.142 seconds (JVM running for 7.762) 2021-01-26 21:13:12.223 INFO 11516 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2021-01-26 21:13:12.224 INFO 11516 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2021-01-26 21:13:12.225 INFO 11516 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms ``` 4. **浏览器访问controller** **设定每秒请求限流为2次。频繁刷新容易报错。** ![](/Users/gujiangbo/IdeaProjects/study/images/error.png) ![](/Users/gujiangbo/IdeaProjects/study/images/normal.png) 5. **Sentinel本地日志** ![log](/Users/gujiangbo/IdeaProjects/study/images/log.png) ##### 3.2 本地Sentinel控制台 > Sentinel提供一个轻量级的开源控制台,它提供机器发现以及健康状况管理、实时监控(单机或者集群)、规则管理和推送的功能。 **本地控制台的搭建** 1. **下载Sentinel控制台的jar包** 下载地址:https://github.com/alibaba/Sentinel/releases/download/1.7.2/sentinel-dashboard-1.7.2.jar 2. **启动Sentinel控制台** 启动Sentinel控制台需要JDK1.8以上版本 使用以下命令启动控制台: > java -Dserver.port=9000 -jar sentinel-dashboard-1.7.2.jar ![mingl](/Users/gujiangbo/IdeaProjects/study/images/sentinel-mingl.png) 3. **访问Sentinel控制台** 通过浏览器http://localhost:9000即可访问Sentinel控制台,用户名密码均是sentinel dashboard ##### 3.3 **应用接入控制台** > 本地应用是以客户的身份来接入控制台,具体步骤如下: 1. **本地应用的pom.xml文件引入依赖** ```xml com.alibaba.csp sentinel-transport-simple-http 1.7.2 ``` 2. **在本地应用的VM启动参数** -Dcsp.sentinel.dashboard.server=localhost:9000 设置Sentinel控制台的主机地址和端口 -Dproject.name=SentinelQuickStart 设置本地应用在Sentinel控制台的名称 ![vm](/Users/gujiangbo/IdeaProjects/study/images/vm.png) 3. **页面访问控制台并刷新** 在访问控制台之前,手动调用接口并触发规则,否则Sentinel不会主动与控制台通讯 ![db](/Users/gujiangbo/IdeaProjects/study/images/dashboard2.png) > 需要注意的是应用配置好之后与控制台的连接参数之后,并不会主动连接控制台,需要触发一次规则才会开始进行初始化,并向控制台发送心跳和应用规则等信息。 > > 此外还需要注意的是Sentinel提供了两种设置流控规则的方式 > > - 在应用中使用代码编写流控规则(案例中使用的方式) > - 在Sentinel控制台设置流控规则 在Sentinel控制台的左侧菜单中选择流控规则即可对限流规则进行调整 ![rule](/Users/gujiangbo/IdeaProjects/study/images/rule.png) 如何使用控制台限流规则? > - 将代码中限流规则去除 > - 在控制台**流控规则**中新增加流控规则,注意,流控规则**资源名**要与代码中的**Hello**保持一致。否则无法触发限流规则!! ##### 3.4 Sentinel定义资源的方式 Sentinel除了基本的定义资源的方式之外,还有其他的定义资源方式。具体如下: - 抛出异常的方式定义资源 - 返回布尔值定义资源 - 异步调用支持 - 注解方式定义资源 - 主流框架默认适配 ###### 3.4.1 抛出异常的方式定义资源 Sentinel中的SphU包含了try-catch风格的API。用这种方式,当资源发生了限流之后会抛出BlockException。这个时候可以捕捉到异常,进行限流之后的逻辑处理,而我们在入门案例中使用 了这种方式定义资源。关键代码如下: ```java try { //限流入口 Entry hello = SphU.entry("Hello"); return "hello Sentinel !"; } catch (BlockException e) { e.printStackTrace(); return "系统繁忙,请稍后重试";//限流或者降级处理 } ``` ###### 3.4.2 **返回布尔值方式定义资源** Sentinel的sphU提供了if-else风格的API。用这种方式,当资源发生了限流之后就会返回false.这个时候可以根据返回值,进行限流之后的逻辑处理。 测试代码如下: ```java //1.进行限流控制 if (SphO.entry("boolean")) {//限流入口 try { //被保护的资源 System.out.println("访问成功"); return "访问成功!"; } finally { //sphO.entry(xxx)需要与SphO.exit()方法成对出现,否则会导致调用链记录异常,抛出ErrorEntryFreeException异常 SphO.exit(); } } else { //被限流或者降级的处理 System.out.println("系统繁忙,请稍后再试!"); return "系统繁忙,请稍后再试!"; } ``` ###### 3.4.3 **异步调用支持** > Sentinel支持异步调用链路的统计。在异步调用中,需要通过SphU.asyncEntry(xxx)方法定义资源,并通常需要在异步的回调函数中调用exit方法。 1. 在本地应用的引导类上添加@EnableAsync,表示SpringBoot项目开始异步调用支持 ```java @SpringBootApplication @EnableAsync public class SentinelApplication { public static void main(String[] args) { SpringApplication.run(SentinelApplication.class, args); } } ``` 2. 创建AsyncService编写异步调用方法 ```java @Service public class AsyncService { @Async public void hello() { System.out.println("异步开始======="); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("异步调用完成======"); } } ``` 3. 创建Controller,实现异步限流控制 ```java @RestController public class AsyncHelloController { @Autowired private AsyncService asyncService; @GetMapping("async") public void sayHello() { //限流开始 AsyncEntry asyncEntry = null; try { asyncEntry = SphU.asyncEntry("Sentinel_Async");//限流入口 asyncService.hello(); } catch (BlockException e) { System.out.println("系统繁忙,稍后再试!!!!!"); } finally { if (asyncEntry != null) { asyncEntry.exit(); } } } /** * 自定义限流规则 */ /* @PostConstruct//当前类的构造函数执行完毕之后再执行的方法 public void initFlowRules() { //1.创建限流规则集合 List rules = new ArrayList(); //2.创建限流规则 FlowRule flowRule = new FlowRule(); //定义资源,标识Sentinel对哪个资源生效 flowRule.setResource("Sentinel_Async"); //定义限流规则类型,QPS flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS); //QPS设置限流值,每秒通过的请求个数 flowRule.setCount(2); //3.将限流规则存放到集合中 rules.add(flowRule); //4.加载限流规则 FlowRuleManager.loadRules(rules); }*/ } ``` 4. 运行测试 在Sentinel控制台增加关于Sentinel_Async资源的流控规则 ![](/Users/gujiangbo/IdeaProjects/study/images/async_rules.png) 6. 测试结果 ###### 3.4.4 **注解方式定义资源** > Sentinel支持通过@SentinelResource注解定义资源并配置blockHandler函数来进行限流之后的处理 1. 在本应用中的pom.xml中引入支持的注解依赖Sentinel-annotation-aspectj依赖 ```xml com.alibaba.csp sentinel-annotation-aspectj 1.7.2 ``` 2. 创建Aspectj的配置类 ```java @Configuration public class SentinelAspectConfiguration { @Bean public SentinelResourceAspect sentinelResourceAspect() { return new SentinelResourceAspect(); } } ``` 3. 创建AnnHelloController,实现限流控制 ```java @RestController public class AnnHelloController { @SentinelResource(value = "Sentinel_Ann", blockHandler = "exceptionHandler") @GetMapping("ann") public String hello() { return "this is sentinel annotation say Hello World !!"; } /** * 自定义降级处理 * @param blockException * @return */ public String exceptionHandler(BlockException blockException) { blockException.printStackTrace(); return "系统繁忙,请稍后再试!"; } } ``` 4. Sentinel控制台对资源名添加降级策略 ann 5. 页面每秒访问超过三次触发降级策略 ![jiangji](/Users/gujiangbo/IdeaProjects/study/images/jiangji.png) - @SentinelResource注解用来标识资源是否被限流、降级等。上述例子该注解的属性’Sentinel_Ann‘表示资源名。 ###### 3.4.5 主流框架的默认适配 > ​ 为了减少开发的复杂程度,我们对大部分的主流框架例如:Web Servlet、Dubbo、SpringCloud、gRPC、Spring WebFlux、Reactor等做了适配。只需要引用对应的依赖即可方便的整合Sentinel. ### 4.Sentinel高级 #### 4.1 Sentinel和SpringCloud整合 > 如果要实现SpringCloud与Sentinel的整合,可以引入Spring Cloud Alibaba Sentinel来方便的整合Sentinel。 > > Spring CloudAlibaba是阿里巴巴提供的,致力于提供微服务开发的一站式解决方案。Spring Cloud Alibaba 默认为Sentinel整合Servlet、RestTemplate、FeignClient和Spring WebFlux。Sentinel在Spring Cloud生态中,不仅补全了Hystrix 在Servlet和RestTemplate这一块的空白,而且还完全兼容了Hystrix和FeignClient中限流降级的用法,并且支持运行时灵活地配置和调整限流降级的规则。 **案例** 使用Spring Cloud + Sentinel 实现访问 http://localhost:8080/ann 路径的流量控制 **具体步骤** 1. 创建SpringCloud 项目,在项目引入spring-cloud-starter-alibaba依赖 ```xml com.alibaba.cloud spring-cloud-alibaba-sentinel 2.1.0.RELEASE ``` 2. 在项目中创建TestController ```java @RestController public class TestController { @GetMapping("hello") @SentinelResource(value = "Sentinel_SpringCloud", blockHandler = "exceptionHandler") public String hello() { return "Hello World !!"; } public String exceptionHandler(BlockException ex) { ex.printStackTrace(); return "触发熔断机制!服务器异常,请稍后再试!"; } } ``` 3. 在application.properties中添加本地项目配置并接入本地控制台 ```properties # 项目名称 spring.application.name=SpringCloudSentinel # 设置Sentinel连接控制台的ip和端口 spring.cloud.sentinel.transport.dashboard=localhost:9000 ``` 4. 运行测试 > 在Sentinel控制台中增加Sentinel_SpringCloud资源的流控规则 #### 4.2 Sentinel 对Feign 的支持 Sentinel适配了Feign组件,如果想使用,除了引入spring-cloud-starter-alibaba-sentinel的依赖外,还需要两个步骤: - 配置文件打开Sentinel对Feign的支持,feign-sentinel.enable=true - 加入spring-cloud-starter-openfeign依赖Sentinel starter中的自动化配置类生效 添加依赖 ```xml org.springframework.cloud spring-cloud-starter-alibaba-sentinel 0.2.1.RELEASE ``` 需要在配置文件中开启 sentinel 对 feign 的支持: ```properties feign.sentinel.enabled=true ```