关于后端:Sentinel熔断降级

45次阅读

共计 8219 个字符,预计需要花费 21 分钟才能阅读完成。

sentinel 流量管制

Sentinel 流量管制 & 服务熔断降级介绍

流量管制介绍

在这里我用景区的例子解释一下

一个旅游景点日接待游客数量为 8K,8K 当前的游客就无奈买票进去景区。

对应编程来说就是,一个接口 QPS(每秒申请数)最大为 100,在 QPS100 之后的申请咱们就要限度其拜访,并给出敌对提醒。

不限度 QPS 有限的次数就会造成服务器的宕机。

服务熔断降级

在调用零碎的时候,如果调用链路中的某个资源呈现了不稳固,最终会导致申请产生沉积,进而导致级联谬误

而熔断降级就能够解决这个问题,所谓的熔断降级就是当检测到调用链路中某个资源呈现不稳固的体现,例如申请响应工夫长或异样比例升高的时候,则对这个资源的调用进行限度,让申请疾速失败,防止影响到其它的资源而导致级联故障

sentinel 流量管制入门

创立本地利用

整体流程剖析

  1. 创立 springBoot 我的项目
  2. 导入 sentinel 的坐标(此处应用为 gradle)
  3. 创立 Controller 测试接口,定义限流规定同时援用限流规定

导入外围依赖

implementation("com.alibaba.cloud:spring-cloud-starter-alibaba-sentinel")

本地 Sentinel 控制台

下载 sentinel 控制台 jar 包

下载地址

启动 Sentinel 控制台

启动 Sentinel 控制台须要 JDK 版本为 1.8 及以上版本。

应用一下命令启动

java -Dserver.port= 端口号 -jar sentinel-dashboard-1.8.0.jar

拜访控制台

通过浏览器拜访 http://localhost: 端口号即可拜访控制台,默认用户名明码都是sentinel

Sentinel 自定义资源形式

  1. 抛出异样的形式定义资源
  2. 返回布尔值形式定义资源
  3. 异步调用反对
  4. 注解形式定义资源
  5. 支流框架的默认适配

抛出异样的形式定义资源

Sentinel 种的 SphU 蕴含了 try-catch 格调的 API。用这种形式,当资源产生了限流之后会抛出BlockException

这个时候就能够捕捉异样,进行限流之后的逻辑操作,在入门案例种就是应用此形式实现的,要害代码:

fun hello(): String {
        // 应用限流规定
        try {SphU.entry("Hello")
        } catch (e: Exception) {return "零碎忙碌,请稍后再试!!"}
        return "Hello Sentinel"
    }

特地地,若 entry 的时候传入了热点参数,那么 exit 的时候也肯定要带上对应的参数(exit(count, args)),否则可能会有统计谬误。这个时候不能应用 try-with-resources 的形式。另外通过 Tracer.trace(ex) 来统计异样信息时,因为 try-with-resources 语法中 catch 调用程序的问题,会导致无奈正确统计异样数,因而统计异样信息时也不能在 try-with-resources 的 catch 块中调用 Tracer.trace(ex)

手动 exit 示例:

Entry entry = null;
// 务必保障 finally 会被执行
try {
  // 资源名可应用任意有业务语义的字符串,留神数目不能太多(超过 1K),超出几千请作为参数传入而不要间接作为资源名
  // EntryType 代表流量类型(inbound/outbound),其中零碎规定只对 IN 类型的埋点失效
  entry = SphU.entry("自定义资源名");
  // 被爱护的业务逻辑
  // do something...
} catch (BlockException ex) {
  // 资源拜访阻止,被限流或被降级
  // 进行相应的解决操作
} catch (Exception ex) {
  // 若须要配置降级规定,须要通过这种形式记录业务异样
  Tracer.traceEntry(ex, entry);
} finally {
  // 务必保障 exit,务必保障每个 entry 与 exit 配对
  if (entry != null) {entry.exit();
  }
}

热点参数埋点示例:

