「springcloud 2021 系列」sentinel实现熔断与限流 原来这么简单

Sentinel 简介

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助您保障微服务的稳定性。

sentinel-opensource-cloud-native-landscape-202006

特性

  • 丰富的应用场景:承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀,可以实时熔断下游不可用应用
  • 完备的实时监控:同时提供实时的监控功能。可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况
  • 广泛的开源生态:提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Boot/Spring Cloud、Dubbo、gRPC、Feign、Spring Cloud Gateway、RocketMQ 的整合
  • 完善的 SPI 扩展点:提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑

核心概念

资源

资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,都会用资源来描述代码块。

只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。

规则

围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。

基本原理

在 Sentinel 里面,所有的资源都对应一个资源名称以及一个 Entry。Entry 可以通过对主流框架的适配自动创建,也可以通过注解的方式或调用 API 显式创建;每一个 Entry 创建的时候,同时也会创建一系列功能插槽(slot chain)。这些插槽有不同的职责,例如:

  • NodeSelectorSlot 负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级
  • ClusterBuilderSlot 则用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据
  • StatisticSlot 则用于记录、统计不同纬度的 runtime 指标监控信息
  • FlowSlot 则用于根据预设的限流规则以及前面 slot 统计的状态,来进行流量控制
  • AuthoritySlot 则根据配置的黑白名单和调用来源信息,来做黑白名单控制
  • DegradeSlot 则通过统计信息以及预设的规则,来做熔断降级
  • SystemSlot 则通过系统的状态,例如 load1 等,来控制总的入口流量

arch overview

Sentinel 功能和设计理念

流量控制

流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状,如下图所示:

arch

流量控制有以下几个角度:

  • 资源的调用关系,例如资源的调用链路,资源和资源之间的关系
  • 运行指标,例如 QPS、线程池、系统负载等
  • 控制的效果,例如直接限流、冷启动、排队等

Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。

熔断降级

什么是熔断降级

除了流量控制以外,降低调用链路中的不稳定资源也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,最终会导致请求发生堆积

image

Sentinel 和 Hystrix 的原则是一致的: 当调用链路中某个资源出现不稳定,例如,表现为 timeout,异常比例升高的时候,则对这个资源的调用进行限制,并让请求快速失败,避免影响到其它的资源,最终产生雪崩的效果。

熔断降级设计理念

在限制的手段上,Sentinel 和 Hystrix 采取了完全不一样的方法。

Hystrix 通过线程池的方式,来对依赖(在的概念中对应资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本,还需要预先给各个资源做线程池大小的分配。

Sentinel 对这个问题采取了两种手段:

  • 通过并发线程数进行限制

和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。

  • 通过响应时间对资源进行降级

除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。

系统负载保护

Sentinel 同时提供系统维度的自适应保护能力防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。

针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。

Sentinel 控制台

安装与使用可以查看官方文档:https://sentinelguard.io/zh-cn/docs/dashboard.html

Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规则管理和推送的功能。

Sentinel 控制台包含如下功能:

  • 查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线
  • 监控 (单机和集群聚合):通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,最终可以实现秒级的实时监控
  • 规则管理和推送:统一管理推送规则
  • 鉴权:生产环境中鉴权非常重要。这里每个开发者需要根据自己的实际情况进行定制

docker 部署脚本:

bladex/sentinel-dashboard, 默认账号密码为 [sentinel sentinel]

version: '3.5'

services:
  sentinel-dashboard:
    image: bladex/sentinel-dashboard:1.8.0
    container_name: sentinel-dashboard
    #account and password: [sentinel sentinel]
    #修改默认密码为 123456    
    environment:
      - sentinel.dashboard.auth.password=123456
    restart: always
    ports:
      - 8858:8858

image-20220107114701821

Sentinel 实战

源码地址:https://github.com/langyastudio/langya-tech/tree/master/spring-cloud

规则的种类

FlowRule 流量控制规则
Field说明默认值
resource资源名,资源名是限流规则的作用对象
count限流阈值
grade限流阈值类型,QPS 模式(1)或并发线程数模式(0)QPS 模式
limitApp流控针对的调用来源default,代表不区分调用来源
strategy调用关系限流策略:直接、链路、关联根据资源本身(直接)
controlBehavior流控效果(直接拒绝/WarmUp/匀速+排队等待),不支持按调用关系限流直接拒绝
clusterMode是否集群限流
DegradeRule 熔断降级规则
Field说明默认值
resource资源名,即规则的作用对象
grade熔断策略,支持慢调用比例/异常比例/异常数策略慢调用比例
count慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值
timeWindow熔断时长,单位为 s
minRequestAmount熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入)5
statIntervalMs统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入)1000 ms
slowRatioThreshold慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)
SystemRule 系统保护规则

Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

