Sentinel技术介绍及源码浅析

栏目: 后端 · 发布时间: 6年前

内容简介:随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。如上图所示,Sentinel中一个资源访问条目被抽象成一个Entry,这个Entry包含几个重要的信息,一个是当前节点(Node)、源节点,一个是资源信息(比如资源名称等)。规则则是对资源访问控制的一种策略。 Node是Sentinel中对应一个资源实时访问指标的抽象,其代表请求进入后的一种状态,每个请求进来,对于关联的资源,采取前面设置的访问策略进行指标

Sentinel介绍

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

  • Sentinel历史
  • 2012 年,Sentinel 诞生
  • 2013-2017 年,作为阿里基础技术模块, Sentinel 承接了阿里巴巴近 几年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等
  • 2018 年,Sentinel 开源

Sentinel基本概念

Sentinel技术介绍及源码浅析

如上图所示,Sentinel中一个资源访问条目被抽象成一个Entry,这个Entry包含几个重要的信息,一个是当前节点(Node)、源节点,一个是资源信息(比如资源名称等)。规则则是对资源访问控制的一种策略。 Node是Sentinel中对应一个资源实时访问指标的抽象,其代表请求进入后的一种状态,每个请求进来,对于关联的资源,采取前面设置的访问策略进行指标统计。

Sentinel基本功能

Sentinel技术介绍及源码浅析

如何使用Sentinel

第一步当然是引入sentinel依赖了,对于不同框架的集成 采用不同的适配模块,详情参考官方文档。

定义资源

抛出异常的方式定义资源 用这种方式,当资源发生了限流之后会抛出 BlockException。这个时候可以捕捉异常,进行限流之后的逻辑处理。示例代码如下:

Entry entry = null;  
// 务必保证finally会被执行
try {  
  // 资源名可使用任意有业务语义的字符串
  entry = SphU.entry("自定义资源名");
  /**
   * 被保护的业务逻辑
   */
} catch (BlockException e1) {
  // 资源访问阻止,被限流或被降级
  // 进行相应的处理操作
} finally {
  if (entry != null) {
    entry.exit();
  }
}

返回布尔值方式定义资源 用这种方式,当资源发生了限流之后会返回 false,这个时候可以根据返回值,进行限流之后的逻辑处理。示例代码如下:

// 资源名可使用任意有业务语义的字符串
  if (SphO.entry("自定义资源名")) {
    // 务必保证finally会被执行
    try {
      /**
      * 被保护的业务逻辑
      */
    } finally {
      SphO.exit();
    }
  } else {
    // 资源访问阻止,被限流或被降级
    // 进行相应的处理操作
  }

注解方式定义资源

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

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

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

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

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

fallback: fallback 函数名称,可选项,仅针对降级功能生效(DegradeException)。fallback 函数的访问范围需要是 public,参数类型和返回类型都需要与原方法相匹配,并且需要和原方法在同一个类中。

若 blockHandler 和 fallback 都进行了配置,则遇到降级的时候首先选择 fallback 函数进行处理。

注意 blockHandler 是处理被 block 的情况(所有类型的 BlockException),而 fallback 仅处理被降级的情况(DegradeException)。其它异常会原样抛出,Sentinel 不会进行处理。

public class TestService {

    // 对应的 `handleException` 函数需要位于 `ExceptionUtil` 类中,并且必须为 static 函数.
    @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})
    public void test() {
        System.out.println("Test");
    }

    // 原函数
    @SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")
    public String hello(long s) {
        return String.format("Hello at %d", s);
    }

    // Fallback 函数,函数签名与原函数一致.
    public String helloFallback(long s) {
        return String.format("Halooooo %d", s);
    }

    // Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.
    public String exceptionHandler(long s, BlockException ex) {
        // Do some log here.
        ex.printStackTrace();
        return "Oops, error occurred at " + s;
    }
}

异步调用支持 Sentinel 从 0.2.0 版本开始支持异步调用资源的定义。在异步调用中,需要通过 SphU.asyncEntry(xxx) 方法定义资源,并通常需要在异步的回调函数中调用 exit 方法。以下是一个简单的示例:

try {  
    AsyncEntry entry = SphU.asyncEntry(resourceName);

    // 异步调用.
    doAsync(userId, result -> {
        try {
            // 在此处处理异步调用的结果.
        } finally {
            // 在回调结束后 exit.
            entry.exit();
        }
    });
} catch (BlockException ex) {
    // Request blocked.
    // Handle the exception (e.g. retry or fallback).
}

关于异步调用的详细用法,请参考官方文档。

主流框架的适配:

Sentinel技术介绍及源码浅析 因篇幅有限,其它各种框架的适配,请参考官方文档。

定义规则

Sentinel 的所有规则都可以在内存态中动态地查询及修改,修改之后立即生效。同时 Sentinel 也提供相关 API,供您来定制自己的规则策略。

规则的定义 Sentinel 支持以下几种规则:流量控制规则、熔断降级规则、系统保护规则 以及 授权规则。因篇幅问题,对应功能的规则配置请参考官方文档,如下只是一个示例:

private static void initFlowQpsRule() {  
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule1 = new FlowRule();
        rule1.setResource(KEY);
        // set limit qps to 20
        rule1.setCount(20);
        rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule1.setLimitApp("default");
        rules.add(rule1);
        FlowRuleManager.loadRules(rules);
    }

