Sentinel

流控降级和容错原理

能干嘛?

从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性

面试题

服务雪崩

一个微小的服务没有做限流,导致和其他的调用和交互出现拖垮全局的情况

服务降级

服务熔断

先熔断,后降级

服务限流

服务隔离

服务超时

Sentinel 组件构成

使用

1.启动Nacos8848

2.启动Sentinel8080端口

3.新建微服务8401,纳入Nacos,然后告诉Sentinel8080你来监控8401微服务

Yaml配置

Controller

Nocos成功检测

Sentinel懒加载

概述

流控模式:

直接
关联

给B加上4秒钟80次请求

要等4秒钟结束了,A服务才能正常访问,因为B服务QPS太高,所以把A限流了

链路
新加个Service层
Controller调用Service层
增加Yaml配置

上下文是否是同一个链路,默认是true

在Sentinel的默认行为下,不管调用来源testA 还是 testB,到达 C 方法的流量都会被视为同一链路的流量。

这意味着对 C 方法的流控规则会同时影响通过 testA 和 testB 进入的请求。如果你为 C 方法设置了一个QPS(每秒查询率)的流控规则,那么无论是通过 testA 还是 testB 进入的请求,总的QPS都不能超过这个限制。

如果你希望根据不同的入口对流量进行区分控制,Sentinel提供了自定义上下文(Context)的功能。你可以定义不同的Sentinel上下文来区分不同的流量入口,这样就可以针对每个入口单独进行流量控制。

配置流控

C链路超过限流 就报错

D不会报错

流控效果

直接

预热WarmUp

WarmUp配置如何计算?

这个问题与系统的负载容量、资源分配、以及性能调优相关。"水位"在这里可以理解为系统的负载水平,"低水位"表示系统处于低负载状态,而"高水位"表示系统负载接近或达到了最大容量。以下是几个关键点来解释为什么系统从低水位突然转到高水位可能会导致压垮:

  1. 资源冷启动
    • 当系统处于低水位时,某些资源可能没有被充分利用,例如缓存可能未完全填充、数据库连接池可能缩小或某些服务可能进入休眠状态。如果流量突然增加,这些资源需要时间来"预热"到能够处理高负载的状态。在这个预热过程中,请求可能会堆积起来,导致延迟增大甚至服务不可用。
  1. 突发流量冲击
    • 突发的高流量可能对系统的各个组件产生压力,特别是那些设计成适应渐进流量增长的组件。例如,负载均衡器可能会由于过多的并发请求而分配不均、或者数据库可能由于突然的大量查询而出现瓶颈。
  1. 自动扩展延迟
    • 对于使用自动扩展的系统,从检测到负载增加到启动新实例,再到这些实例完全启动并开始处理请求,有一个时间延迟。如果流量太突然增加,那么在新资源准备就绪之前,现有的系统可能已经不堪重负。
  1. 队列和缓冲区溢出
    • 系统中可能有各种队列和缓冲区,它们在正常负载下能够平滑地处理请求。然而,在突发流量下,这些队列可能会迅速填满,导致新的请求被拒绝或者丢失。

为了缓解上述问题,可以采取预热(Warm-up)策略,逐渐增加系统负载:

  1. 渐进式流量增加
    • 通过逐步增加流量,可以给系统的各个组件提供"预热"时间,让它们适应更高负载的要求。这种方式可以避免资源冷启动导致的问题。
  1. 性能监控与调优
    • 在预热阶段,可以对系统性能进行监控,及时发现并解决潜在的性能问题,进行必要的调优,比如增加缓存大小、优化数据库索引、扩展负载均衡器的能力等。
  1. 避免突发流量冲击
    • 渐进地增加流量可以避免给系统带来突发的冲击,使得系统有足够的时间进行自动扩展和资源分配。
  1. 队列和缓冲区管理
    • 预热期间,可以逐渐调整队列长度和缓冲区大小来适应持续增长的流量,防止溢出。

总而言之,系统从低负载突然转到高负载时可能会面临资源不足、组件压力过大或调度不及时等问题,导致性能急剧下降乃至服务崩溃。通过渐进式预热,系统各部分可以有序地适应流量增长,从而更平稳地处理高负载压力。

一旦预热(Warm-Up)期完成,系统的流量阈值会稳定在你所配置的单机阈值(在你的例子中是10)。之后,只要系统没有重新启动或者没有对预热配置进行修改,这个阈值就会保持不变,不会再回到预热期的初始阈值(在你的例子中是3)。

预热机制设计的目的是为了在系统刚启动时避免因为资源不足(如缓存未建立、JVM未充分优化等)而导致的性能问题。一旦系统经过预热期,资源得到充分准备,系统便可以承担更高的流量。这时,系统的性能和资源状态被认为是稳定的,因此不需要再次回到低阈值状态。

