当前位置: 首页 > news >正文

Spring Cloud Alibaba Sentinel 简介与入门

简介

Sentinel直译过来是哨兵。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。

高并发性能指标

在学习Sentinel之前最好是了解一下各个性能指标的意思,可移步我们的 《高并发性能指标》一文

Sentinel的主要功能

流量控制

任何单个应用的处理能力都是有限的,比如某个应用最大只能在单位时间内处理1000QPS,那么再大一点的流量,可能导致应用整体不可用,导致系统瘫痪等等一系列的问题。

Sentinel 从以下几个角度进行流量控制:

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

熔断降级

Spring Cloud生态中,Hystrix(netflix 开源的熔断降级处理依赖) 和 Spring Cloud Circuit breaker (Spring 官方提供的熔断降级处理依赖)都提供了熔断降级功能。
什么是熔断降级:由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,最终会导致请求发生堆积。例如下图的Service D
请添加图片描述

Service D出现不稳定,会导致整个调用路径请求堆积,直接影响到Service G 和 Service A的返回。

Sentinel 基本概念

资源

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

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

规则

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

Sentinel 客户端和控制台

  • Sentinel 客户端:不依赖任何框架/库,能够运行于 Java 8 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。也就是说不只是Spring Cloud中,只要是java代码我们就能使用Sentinel。
  • 控制台(Dashboard):Dashboard 主要负责管理推送规则、监控、管理机器信息等。

入门示例

引入Sentinel依赖

Sentinel 是可以单独使用的,所以我们的入门程序就只依赖 Sentinel 即可

        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>1.8.6</version>
        </dependency>

测试代码

package com.yyoo.sentinel.demo;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;

import java.util.ArrayList;
import java.util.List;

public class Test1 {

    public static void main(String[] args) {
        // 配置规则.
        initFlowRules();

        while (true) {
            // try-with-resources 特性(可以不用在finally中关闭)
            try (Entry entry = SphU.entry("HelloWorld")) {
                // 被保护的逻辑
                System.out.println("hello world");
            } catch (BlockException ex) {
                // 处理被流控的逻辑
                System.out.println("blocked!");
            }
        }
    }


    /**
     * 定义规则(rule)列表
     */
    private static void initFlowRules(){
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        // 设置规则匹配的资源名称
        rule.setResource("HelloWorld");
        // 规则作用类型(此为QPS类型,还有一种为FLOW_GRADE_THREAD,线程数类型)
        // 默认为QPS类型
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 按Grade设置,此处为限制 QPS 为 20.
        rule.setCount(20);
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }

}

Demo 运行之后,我们可以在日志 ~/logs/csp/${appName}-metrics.log.xxx 里看到下面的输出:(~ 即为用户目录)

|--timestamp-|------date time----|--resource-|p |block|s |e|rt
1675402839000|2023-02-03 13:40:39|HelloWorld|20|34171|20|0|0|0|0|0
1675402840000|2023-02-03 13:40:40|HelloWorld|20|99964|20|0|0|0|0|0
1675402841000|2023-02-03 13:40:41|HelloWorld|20|117545|20|0|0|0|0|0
1675402842000|2023-02-03 13:40:42|HelloWorld|20|137136|20|0|0|0|0|0
1675402843000|2023-02-03 13:40:43|HelloWorld|20|164920|20|0|0|0|0|0
1675402844000|2023-02-03 13:40:44|HelloWorld|20|152597|20|0|0|0|0|0
1675402845000|2023-02-03 13:40:45|HelloWorld|20|151261|20|0|0|0|0|0
1675402846000|2023-02-03 13:40:46|HelloWorld|20|155132|20|0|0|0|0|0
1675402847000|2023-02-03 13:40:47|HelloWorld|20|152466|20|0|0|0|0|0
1675402848000|2023-02-03 13:40:48|HelloWorld|20|143706|20|0|0|0|0|0
1675402849000|2023-02-03 13:40:49|HelloWorld|20|167305|20|0|0|0|0|0
1675402850000|2023-02-03 13:40:50|HelloWorld|20|163450|20|0|0|0|0|0
1675402851000|2023-02-03 13:40:51|HelloWorld|20|161299|20|0|0|0|0|0
1675402852000|2023-02-03 13:40:52|HelloWorld|20|160861|20|0|0|0|0|0
1675402853000|2023-02-03 13:40:53|HelloWorld|20|160179|20|0|0|0|0|0
1675402854000|2023-02-03 13:40:54|HelloWorld|20|153261|20|0|0|0|0|0
  • p 代表通过的请求
  • block 代表被阻止的请求
  • s 代表成功执行完成的请求个数
  • e 代表用户自定义的异常
  • rt 代表平均响应时长

