暂无图片
暂无图片
2
暂无图片
暂无图片
暂无图片

SpringCloud实践:Sentinel流控

一叶扁舟 2022-02-21
1407

Nacos: 注册中心,解决服务注册与发现

Ribbon: 客户端的负载均衡器,解决服务集群的负载均衡

OpenFeign:声明式的HTTP客户端,服务远程调用

Nacos:配置中心,中心化管理配置文件

Sentinel:微服务流量卫兵,以流量为入口,保护微服务,防止出现服务雪崩

Gateway: 微服务网关,服务集群的入口,路由转发以及负载均衡(结合Sentinel)

Sleuth: 链路追踪,链路快速梳理、故障定位等

Seata: 分布式事务解决方案

目录

一、概述

1.1、高并发带来的问题

在微服务架构中,我们将业务拆分成一个个的服务,服务与服务之间可以相互调用,但是由于网络原因或者自身的原因,服务并不能保证服务的100%可用,如果单个服务出现问题,调用这个服务就会出现网络延迟,此时若有大量的网络涌入,会形成任务堆积,最终导致服务瘫痪。

1.2、服务雪崩

1.2.1、服务雪崩现象

【现象】

一个服务不可用导致一些列的服务不可用

image.png

1.3、常见容错方案

要防止雪崩的扩散,我们就要做好服务的容错,容错说白了就是保护自己不被猪队友拖垮的一些措施。

常见的容错思路有隔离、超时、限流、熔断、降级这几种。

1.3.1、隔离机制

比如服务A内总共有100个线程, 现在服务A可能会调用服务B,服务C,服务D.我们在服务A进行远程调用的时候,给不同的服务分配固定的线程,不会把所有线程都分配给某个微服务. 比如调用服务B分配30个线程,调用服务C分配30个线程,调用服务D分配40个线程. 这样进行资源的隔离,保证即使下游某个服务挂了,也不至于把服务A的线程消耗完。比如服务B挂了,这时候最多只会占用服务A的30个线程,服务A还有70个线程可以调用服务C和服务D。

image.png

1.3.2、超时机制

在上游服务调用下游服务的时候,设置一个最大响应时间,如果超过这个时间,下游未作出反应,就断开请求,释放掉线程。

image.png

1.3.3、限流机制

限流就是限制系统的输入和输出流量已达到保护系统的目的。为了保证系统的稳固运行,一旦达到的需要限制的阈值,就需要限制流量并采取少量措施以完成限制流量的目的。

image.png

1.3.4、熔断机制

在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断。

服务熔断一般有三种状态:

  1. 熔断关闭状态(Closed):服务没有故障时,熔断器所处的状态,对调用方的调用不做任何限制。
  2. 熔断开启状态(Open):后续对该服务接口的调用不再经过网络,直接执行本地的fallback方法。
  3. 半熔断状态(Half-Open):尝试恢复服务调用,允许有限的流量调用该服务,并监控调用成功率。如果成功率达到预期,则说明服务已恢复,进入熔断关闭状态;如果成功率仍旧很低,则重新进入熔断关闭状态。

image.png

1.3.5、降级机制

降级其实就是为服务提供一个兜底方案,一旦服务无法正常调用,就使用兜底方案。

image.png

1.4、常见容错组件

Hystrix Resilience4J Sentinel
Hystrix是由Netflflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。 Resilicence4J一款非常轻量、简单,并且文档非常清晰、丰富的熔断工具,这也是Hystrix官方推荐的替代产品。不仅如此,Resilicence4j还原生支持Spring Boot 1.x/2.x,而且监控也支持和prometheus等多款主流产品进行整合。 Sentinel 是阿里巴巴开源的一款断路器实现,本身在阿里内部已经被大规模采用,非常稳定。

image.png

1.5、Sentinel

1.5.1、什么是Sentinel

Sentinel (分布式系统的流量防卫兵) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点, 从流量控、熔断降级、系统负载保护等多个维度来保护服务的稳定性。

1.5.2、Sentinel特点

