Spring Cloud Alibaba 微服务组件 Sentinel 服务保护(七)

  Spring Cloud Aibaba 学习目录

1. Spring Cloud Alibaba 微服务介绍(一)

2. Spring Cloud Alibaba 之Nacos 安装(二)

3. Spring Cloud Alibaba 微服务组件 Nacos 注册中心(三)

4. Spring Cloud Alibaba 微服务负载均衡 Ribbon(四)

5. Spring Cloud Alibaba 微服务整合 OpenFeign(五)

6. Spring Cloud Alibaba 微服务组件 Nacos 配置中心(六)

7. Spring Cloud Alibaba 微服务组件 Sentinel 服务保护(七)

8. Spring Cloud Alibaba 分布式事务概念(八)

9. Spring Cloud Alibaba 微服务组件 Seata 分布式事务(九)

10. Spring Cloud Alibaba 服务网关 Gateway(十)

11. Spring Cloud Alibaba 微服务组件 Skywalking 分布式任务链(十一)

语雀文档:Spring Cloud Aibaba 学习 · 语雀


概述

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流流量整形熔断降级系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性

分布式遇到的问题

服务的可用性问题


服务的可用性场景

 

如果其中的下单服务不可用, 就会出现线程池里所有线程都因等待响应而被阻塞, 从而造成整个服务链路不可用, 进而导致整个系统的服务雪崩. 如图所示:


服务雪崩效应:因服务提供者的不可用导致服务调用者的不可用,并将不可用逐渐放大的过程,就叫服务雪崩效应

导致服务不可用的原因

在服务提供者不可用的时候,会出现大量重试的情况:用户重试、代码逻辑重试,这些重试最终导致:进一步加大请求流量。所以归根结底导致雪崩效应的最根本原因是:大量请求线程同步等待造成的资源耗尽。当服务调用者使用同步调用时, 会产生大量的等待线程占用系统资源。一旦线程资源被耗尽,服务调用者提供的服务也将处于不可用状态, 于是服务雪崩效应产生了

解决方案

稳定性、恢复性

常见的容错机制:


超时机制
在不做任何处理的情况下,服务提供者不可用会导致消费者请求线程强制等待,而造成系统资源耗尽。加入超时机制,一旦超时,就释放资源。由于释放资源速度较快,一定程度上可以抑制资源耗尽的问题。
服务限流


隔离
原理:用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,则会进行降级处理,用户的请求不会被阻塞,至少可以看到一个执行结果(例如返回友好的提示信息),而不是无休止的等待或者看到系统崩溃。

  • 隔离前:

  • 隔离后

  • 信号隔离

 信号隔离也可以用于限制并发访问,防止阻塞扩散, 与线程隔离最大不同在于执行依赖代码的线程依然是请求线程(该线程需要通过信号申请, 如果客户端是可信的且可以快速返回,可以使用信号隔离替换线程隔离,降低开销。信号量的大小可以动态调整, 线程池大小不可以。

服务熔断

远程服务不稳定或网络抖动时暂时关闭,就叫服务熔断。
现实世界的断路器大家肯定都很了解,断路器实时监控电路的情况,如果发现电路电流异常,就会跳闸,从而防止电路被烧毁。


软件世界的断路器可以这样理解:实时监测应用,如果发现在一定时间内失败次数/失败率达到一定阈值,就“跳闸”,断路器打开——此时,请求直接返回,而不去调用原本调用的逻辑。跳闸一段时间后(例如10秒),断路器会进入半开状态,这是一个瞬间态,此时允许一次请求调用该调的逻辑,如果成功,则断路器关闭,应用正常调用;如果调用依然不成功,断路器继续回到打开状态,过段时间再进入半开状态尝试——通过”跳闸“,应用可以保护自己,而且避免浪费资源;而通过半开的设计,可实现应用的“自我修复“

所以,同样的道理,当依赖的服务有大量超时时,在让新的请求去访问根本没有意义,只会无畏的消耗现有资源。比如我们设置了超时时间为1s,如果短时间内有大量请求在1s内都得不到响应,就意味着这个服务出现了异常,此时就没有必要再让其他的请求去访问这个依赖了,这个时候就应该使用断路器避免资源浪费.

服务降级

有服务熔断,必然要有服务降级。
所谓降级,就是当某个服务熔断之后,服务将不再被调用,此时客户端可以自己准备一个本地的fallback(回退)回调,返回一个缺省值。 例如:(备用接口/缓存/mock数据) 。这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强,当然这也要看适合的业务场景。


Sentinel 分布式系统流量防卫系统

Sentinel 是什么

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。

源码地址:https://github.com/alibaba/Sentinel
官方文档:https://github.com/alibaba/Sentinel/wiki

Sentinel具有以下特征

● 丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。
● 完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
● 广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
● 完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑。例如定制规则管理、适配数据源等。


Sentinel和Hystrix对比
https://github.com/alibaba/Sentinel/wiki/Sentinel­%E4%B8%8E­Hystrix­%E7%9A%84%E5%AF%B9%E6%AF%94

Sentinel 快速开始


https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8
在官方文档中,定义的Sentinel进行资源保护的几个步骤:
1. 定义资源
2. 定义规则
3. 检验规则是否生效
 

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();
  }
}