资源与规则

用 Sentinel 来进行资源保护,主要分为几个步骤:

  • 定义资源
  • 定义规则
  • 检验规则是否生效

定义资源的方式

方式一:主流框架的默认适配

为了减少开发的复杂程度,我们对大部分的主流框架,例如 Web Servlet、Dubbo、Spring Cloud、gRPC、Spring WebFlux、Reactor 等都做了适配。您只需要引入对应的依赖即可方便地整合 Sentinel。具体示例我们会在后续文章中会提到。

方式二:抛出异常的方式定义资源

我们的入门示例即是此方式

// 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
try (Entry entry = SphU.entry("resourceName")) {
  // 被保护的业务逻辑
  // do something here...
} catch (BlockException ex) {
  // 资源访问阻止,被限流或被降级
  // 在此处进行相应的处理操作
}

方式三:返回布尔值方式定义资源

SphO 提供 if-else 风格的 API。用这种方式,当资源发生了限流之后会返回 false,这个时候可以根据返回值,进行限流之后的逻辑处理。示例代码如下:

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

方式四:注解方式定义资源

Sentinel 支持通过 @SentinelResource 注解定义资源并配置 blockHandler 和 fallback 函数来进行限流之后的处理。示例:

// 原本的业务方法.
@SentinelResource(blockHandler = "blockHandlerForGetUser")
public User getUserById(String id) {
    throw new RuntimeException("getUserById command failed");
}

// blockHandler 函数,原方法调用被限流/降级/系统保护的时候调用
public User blockHandlerForGetUser(String id, BlockException ex) {
    return new User("admin");
}

注意 blockHandler 函数会在原方法被限流/降级/系统保护的时候调用,而 fallback 函数会针对所有类型的异常。

方式五:异步调用支持

Sentinel 支持异步调用链路的统计。在异步调用中,需要通过 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).
}

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

规则的种类

  • 流量控制规则 (FlowRule)
  • 熔断降级规则 (DegradeRule)
  • 系统保护规则(SystemRule)
  • 来源访问控制规则 (AuthorityRule)
  • 热点参数规则(ParamFlowRule)

相关文章:

  • 「自控元件及线路」3.2 三相、两相、单相异步电动机
  • Java高手速成 | JSP的MVC模式
  • 把次卧整出来当办公室
  • MYSQL必知必会笔记:第十二章汇总数据
  • 多线程第三讲
  • 寄存器和移位寄存器分析与建模
  • Learning C++ No.4【类和对象No.3】
  • 详解视觉BEV3D检测模型: Fast-BEV: A Fast and Strong Bird‘s-Eye View Perception Baseline
  • PMT有两种基本模式――单光子计数和电流感应选择
  • 格式化并挂载ubi文件系统过程详解
  • 自由概率(Free probability)
  • 大数据之HBase基础
  • Python爬虫教你爬取视频信息
  • 基于微信小程序的火锅店点餐系统小程序
  • 一文带你吃透红黑树---红黑树如此简单
  • 经济的1000+篇文章总结
  • 【数据结构】基础:AVL树(平衡二叉树)
  • 【C++11】lambda表达式、包装器、bind 与 placeholders
  • 【深度学习基础6】自编码器及其变体
  • 数学知识-约数
  • 电加热油锅炉工作原理_电加热导油
  • 大型电蒸汽锅炉_工业电阻炉
  • 燃气蒸汽锅炉的分类_大连生物质蒸汽锅炉
  • 天津市维修锅炉_锅炉汽化处理方法
  • 蒸汽汽锅炉厂家_延安锅炉厂家
  • 山西热水锅炉厂家_酒店热水 锅炉
  • 蒸汽锅炉生产厂家_燃油蒸汽发生器
  • 燃煤锅炉烧热水_张家口 淘汰取缔燃煤锅炉
  • 生物质锅炉_炉
  • 锅炉天然气_天燃气热风炉