SpringCloudAlibaba-Sentinel

1、什么是Sentinel:

Sentinel是阿里开源的项目,提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。
官网:https://github.com/alibaba/Sentinel/wiki

2012年,Sentinel诞生于阿里巴巴,其主要目标是流量控制。2013-2017年,Sentinel迅速发展,并成为阿里巴巴所有微服务的基本组成部分。 它已在6000多个应用程序中使用,涵盖了几乎所有核心电子商务场景。2018年,Sentinel演变为一个开源项目。2020年,Sentinel Golang发布。


2、Sentinel 具有以下特征:

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

完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快
速地定制逻辑。例如定制规则管理、适配动态数据源等。

Sentinel主要特性:

3、 Sentinel 的使用

        获取 Sentinel 控制台

您可以从 release 页面 下载最新版本的控制台 jar 包。

您也可以从最新版本的源码自行构建 Sentinel 控制台:

  • 下载 控制台 工程
  • 使用以下命令将代码打包成一个 fat jar: mvn clean package
  • 启动 Sentinel 控制台需要 JDK 版本为 1.8 及以上版本。

sentinel服务启动:

java -Dserver.port=8858 -Dsentinel.dashboard.auth.username=tianzhen -Dsentinel.dashboard.auth.password=123456 -jar sentinel-dashboard-1.8.0.jar

使用如上命令启动控制台:
其中 - Dserver.port=8888 用于指定 Sentinel 控制台端口为 8888 。
从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,本上面代码用户名为tianzhen和密码为123456可以参考 鉴权模块文档 配置用户名和密码。

查看效果:

4、整合springcloudAlibaba

项目概述: 小编在这里准备了一个独立的项目

(1)导入依赖 sentinel启动器的依赖

<!--sentinel启动器-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

(2)配置yml文件

server:
  port: 8060
  #应用名称(nacos会将该名称当作服务名称)
spring:
  application:
    name: order-sentinel
  cloud:
    sentinel:
     transport:
       dashboard: 127.0.0.1:8858server:
  port: 8060
  #应用名称(nacos会将该名称当作服务名称)
spring:
  application:
    name: order-sentinel
  cloud:
    sentinel:
     transport:
       dashboard: 127.0.0.1:8858

当项目启动时,会将该服务自动出现在sentinel控制台可进行一系列设置限流等规则

 

至此springcloud整合完sentinel 

5、Sentinel 流控(限流)

流量控制(Flow Control),原理是监控应用流量的QPS或并发线程数等指标,当达到指定阈值时对流量进行控制,避免系统被瞬时的流量高峰冲垮,保障应用高可用性。

网页限流规则配置

阈值类型/单机阈值:

1.QPS:每秒请求数,当前调用该api的QPS到达阈值的时候进行限流

2.线程数:当调用该api的线程数到达阈值的时候,进行限流

1.选择QPS,直接,快速失败,单机阈值为2。

测试

频繁刷新请求,1秒访问2次请求,正常,超过设置的阈值,将报默认的错误。 

 再次的1秒访问2次请求,访问正常。超过2次,访问异常

 通过一个注解@SentinelResource来指定返回啥

   @RequestMapping("/flow")
    @SentinelResource(value = "flow",blockHandler = "flowBlockHandler")
    public String flow(){
        return "正常访问";
    }


    public String flowBlockHandler(BlockException e){
        return "流控";
    }

        测试效果

 2.选择,直接线程数,快速失败,单机阈值为2。

这里使用俩个浏览器来实现多个线程数,并让方法进行休息5秒钟进入

    public String flowBlockHandler(BlockException e){
        return "流控";
    }



    @RequestMapping("/flowThread")
    @SentinelResource(value = "flow",blockHandler = "flowBlockHandler")
    public String flowThread() throws InterruptedException {
        TimeUnit.SECONDS.sleep(5);
        return "正常访问";
    }

 

 当第一个浏览器访问时,第二个浏览器访问就会进行限流

  3.BlockException异常统一处理    统一返回结果处理

        限流配置

