1. 初识熔断和限流
在互联网应用中,会有很多突发性的高并发访问场景,比如618/双十一等,这些场景的特点是访问量会突增,远远超出系统所能处理的高并发数,如果系统没有有效的保护机制,所有的流量都进入服务器,很可能造成服务器的当即,从而造成损失
1.1 熔断
服务熔断机制类似于电力系统中的保险丝,从电流超过保险丝的常在能力时,保险丝会熔断以保护电路,在分布式系统中,服务间依赖非常常见,当某个服务不可用时,如果没有有效的隔离措施,故障会迅速扩散到整个系统,导致系统雪崩效应,暂时将出现故障的接口隔离出来,断绝与外部接口的联系,当出发熔断之后,后续一段时间内该服务调用者的请求会直接失败,指导目标服务回复正常
熔断机制能够在服务故障时及时切断调⽤链,防⽌故障扩散,减少对故障服务的资源消耗,保护系统资源 不被耗尽,从⽽保护系统的整体稳定性和可⽤性.
1.2 限流
限流,就是限制流量的意思.
在互联⽹应⽤中,限流可以确保服务器能够处理的请求数量在合理范围内,避免因请求过多导致服务响 应慢或失败.可以保证⽤⼾在⾼流量时段仍然能够获得快速响应,提升⽤⼾体验.
2. Sentinel 介绍
Sentinel是由阿里巴巴开源的一个面向分布式/多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路、流量控制、流量整形/熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性
2.1 Sentinel的特性
-
丰富的应用场景
-
易于使用、快速接入
-
多样化的流量控制
-
可视化的监控和规则管理
2.2 使用


可以修改端口号和账号密码
java -jar -Dserver.port=8100 -Dsentinel.dashboard.auth.username=admin
Dsentinel.dashboard.auth.password=admin
Dserver.servlet.session.timeout=1440m sentinel-dashboard.jar
3. Sentinel 快速上手
引入依赖
定义资源
资源是Sentinnel的关键概念,被Sentinel 监控的每个接口就是一个资源,它可以是一个资源,可以是java应用程序的任何内容,例如由应用程序提供的服务,或由应用程序调用其他应用提供的服务,甚至可以是一段代码
限流、熔断都是针对资源来设置的
针对资源定义限流规则

使用资源,校验规则是否生效

4. Spring Cloud 集成Sentinel
添加依赖
配置


默认情况下,Sentinel starter会为Spring MVC的所有HTTP服务提供限流埋点,所以如果只想对HTTP服务进行限流,那么只需要添加依赖即可,不需要修改任何代码,如果想要对特定的方法进行限流或者降级,则可以自定义资源来实现
5. 流量控制
5.1 配置流控规则

配置阈值为1,即每秒只允许1次请求,超出的请求会被拦截并报错

使用jmeter模拟



5.2 基于QPS/并发数的流量控制
流量控制主要有两种统计类型,一种是统计线程数,另外一种则是统计QPS
并发线程数
线程数限流用于保护业务线程数不被耗尽

QPS流量控制
当QPS超过某个阈值的之后,则进行流量控制
流量控制的手段分为三种:快速失败、Warm Up 、排队等待
5.3 流控效果
流量超过配置的阈值时,会采用流量控制
快速失败
默认的流控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝的方式会抛出FlowException,这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位
Warm Up
也叫预热模式墨鱼汁一般时一个微服务承受的最大QPS,但是一个服务刚刚启动时,一切资源尚未初始化,如果直接将QPS跑到最大值,可能会导致服务瞬间当即
该方式主要用于系统长期处于低水位的情况下,当流量突增时,直接把系统拉升到高水位可能瞬间把系统压垮,通过冷启动,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热时间,避免冷系统被压垮的情况



排队等待
这种方式严格控制了请求通过的间隔时间,即让请求以均匀的速度通过,可以理解为让所有请求进入一个队列中,然后按照阈值允许的时间间隔以此执行,后面的请求必须等到前面执行完成,直到超时

这种方式主要用于处理间隔性突发的流量,例如消息队列

超过是按也就是i请求等待时长


5.4 流控模式
在添加限流规则时,点击高级选项,可以发现流控模式分为三种:直接、链路
调用关系包括调用方、被调用方,方法又可能会调用其他方法,形成一个调用链路的层次关系,Sentinel记录资源之间的调用链路,这些资源通过调用关系,相互之间构成一颗调用树