Field说明默认值
highestSystemLoadload1 触发值,用于触发自适应控制阶段-1 (不生效)
avgRt所有入口流量的平均响应时间-1 (不生效)
maxThread入口流量的最大并发数-1 (不生效)
qps所有入口资源的 QPS-1 (不生效)
highestCpuUsage当前系统的 CPU 使用率(0.0-1.0)-1 (不生效)
AuthorityRule 访问控制规则

很多时候,我们需要根据调用方来限制资源是否通过,这时候可以使用 Sentinel 的访问控制(黑白名单)的功能。黑白名单根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。

授权规则,即黑白名单规则(AuthorityRule)非常简单,主要有以下配置项:

  • resource:资源名,即规则的作用对象
  • limitApp:对应的黑名单/白名单,不同 origin 用 , 分隔,如 appA,appB
  • strategy:限制模式,AUTHORITY_WHITE 为白名单模式,AUTHORITY_BLACK 为黑名单模式,默认为白名单模式
ParamFlowRule 热点参数规则
属性说明默认值
resource资源名,必填
count限流阈值,必填
grade限流模式QPS 模式
durationInSec统计窗口时间长度(单位为秒),1.6.0 版本开始支持1s
controlBehavior流控效果(支持快速失败和匀速排队模式),1.6.0 版本开始支持快速失败
maxQueueingTimeMs最大排队等待时长(仅在匀速排队模式生效),1.6.0 版本开始支持0ms
paramIdx热点参数的索引,必填,对应 SphU.entry(xxx, args) 中的参数索引位置
paramFlowItemList参数例外项,可以针对指定的参数值单独设置限流阈值,不受前面 count 阈值的限制。仅支持基本类型和字符串类型
clusterMode是否是集群参数流控规则false
clusterConfig集群流控相关配置
判断限流降级异常

在 Sentinel 中所有流控降级相关的异常都是异常类 BlockException 的子类:

  • 流控异常:FlowException
  • 熔断降级异常:DegradeException
  • 系统保护异常:SystemBlockException
  • 热点参数限流异常:ParamFlowException

我们可以通过以下函数判断是否为 Sentinel 的流控降级异常:

BlockException.isBlockException(Throwable t);

创建 sentinel-server 模块

这里创建一个 sentinel-server 模块,用于演示 Sentinel 的熔断与限流功能

在 pom.xml 中添加相关依赖,这里使用 Nacos 作为注册中心,所以需要同时添加 Nacos 的依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

在 application.yml 中添加相关配置,主要是配置了 Nacos 和 Sentinel 控制台的地址

server:
  port: 20001

spring:
  application:
    name: sentinel-server

  cloud:
    #Nacos
    nacos:
      username: nacos
      password: nacos
      discovery:
        server-addr: 192.168.123.22:8848

    #配置sentinel dashboard地址
    sentinel:
      transport:
        dashboard: 192.168.123.22:8858
        port: 8719


management:
  endpoints:
    health:
      show-details: always
    web:
      exposure:
        include: '*'

限流功能

Sentinel Starter 默认为所有的 HTTP 服务提供了限流埋点,也可以通过使用 @SentinelResource 来自定义一些限流行为

创建 RateLimitController 类

用于测试熔断和限流功能

/**
 * 限流功能
 */
@RestController
@RequestMapping("/rateLimit")
public class RateLimitController
{
    /**
     * 按资源名称限流,需要指定限流处理逻辑
     */
    @GetMapping("/byResource")
    @SentinelResource(value = "byResource", blockHandler = "handleException")
    public ResponseEntity byResource()
    {
        return new ResponseEntity("按资源名称限流", HttpStatus.OK);
    }

    /**
     * 按URL限流,有默认的限流处理逻辑
     */
    @GetMapping("/byUrl")
    @SentinelResource(value = "byUrl", blockHandler = "handleException")
    public ResponseEntity byUrl()
    {
        return new ResponseEntity("按url限流", HttpStatus.OK);
    }