package com.xinzhi.exception;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowArgument;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xinzhi.domain.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {
    Logger log = LoggerFactory.getLogger(this.getClass());
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, BlockException e) throws Exception {
        //getRule() 资源 规则的详细信息
        log.info("BlockExceptionHandler BlockException============="+e.getRule());
        Result r = null;
        if (e instanceof FlowException){
            r = Result.error(100,"接口被限流了");
        }else if (e instanceof DegradeException){
            r = Result.error(101,"服务降级了");
        }else if (e instanceof ParamFlowException){
            r = Result.error(102,"热点参数限流了");
        }else if (e instanceof SystemBlockException){
            r = Result.error(103,"触发系统保护规则了");
        }else if (e instanceof AuthorityException){
            r = Result.error(104,"授权规则不通过");
        }
        //返回json数据
        response.setStatus(500);
        response.setCharacterEncoding("utf-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        new ObjectMapper().writeValue(response.getWriter(),r);

    }
}

返回json数据类

package com.xinzhi.domain;

public class Result<T> {
    private Integer code;
    private String msg;
    private T data;

    public Result() {
    }

    public Result(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
    public Result(Integer code, String msg ){
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
    public static Result error(Integer code, String msg){
        return new Result(code,msg);
    }
}
删除这个注解 @SentinelResource(value = "flow",blockHandler = "flowBlockHandler")

测试效果

关联模式 :

调用关系包括调用方、被调用方;一个方法又可能会调用其它方法,形成一个调用链路的层次关系。

当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。

比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢.

举例来说  一个生成订单,一个查询订单,我们可以给查询订单设置限流规则来达到生成订单优先的目的

 @RequestMapping("/add")
    public String add(){
        System.out.println("下单成功");
        return "生成订单";
    }
    @RequestMapping("/get")
    public  String get(){
        return "查询订单";
    }

 使用Jmeter进行测试1秒对生成订单访问3次时,当你打开查询订单接口时就会被限流 

链路类型的关联也类似,就不再演示了。多个请求调用同一微服务。

Warm up(预热)模式 

        

当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。即如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值。Warm Up(冷启动,预热)模式就是为了实现这个目的的。

默认 coldFactor 为 3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。

防止激增流量进来打垮冷系统所以让流量 慢慢访问 防止击穿穿透  存入缓存

公式: 冷加载因子  codeFactor默认是3     即qps从阈值/3慢慢递增

 

根据sentinel控制台监控,绿线代表通过个数可发现通过个数慢慢成为10个

如秒杀系统在开启瞬间,会有很多流量上来,很可能把系统打死,预热方式就是为了保护系统,可慢慢的把流量放进来,慢慢的把阈值增长到设置的阈值。

通过jmeter进行测试

排队等待模式

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

 

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

某瞬时来了大流量的请求, 而如果此时要处理所有请求,很可能会导致系统负载过高,影响稳定性。但其实可能后面几秒之内都没有消息投递,若直接把多余的消息丢掉则没有充分利用系统处理消息的能力。Sentinel的Rate Limiter模式能在某一段时间间隔内以匀速方式处理这样的请求, 充分利用系统的处理能力, 也就是削峰填谷, 保证资源的稳定性.

Sentinel会以固定的间隔时间让请求通过, 访问资源。当请求到来的时候,如果当前请求距离上个通过的请求通过的时间间隔不小于预设值,则让当前请求通过;否则,计算当前请求的预期通过时间,如果该请求的预期通过时间小于规则预设的 timeout 时间,则该请求会等待直到预设时间到来通过;反之,则马上抛出阻塞异常。

使用Sentinel的这种策略, 简单点说, 就是使用一个时间段(比如20s的时间)处理某一瞬时产生的大量请求, 起到一个削峰填谷的作用, 从而充分利用系统的处理能力,
 

解决脉冲流量

 

 

 效果:

有了排队等待以后每秒进5个, 但我们Jmeter设置1秒进10,剩下5个在后面等待,最多等待5秒,当然我们这里实例1秒可以处理好多个,所以排队等待的5个里面都执行完成了,这就是消峰填谷

 6、Sentinel 熔断降级(降级)

 什么是熔断降级

熔断降级对调用链路中不稳定的资源进行熔断降级是保障高可用的重要措施之一。

由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积。Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)