image.png

  1. 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景, 例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
  2. 完备的实时监控:Sentinel 提供了实时的监控功能。通过控制台可以看到接入应用的单台机器秒级数据, 甚至 500 台以下规模的集群的汇总运行情况。
  3. 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块, 例如与 SpringCloud、Dubbo、gRPC 的整合。只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。
  4. 完善的SPI扩展点:Sentinel提供了简单易用、完善的SPI扩展接口。您可以通过实现扩展接口来快速的定制逻辑。例如定制规则管理、适配动态数据源等。

1.5.3、Sentinel组成部分

Sentinel分为两部分:

  1. 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo /Spring Cloud 等框架也有较好的支持。
  2. 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

1.5.4、Sentinel架构

image.png

【API调用】常见api

# 接口概览 http://localhost:8720/api # 获取资源的metrics信息 http://localhost:8720/cnode?id=接口路径 # 获取流控规则接口 http://localhost:8720/getRules?type=flow # 设置规则接口 http://localhost:8720/setRules

二、安装部署Dashboard

下载jar包

https://github.com/alibaba/Sentinel/releases/tag/v1.8.0

启动控制台

# 直接到jar包目录下,使用jar命令启动项目(控制台本身是一个SpringBoot项目) java -Dserver.port=8088 -Dcsp.sentinel.dashboard.server=localhost:8088 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar

页面访问

# http://localhost:8088/#/login # 默认用户名密码是 sentinel/sentinel

image.png

三、快速开始

  • 添加依赖

    <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
  • 配置文件

    spring: application: name: cloud-order # 服务名 cloud: nacos: discovery: server-addr: localhost:8848 # 指定nacos-server的地址 username: nacos password: nacos namespace: dev sentinel: transport: port: 8719 # 默认8719(当前服务,对sentinel提供的,调用端口号,用于监控服务访问数据) dashboard: localhost:8888 # dashboard地址 eager: true # 启动项目是自动注入到sentinel中 server: port: 9003

启动项目,访问http://localhost:8088即可看到,已经注册进去了

四、Sentinel规则操作

【流控】:当满足被流控规则,控制流量 — api层

【熔断降级】:当满足规则,走备用方案 — api层

【热点】:细化到接口的参数,当携带指定参数时,对其做QPS限制 — api的参数层

【系统规则】: 对整个服务做流控— 整个服务层

【授权】:配置当前服务,那些服务能调用(白名单)、哪些服务不能调用(黑名单)。

image.png

4.1、流控

【流控】:当满足被流控规则,控制流量 — api层

image.png

4.1.1、针对来源

针对某个服务做流控

default代表对所有服务的调用做流控

4.1.2、流控规则

  • QPS

    每秒请求数

  • 线程数

    同时请求的线程数量

4.1.3、流控模式

  • 直接(默认)

    接口达到限流条件时,开启限流

  • 关联

    当关联的资源达到限流条件时,开启限流 [适合做应用让步]

  • 链路

    当从某个接口,进入当前资源达到限流条件时,开启限流

4.1.4、流控效果

  • 快速失败(默认)

    直接失败,抛出异常,不做任何额外的处理,是最简单的效果

  • Warm Up

    它从开始阈值到最大QPS阈值会有一个缓冲阶段,一开始的阈值是最大QPS阈值的1/3,然后慢慢增长,直到最大阈值,适用于将突然增大的流量转换为缓步增长的场景。

    eg:预热时长填10,QPS填900,那么如果一下进来900,会立即完成300,剩下的在10s内完成

  • 排队等待

    让请求以均匀的速度通过,单机阈值为每秒通过数量,其余的排队等待; 它还会让设置一个超时时间,当请求超过超时间时间还未处理,则会被丢弃。

4.2、熔断降级

【熔断降级】:当满足规则,走备用方案 — api层

4.2.1、慢调用比例

image.png

上面配置表示

在【统计时长】内,请求数超过【最小请求数】,并且请求中响应时间大于【最大RT】的比例超过【比例阈值】,就会触发熔断,在【熔断时长内】直接走降级方法。

4.2.2、异常比例

image.png

上面配置表示

在【统计时长】内,请求数超过【最小请求数】,并且异常比例超过【比例阈值】,就会触发熔断,在【熔断时长内】直接走降级方法。