    public ResponseEntity handleException(BlockException exception)
    {
        return new ResponseEntity(exception.getClass().getCanonicalName(), HttpStatus.OK);
    }
}
根据资源名称限流

可以根据 @SentinelResource 注解中定义的 value(资源名称)来进行限流操作,但是需要指定限流处理逻辑

  • 由于使用了 Nacos 注册中心,先启动 Nacos 和 sentinel-server
  • 由于 Sentinel 采用的懒加载规则,需要先访问下接口,Sentinel 控制台中才会有对应服务信息,先访问下该接口:http://localhost:20001/rateLimit/byResource
  • 流控规则可以在 Sentinel 控制台进行配置。在 Sentinel 控制台配置流控规则,根据 @SentinelResource 注解的 value 值

image-20220107154119609

快速访问上面的接口,可以发现返回了自己定义的限流处理信息

image-20220107154257063

根据 URL 限流

还可以通过访问的 URL 来限流,会返回默认的限流处理信息

在 Sentinel 控制台配置流控规则,使用访问的 URL

img

多次访问该接口,会返回默认的限流处理结果:http://localhost:20001/rateLimit/byUrl

Blocked by Sentinel (flow limiting)
自定义限流处理逻辑

可以自定义通用的限流处理逻辑,然后在 @SentinelResource 中指定

创建 CustomBlockHandler 类用于自定义限流处理逻辑

public class CustomBlockHandler
{
    public ResponseEntity handleException(BlockException exception)
    {
        return new ResponseEntity("自定义限流信息", HttpStatus.OK);
    }
}

在 RateLimitController 中使用自定义限流处理逻辑

/**
 * 自定义通用的限流处理逻辑
 */
@GetMapping("/customBlockHandler")
@SentinelResource(value = "customBlockHandler", blockHandler = "handleException",
        blockHandlerClass = CustomBlockHandler.class)
public ResponseEntity blockHandler()
{
    return new ResponseEntity("限流成功", HttpStatus.OK);
}

熔断功能

Sentinel 支持对服务间调用进行保护,对故障应用进行熔断操作,这里调用 nacos-discovery-provider 服务所提供的接口来演示下该功能

RestTemplete 实现

首先需要使用 @SentinelRestTemplate 来包装下 RestTemplate 实例

@Configuration
public class RestTemplateConfig
{
    @Bean
    @SentinelRestTemplate
    public RestTemplate restTemplate()
    {
        return new RestTemplate();
    }
}

添加 CircleBreakerController 类,定义对 nacos-discovery-provider 提供接口的调用

/**
 * 熔断功能
 */
@Log4j2
@RestController
@RequestMapping("/breaker")
public class CircleBreakerController
{
    @Autowired
    private RestTemplate restTemplate;

    @Value("${service-url.nacos-discovery-provider}")
    private String providerServiceUrl;

    @RequestMapping("/fallback/{id}")
    @SentinelResource(value = "fallback", fallback = "handleFallback")
    public ResultInfo fallback(@PathVariable String id)
    {
        return restTemplate.getForObject(providerServiceUrl + "/echoex/" + id, ResultInfo.class);
    }

    /**
     * 忽略 NullPointerException 异常的降级
     */
    @RequestMapping("/fallbackex/{id}")
    @SentinelResource(value = "fallbackex", fallback = "handleFallback", exceptionsToIgnore =
            {NullPointerException.class})
    public ResultInfo fallbackEx(@PathVariable String id)
    {
        if (Objects.equals(id, "1"))
        {
            throw new NullPointerException();
        }

        return restTemplate.getForObject(providerServiceUrl + "/echoex/" + id, ResultInfo.class);
    }

    /*
     * 熔断降级逻辑
     */
    public ResultInfo handleFallback(String id)
    {
        return ResultInfo.data("handleFallback 服务降级返回");
    }
}
  • 启动 nacos-discovery-provider 和 sentinel-service 服务

  • 访问 http://localhost:20001/breaker/fallback/0 由于调用的 nacos-discovery-provider 服务输出了 NullPointerException 异常,所以引发熔断服务 - 调用 handleFallback 函数的内容进行返回

  • 访问 http://localhost:20001/breaker/fallbackex/1 由于使用了 exceptionsToIgnore 参数忽略了 NullPointerException,所以访问接口报空指针时不会发生服务降级