  几种熔断策略

1.平均响应时间 (DEGRADE_GRADE_RT) 慢调用比例:

      当资源的平均响应时间超过阈值(DegradeRule 中的 count,以 ms 为单位)之后,资源进入准降级状态。如果接下来 1s 内持续进入 5 个请求(即 QPS >= 5),它们的 RT 都持续超过这个阈值,那么在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException)。

      注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置。

2.异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):

当资源的每秒异常总数占通过量的比值超过阈值(DegradeRule 中的 count)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地返回。

异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

3.异常数 (DEGRADE_GRADE_EXCEPTION_COUNT):

当资源近 1 分钟的异常数目超过阈值之后会进行熔断。

注意由于统计时间窗口是分钟级别的,若 timeWindow 小于 60s,则结束熔断状态后仍可能再进入熔断状态。

4. 测试结果

sentinel整合openfeign 进行降级

1.添加依赖

<dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
            <!--添加openfeign依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
<!--添加sentinel依赖整合openfeign-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

 2.添加yml配置

feign:
  sentinel:
    enabled: true

3.添加实现openfeign接口的实现类

package com.xinzhi.feign;

import org.springframework.stereotype.Component;

@Component
public class StockFeignServiceFallback implements  StockfeignService{
    
    @Override
    public String reduct2() {
        return "降级了!!!";
    }
}

4.在服务提供者tock-1服务中添加异常

  @RequestMapping("/reduct2")
    public String reduct2() {
        int a = 1/0;
        System.out.println("扣减库存");
        return "Hello 扣减库存"+port;
    }

5.在openfeign接口中注解添加

@FeignClient(value = "tock-service",path = "/stock",fallback = StockFeignServiceFallback.class)

实现效果对比

7、Sentinel 热点参数限流(限流) 

热点参数限流 热点识别流控

何为热点

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

 单机阈值:1.假设大部分值都是热点参数,那单机阈值主要针对热点参数进行配置,后续额外针对普通参数值就行流控

                    2.假设大部分值都是普通流量,那单机阈值主要针对普通参数进行配置,后续额外针对热点参数值就行流控

 创建测试接口对参数id进行流控

 /**
     * 热点规则,必须使用@SentinelResource
     * @param id
     * @return
     */

    @RequestMapping("/get/{id}")
    @SentinelResource(value = "getById",blockHandler = "HotBlockHandler")
    public String getById(@PathVariable("id") Integer id){
        System.out.println("正常访问");
        return "正常访问";
    }
    public String HotBlockHandler (@PathVariable("id") Integer id,BlockException e) throws InterruptedException{
        return "热点异常处理";
    }

点击编辑可针对热点id进行qps流控 

测试

 8、Sentinel 持久化规则

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

特性
规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源

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

缺点
需要引入第三方依赖

1.导入依赖

  <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

2.配置yml 

spring:
  application:
    name: order-sentinel
  cloud:
    sentinel:
     transport:
       dashboard: 127.0.0.1:8858
     web-context-unify: false #默认将调用链路收敛了
     datasource:
       flow-rule: #可以自定义
         nacos:
          server-addr: 192.168.195.128:8847
          username: nacos
          password: nacos
          dataId: order-sentinel-flow-rule
          rule-type: flow

3.nacos中添加配置文件

[
    {

        "resource": "/order/flow",
        "controlBehavior": 0,
        "count": 2,
        "grade": 1,
        "limitApp": "default",
        "startegy": 0
    }
]

4.测试效果

 添加成功

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值