Sentinel 简介

 

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

 

下面智一面来介绍下Sentinel的特征

 

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。

  • 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。

  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。

  • 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

 

Sentinel 的主要特性:

Sentinel 的开源生态:

Sentinel 分为两个部分:

 

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。

  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

 

Sentinel、Hystrix、resilience4j 对比

功能对比

 

Sentinel

Hystrix

resilience4j

隔离策略

信号量隔离(并发控制)

线程池隔离/信号量隔离

信号量隔离

熔断降级策略

基于慢调用比例、异常比例、异常数

基于异常比例

基于异常比例、响应时间

实时统计实现

滑动窗口(LeapArray)

滑动窗口(基于 RxJava)

Ring Bit Buffer

动态规则配置

支持多种数据源

支持多种数据源

有限支持

扩展性

多个扩展点

插件的形式

接口的形式

基于注解的支持

支持

支持

支持

限流

基于 QPS,支持基于调用关系的限流

有限的支持

Rate Limiter

流量整形

支持预热模式与匀速排队控制效果

不支持

简单的 Rate Limiter 模式

系统自适应保护

支持

不支持

不支持

多语言支持

Java/Go/C++

Java

Java

Service Mesh 支持

支持 Envoy/Istio

不支持

不支持

控制台

提供开箱即用的控制台,可配置规则、实时监控、机器发现等

简单的监控查看

不提供控制台,可对接其它监控系统

 

Sentinel 名词

 

资源

 

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

 

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

 

规则

 

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

 

流量控制

 

什么是流量控制

 

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

 

流量控制设计理念

 

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

 

  • 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;

  • 运行指标,例如 QPS、线程池、系统负载等;

  • 控制的效果,例如直接限流、冷启动、排队等。

 

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

 

熔断降级

 

什么是熔断降级

 

除了流量控制以外,及时对调用链路中的不稳定因素进行熔断也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,可能会导致请求发生堆积,进而导致级联错误。

 

图片

 

Sentinel 和 Hystrix 的原则是一致的: 当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。

 

熔断降级设计理念

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

系统自适应保护

 

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

 

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

 

Sentinel 原理

 

Sentinel 的主要工作机制如下:

 

  • 对主流框架提供适配或者显示的 API,来定义需要保护的资源,并提供设施对资源进行实时统计和调用链路分析。

  • 根据预设的规则,结合对资源的实时统计信息,对流量进行控制。同时,Sentinel 提供开放的接口,方便您定义及改变规则。

  • Sentinel 提供实时的监控系统,方便您快速了解目前系统的状态。

 

Sentinel 使用

 

普通使用

 

  1. 如果应用使用 pom 工程,则在 pom.xml 文件中加入以下代码即可:


<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-core</artifactId>
  <version>1.8.1</version>
</dependency>
  1. 接下来,我们把需要控制流量的代码用 Sentinel API SphU.entry("HelloWorld")  entry.exit() 包围起来即可。在下面的例子中,我们将 System.out.println("hello world"); 这端代码作为资源,用 API 包围起来(埋点)。参考代码如下:


while (true) {
  Entry entry = null;
  try {
    entry = SphU.entry("HelloWorld");
    /*您的业务逻辑 - 开始*/
    System.out.println("hello world");
    TimeUnit.MILLISECONDS.sleep(10);
    /*您的业务逻辑 - 结束*/
  } catch (BlockException e1) {
    /*流控逻辑处理 - 开始*/
    System.out.println("block!");
    /*流控逻辑处理 - 结束*/
  } catch (InterruptedException e) {
    e.printStackTrace();
  } finally {
    if (entry != null) {
      entry.exit();
    }
  }
}
  1. 接下来,通过规则来指定允许该资源通过的请求次数,例如下面的代码定义了资源 HelloWorld 每秒最多只能通过 20 个请求。


// 规则配置
private static void initFlowRules() {
  List<FlowRule> rules = new ArrayList<>();
  FlowRule rule = new FlowRule();
  rule.setResource("HelloWorld");
  rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
  // Set limit QPS to 20.
  rule.setCount(20);
  rules.add(rule);
  FlowRuleManager.loadRules(rules);
}

4.Demo 运行之后,我们可以在日志 ~/logs/csp/${appName}-metrics.log.xxx 里看到下面的输出:


|--timestamp-|------date time----|-resource-|p |block|s |e|rt
1619954886000|2021-05-02 19:28:06|HelloWorld|20|1|20|0|12|0|0|0
1619954887000|2021-05-02 19:28:07|HelloWorld|20|3197|20|0|11|0|0|0
1619954888000|2021-05-02 19:28:08|HelloWorld|20|2857|20|0|11|0|0|0

其中 p 代表通过的请求, block 代表被阻止的请求, s 代表成功执行完成的请求个数, e 代表用户自定义的异常, rt 代表平均响应时长。

 

可以看到,这个程序每秒稳定输出 "hello world" 20 次,和规则中预先设定的阈值是一样的。

 

注解方式

 

  1. Sentinel 提供了 @SentinelResource 注解用于定义资源,并提供了 AspectJ 的扩展用于自动定义资源、处理 BlockException 等。使用 Sentinel Annotation AspectJ Extension 的时候需要引入以下依赖:


<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-annotation-aspectj</artifactId>
    <version>x.y.z</version>
</dependency>
  1. 示例

// 对应的 `handleException` 函数需要位于 `ExceptionUtil` 类中,并且必须为 public static 函数.
// 对应的返回值也需要和当前方法一样
@SentinelResource(value = "createOrder",
                  blockHandler = "blockHandler",
                  blockHandlerClass = {ExceptionUtils.class})
@GetMapping("/createOrder")
public OrderDto createOrder(OrderDto dto) {
  return new OrderDto();
}
// ExceptionUtils
public class ExceptionUtils {
  public static OrderDto blockHandler(OrderDto dto, BlockException ex) {
    ex.printStackTrace();
        return null;
  }
}

@SentinelResource  注解

 

注意:注解方式埋点不支持 private 方法。

 

@SentinelResource  用于定义资源,并提供可选的异常处理和 fallback 配置项。@SentinelResource  注解包含以下属性:

 

  • value:资源名称,必需项(不能为空)

  • entryType:entry 类型,可选项(默认为 EntryType.OUT)

  • blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

  • fallback / fallbackClass:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:

    • 返回值类型必须与原函数返回值类型一致;

    • 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。

    • fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

 

  • defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:

    • 返回值类型必须与原函数返回值类型一致;

    • 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。

    • defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

 

  • exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
    1.8.0 版本开始,defaultFallback 支持在类级别进行配置。

 

注:1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException)进行处理,不能针对业务异常进行处理。

 

特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandler、fallback 和 defaultFallback,则被限流降级时会将 BlockException 直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)。

 

Sentinel 控制台

 

  1. 下载控制台程序地址:

https://github.com/alibaba/Sentinel/releases/tag/1.8.1
  1. 启动命令

java -Dserver.port=8089 
-Dcsp.sentinel.dashboard.server=127.0.0.1:8089 
-Dproject.name=sentinel-dashboard 
-jar sentinel-dashboard-1.8.1.jar
  1. 登录账号,默认的登录帐号和密码都是:sentinel

  2. 登录控制台后我们可以通过右侧菜单对我们的服务进行配置

运维开发工程师在线评测:http://www.gtalent.cn/exam/interview/nsYteJ5wFfWkMdb2