Sentinel 根据这些调用关系,建立不同资源间的调用关系,并记录每个资源的实时统计信息,有了调用链路的统计信息,可以衍生出多种流量控制手段
根据调用方限流
默认的流量控制方法,当QPS超过任意规则的阈值后,对当前资源直接限流
针对来源:
default:表示不区分调用者,来自任何调用这的请求都将进行限流统计,如果整个资源名的调用综合超过了这条规则定义的阈值,则触发限流
根据调用链路入口限流
链路限流是指:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流






具有关系的资源流量控制
当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源具有关联
关联限流就是 统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流
比如对数据库同一个字段的都操作和写操作存在争抢,都的速度过高会影响写的速度,写的速度过高会影响读的速度,如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量,可使用关联限流来避免具有关联关系的资源之前过度的争抢

应用场景:
- 在数据库操作中,读操作和写操作可能会争抢资源,通过关联模式,可以限制读操作的频率,以避免对写操作造成过大的影响
- 订单服务可能会依赖与库存服务,如果库存服务的请求量过大,可以通过关联模式限制订单服务的请求量
6. 热点参数限流
热点即经常访问的数据
在上述配置中,对一个接口进行限流,所有的请求参数一视同仁,只有达到阈值,就一起限流
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流,热点参数限流可以看作是一种特殊的流量控制,仅对包含热点参数的资源调用生效
热点参数限流配置
定义资源

设置热点参数限流

对 / sentinel/id 这个资源的0号参数(第⼀个参数)做统计,每1秒相同参数值的请求数不能超过5 参数索引:资源热点参数的索引,从0开始

和上⾯的流控差异是:热点参数限流是以资源的参数值为维度来统计的,流控是以资源名称为维度来 进⾏统计的

参数例外项
上述的热点参数配置,所有参数一视同仁,QPS都被限定为5,但是在一些场景下,希望可以对某些热点数据参数进行单独限流

编辑热点规则

7. 限流算法
7.1 计数器算法
也叫固定窗口限定算法,是一种比较简单的限流实现算法
首先维护一个计数器,在指定周期内,累计访问次数,当访问次数到达设定的阈值时,触发限流的策略,当进入下一个时间周期,将访问次数清零,整个时间周期,就可以理解为一个窗口,计数器记这个窗口接收请求的次数

存在一个临界问题:

在两个时间段分别发送100个请求,整体来看就会出现4秒内的总请求量达到200,超出了设置的每分钟100的阈值
7.2 滑动窗口算法
为了解决计数器算法带来的临界问题,引入滑动窗口算法,是一种流量控制技术
简单来说,滑动窗⼝算法的原理是在固定窗⼝中分割出多个⼩时间窗⼝,分别在每个⼩时间窗⼝中记录 访问次数,然后根据时间将窗⼝往前滑动并删除过期的⼩时间窗⼝.最终只需要统计滑动窗⼝范围内的 所有⼩时间窗⼝总的计数即可

如图所⽰,我们将⼀分钟拆分为4个⼩时间窗⼝,每个⼩时间窗⼝最多能够处理25个请求.并且通过虚线 框表⽰滑动窗⼝的⼤⼩(当前窗⼝的⼤⼩是2,也就是在这个窗⼝内最多能够处理50个请求).同时滑动窗 ⼝会随着时间往前移动,⽐如前⾯15s结束之后,窗⼝会滑动到15s~45s这个范围,然后在新的窗⼝中重新 统计数据.这种⽅式很好地解决了固定窗⼝算法的临界值问题
Sentinel 采用的时滑动窗口
7.3 漏桶算法
漏桶算法⾯对限流,就更加的柔和,不存在直接的粗暴拒绝.漏桶限流算法的主要作⽤是控制数据注⼊⽹ 络的速度,平滑⽹络上的突发流量.
它的原理很简单,可以认为就是注⽔漏⽔的过程,往漏桶中以任意速率流⼊⽔,以固定的速率流出⽔.当 ⽔超过桶的容量时,会被溢出,也就是被丢弃.因为桶容量是不变的,保证了整体的速率.