Entry entry = null;
try {
    // 若须要配置例外项,则传入的参数只反对根本类型。// EntryType 代表流量类型,其中零碎规定只对 IN 类型的埋点失效
    // count 大多数状况都填 1,代表统计为一次调用。entry = SphU.entry(resourceName, EntryType.IN, 1, paramA, paramB);
    // Your logic here.
} catch (BlockException ex) {// Handle request rejection.} finally {
    // 留神:exit 的时候也肯定要带上对应的参数,否则可能会有统计谬误。if (entry != null) {entry.exit(1, paramA, paramB);
    }
}

SphU.entry() 的参数形容:

参数名 类型 解释 默认值
entryType EntryType 资源调用的流量类型,是入口流量(EntryType.IN)还是进口流量(EntryType.OUT),留神零碎规定只对 IN 失效 EntryType.OUT
count int 本次资源调用申请的 token 数目 1
args Object[] 传入的参数,用于热点参数限流

留神SphU.entry(xxx) 须要与 entry.exit() 办法成对呈现,匹配调用,否则会导致调用链记录异样,抛出 ErrorEntryFreeException 异样。常见的谬误:

  • 自定义埋点只调用 SphU.entry(),没有调用 entry.exit()
  • 程序谬误,比方:entry1 -> entry2 -> exit1 -> exit2,应该为 entry1 -> entry2 -> exit2 -> exit1

返回布尔值形式定义资源

Sentinel 种的 SphO 蕴含了 if-else 格调的 API。用这种形式,当资源产生了限流之后会返回 false,这时候能够依据返回值,进行限流之后的逻辑解决。

  1. 在我的项目中创立 TestBooleanController 中应用返回 booelan 的形式定义资源

     @GetMapping("sentinel_boolean")
        fun hello(): Boolean {
            // 配置限流入口, 在这里是应用控制台进行配置限流规定
            return if (SphO.entry("Boolean")) {
                try {// 被爱护的资源
                    println("Hello Sentinel Boolean")
                    true
                } finally {
                    // 限流进口
                    SphO.exit()}
            } else {
                // 限流或者降级解决
                println("零碎忙碌,请稍后重试")
                false
            }
        }

在控制台进行流控规定的配置

留神 SphO.entry(xxx) 须要于 SphO.exit() 办法成对呈现,匹配调用,地位正确,否则就会导致调用链记录异样,抛出 ErrorEntryFreeException 异样

异步调用反对

Sentinel 反对异步调用链路的统计。在异步调用中,须要通过 SphU.asyncEntry(xxx) 办法定义资源,并通常须要在异步的回调函数中调用 exit 办法。

  1. 启动类开启异步调用 @EnableAsync
  2. 开启办法的异步调用 @Async

SphU.asyncEntry(xxx) 不会影响以后(调用线程)的 Context,因而以下两个 entry 在调用链上是平级关系(处于同一层),而不是嵌套关系:

// 调用链相似于:// -parent
// ---asyncResource
// ---syncResource
asyncEntry = SphU.asyncEntry(asyncResource);
entry = SphU.entry(normalResource);

若在异步回调中须要嵌套其它的资源调用(无论是 entry 还是 asyncEntry),只须要借助 Sentinel 提供的上下文切换性能,在对应的中央通过 ContextUtil.runOnContext(context, f) 进行 Context 变换,将对应资源调用处的 Context 切换为生成的异步 Context,即可维持正确的调用链路关系。示例如下:

public void handleResult(String result) {
    Entry entry = null;
    try {entry = SphU.entry("handleResultForAsync");
        // Handle your result here.
    } catch (BlockException ex) {// Blocked for the result handler.} finally {if (entry != null) {entry.exit();
        }
    }
}

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

        // Asynchronous invocation.
        doAsync(userId, result -> {
            // 在异步回调中进行上下文变换,通过 AsyncEntry 的 getAsyncContext 办法获取异步 Context
            ContextUtil.runOnContext(entry.getAsyncContext(), () -> {
                try {
                    // 此处嵌套失常的资源调用.
                    handleResult(result);
                } finally {entry.exit();
                }
            });
        });
    } catch (BlockException ex) {
        // Request blocked.
        // Handle the exception (e.g. retry or fallback).
    }
}