Feign 实现

Sentinel 也适配了 Feign 组件,使用 Feign 进行服务间调用时,也可以使用它来进行熔断

首先需要在 pom.xml 中添加 Feign 相关依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

在 application.yml 中打开 Sentinel 对 Feign 的支持

feign:
  sentinel:
    #打开sentinel对feign的支持
    enabled: true 

在应用启动类上添加 @EnableFeignClients 启动 Feign 的功能

@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class SentinelServerApplication
{
    public static void main(String[] args)
    {
        SpringApplication.run(SentinelServerApplication.class, args);
    }
}

创建一个 EchoService 接口,用于定义对 nacos-discovery-provider 服务的调用

@FeignClient(name = "${service-url.nacos-discovery-provider}", fallback = EchoServiceImpl.class,
        configuration = FeignConfig.class)
public interface EchoService
{
    @GetMapping("/echo/{str}")
    ResultInfo echoex(@PathVariable("str") String str);
}

创建 EchoServiceImpl 类实现 EchoService 接口,用于处理服务降级逻辑

public class EchoServiceImpl implements EchoService
{
    @Override
    public ResultInfo echoex(@PathVariable("str") String str)
    {
        return ResultInfo.data("EchoService 服务降级返回");
    }
}

在 EchoFeignController 中使用 EchoService 通过 Feign 调用 nacos-discovery-service 服务中的接口

@RestController
@RequestMapping("/feign")
public class EchoFeignController
{
    @Autowired
    private EchoService echoService;

    @GetMapping("/echo/{str}")
    public ResultInfo feign(@PathVariable String str)
    {
        return echoService.echoex(str);
    }
}

调用如下接口会发生服务降级,返回服务降级处理信息:http://localhost:20001/feign/echo/0

{
    "code": 111111,
    "msg": "请求成功",
    "time": 1651806857944,
    "result": "EchoService 服务降级返回"
}

Nacos 规则持久化

默认情况下,当在 Sentinel 控制台中配置规则时,控制台推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中。一旦重启应用,规则将消失。下面介绍下如何将配置规则进行持久化,以存储到 Nacos 为例

推荐的推送规则的做法:配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,这样的流程就非常清晰了:

Remote push rules to config center

先在 pom.xml 中添加相关依赖

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

修改 application.yml 配置文件,添加 Nacos 数据源配置

spring:
  cloud:
    sentinel:
      datasource:
          ds1:
            nacos:
              server-addr: localhost:8848
              username: nacos
              password: nacos
              dataId: ${spring.application.name}-sentinel
              #namespace: public
              groupId: DEFAULT_GROUP
              data-type: json
              rule-type: flow

在 Nacos 中添加配置

image-20220110173902058

添加配置信息如下

[
    {
        "resource": "/rateLimit/byUrl",
        "limitApp": "default",
        "grade": 1,
        "count": 1,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]

相关参数解释:

  • resource:资源名称
  • limitApp:来源应用
  • grade:阈值类型,0表示线程数,1表示QPS
  • count:单机阈值
  • strategy:流控模式,0表示直接,1表示关联,2表示链路
  • controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待
  • clusterMode:是否集群

发现 Sentinel 控制台已经有了如下限流规则:

image-20220110174447810

GateWay支持

若想跟 Sentinel Starter 配合使用,需要加上 spring-cloud-alibaba-sentinel-gateway 依赖,同时需要添加 spring-cloud-starter-gateway 依赖来让 spring-cloud-alibaba-sentinel-gateway 模块里的 Spring Cloud Gateway 自动化配置类生效:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

同时请将 spring.cloud.sentinel.filter.enabled 配置项置为 false(若在网关流控控制台上看到了 URL 资源,就是此配置项没有置为 false)。Sentinel 网关流控默认的粒度是 route 维度以及自定义 API 分组维度,默认不支持 URL 粒度

注意:网关流控规则数据源类型是 gw-flow,若将网关流控规则数据源指定为 flow 则不生效。

参考

Sentinel 官方文档

Sentinel wiki

spring-cloud-alibaba wiki

Spring Cloud Alibaba:Sentinel 实现熔断与限流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郎涯技术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值