Sentinel资源保护的方式

引入依赖

<dependency>
  <groupId>com.alibaba.csp</groupId>
 <artifactId>sentinel‐core</artifactId>
  <version>1.8.0</version>
</dependency>


 编写测试逻辑

@RestController
@Slf4j(topic = "HelloController")
public class HelloController {

    private static final String RESOURCE_NAME = "hello";

    @RequestMapping(value = "/hello")
    public String hello() {

        Entry entry = null;
        try {
            // 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
            entry = SphU.entry(RESOURCE_NAME);
            // 被保护的业务逻辑
            String str = "hello world";
            log.info("=====" + str);
            return str;
        } catch (BlockException e1) {
            // 资源访问阻止,被限流或被降级
            //进行相应的处理操作
            log.info("block!");
        } catch (Exception ex) {
            // 若需要配置降级规则,需要通过这种方式记录业务异常
            Tracer.traceEntry(ex, entry);
        } finally {
            if (entry != null) {
                entry.exit();
            }
        }
        return null;
    }

    /**
     * 定义流控规则
     */
    @PostConstruct
    private static void initFlowRules() {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        //设置受保护的资源
        rule.setResource(RESOURCE_NAME);

        // 设置流控规则 QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置受保护的资源阈值
        // Set limit QPS to 20.
        rule.setCount(1);
        rules.add(rule);
        // 加载配置好的规则
        FlowRuleManager.loadRules(rules);
    }
}


测试效果

缺点:
业务侵入性很强,需要在controller中写入非业务代码.
● 配置不灵活 若需要添加新的受保护资源 需要手动添加 init方法来添加流控规则

Sentinel 控制台

下载控制台 jar 包并在本地启动:可以参见 此处文档
https://github.com/alibaba/Sentinel/releases

启动控制台命令

java -jar sentinel‐dashboard‐1.8.0.jar


用户可以通过如下参数进行配置:
-Dsentinel.dashboard.auth.username=sentinel  用于指定控制台的登录用户名为  sentinel ;
-Dsentinel.dashboard.auth.password=123456  用于指定控制台的登录密码为  123456 ;如果省略这两个参数,默认用户和密码均为sentinel
-Dserver.servlet.session.timeout=7200  用于指定 Spring Boot 服务端 session 的过期时间,如  7200  表示 7200 秒; 60m  表示 60 分钟,默认为 30 分钟;

为了方便快捷启动可以在桌面创建.bat 文件

java -Dserver.port=8858 -Dsentinel.dashboard.auth.username=sentinel -Dsentinel.dashboard.auth.password=sentinel -jar D:\javaTools\Alibaba\sentinel\sentinel-dashboard-1.8.0.jar
paus