Sentinel工作原理

Sentinel技术介绍及源码浅析

Sentinel的工作原理,简单理解可以看上面的脑图

对于需要保护的资源,系统先定义其rule,创建资源、然后创建默认的SoltChain。这个SoltChain,我们可以自行装配,也可以直接默认使用Sentinel提供的DefaultProcessorSlotChain。Sentinel提供了基于SPI的拓展口子,让我们可以自己拓展每一个ProcessorSlot。Sentinel中ProcessorSlot的设计思路和Netty中Handler处理链的设计非常像。一个请求进入某个资源,其接下来的调用链路都会由前面装配好的路劲进行,每个插槽(Solt)的作用参考上图。这其中,无论多少个request进入资源,其全部共享同一个上下文Context,Sentinel内部是按资源名划分的。每个Entry都关联一个Context和一个Chain。这块的设计思路,如果看过Netty的源码,你会感觉非常的熟悉。后面我会谈到Sentinel的核心组件和源码。

Sentinel和Hystrix 对比

Hystrix 的关注点在于以 隔离 和 熔断 为主的容错机制,超时或被熔断的调用将会快速失败,并可以提供 fallback 机制。

Sentinel 的侧重点在于:多样化的流量控制、熔断降级、系统负载保护、实时监控和控制台

Hystrix中每个 Command 创建时都要指定 commandKey 和 groupKey(用于区分资源)以及对应的隔离策略(线程池隔离 or 信号量隔离)

Sentinel 的资源定义与规则配置的耦合度更低

Sentinel 中资源定义和规则配置是分离的。用户先通过 Sentinel API 给对应的业务逻辑定义资源(埋点),然后可以在需要的时候配置规则。

Hystrix在采用线程池隔离时,有个最大的缺点就是,Hystrix 的线程池隔离针对不同的资源分别创建不同的线程池,对于一些线程或者资源敏感的应用,可能会创建太多的线程,造成线程上下文切换的 overhead 比较大。比如使用Tomcat的应用,Tomcat作为web服务器,本身就消耗不少线程资源,如果再使用Hystrix,在采用线程池隔离时上下文切换会有非常大的损耗。

总结如下 Sentinel技术介绍及源码浅析

Sentinel源码浅析

Sentinel核心组件UML图

Sentinel技术介绍及源码浅析

这个图有点大,超出限制,我截图后压缩后才能正常上传,有同学如果感兴趣可以私下找我要。 Sentinel中比较重要的几个比较重要的抽象

Entry,Context,Node,Sph,ProcessorSlot,Rule,RuleManager,SentinelProperty,PropertyListener,Metric,MetricBucket,MetricWriter,MetricsReader,MetricTimerListener,WindowWrap,LeapArray、transport。具体抽象的其实就是(对应上面)资源、节点、获取资源条目的抽象、资源上下文、处理插槽、规则、规则管理器、配置、配置监听器、度量、度量槽、度量写、度量读、度量信息监听器(定时写入)、时间片、滑动窗口、发送统计数据到指定的sentinel控制台。对于每一个请求,进入某个资源,都会通过资源名称获取一个资源以及其对应的上下文,所有的请求都会经过一个处理链(SoltChain),其中StatisticSlot负责统计度量指标,具体的操作是调用StatisticNode这个统计节点进行操作的,统计节点StatisticNode维持两个滑动窗口,一个窗口周期是1秒,一个是60秒。滑动窗口实现没啥特别之处,每个时间片内部的桶信息计数是通过大名鼎鼎的Doug Lea写的实现的并发累计器Striped64实现的LongAdder。具体思路和算法是将AtomicInteger的内部核心数据value分离成一个数组,每个线程访问时,通过哈希等算法映射到其中一个数字进行计数,而最终的计数结果,则为这个数组的求和累加,其中,热点数据value被分离成多个单元cell,每个cell独自维护内部的值,当前对象的实际值由所有的cell累计合成,这样,热点就进行了有效的分离,提高了并行度,LongAdder正是使用了这种思想,类似ConcurrentHashMap,热点分离。因为传统的CAS实现,在并发量较大的时候,修改失败的概率就很高,在大量修改失败时,这些原子操作就会进行多次循环尝试,因此性能就会受到影响。LongAdder的热点分离分段累加操作,在并发较高时性能相对会好很多。

下面发下请求进入资源后的整个时序图

Sentinel技术介绍及源码浅析

最后放下个人学习sentinel的一个思维导图,起个抛砖引玉的作用。 Sentinel技术介绍及源码浅析

总的来说sentinel的模块和代码实现还是非常清晰的,大家有兴趣可以再深入研究下,以上均为个人理解观点,因为个人能力和研究深度有限,观点难免有失偏颇,欢迎各位同学私下斧正交流。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

ANSI Common Lisp

ANSI Common Lisp

Paul Graham / Prentice Hall / 1995-11-12 / USD 116.40

For use as a core text supplement in any course covering common LISP such as Artificial Intelligence or Concepts of Programming Languages. Teaching students new and more powerful ways of thinking abo......一起来看看 《ANSI Common Lisp》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

随机密码生成器
随机密码生成器

多种字符组合密码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试