4.2.3、异常数

image.png

上面配置表示

在【统计时长】内,请求数超过【最小请求数】,并且异常数超过【异常数】,就会触发熔断,在【熔断时长内】直接走降级方法。

4.3、热点

【热点】:细化到接口的参数,当携带指定参数时,对其做QPS限制 — api的参数层

image.png

上面配置表示

请求该资源时,携带了第【参数索引】个参数,且在【统计窗口时长】内,QPS达到【单机阈值】则被流控

📢:资源需要使用注解@SentinelResource自定义才能生效

并且,由于使用了该注解,抛出的异常ParamFlowException,需要自己捕获

4.4、系统规则

【系统规则】: 对整个服务做流控— 整个服务层

系统规则支持以下的模式:

  • Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5
  • CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
  • 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
image.png

4.5、授权

【授权】:配置当前服务,那些服务能调用(白名单)、哪些服务不能调用(黑名单)。

image.png

上面配置表示

/auth/login接口,不允许cloud-order和cloud-jifen这两个服务调用

五、@SentinelResource及异常捕获

5.1、@SentinelResource使用

5.1、使用规则

【作用】

1、用在方法上,用于自定义资源名

2、在设置热点时,必须使用该注解自定义的资源名,不然不生效

@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。

属性 作用
value 资源名称,必需项(不能为空)
entryType entry 类型,可选项(默认为 EntryType.OUT
blockHandler/blockHandlerClass blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
fallback/fallbackClass fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求: 1. 返回值类型必须与原函数返回值类型一致; 2.方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。 3.fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
defaultFallback 默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求: 1. 返回值类型必须与原函数返回值类型一致; 2. 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。 3. defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
exceptionsToIgnore 用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

5.2、使用示例

使用@SentinelResource对流控、降级做自定义处理

  • 方式一:写在同一个类里

    @RestController @Slf4j public class AnnoController { @RequestMapping("/anno1") /** * 当访问资源出现【流控】,会进入 blockHandler 指定的方法去进行处理 * 当访问资源出现 异常/【熔断降级】,会进入到 fallback 去进行处理 */ @SentinelResource(value = "anno1", blockHandler="anno1BlockHandler", fallback = "anno1Fallback") public String anno1(String name){ if("wolfcode".equals(name)){ throw new RuntimeException(); } return "anno1"; } /** * 限流备用方法 */ public String anno1BlockHandler(String name, BlockException ex){ log.error("{}", ex); return "接口被限流了"; } /** * 异常或降级备用方法 */ public String anno1Fallback(String name, Throwable throwable) { log.error("{}", throwable); // 区分熔断降级和其他异常 if (throwable instanceof DegradeException) { return "熔断降级"; } return "非sentinel异常"; } }
  • 写到别的类中

    @RestController @Slf4j public class AnnoController { @RequestMapping("/anno1") /** * 当访问资源出现【限流】,会进入 BlockExceptionHandler类的anno1BlockHandler方法 */ @SentinelResource(value = "anno1", blockHandler="anno1BlockHandler", blockHandlerClass="BlockExceptionHandler", fallback = "anno1Fallback", fallbackClass = "DegradeExceptionHandler" ) public String anno1(String name){ if("wolfcode".equals(name)){ throw new RuntimeException(); } return "anno1"; } }
    • 限流备用方法

      public class BlockExceptionHandler { /** * 静态方法 */ public static String anno1BlockHandler(String name, BlockException ex){ log.error("{}", ex); return "接口被限流了"; } public static String anno1Fallback(String name, Throwable throwable) { log.error("{}", throwable); // 区分熔断降级和其他异常 if (throwable instanceof DegradeException) { return "熔断降级"; } return "非sentinel异常"; } }
    • 异常/降级备用方法

      public class DegradeExceptionHandler { /** * 静态方法 */ public static String anno1Fallback(String name, Throwable throwable) { log.error("{}", throwable); // 区分熔断降级和其他异常 if (throwable instanceof DegradeException) { return "熔断降级"; } return "非sentinel异常"; } }

5.2、全局异常捕获

给每个方法都写一个对应的兜底方法,效率太低了

可以使用全局异常捕获对所有方法做兜底

5.2.1、Sentinel异常

image.png

5.2.2、全局异常处理

@Slf4j @ControllerAdvice public class MyResponseBodyAdvice implements ResponseBodyAdvice<Object> { /** * 流控处理 */ @ExceptionHandler(FlowException.class) @ResponseStatus(HttpStatus.OK) @ResponseBody // 该注解用于把对象转为json public OperationInfo handlerFlowException(HttpServletRequest request, HttpServletResponse response, Exception ex) { log.info("{}, 流控, {}", request.getRequestURI(), LogUtil.getStack(ex)); return OperationInfo.failure("流控"); } /** * 熔断降级处理 */ @ExceptionHandler(DegradeException.class) @ResponseStatus(HttpStatus.OK) @ResponseBody public OperationInfo handlerDegradeException(HttpServletRequest request, HttpServletResponse response, Exception ex) { log.info("{}, 熔断降级, {}", request.getRequestURI(), LogUtil.getStack(ex)); return OperationInfo.failure("熔断降级"); } // 其他略。。。。。。 }

六、规则持久化

image.png

6.1、默认模式

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

【好处】:简单、无依赖

【坏处】:应用重启规则会消失,仅用于测试,不能用于生产环境

image.png

6.2、Pull模式

【规则】

FileRefreshableDataSource定时从指定文件中读取规则JSON文件【本地文件】,如果发现文件发生变化,就更新规则缓存

FileRefreshableDataSource接收控制台规则推送,并根据配置,修改规则JSON【本地文件】

【优点】

能持久化配置的规则

【缺点】

服务部署在多台服务器上,无法共享本地文件

image.png

6.3、Push模式

【规则】

将规则持久化在配置中心中,Sentinel把规则发送到配置中心,各个服务从配置中心中拿对应的规则

image.png

6.3.1、Sentinel Dashboard代码改造

  • Sentinel Dashboard默认是往Sentinel客户端发送,需要修改为往 配置中心发送

    修改源码之后,重新打成jar包使用

6.3.2、微服务端

在要做Sentinel控制的服务里面配置

  • 添加依赖

    <!-- sentinel --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <!--sentinel的nacos持久化--> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>spring-datasource-nacos</artifactId> </dependency>
  • 添加配置

    spring: cloud: nacos: config: server-addr: localhost:8848 namespace: dev group: DEFAULT_GROUP prefix: cloud-order file-extension: yml username: nacos password: nacos sentinel: transport: port: 8730 # 默认8719(当前服务对sentinel提供访问的端口号) dashboard: localhost:8088 # dashboard地址 eager: true # 启动项目是自动注入到sentinel中 web-content-unify: false datasource: flow: nacos: server-addr: {nacos.server-addr} username: {nacos.username} password: {nacos.password} namespace: {nacos.namespace} groupId: DEFAULT_GROUP dataId: ${spring.application.name}-flow-rules data‐type: json rule‐type: flow degrade: nacos: server-addr: {nacos.server-addr} username: {nacos.username} password: {nacos.password} namespace: {nacos.namespace} groupId: DEFAULT_GROUP dataId: ${spring.application.name}-degrade-rules data‐type: json rule‐type: degrade param-flow: nacos: server-addr: {nacos.server-addr} username: {nacos.username} password: {nacos.password} namespace: {nacos.namespace} groupId: DEFAULT_GROUP dataId: ${spring.application.name}-param-flow-rules data‐type: json rule‐type: param-flow system: nacos: server-addr: {nacos.server-addr} username: {nacos.username} password: {nacos.password} namespace: {nacos.namespace} groupId: DEFAULT_GROUP dataId: ${spring.application.name}-system-rules data‐type: json rule‐type: system authority: nacos: server-addr: {nacos.server-addr} username: {nacos.username} password: {nacos.password} namespace: {nacos.namespace} groupId: DEFAULT_GROUP dataId: ${spring.application.name}-authority-rules data‐type: json rule‐type: authority nacos: server-addr: localhost:8848 username: nacos password: nacos namespace: sentinel
最后修改时间:2022-03-15 17:58:55
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
1人已赞赏
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论