@Author:zxw
@Email:502513206@qq.com


1.前言

再研究Sentinel源码之前,先对Sentinel的理念进行了解。对于网络请求调用可以想象以下两个问题

  1. 当服务A请求服务B时,服务B响应不及时,导致服务A的线程全部hang死在请求服务B?
  2. 当服务收到大量网络请求,应用无法支撑时如何解决?

以上会导致服务的稳定性降低从而引发系统的崩溃,这对于线上业务来说是不可接受,那么一般的解决方案如下:

  1. 请求超时:设置请求超时时间
  2. 请求资源隔离:通过线程池和信号量
  3. 断路器:当请求异常数量过多时,打开断路器快速失败,避免产生服务雪崩
  4. 服务降级:当请求熔断后采取后续策略

Sentinel作为流量控制框架就可以帮助我们解决上述问题,除了Sentinel还有其它框架,例如:Hystrix,resilience4j,接下来看看这几个框架的对比

表格来源网络,侵删

Sentinel Hystrix resilience4j
隔离策略 信号量隔离(并发线程数限流) 线程池隔离/信号量隔离 信号量隔离
熔断降级策略 基于响应时间、异常比率、异常数 基于异常比率 基于异常比率、响应时间
实时统计实现 滑动窗口(LeapArray) 滑动窗口(基于 RxJava) Ring Bit Buffer
动态规则配置 支持多种数据源 支持多种数据源 有限支持
扩展性 多个扩展点 插件的形式 接口的形式
基于注解的支持 支持 支持 支持
限流 基于 QPS,支持基于调用关系的限流 有限的支持 Rate Limiter
流量整形 支持预热模式、匀速器模式、预热排队模式 不支持 简单的 Rate Limiter 模式
系统自适应保护 支持 不支持 不支持
控制台 提供开箱即用的控制台,可配置规则、查看秒级监控、机器发现等 简单的监控查看 不提供控制台,可对接其它监控系统

接下来看下Sentinel中比较重要的两个概念

来源Sentinel官方文档

资源
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。
只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
规则
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。

2.资源

2.1 ResourceWrapper

了解过RESTFUL,网络上的一个实体就是资源,它可以是一段文本、图片等等。对于Sentinel来说,使用Sentinel API定义的代码就是一个资源,通常就是我们controller层中的接口了。首先想要定义一个资源,那么肯定需要有个资源名称。

  1. protected final String name;

虽然一切都为资源,但是我们有时候也需要对资源进行标识来对资源进行不同的操作,对此提供一个字段标识资源类型,Sentinel在枚举ResourceTypeConstants中定义了以下资源

  1. // 资源类型
  2. protected final int resourceType;
  3. // ----- 资源类型枚举
  4. public static final int COMMON = 0;
  5. public static final int COMMON_WEB = 1;
  6. public static final int COMMON_RPC = 2;
  7. public static final int COMMON_API_GATEWAY = 3;
  8. public static final int COMMON_DB_SQL = 4;

以上就是Sentinel对资源类ResourceWrapper的定义,EntryType是用来标识流量是入站和出站的,主要是后面用来做统计,默认为OUT

  1. public abstract class ResourceWrapper {
  2. protected final String name;
  3. protected final int resourceType;
  4. protected final EntryType entryType;
  5. }

可以看到该类是个抽象类,Sentinel提供了两个资源类的子类,分别为StringResourceWrapperMethodResourceWrapper,Method资源类,内部则是包装了一个Method字段,String资源类则是单纯的继承父资源类。
image.png

2.2 Entry

现在有了资源类的定义,但是我们还需要例如当前资源的创建时间以及对于请求的耗时统计则需要完成时间

  1. private final long createTimestamp;
  2. private long completeTimestamp;

我们先前提到对于请求超时以及服务降级,这种属于异常情况,那么就需要异常的回调来处理这些异常情况,Sentinel将回调的异常封装为了BlockException

  1. private BlockException blockError;

以请求路径http://localhost:8080/order/findById为例,在构造node节点时时,就会生成类似下面的路径,这样我们进行流量控制的时候可以更加细化的去控制每个每个资源的流控
order
\
findById
在Entry内部存了两个node节点,一个表示当前node,另一个则是来源node

  1. private Node curNode;
  2. private Node originNode;

对于node是Sentinel 里面的各种种类的统计节点:

  • StatisticNode:最为基础的统计节点,包含秒级和分钟级两个滑动窗口结构。
  • DefaultNode:链路节点,用于统计调用链路上某个资源的数据,维持树状结构。
  • ClusterNode:簇点,用于统计每个资源全局的数据(不区分调用链路),以及存放该资源的按来源区分的调用数据(类型为 StatisticNode)。特别地,Constants.ENTRY_NODE 节点用于统计全局的入口资源数据。
  • EntranceNode:入口节点,特殊的链路节点,对应某个 Context 入口的所有调用数据。Constants.ROOT 节点也是入口节点。

以上就是资源类Entry所包含的一些元数据

  1. public abstract class Entry implements AutoCloseable {
  2. private static final Object[] OBJECTS0 = new Object[0];
  3. private final long createTimestamp;
  4. private long completeTimestamp;
  5. private Node curNode;
  6. /**
  7. * {@link Node} of the specific origin, Usually the origin is the Service Consumer.
  8. */
  9. private Node originNode;
  10. private Throwable error;
  11. private BlockException blockError;
  12. protected final ResourceWrapper resourceWrapper;
  13. }

但是该类是个抽象类,那么看下sentinel提供哪些实现子类
image.png

2.3 CtEntry

先来看看CtEntry中有哪些元数据

  1. class CtEntry extends Entry {
  2. protected Entry parent = null;
  3. protected Entry child = null;
  4. protected ProcessorSlot<Object> chain;
  5. protected Context context;
  6. protected LinkedList<BiConsumer<Context, Entry>> exitHandlers;
  7. }

在Sentinel调用时会产生如下一条链路
image.png
该链路就是ProcessorSlot<Object> chain字段所表示的一条链路。
另外Sentinel还为每个请求的线程准备了一个应用上下文Context,该上下文贯穿整个链路调用中,供我们去使用。

3.总结

对于Sentinel中的资源类已经分析的差不多了,其中几个比较关键的属性如下
image.png
-