如图所⽰:在漏桶算法内部同样维护⼀个容器,这个容器会以恒定速度出⽔,不管上⾯的⽔流速度多快, 漏桶⽔滴的流出速度始终保持不变.实际上消息中间件就使⽤了漏桶限流的思想,不管⽣产者的请求量 有多⼤,消息的处理能⼒取决于消费者.
漏桶算法的原理,也就决定着,使⽤漏桶算法会有以下缺点:
-
⽆法处理突发流量:漏桶算法以固定的速率处理请求,当流量突然增加时,⽆法快速响应和处理这些 请求.超出漏桶容量的请求会被丢弃,这可能导致⽤⼾体验下降.
-
可能导致请求延迟:由于漏桶的流出速率是固定的,即使在流量较⼩的情况下,请求也需要排队等待 处理,这可能导致请求的响应时间变⻓.
这些缺点使得漏桶算法在某些需要快速响应或处理突发流量的场景中可能不是最佳选择,其他限流算法 如令牌桶算法可能更适合处理复杂多变的流量场景
7.4 令牌桶算法
令牌桶是⽹络流量整形和速率限制中最常使⽤的⼀种算法.对于每⼀个请求,都需要从令牌桶中获得⼀ 个令牌,如果没有获得令牌,则触发限流策略.

如图所⽰,系统会以⼀个恒定速度往固定容量的令牌桶中放⼊令牌,如果此时有客⼾端请求过来,则需 要先从令牌桶中拿到令牌以获得访问资格. 假设令牌⽣成速度是每秒10个,也就等同于QPS=10,
在请求获取令牌的时候,会存在三种情况:
-
请求速度>令牌⽣成速度:令牌会很快被取完,后续再进来的请求会被限流.
-
请求速度=令牌⽣成速度:流量处于平稳状态.
-
请求速度<令牌⽣成速度:说明系统的并发数不⾼,请求能被正常处理
由于令牌桶有固定的⼤⼩,当请求速度⼩于令牌⽣成速度时,令牌桶会被填满.所以令牌桶能够处理突发 流量.也就是在短时间内新增的流量系统能够正常处理,这是令牌桶的特性。
Spring Cloud Gateway的RequestRateLimiter Filter默认使⽤了RedisRateLimiter的限流实现,就是 采⽤令牌桶算法实现限流功能.
漏桶限流算法和令牌桶限流算法的实现原理相差不⼤,最⼤的区别是漏桶⽆法处理短时间内的突发流 量,漏桶限流算法是⼀种恒定速度的限流算法
8. 熔断降级
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保证高可用的重要措施之一
现代微服务架构都是分布式的,由非常多的服务组成,不同服务之间相互调用,组成复杂的调用链路,复杂链路上某一环不稳定,就可能会层层级联,最终导致整个链路都不可用,因此我们需要对不稳定的弱依赖服务调用进行熔断暂时切断不稳定调用,比卖你局部不稳定因素导致整体的雪崩
熔断降级作为保护自身的手段,同窗在客户端进行配置
Sentinel 提供了三种熔断策略:慢调用、异常比例、异常数
慢调用比例:需要设置允许的满调用RT(最大的响应时间),请求的响应时间大于该阈值则统计为慢调用,在指定时间内,如果请求数量>设定的最小数量,且慢调用比例>设定的阈值,则触发熔断
异常比例:指定时间内,请求数量>设置的最小数量,且异常比例大于阈值,则触发熔断
异常数:指定时间内,异常数超过阈值后,则触发熔断
8.1 状态机
熔断思路是由断路器(或者叫熔断器)统计服务调用的慢请求比例,异常比例等,如果超过阈值,则熔断该服务,也就是拦截对该服务的请求,访服务恢复时,断路器会放行访问该服务的请求

- Closed:关闭状态,所有的请求都会通过短路器,并开始统计慢请求比例、异常比例,超过阈值则切换到open状态
- Open:打开状态,服务调用被熔断,这时所有访问对熔断服务的请求都会被拒绝
- Half-open:半开状态,当经过一段时间后,断路器会从Open状态切换到Half-open状态,这时会由一定数量的请求被放入,根据这些请求的失败率来判断后续操作(失败率低于阈值会切换到closed状态,反之切换到open状态)
8.2 慢调用比例

定义资源

配置熔断规则


8.3 降级
当调用失败后,义务直接报错,给用户的体验不好,因该分会用户一个友好提示或者默认结果,整个就是降级
通常有以下方法:
-
捕获异常,根据异常进行降级逻辑处理
-
通过 FallbackFactory,对远程调用的异常做处理
捕获异常



FallbackFactory
FallbackFactory时一个在微服务架构中用于实现服务降级的接口,当远程服务调用失败或超时,FallbackFactory会根据提供的异常信息创建一个降级处理实例,以替代服务调用,并且可以根据错误类型,返回不同的降级响应
在 product-api中定义降级处理类