然而,如果系统重新启动,预热逻辑会再次生效,阈值也会再次从低开始逐渐升高,直到达到配置的阈值。这是因为系统重启后,之前的资源状态(比如缓存、JVM优化状态)会丢失,需要重新"预热"以达到最佳性能状态。

排队等待

增加Controller
Sentinel配置
测试

1秒打20个请求

超过10个的都被丢弃了

关于你的例子,如果在1秒钟内来了20个请求,只有一个请求会立即得到处理(因为单机阈值为1),其余的请求会排队等待。排队的请求如果在10秒内未能得到处理,就会被丢弃。

因此,不是有10个请求会被丢弃,而是除了第一个请求外,其余的19个请求都会进入等待状态。然后,这些排队的请求如果在10秒超时时间内由于没有空闲资源而得不到处理,将会被丢弃。所以实际上,最终可能有多于10个请求被丢弃,具体数字取决于系统处理请求的速度。

并发线程数【阈值类型】

默认就是快速失败

在Sentinel中,流控效果设置为"并发线程数"时,单机阈值配置为1表示**在任意时刻,对于特定的受保护资源(比如某个接口或者方法),只允许一个线程进入。**换句话说,如果有多个请求同时到达,只有一个请求能够被处理,其他的请求要么等待直到当前的请求完成,要么根据其他流控规则(如排队等待时间)被拒绝。

这种设置通常用于那些非常敏感的资源,这些资源可能涉及到关键的业务逻辑,或者因为某些原因(如IO限制、外部系统同步调用限制等)不能支持高并发。通过限制并发线程数为1,Sentinel保证了对于这个资源的访问是串行化的,从而避免了竞争条件、过载等问题。

QPS(Queries Per Second)

QPS 是指每秒能处理的查询数量,这是一个度量单位,用来衡量系统每秒可以处理多少次请求。在流量控制中,通过设置 QPS 限制,你可以指定在一秒钟内允许通过的最大请求次数。超过这个限制的请求将会被拒绝或者进行排队处理(取决于你的具体规则配置)。

QPS 限制适用于需要控制整体请求量或者应对突发流量的场景。比如,如果你的后端服务只能处理每秒100个请求,那么你可以设置 QPS 限制为100,以避免过载。

并发线程数

并发线程数限制指的是在任意时刻,能够同时处理一个资源的线程数量。当设置并发线程数为1时,表示在同一时间只能有一个线程执行该资源,其他尝试访问的线程必须等待或者被拒绝。

并发线程数限制适用于资源对并发非常敏感的场景。例如,可能有一些操作涉及到单个硬件资源的调用(比如打印机),或者需要操作非线程安全的数据结构,这时候限制并发线程数可以保护这些资源不被并发访问导致的问题。

QPS vs. 并发线程数

以下是 QPS 与并发线程数限制的主要区别:

  1. 性质
    • QPS 关注的是时间(秒)维度内的请求频率。
    • 并发线程数关注的是同一时间点上的并发执行能力。
  1. 场景
    • QPS通常用于控制流量的整体水平,适合限制后端服务的请求处理量。
    • 并发线程数用于控制资源的并发访问情况,适合那些对并发性要求高的单一资源。
  1. 效果
    • QPS 限制可能会导致在一秒钟的开始阶段就迅速达到请求上限,而在剩余的时间内无法进行处理。
    • 并发线程数限制能够更平滑地分配请求,但可能导致请求等待时间较长。
  1. 适用性
    • QPS 是一种比较通用的限流策略。
    • 并发线程数适用于需要严格控制单个资源并发访问的特殊场合。

熔断降级

其他微服务出现故障,容易让本身的业务出现不可控的情况。

熔断策略

慢调用比例

超过我设定的最大RT就是慢调用。

统计时长1秒内,请求必须要大于5次切最大RT占所有请求数的10%就是慢调用,高于阈值比例

测试

1秒10个请求,每个最大RT200ms 所以肯定超时 比例100%

效果

熔断时间超5秒后,压测关掉,成功访问【请求书小于每秒5个 所以不会触发熔断】

异常比例

测试

异常数

1秒内异常数大于指定个数,且达到置信区间

@SentinelResource注解

注解介绍

测试

先不引入@SentinelResource注解

没有引入

触发限流保护

自定义限流

不想用默认返回提示

因为byResource()方法没有返回参数,所以在handlerBolckHandler里要加上BlockException。

如果有参数直接拷贝。

正常访问

触发限流

变成自己的提示了

服务降级配置

共存

参数直接拷贝,有几个拷贝几个,最后记得加个BolckException或者Throwable