此时的调用链就相似于:

-parent
---asyncInvocation
-----handleResultForAsync

更具体的示例能够参考 Demo 中的 AsyncEntryDemo,外面蕴含了一般资源与异步资源之间的各种嵌套示例。

注解形式定义资源

Sentinel 反对通过 @SentinelResource 注解定义资源并配置 blockHandlerfallback 函数来进行限流之后的解决。应用 Sentinel Annotation AspectJ Extension 的时候须要引入以下依赖

implementation("com.alibaba.csp:sentinel-annotation-aspectj:1.7.2")

@RestController
class TestAnnController {
    /**
     * SentinelResource:定义声援
     * value:设置资源的名称
     * blockHandler:设置降级或者限流的处理函数
     */
    @SentinelResource(value = "sentinel_ann", blockHandler = "exceptionHandler")
    @GetMapping("ann")
    fun hello(): String{return "hello Sentinel"}

    /**
     * 限流或者降级的函数
     */
    fun exceptionHandler(ex: BlockException) :String {ex.printStackTrace()
        return "零碎忙碌,请稍后"
    }
}

@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 解决逻辑。若未配置 blockHandlerfallbackdefaultFallback,则被限流降级时会将 BlockException 间接抛出(若办法自身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)。

示例:

public class TestService {

    // 原函数
    @SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")
    public String hello(long s) {return String.format("Hello at %d", s);
    }
    
    // Fallback 函数,函数签名与原函数统一或加一个 Throwable 类型的参数.
    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;
    }

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

从 1.4.0 版本开始,注解形式定义资源反对主动统计业务异样,无需手动调用 Tracer.trace(ex) 来记录业务异样。Sentinel 1.4.0 以前的版本须要自行调用 Tracer.trace(ex) 来记录业务异样。

配置

Spring Cloud Alibaba

若您是通过 Spring Cloud Alibaba 接入的 Sentinel,则无需额定进行配置即可应用 @SentinelResource 注解。

Spring AOP

若您的利用应用了 Spring AOP(无论是 Spring Boot 还是传统 Spring 利用),您须要通过配置的形式将 SentinelResourceAspect 注册为一个 Spring Bean:

@Configuration
public class SentinelAspectConfiguration {

    @Bean
    public SentinelResourceAspect sentinelResourceAspect() {return new SentinelResourceAspect();
    }
}

咱们提供了 Spring AOP 的示例,能够参见 sentinel-demo-annotation-spring-aop。

AspectJ

若您的利用间接应用了 AspectJ,那么您须要在 aop.xml 文件中引入对应的 Aspect:

<aspects>
    <aspect name="com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect"/>
</aspects>

支流框架的默认适配

文档地址:https://github.com/alibaba/Se…

规定的品种

Sentinel 的所有规定都能够在内存态中动静地查问及批改,批改之后立刻失效。同时 Sentinel 也提供相干 API,供您来定制本人的规定策略。

Sentinel 反对以下几种规定:流量管制规定 熔断降级规定 零碎爱护规定 起源访问控制规定 热点参数规定

流量管制规定 (FlowRule)

流量规定的定义

重要属性:

Field 阐明 默认值
resource 资源名,资源名是限流规定的作用对象
count 限流阈值
grade 限流阈值类型,QPS 模式(1)或并发线程数模式(0) QPS 模式
limitApp 流控针对的调用起源 default,代表不辨别调用起源
strategy 调用关系限流策略:间接、链路、关联 依据资源自身(间接)
controlBehavior 流控成果(间接回绝 /WarmUp/ 匀速 + 排队期待),不反对按调用关系限流 间接回绝
clusterMode 是否集群限流

同一个资源能够同时有多个限流规定,查看规定时会顺次查看。

通过代码定义流量管制规定

了解下面规定的定义之后,咱们能够通过调用 FlowRuleManager.loadRules() 办法来用硬编码的形式定义流量管制规定,比方:

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

正文完
 0