登入界面
访问http://127.0.0.1:8858 ,默认用户名密码: sentinel/sentinel

Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包,所以要确保客户端有访问量;


Spring Cloud Alibaba 整合 Sentinel


引入依赖

 <dependency>
     <groupId>com.alibaba.cloud</groupId>
     <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
 <!--加入actuator-->
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>


添加yml配置

为微服务设置 sentinel 控制台地址添加Sentinel后,需要暴露/actuator/sentinel 端点,而Springboot 默认是没有暴露该端点的,所以需要设置,测试 http://localhost:9110/actuator/sentinel

server:
  ## 启动端口
  port: 9110

spring:
  application:
    ## 注册服务名
    name: sentinel-order
  cloud:
    nacos:
      ## 注册中心地址
      discovery:
        server-addr: 127.0.0.1:8848
    ## sentinel config
    sentinel:
      transport:
        dashboard: localhost:8858

  main:
    allow-bean-definition-overriding: true

## 暴露端点
management:
  endpoints:
    web:
      exposure:
        include: '*'

在sentinel控制台中设置流控规则

sentinel 控制台中设置流控规则

  • 资源名:  接口的API  
  • 针对来源:  默认是default,当多个微服务都调用这个资源时,可以配置微服务名来对指定的微服务设置阈值
  • 阈值类型: 分为QPS和线程数 假设阈值为10
  • QPS类型: 只得是每秒访问接口的次数>10就进行限流
  • 线程数: 为接受请求该资源分配的线程数>10就进行限流

QPS 流控

测试: 因为QPS是1,所以1秒内多次访问会出现如下情形:

在访问 http://localhost:9110/actuator/sentinel 可以查看 flowRules (流控规则)

{
  "blockPage": null,
  "appName": "sentinel-order",
  "consoleServer": [
    {
      "r1": "localhost",
      "r2": 8858
    }
  ],
  "coldFactor": "3",
  "rules": {
    "systemRules": [
      
    ],
    "authorityRule": [
      
    ],
    "paramFlowRule": [
      
    ],
    "flowRules": [
      {
        "resource": "sayHello",
        "limitApp": "default",
        "grade": 1,
        "count": 1.0,
        "strategy": 0,
        "refResource": null,
        "controlBehavior": 0,
        "warmUpPeriodSec": 10,
        "maxQueueingTimeMs": 500,
        "clusterMode": false,
        "clusterConfig": {
          "flowId": null,
          "thresholdType": 0,
          "fallbackToLocalWhenFail": true,
          "strategy": 0,
          "sampleCount": 10,
          "windowIntervalMs": 1000
        }
      }
    ],
    "degradeRules": [
      
    ]
  },
  "metricsFileCharset": "UTF-8",
  "filter": {
    "order": -2147483648,
    "urlPatterns": [
      "/**"
    ],
    "enabled": true
  },
  "totalMetricsFileCount": 6,
  "datasource": {
    
  },
  "clientIp": "192.168.6.92",
  "clientPort": "8720",
  "logUsePid": false,
  "metricsFileSize": 52428800,
  "logDir": "C:\\Users\\user.DESKTOP-8A9L631\\logs\\csp\\","heartbeatIntervalMs":10000}

微服务和Sentinel Dashboard通信原理
Sentinel控制台与微服务端之间,实现了一套服务发现机制,集成了Sentinel的微服务都会将元数据传递给Sentinel控制台,架构图如下所示:
流控针对privoder   熔断降级 针对consumer

实时监控

监控接口的通过的QPS和拒绝的QPS


 簇点链路

用来显示微服务的所监控的API


流控规则

流量控制

其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,

以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。   

FlowRule     RT(响应时间)   1/0.2s  =5    

同一个资源可以创建多条限流规则。 FlowSlot  会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍历完毕。一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果。

  • resource:资源名,即限流规则的作用对象
  • count: 限流阈值
  • grade: 限流阈值类型(QPS 或并发线程数)
  • limitApp: 流控针对的调用来源,若为 default 则不区分调用来源
  • strategy: 调用关系限流策略
  • controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)