在order-srvice上配置文件

开启配置



当触发限流时,也会执行FallbackFactory预设的逻辑
删除熔断配置,添加限流配置
8.4 异常比例


未触发熔断:
快速多次请求/order/1 一直不会熔断
一次请求:/order/2 出现异常,走降级逻辑
在请求:/order/1 正常返回
触发熔断:
快速多次请求 /order/2 触发熔断
在请求 /order/1 走降级逻辑
8.5 异常数

9. 授权规则
存在问题:
目前订单项目,直接就可以通过浏览器访问,获取订单信息,这样很不安全
解决方案:
微服务架构中,许多系统包含敏感信息,只有经过验证或者授权的用户才可以访问这些数据
授权规则时对请求者的身份进行判断,决定是否允许该请求访问特定资源
Sentinel提供了两种授权模式:白名单和黑名单
9.1 服务段获取来源
服务端即 order-service

9.2 客户端设置来源
即getway项目

9.3 配置授权规则

9.4 验证




10. 定义异常返回结果

这个返回结果不是很友好,而且调用方分辨不出来异常结果
Sentinel 提供了一个接口 BlockExceptionHandler,用于自定义BlockException异常,当请求被Sentinel限流、降级或授权拒绝时,会抛出BlockException,通过实现BlockExceptionHandler接口,可以定义统一的异常处理逻辑,返回更友好的错误信息或执行特定的降级操作
默认处理
Sentinel 默认提供了⼀个 DefaultBlockExceptionHandler 实现类,当请求被阻塞时,会返回⼀ 个简单的字符串提⽰"BlockedbySentinel(flowlimiting)"(不同版本可能会不⼀样).

自定义处理
异常处理


11. 规则管理及推送
在前面的学习中,发现一个问题就是服务重启后,之前设定的规则就会消失,这是因为Sentinel默认是将这些管理规则保存在内存中,在生产环境中,这个问题是不可接收的,规则的丢失会导致系统失去流量控制和保护机制
在生产环境中,Sentinel需要规则持久化,sentinel-core 提供API和扩展口来接收信息,开发者需要根据自己的环境,选取一个可靠的推送规则方式,同时,规则最好在控制台中集中管理


原始模式
如果不做任何修改,Dashboard的推送方式是通过API将规则推送至客户端并直接更新到内存中

优点:简单,无依赖
缺点:应用重启规则就会消失,仅用于简单测试,不能用于生产环境
Pull模式
pull模式的数据源(比如本地文件、RDBNS等)一般是可写入的,使用时需要在客户端注册数据源,将对应的读数据源注册至对应的RuleManager,将写数据源注册至transport 的writeDataSourceRegistry中




[
{
"clusterConfig": {
"acquireRefuseStrategy": 0,
"clientOfflineTime": 2000,
"fallbackToLocalWhenFail": true,
"resourceTimeout": 2000,
"resourceTimeoutStrategy": 0,
"sampleCount": 10,
"strategy": 0,
"thresholdType": 0,
"windowIntervalMs": 1000
},
"clusterMode": false,
"controlBehavior": 0,
"count": 20,
"grade": 1,
"limitApp": "default",
"maxQueueingTimeMs": 500,
"resource": "/order/{orderId}",
"strategy": 0,
"warmUpPeriodSec": 10
}
]
本地文件数据源会定时轮询文件的变更,读取规则。这样既可以在应用本地直接修改文件来更新规则,也可以通过Sentinel控制台推送规则。

首先Sentinel控制台通过API将规则推送到客户端并更新到内存中,接着注册的写数据会将新的规则保存到本地文件中,使用pull模式的数据源时一般不需要对Sentinel控制台进行改造
这种方法的好处时简单,不引入新的依赖,坏处是无法保证监控数据的一致性
Push模式
生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心(ZooKeeper, Nacos, Apollo等等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是 配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,而不是经 Sentinel 数据源推送至配置中心。这样的流程就非常清晰了:

使用nacos
引入依赖

注册数据源


配置数据源路径

Sentinel集成Nacos持久化
上述我们发现,Nacos修改的内容会及时同步到SentinelDashBoard,但是SentinelDashBoard更新的 内容,并不会同步到Nacos,这在⽣产环境中,也是不⽅便使⽤的.所以我们需要修改Sentinel的源码,让 其⽀持双向通讯.
-
下载源码
-
修改pom文件

- 添加Nacos支持

- 修改Nacos配置

- 配置Nacos数据源

- 修改前端页面


配置

