Sentinel 入门教程 - 基础篇
大纲
前言
本文针对 Sentinel 1.8.0 及以上版本编写,特别说明除外。由于 1.8.0 版本对熔断降级特性进行了全新的改进升级,建议使用最新版本以更好地利用熔断降级的能力。
流量控制与熔断降级
流量控制概述
拿旅游景点举个示例,旅游景点通常都会有最大的接待量,不可能无限制的放游客进入,比如故宫每天只卖八万张票,超过八万的游客,无法买票进入,因为如果超过八万人,景点的工作人员可能就忙不过来,过于拥挤的景点也会影响游客的体验和心情,并且还会有安全隐患;只卖 N 张票,这就是一种限流的手段。流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。在网络传输时,任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的,因此需要根据系统的处理能力对流量进行控制。
熔断降级概述
在调用系统的时候,如果调用链路中的某个资源出现了不稳定或者不可用,最终会导致请求发生积压(如下图),而熔断降级就可以解决这个问题。所谓的熔断降级就是当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间过长或者异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其他的资源而导致级联故障(服务雪崩)。
流量控制与熔断降级实现方案
Hystrix 是由 Netflix 开源的一个针对分布式系统容错处理的开源组件,2011 - 2012 年相继诞生和成熟,在 2018 年 11 月 20 日之后已经停止维护,最后一个正式版本为 1.5.18。Hystrix 单词意为 “豪猪”,浑身有刺保护自己,Hystrix 就是这样一个用来捍卫应用程序健康的利器。进一步说,Hystrix 是一个延迟和容错库,用在隔离远程系统、服务和第三方库,阻止级连故障,在复杂的分布式系统中实现恢复能力,以提高分布式系统的弹性。
Sentinel 是阿里巴巴出品的面向分布式服务架构的轻量级流量控制组件,主要以流量为入点,从限流、流量整形、熔断降级、系统负载保护等多个维度来保障微服务的稳定性。
Resilience4j 是一款轻量级,易于使用的容错库,其灵感来自于 Netflix Hystrix,但是专为 Java 8 和函数式编程而设计。轻量级,因为库只使用了 Vavr,它没有任何其他外部依赖下。相比之下,Netflix Hystrix 对 Archaius 具有编译依赖性,Archaius 具有更多的外部库依赖性,例如 Guava 和 Apache Commons Configuration。在 Spring Cloud Greenwich 版中,Spring 官方推荐使用 Resilience4j 替代 Hystrix。
开源实现方案对比
Sentinel 介绍
Sentinel 简介
Sentinel 是阿里巴巴出品的面向分布式服务架构的轻量级流量控制组件,主要以流量为入点,从限流、流量整形、熔断降级、系统负载保护等多个维度来保障微服务的稳定性,更多介绍可参考:Sentinel 项目、Sentinel 官方中文文档
Sentinel 历史
2012 年,Sentinel 诞生,主要功能为入口流量控制
2013 - 2017 年,Sentinel 在阿里巴巴集团内部迅速发展,成为基础技术模块,覆盖了所有的核心场景,Sentinel 也因此积累了大量的流量归整场景以及生产实践
2018 年,Sentinel 开源,并持续演进
2019 年 Sentinel 朝着多语言扩展的方向不断探索,推出 C++ 原生版本,同时针对 Service Mesh 场景也推出了 Envoy 集群流量控制支持,以解决 Service Mesh 架构下多语言限流的问题
2020 年,推出 Sentinel 的 Go 原生版本,继续朝着云原生的方向演进,同时已覆盖微服务、API Gateway 和 Service Mesh 三大板块的核心生态
Sentinel 组成
- 核心库:主要指 Java 客户端,不依赖任何框架 / 库,能够运行于 Java 7 及以上的版本的运行环境,同时对 Dubbo、Spring Cloud、Spring Cloud Alibaba 等框架也有较好的支持
- 控制台:控制台主要负责管理推送规则、监控、集群限流分配管理、机器发现等
Sentinel 优势
- 友好的控制面板,支持实时监控
- 多种限流。支持 QPS 限流,线程数限流,多种限流策略,如:直接拒绝,冷启动,匀速模式(漏斗)
- 多种降级模式,支持按平均返回时间降级,按多种异常数降级,按异常比率降级
- 方便扩展开发,支持 SPI 模式对 chain 进行扩展
- 支持链路的关联,按链路统计限流,系统保护,热门资源保护等等
Sentinel 特点
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架 / 库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合,只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel
- 完备的实时监控:Sentinel 同时提供实时的监控功能。开发者可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况
- 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。可以通过实现扩展接口来快速地定制逻辑,例如定制规则管理、适配动态数据源等
Sentinel 开源生态
AHAS Sentinel 控制台
AHAS Sentinel 简介
AHAS Sentinel 是 Sentinel 的阿里云上版本(商业版),提供企业级的高可用防护服务,包括:
- 可靠的实时监控和历史秒级监控数据查询,包含 QPS、RT、load、CPU 使用率等指标,支持按照调用类型分类,支持同比 / 环比展示
- 热力图概览,可以快速定位不稳定的机器
- 动态规则管理 / 推送,无需自行配置外部数据源
- 告警中心(触发流控、CPU 利用率高等事件)
- 全自动托管、高可用的集群流量控制
- 针对 Istio/Envoy 集群的 Mesh 高可用防护
- Nginx 网关流控
AHAS Sentinel 控制台体验
这里只是简单使用 AHAS Sentinel 官方提供的 Demo 包接入到 AHAS Sentinel 控制台,若希望将已有的 Sentinel 项目接入到 AHAS Sentinel 控制台,具体可参考 Sentinel 官方文档。
阿里云开通 AHAS
- 打开 AHAS 产品主页
- 在页面右上角单击登录
- 在页面上输入您的阿里云账号和密码,并单击登录
- 在产品主页上单击申请免费开通,然后在云产品开通页页面上勾选” 我已阅读并同意《应用高可用服务服务协议》”,并单击立即开通
接入新应用
若应用运行在非阿里云 ECS 环境或本地,需要在左上角选择切换公网环境
获取 Demo 包
点击控制台左侧菜单栏的 应用防护
,找到 Tab 页面选择 JAVA 语言
-> 体验 Demo
,然后根据页面提示下载 Demo 包
启动 Demo 应用
公网和阿里云经典网络环境下,需要额外指定 License 用于身份校验,VPC 专有网络无需配置 License
1 | # 启动命令 |
等待一会,AHAS Sentinel 控制台就会显示相关监控数据
Sentinel 基础
Sentinel 基本概念
资源
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序自身提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
规则
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
Sentinel 设计理念
流量控制设计理念
Sentinel 流量控制有以下几个角度:
- 运行指标,例如 QPS、线程池、系统负载等
- 控制的效果,例如直接限流、冷启动、排队等
- 资源的调用关系,例如资源的调用链路,资源和资源之间的关系
熔断降级设计理念
Sentinel 和 Hystrix 的原则是一致的,即当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间过长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。但在限制的手段上,Sentinel 和 Hystrix 采取了完全不一样的方法。Hystrix 通过线程池隔离的方式,来对依赖(在 Sentinel 的概念中对应资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本(过多的线程池导致线程数目过多),还需要预先给各个资源做线程池大小的分配,并且对于一些使用了 ThreadLocal
的场景来说会有问题(如 Spring 的事务)。Sentinel 对这个问题采取了以下两种手段来解决:
通过并发线程数进行限制
和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝,堆积的线程完成任务后才开始继续接收请求。
针对慢调用和异常对资源进行降级
除了对并发线程数进行控制以外,Sentinel 还可以根据响应时间和异常等不稳定因素来快速对不稳定的调用进行熔断。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新渐进式地恢复。
系统自适应保护理念
Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。
Sentinel 流量控制入门案例
版本说明
本案例使用的 Spring Boot 版本为 2.1.4.RELEASE,Sentinel 版本为 1.8.0,点击下载完整的案例代码。
本地 Sentinel 控制台搭建
Sentinel 提供了一个轻量级的开源控制台,它提供机器发现以及健康状况管理、实时监控(单机和集群),规则管理和推送功能
下载 Sentinel 控制台
Sentinel 控制台下载有两种方式,一种是直接下载编译好的 Release 版本程序包,另一种是下载 Sentinel 控制台的工程源码,在本地打包后启动,这里采用第一种方式
1 | # 下载命令 |
启动 Sentinel 控制台
启动 Sentinel 控制台需要依赖 JDK 版本为 1.8 及以上版本,使用以下命令启动控制台:
1 | $ java -Dserver.port=9000 -jar sentinel-dashboard-1.8.0.jar |
浏览器访问 http://127.0.0.1:9000
,默认登录的用户名和密码为:sentinel/sentinel
Sentinel 控制台启动参数说明
Sentinel 控制台启动时,可配置的 JVM 参数如下:
-Dserver.port
指定 Sentinel 控制台监听的端口-Dproject.name
,设置应用在 Sentinel 控制台中显示的名称-Dcsp.sentinel.dashboard.server
设置应用需要连接到的 Sentinel 控制台的主机地址和端口号-Dsentinel.dashboard.auth.password=123456
用于指定控制台的登录密码为 123456-Dsentinel.dashboard.auth.username=sentinel
用于指定控制台的登录用户名为 sentinel-Dserver.servlet.session.timeout=7200
用于指定 Spring Boot 服务端 session 的过期时间,如 7200 表示 7200 秒;60m 表示 60 分钟,默认为 30 分钟
特别注意:Sentinel 控制台启动时,若在 JVM 参数中添加了 -Dproject.name
与 -Dcsp.sentinel.dashboard.server
,那么 Sentinel 控制台自身也可以注册到其他 Sentinel 控制台中,Sentinel 控制台甚至可以自己监控自己,启动配置示例如下:
1 | $ java -Dserver.port=9000 -Dcsp.sentinel.dashboard.server=127.0.0.1:9000 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar |
此时浏览器访问 http://127.0.0.1:9000
,可以发现控制台会多出一个 sentinel-dashboard
节点:
构建 Sentinel 本地应用
引入 Maven 依赖
由于需要将应用接入到 Sentinel 控制台,因此引入了 sentinel-transport-simple-http
依赖
1 | <parent> |
添加 Java SDK 代码
1 |
|
1 |
|
将应用连接到 Sentinel 控制台
若应用程序需要连接到 Sentinel 控制台, Sentinel 提供如下两种常用的配置方式,具体可参考 Sentinel 官方文档中的启动配置项
- JVM
-D
参数方式 properties
文件方式(1.7.0 版本开始支持)
这里采用添加 JVM 参数的启动方式,即启动应用时加入以下 JVM 参数:
-Dproject.name=sentinel-demo
,设置本地应用在 Sentinel 控制台中显示的名称-Dcsp.sentinel.dashboard.server=127.0.0.1:9000
,设置应用需要连接到的 Sentinel 控制台的主机地址和端口号
或者将 JVM 参数添加到 IDEA Configuration 里的 VM options
中:
测试代码
- 1)启动本地的 Sentinel 控制台,命令如下:
1 | $ java -Dserver.port=9000 -jar sentinel-dashboard-1.8.0.jar |
- 2)在 Spring Boot 应用的 JVM 参数中配置 Sentinel 控制台,然后启动应用,若控制台输出以下日志信息,则说明 Sentinel 加载成功
1 | INFO: Sentinel log output type is: file |
特别注意:当代码里硬编码了流控规则(即使用 Java API 定义和加载流控规则)时,IDE 的控制台才会在应用启动时输出上面 Sentinel 相关的日志信息
- 3)浏览器访问
http://127.0.0.:9090
,查看 Sentinel 控制台的监控信息;这里需要先手动调用一次http://127.0.0.1:8080/hello
接口,Sentinel 控制台才会显示监控数据
- 4)浏览器访问
http://127.0.0.1:8080/hello
,当快速刷新页面时,请求的响应结果变为系统繁忙,请稍后 ...
,则说明 Sentinel 的流控规则生效了
动态配置 Sentinel 的流控规则
在上述案例中,将 Sentinel 的流控规则硬编码在 Java 代码里,但在实际的企业项目开发中,这种方式不推荐使用。在日常测试和演示中,一般都会在 Sentinel 控制台里动态配置流控规则,因为这样使用起来比较灵活。首先,将上述案例中添加 Sentinel 流控规则的代码注释掉(示例代码如下),然后在 Spring Boot 应用的 JVM 参数中配置 Sentinel 控制台。重新启动应用后,此时打印的启动日志信息不会再有 Sentinel 相关的内容。特别注意,默认情况下通过 Sentinel 控制台动态添加的规则配置是存放在内存里的,即动态添加的规则配置在 Sentinel 控制台应用重启后会失效。
1 |
|
浏览器手动调用一次 http://127.0.0.1:8080/hello
接口,然后打开 Sentinel 控制台,动态添加流控规则,表单里的资源名必须与 Java 代码里指定的资源名一致,如下图所示:
浏览器再次访问 http://127.0.0.1:8080/hello
,当快速刷新页面时,请求的响应结果变为 系统繁忙,请稍后 ...
,则说明动态配置的 Sentinel 流控规则生效了
Sentinel 定义资源的方式
资源是 Sentinel 的关键概念,它可以是 Java 应用程序中的任何内容,例如,由应用程序自身提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。使用 Sentinel 来进行资源保护,主要分为两个步骤,包括定义资源和定义规则。先把可能需要保护的资源定义好,之后再配置规则。在编码的时候,只需要考虑这个代码是否需要保护,如果需要保护,就可以将之定义为一个资源。
Sentinel 除了基本的定义资源的方式之外,还有其他定义资源的方式,具体如下:
- 抛出异常的方式定义资源
- 返回布尔值方式定义资源
- 异步调用支持
- 注解方式定义资源
- 主流框架的默认适配
版本声明
本案例使用的 Spring Boot 版本为 2.1.4.RELEASE,Spring Cloud Alibaba Sentinel 版本为 2.1.3.RELEASE,Sentinel 1.8.0。以下代码,默认都通过 Sentinel 控制台动态配置流控规则来测试,具体不再累述,点击下载完整的案例代码。
添加 Maven 依赖
1 | <parent> |
如果不需要使用注解的方式来定义 Sentinel 资源,一般只需要引入以下两个依赖即可,此时启动应用时需要添加 JVM 参数来连接 Sentinel 控制台。否则需要引入 spring-cloud-starter-alibaba-sentinel
依赖,才能让 @SentinelResource
注解生效。
1 | <dependencies> |
配置 application.yml
1 | server: |
抛出异常的方式定义资源
Sentinel 的 SphU 包含了 try-catch
风格的 API。用这种方式,当资源发生了限流之后就会抛出 BlockException
异常。这个时候可以捕获异常,进行限流之后的逻辑处理,而在上述的入门案例中就使用了此种方式进行定义资源,关键代码如下:
1 |
|
返回布尔值方式定义资源
Sentinel 的 SphO 提供 if-else
风格的 API,用这种方式,当资源发生了限流之后就会返回 false
,这个时候可以根据返回值,进行限流之后的逻辑处理。
1 |
|
特别注意:SphO.entry()
需要与 SphO.exit()
方法成对出现,否则会导致调用链记录异常,抛出 ErrorEntryFreeException
异常。
异步调用方式定义资源
Sentinel 支持异步调用链路的统计,在异步调用中,需要通过 SphU.asyncEntry()
方法定义资源,并在需要异步的回调函数中调用 exit()
方法。
1 | /** |
1 |
|
1 |
|
注解方式定义资源
- 通过
@SentinelResource
注解的blockHandler
属性制定具体的限流处理方法 - 实现处理方法,该方法的传参必须与资源点的传参一样,并且最后必须加上
BlockException
异常参数,同时返回类型也必须一样 - 从 1.4.0 版本开始,使用注解的方式定义资源,默认支持自动统计业务异常,无需再手动调用
Tracer.trace(ex)
来记录业务异常 - 更多注解属性说明,可以看这里
1 |
|