参考文档: https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6

限流阈值类型 

QPS(Query Per Second):每秒请求数,就是说服务器在一秒的时间内处理了多少个请求。

进入簇点链路选择具体的访问的API,然后点击流控按钮 


并发线程数

并发数控制用于保护业务线程池不被慢调用耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为应对太多线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。并发数控制通常在调用端进行配置。

我们让代码休眠2s钟,连续访问接口 http://127.0.0.1:9110/flowThread

 @GetMapping("/flowThread")
    public String flowThread() throws InterruptedException {
        TimeUnit.SECONDS.sleep(2);
        log.info("flowThread======>正常访问");
        return "正常访问";
    }

页面返回结果

BlockException 异常统一处理

Springwebmvc 接口资源限流入口在 HandlerInterceptor的实现类 AbstractSentinelInterceptor 的preHandle方法中,对异常的处理是 BlockExceptionHandler 的实现类

自定义BlockExceptionHandler 的实现类统一处理BlockException

/**
 * 自定义sentinel统一异常
 * @date: 2021/11/11 11:22
 */
@Component
@Slf4j(topic = "MyBlockExceptionHandler")
public class MyBlockExceptionHandler implements BlockExceptionHandler {

    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, BlockException e) throws Exception {

        log.info("handle BlockException:{}",e.getRule());
        RespCode respCode = null;
        if (e instanceof FlowException){
            respCode = RespCode.FLOW_EXCEPTION;
        }else if (e instanceof DegradeException) {
            respCode = RespCode.DEGRADE_EXCEPTION;
        }else if (e instanceof ParamFlowException) {
            respCode = RespCode.PARAM_FLOW_EXCEPTION;
        }else if (e instanceof SystemBlockException) {
            respCode = RespCode.SYSTEM_BLOCK_EXCEPTION;
        }else if (e instanceof AuthorityException) {
            respCode = RespCode.AUTHORITY_EXCEPTION;
        }

        response.setStatus(500);
        response.setCharacterEncoding("utf-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.getWriter().println(JSONUtil.parse(R.failed(respCode)));
        response.getWriter().flush();


    }
}

流控模式
基于调用关系的流量控制。调用关系包括调用方、被调用方;一个方法可能会调用其它方法,形成一个调用链路的层次关系。
直接
资源调用达到设置的阈值后直接被流控抛出异常
关联
当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_db 和 write_db 这两个资源分别代表数据库读写,我们可以给 read_db 设置限流规则来达到写优先的目的:设置 strategy 为 RuleConstant.STRATEGY_RELATE 同时设置 refResource 为 write_db。这样当写库操作过于频繁时,读数据的请求会被限流。

链路
根据调用链路入口限流。
NodeSelectorSlot 中记录了资源之间的调用链路,这些资源通过调用关系,相互之间构成一棵调用树。这棵树的根节点是一个名字为 machine-root 的虚拟节点,调用链的入口都是这个虚节点的子节点。
一棵典型的调用树如下图所示:

                   machine-root
                    /       \
                   /         \
             Entrance1     Entrance2
                 /             \
                /               \
      DefaultNode(nodeA)   DefaultNode(nodeA)

上图中来自入口 Entrance1 和 Entrance2 的请求都调用到了资源 NodeA,Sentinel 允许只根据某个入口的统计信息对资源限流。

getUser 是实现类业务方法

测试会发现链路规则不生效

注意,高版本此功能直接使用不生效,如何解决?


从1.6.3版本开始,Sentinel Web filter默认收敛所有URL的入口context,导致链路限流不生效。从1.7.0版本开始,官方在CommonFilter引入了WEB_CONTEXT_UNIFY参数,用于控制是否收敛context,将其配置为false即可根据不同的URL进行链路限流。


1.8.0 需要引入sentinel-web-servlet依赖