Sentinel是一个流量控制组件,其提供了丰富的流量控制策略,包括但不限于限流、熔断、系统自适应保护等。在使用Sentinel进行资源保护时,可以定义blockHandler和fallback方法来处理流量控制或降级时的逻辑。这两者虽然在某些场景下看似相似,但实际上有明确的使用场景和目的区分。

blockHandler

blockHandler是在资源访问被Sentinel的规则(比如流量控制规则、熔断规则等)阻塞时触发的处理器。当请求超过阈值(如QPS过高)、触发熔断规则或系统保护规则时,Sentinel会拦截这次请求,并调用相应的blockHandler方法来处理这次被拦截的请求。

blockHandler方法通常用于自定义资源访问被阻塞时的处理逻辑,比如返回一个友好的提示信息或者执行一些替代逻辑。

fallback

fallback方法是在资源访问发生异常时触发的处理器。当被保护的资源访问过程中抛出异常,或由于Sentinel规则配置的流控效果导致无法调用目标资源时,就会调用fallback方法。

fallback主要用于处理降级逻辑,例如当远程调用失败、抛出异常或者被熔断时,可以提供另一个处理路径,确保系统的稳定性和可用性。

触发条件对比
  • blockHandler:主要是被Sentinel的规则阻塞时触发,如限流、熔断等规则使得请求无法被正常处理。
  • fallback:主要是被保护的资源内部发生异常,或因Sentinel配置无法调用目标方法时触发,用于处理异常导致的降级。

热点规则

热点参数限流

1秒qps p1参数请求超过1次就限流

限流 只要带参数p1就会触发限流

p2参数不管咋样都不限流

参数例外项【必须是基本数据类型】

对p1既限流又不限流

记得要点添加!!!!

拼命点都不会触发限流

但是p1不等于5就触发限流了

授权规则

新加个Controller

要实现RequestOriginParser接口用于处理器转换

地址里要有serverName这个值,通过这个来确定是白名单还是黑名单

如果我的地址带有test1和test2就黑名单

test和test2就会出现黑名单【必须要有serverName】

规则持久化

新加依赖

让Nacos具备数据保存

Yaml配置

配置flow就是流控规则

配置多个

规则json配置

后台就自动配置出来了 重启也会出现

OpenFeign整合Sentinel

不要每个接口都搞一个fallbackMethod,要统一fallback降级处理

OpenFeign对整个接口全部方法服务降级

步骤

提供方

1.引入依赖

2.修改YAML

3.业务代码

记住 千万不要fallback写里头,拆出去!!!

9001可以对外暴露接口了 自测通过

4.定义一个新的OpenFeign对外暴露接口

5.异常情况触发全局的fallback

6.新建一个类

也要是这个接口的实现类!!!

调用方

1.来到接口调用方需要引入依赖

2.激活Sentinel对OpenFeign支持

3.开启激活功能OpenFeign

4.调用OpenFeign提供的那个接口

SpringBoot和SpringCloud如果版本不匹配,版本太高会报错

测试

给9003增加流控规则

成功触发流控服务

服务降级【关闭9001服务】

GateWay整合Sentinel

步骤

1.引入依赖

2.改YAML

