Spring Cloud Alibaba Sentinel 初始
(一)Sentinel 简介
(1)随着微服务的普及,服务调用的稳定性变得越来越重要。Sentinel 以“流量”为突破口,在流量控制、熔断降级、负载保护等多个领域进行工作,保障服务可靠性。通俗:用来在微服务系统中保护微服务的作用,如何应对 服务雪崩 服务熔断 服务降级 就是用来替换 Hystrix;
(2)特性
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰、集群流量控制、实时熔断下游不可用应用等;
- 完备的实时监控:Sentinel 同时提供实时的监控功能,您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群汇总运行情况;
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架 / 库的整合模块,例如与 Spring Cloud、Dubbo、RPC 的整合,您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
(二)Sentinel 下载安装
(1)下载地址:Sentinel版本地址
(2)我选择 sentinel-dashboard-1.8.1.jar
(3)移动到指定目录,通过运行 Jar 包的命令执行 sentinel-dashboard-1.8.1.jar
手动指定端口后台运行:nohup java -Dserver.port=8888 -jar sentinel-dashboard-1.8.1.jar > catalina.out 2>&1 &
查看日志命令:tail -f catalina.out
(4)浏览器地址栏:http://localhost:8888 输入默认的用户名和密码 sentinel 登记成功
(三)Sentinel 实时监控服务
(1)创建项目引入依赖
<!--引入nacos client 依赖,服务注册与发现--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--引入sentinel 依赖 服务监控--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
(2)配置文件中写配置
server.port=8789 # 指定服务名称【默认和应用名称同名】 spring.application.name=nacosclient # 1)先声明 nacos 的注册中心地址;2)服务发现【客户端】进行注册,引用的就是注册中心的地址; spring.cloud.nacos.server-addr=localhost:8848 spring.cloud.nacos.discovery.server-addr=${spring.cloud.nacos.server-addr} # 暴露所有web端点【 nacos client 需要和其他微服务做通信,需要健康检查】 management.endpoints.web.exposure.exclude='*' # sentinel dashboard 8888 外部访问仪表盘界面的端口 和 sentinel logs 8719 内部服务的通信端口 # 此时 nacos-client 服务调用时产生的日志信息就会传递给 sentinel 进行展示,进而对服务进行流量控制、熔断降级、负载保护 spring.cloud.sentinel.enabled=true spring.cloud.sentinel.transport.dashboard=localhost:8888 spring.cloud.sentinel.transport.port=8719
注意:项目启动后,该微服务会自动交给 Sentinel 流量卫兵进行管理,但是访问 Dashboard 界面查看服务监控,发现界面什么都没有?
默认情况下 Sentinel 为延迟加载,不会在启动之后立即创建服务监控,需要对服务进行调用时才会初始化;
也可以在微服务启动时,手动加入配置:spring.cloud.sentinel.eager=true 此时 Sentinel 立即加载该微服务,而非延迟加载。
(3)编写简单测试 demo
@RestController @Slf4j public class SentinelController { @GetMapping("/sentinel/test") public String test(){ log.info("sentinel test"); return "sentinel test "; } @GetMapping("/sentinel/test1") public String test1(){ log.info("sentinel test1"); return "sentinel test1 "; } }
(4)调用该微服务时,会出现 QPS 的调用量
(四)S
- 流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性;
- 同一个资源可以创建多条限流规则。FlowSlot 会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍历完毕;
- 一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果;
resource:资源名,即限流规则的作用对象 【服务路径 /test/test 还可以给服务路径起别名】
count: 限流阈值
grade: 限流阈值类型(QPS 或并发线程数)
limitApp: 流控针对的调用来源,若为 default 则不区分调用来源
strategy: 调用关系限流策略 【直接 关联 链路】
controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)
- 流量控制主要有两种统计类型,一种是统计并发线程数,另外一种则是统计 QPS 。
(2)配置 QPS 流量控制
(3)QPS 测试,每秒只能接收 1 个请求,超过 1 个就会报错
(4)配置并发线程数限流【测试需要结合 jmeter 压力测试工具,可以先开启 10 个并发线程访问 /test/test1,然后手动在浏览器访问该路径,可以测试出限流效果】
(5)流控模式
- 直接:标识流量控制规则到达阈值直接触发流量控制;
- 关联: 当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_db 和 write_db 这两个资源分别代表数据库读写,我们可以给 read_db 设置限流规则来达到写优先的目的:设置 strategy 为 RuleConstant.STRATEGY_RELATE 同时设置 refResource 为 write_db。这样当写库操作过于频繁时,读数据的请求会被限流。
- 链路限流:wiki 解释地址
(6)流控效果 wiki 解释地址
- 直接拒绝:(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException;
- Warm Up:(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮;
- 匀速排队:(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。只能对请求进行排队等待。
(五)熔断降级 熔断降级wiki地址
(1)除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积。Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 `DegradeException`)。
(2)降级策略【可以自行实践一下,平均响应时间可以借用 jmeter 压力工具进行实践,异常比例和异常数可以自行在程序中处理即可测试效果】
(4)@SentinelResource 注解代码测试【自行配置 流量控制,服务降级,执行程序逻辑处理的友好提示逻辑即可】
@GetMapping("/test/test") @SentinelResource(value = "aa", fallback = "testFallBack", blockHandler = "testBlockHandler") public String test(int id) { if (id < 0 ) { throw new RuntimeException("参数错误!!!"); } log.info("进入 test 服务"); return "test 服务调用成功"; } // 程序逻辑出现异常友好提示【在界面上就不会出现异常堆栈信息,不友好,直接提示: id 为非法参数,请检查重新尝试】 public String testFallBack(int id) { return id + " 为非法参数,请检查重新尝试!!!"; } // 流控异常自定义信息方法 public String testBlockHandler(int id, BlockException blockException) { if (blockException instanceof FlowException) { return "活动异常火爆,当前请求被限流!!!" + blockException.getClass().getSimpleName(); } if (blockException instanceof DegradeException) { return "当前服务已被降级处理,请稍后再试!!!" + blockException.getClass().getSimpleName(); } return "当前服务不可用!!!"; }
年少轻狂的我,每天进步一点点......