<!--- 解决流控链路不生效的问题-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-web-servlet</artifactId>
</dependency>

添加配置类,配置CommonFilter过滤器,指定WEB_CONTEXT_UNIFY=false,禁止收敛URL的入口context 


@Configuration
public class SentinelConfig {
    @Bean
    public FilterRegistrationBean sentinelFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new CommonFilter());
        registration.addUrlPatterns("/*");
        // 入口资源关闭聚合   解决流控链路不生效的问题
        registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
        registration.setName("sentinelFilter");
        registration.setOrder(1);
        return registration;
    }
}


再次测试链路规则,链路规则生效,但是出现异常


原因分析:

原因分析:

1. Sentinel流控规则的处理核心是 FlowSlot, 对getUser资源进行了限流保护,当请求QPS超过阈值2的时候,就会触发流控规则抛出FlowException异常

2. 对getUser资源保护的方式是@SentinelResource注解模式,会在对应的SentinelResourceAspect切面逻辑中处理BlockException类型的FlowException异常

(解决方案: 在@SentinelResource注解中指定blockHandler处理BlockException

@Override
    @SentinelResource(value="getUser",blockHandler = "blockHandlerGetUser")
    public String getUser() {
        return "查询用户";
    }

    public String blockHandlerGetUser(BlockException e) {
        return "流控用户";
    }

流控效果

当 QPS 超过某个阈值的时候,则采取措施进行流量控制。流量控制的效果包括以下几种:快速失败(直接拒绝)Warm Up(预热)、匀速排队(排队等待)。对应 FlowRule 中的 controlBehavior 字段。

快速失败

(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。

Warm Up

Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。

冷加载因子: codeFactor 默认是3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值

通常冷启动的过程系统允许通过的 QPS 曲线如下图所示

jmeter测试

查看实时监控,可以看到通过QPS存在缓慢增加的过程

匀速排队

匀速排队(`RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER`)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。

该方式的作用如下图所示:

这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。

注意:匀速排队模式暂时不支持 QPS > 1000 的场景。

降级规则

除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。

熔断降级规则说明

熔断降级规则(DegradeRule)包含下面几个重要的属性:

Field

说明

默认值

resource

资源名,即规则的作用对象

grade

熔断策略,支持慢调用比例/异常比例/异常数策略

慢调用比例

count

慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值

timeWindow

熔断时长,单位为 s

minRequestAmount

熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入)

5

statIntervalMs

统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入)

1000 ms

slowRatioThreshold

慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)

熔断策略

慢调用比例

慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。

异常比例

异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

查看实时监控,可以看到断路器熔断效果

异常数

异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。

配置降级规则

热点参数限流

何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。

注意:

  1. 热点规则需要使用@SentinelResource("resourceName")注解,否则不生效
  2. 参数必须是7种基本数据类型才会生效

测试用例

@RequestMapping("/info/{id}")
@SentinelResource(value = "userinfo",
                  blockHandlerClass = CommonBlockHandler.class,
                  blockHandler = "handleException2",
                  fallbackClass = CommonFallback.class,
                  fallback = "fallback"
                 )
public R info(@PathVariable("id") Integer id){
    UserEntity user = userService.getById(id);
    return R.ok().put("user", user);
}

配置热点参数规则

注意: 资源名必须是@SentinelResource(value="资源名")中 配置的资源名,热点规则依赖于注解

具体到参数值限流,配置参数值为3,限流阈值为1

测试:

http://localhost:8800/user/info/1 限流的阈值为3

http://localhost:8800/user/info/3 限流的阈值为1

系统规则

流控效果

当 QPS 超过某个阈值的时候,则采取措施进行流量控制。流量控制的效果包括以下几种:快速失败(直接拒绝)、Warm Up(预热)、匀速排队(排队等待)。对应 FlowRule 中的 controlBehavior 字段。