在Spring Cloud Gateway中,配置路由(routes)是将请求从网关导向后端服务的关键步骤。路由配置由两个主要部分组成:uri和predicates。

  1. uri:此配置指定了转发请求到的下游服务的地址。在您提供的示例中,uri: http://localhost:9091表示所有匹配当前路由的请求将被转发到运行在localhost上,端口9091的服务。
  2. predicates:谓词用于匹配进入网关的请求,只有满足谓词条件的请求才会被路由转发到对应的uri指定的服务。在您的案例中,Path=/pay/**表示只有路径以/pay/开头的请求会被匹配到这个路由。**是一个通配符,意味着任何/pay/后面的路径(包括子路径)都将被匹配。

综合来看,当一个请求的路径符合/pay/**这个模式时,Spring Cloud Gateway将会把这个请求转发到http://localhost:9091地址下的服务。例如,如果有一个请求是向http://your-gateway-host/pay/transaction/123发起的,那么它将会被转发到http://localhost:9091/pay/transaction/123。

如果请求的路径不匹配/pay/**(例如/order/item/123),则该请求不会被上述路由规则匹配,因此不会被转发到http://localhost:9091。它可能被其他路由规则匹配,或者如果没有合适的路由,请求将得到一个错误响应,比如404 Not Found。

总而言之,uri决定了请求的目的地,而predicates决定了哪些请求应该被路由到该目的地。两者结合使用才能实现准确的请求路由。

http://your-gateway-host在您的示例中代表的是Spring Cloud Gateway网关的地址,而不是您的后端服务地址。这里的your-gateway-host是网关服务器的主机名或者IP地址,而与之相应的端口(在您的示例中可能是8841)则是网关服务监听的端口。所以,完整的网关访问URL可能看起来像http://your-gateway-host:8841

当有请求发送到网关的某个特定路由上,例如 http://your-gateway-host:8841/pay/transaction/123,Spring Cloud Gateway根据配置的路由规则来决定如何处理这个请求。在您的案例中,配置了一个路由规则,其中:

  • **predicates**部分指定了路径匹配规则Path=/pay/**,这意味着只有当请求路径匹配/pay/及其后任意路径的请求才会被这条路由规则匹配。
  • **uri**部分指定了这些请求应当被转发到的下游服务地址,即http://localhost:9091

因此,如果有请求发送到http://your-gateway-host:8841/pay/transaction/123,且您的网关配置正确,那么Spring Cloud Gateway会将该请求转发到http://localhost:9091/pay/transaction/123。这里,http://localhost:9091是您后端服务的地址,可能是另一个Spring Boot应用、微服务或其他任何类型的HTTP服务,它监听在9091端口。

3.主启动类

4.那接下来 如何实现网关限流?意思就是做好限流后,根本达不到9001就被网关给摆平了

route维度

需要加入适配器依赖

代码

上面直接copy模板

不要导入错包

自定义流控规则 和路由的id要一致

配置完后加载 一秒钟qps不能超过2

需要个BlockRequestHandler()

这里实际上就是Servlet

在Spring Cloud Gateway整合Sentinel时,BlockRequestHandler接口中的handlerRequest方法用于自定义处理被Sentinel阻塞的请求

当请求触发了Sentinel的某个规则(例如,流量超过了设定的阈值),Sentinel会根据配置的流控规则阻塞该请求。在这种情况下,可以通过实现BlockRequestHandler接口来自定义被阻塞请求的处理逻辑。这允许开发者对于不同的流控违规情况给出不同的响应行为,例如返回一个错误码、错误信息或者重定向到另一个页面等

实现BlockRequestHandler接口通常涉及以下步骤:

  1. 实现BlockRequestHandler接口,并覆写handlerRequest方法以定义自定义的阻塞逻辑。
  2. 将实现类作为Bean注册到Spring容器中。

自定义的handlerRequest方法通常会接收两个参数:ServerWebExchangeBlockException。ServerWebExchange是一个HTTP请求-响应交换的封装,它提供了对请求和响应的访问;BlockException是一个异常对象,它表示请求被阻塞的原因。

以下是一个简单的示例,展示了如何自定义处理被Sentinel阻塞的请求:

1@Component
2public class CustomBlockRequestHandler implements BlockRequestHandler {
3
4    @Override
5    public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable t) {
6        // 自定义的错误信息
7        Map<String, String> errorDetails = new HashMap<>();
8        errorDetails.put("message", "访问过于频繁,请稍后再试");
9        errorDetails.put("status", HttpStatus.TOO_MANY_REQUESTS.toString());
10
11        // 返回Mono<ServerResponse>,包含自定义的错误信息和HTTP状态码
12        return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
13                .contentType(MediaType.APPLICATION_JSON)
14                .body(BodyInserters.fromValue(errorDetails));
15    }
16}

在这个示例中,对于被Sentinel阻塞的请求,我们返回了一个429 Too Many Requests的HTTP状态码和自定义的错误信息。这样,当客户端触发流控规则时,它将收到一个明确的指示,知道需要稍后再尝试请求。

通过提供这种自定义逻辑,您可以为用户提供更好的体验,并在系统受到高流量压力时保持控制。

此时启动网关9528和9001微服务

测试

9001直接访问 没问题

9001 通过网关9528转发 也没问题

点快一点 就报错了

相关推荐
程序媛小果几秒前
基于java+SpringBoot+Vue的宠物咖啡馆平台设计与实现
java·vue.js·spring boot
追风林6 分钟前
mac m1 docker本地部署canal 监听mysql的binglog日志
java·docker·mac
芒果披萨20 分钟前
El表达式和JSTL
java·el
duration~1 小时前
Maven随笔
java·maven
zmgst1 小时前
canal1.1.7使用canal-adapter进行mysql同步数据
java·数据库·mysql
跃ZHD1 小时前
前后端分离,Jackson,Long精度丢失
java
blammmp2 小时前
Java:数据结构-枚举
java·开发语言·数据结构
暗黑起源喵2 小时前
设计模式-工厂设计模式
java·开发语言·设计模式
WaaTong2 小时前
Java反射
java·开发语言·反射
九圣残炎3 小时前
【从零开始的LeetCode-算法】1456. 定长子串中元音的最大数目
java·算法·leetcode