快速失败
(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。
Warm Up
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
冷加载因子: codeFactor 默认是3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。
通常冷启动的过程系统允许通过的 QPS 曲线如下图所示

openfeign 整合 sentinel

配置信息

feign:
  sentinel:
    # openfeign 整合 sentinel
    enabled: true

Feign 接口继承

@FeignClient(value="sentinel-stock",
        path = "/stock",
        contextId = "StockFeign",
        fallbackFactory = StockFeignServiceFallbackFactory.class
)
public interface StockFeign extends StockFeignService {


}

@Slf4j
@Component
public class StockFeignServiceFallbackFactory implements FallbackFactory<StockFeignService> {


    @Override
    public StockFeignService create(Throwable throwable) {

        return new StockFeignService() {

            @Override
            public String reduct(int orderId) {
                log.error("原因:{}", throwable.getMessage());
                return " 降级啦!!!";
            }
        };
    }
}


测试结果

Sentinel持久化模式

Sentinel规则的推送有下面三种模式:

推送模式

说明

优点

缺点

原始模式

API 将规则推送至客户端并直接更新到内存中,扩展写数据源

(WritableDataSource)

简单,无任何依赖

不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境

Pull 模式

扩展写数据源(WritableDataSource), 客户端主

动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件 等

简单,无任何依赖;规则持久化

不保证一致性;实时性不保证,拉取过

于频繁也可能会有性能问题。

Push 模式

扩展读数据源(ReadableDataSource),规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、

Zookeeper 等配置中心。这种方式有

更好的实时性和一致性保证。生产环境

下一般采用 push 模式的数据源。

规则持久化;一致性;快速

引入第三方

原始模式

如果不做任何修改,Dashboard 的推送规则方式是通过 API 将规则推送至客户端并直接更

新到内存中:

这种做法的好处是简单,无依赖;坏处是应用重启规则就会消失,仅用于简单测试,不能

用于生产环境。

拉模式

pull 模式的数据源(如本地文件、RDBMS 等)一般是可写入的。使用时需要在客户端注册数据源:将对应的读数据源注册至对应的 RuleManager,将写数据源注册至 transport 的WritableDataSourceRegistry  中。

 推模式

生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心、(ZooKeeper, Nacos, Apollo等等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是 配置中心控制台/Sentinel 控制台 → 配置中心 →Sentinel 数据源 → Sentinel,而不是经 Sentinel 数据源推送至配置中心。

这样的流程就非常清晰了:

基于Nacos配置中心控制台实现推送

官方demo:  sentinel­demo­nacos­datasource

引入依赖

<!--nacos 持久化-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

nacos 配置中心中配置流控规则

[
  {
    "clusterMode": false,
    "controlBehavior": 0,
    "count": 1.0,
    "grade": 1,
    "limitApp": "default",
    "maxQueueingTimeMs": 500,
    "resource": "sayHello",
    "strategy": 0,
    "warmUpPeriodSec": 10
  }
]

yml 中配置

server:
  ## 启动端口
  port: 9110

spring:
  application:
    ## 注册服务名
    name: sentinel-order
  cloud:
    nacos:
      ## 注册中心地址
      discovery:
        server-addr: 127.0.0.1:8848
    ## sentinel config
    sentinel:
      transport:
        dashboard: localhost:8858

      datasource:
        sayhello-flow‐rules: #名称自定义,唯一
          nacos:
            server-addr: localhost:8858
            username: nacos
            password: nacos
            dataId: order-sentinel-flow-rule
            rule-type: flow

  main:
    allow-bean-definition-overriding: true


feign:
  sentinel:
    # openfeign 整合 sentinel
    enabled: true

## 暴露端点
management:
  endpoints:
    web:
      exposure:
        include: '*'

 


 项目地址https://gitee.com/gaibianzlp/springcould-alibaba-example.githttps://gitee.com/gaibianzlp/springcould-alibaba-example.giticon-default.png?t=LA92https://gitee.com/gaibianzlp/springcould-alibaba-example.git点关注不迷路,觉得对你有帮助请给一个赞或者长按一键三连,谢谢!

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值