【SpringCloud】11-Sentinel实现熔断与限流

:语雀原文链接


Sentinel概述

在这里插入图片描述


Sentinel:分布式系统的流量防卫兵

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

单独一个组件,可以独立出来
直接界面化的细粒度统一配置

Sentinel下载安装运行

下载地址:https://github.com/alibaba/Sentinel/releases/tag/1.7.0 ,下载jar版本

Sentinel的组件由2部分组成,后台和前台8080
Sentinel分为两个部分:

  • 核心库(Java客户端)不依赖任何框架/库,能够运行所有的Java环境,同时对Dubbo/Spring Cloud等框架也有很好的支持
  • 控制台(Dashboard)基于Spring Boot开发,打包后可以直接运行,不需要额外的Tomcat等应用容器;

Sentinel结构化演示工程

8401可以注册进8848微服务控制

  1. 启动Nacos8848成功
  2. 新建sentinel8401Module
    1. pom主要依赖:
<!--SpringCloud ailibaba nacos -->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel -->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

b.配置文件yml


server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos服务注册中心地址
    sentinel:
      transport:
        dashboard: localhost:8080 #配置Sentinel dashboard地址
        port: 8719
#      datasource:
#        ds1:
#          nacos:
#            server-addr: localhost:8848
#            dataId: cloudalibaba-sentinel-service
#            groupId: DEFAULT_GROUP
#            data-type: json
#            rule-type: flow

management:
  endpoints:
    web:
      exposure:
        include: '*'

#feign:
#  sentinel:
#    enabled: true # 激活Sentinel对Feign的支持

c. 项目主启动文件
d.编写一个简单的controller

  1. 启动Sentinel8080
  2. 启动微服务8401
  3. 启动8401微服务查看sentinel控制台

Sentinel流控

简单概述
  • 资源名:唯一名称,默认请求路径
  • 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
  • 阈值类型/单机阈值
    • QPS(每秒请求的数量):当调用该api的QPS达到阈值的时候,进行限流
    • 线程数:当调用该api的线程数达到阈值的时候,进行限流
  • 是否集群:不需要集群
  • 流控模式:
    • 直接:api达到限流条件时,直接限流
    • 关联:当关联资源达到阈值时,就限流自己
    • 链路:值记录指定链路上的流量(指定资源从入口进来的流量,如果达到阈值,就进行限流)
  • 流控效果:
    • 快速失败:直接失败,抛异常
    • Warm Up(预热):根据codeFactory(冷加载因子,默认3)的值,用阈值/codeFactory。经过预热时长,才达到设置的QPS阈值;
    • 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效

具体使用:

QPS 直接 快速失败

在这里插入图片描述

关联 快速失败
当与A关联的资源B达到阈值的时候,限流A自己
例如支付接口达到阈值的时候就限流下订单的接口

在这里插入图片描述

预热

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

在这里插入图片描述

降级规则

降级概述

Sentinel熔断降级会在链路中某个资源出现不稳定状态事务(例如调用超时或者异常比例升高),对这个资源的调用进行限制,让请求快速失效,避免影响到其它的资源而导致级联错误。

当资源被降级后,在接下来的降级时间窗口内,对该资源的调用都自动熔断(默认行为是抛出DegradeException)

RT(平均响应时间,秒级)
平均响应时间 超出阈值 且 在时间窗口内通过的请求>=5 ,两个条件同时满足后触发降级
窗口期过后关闭断路器
RT最大4900(更大的需要通过-Dscp.sentinel.statistic.max.rt=XXXX才能生效)

异常比例(秒级)
QPS>=5且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级

异常数(分钟级)
异常数(分钟统计)超过阈值时,触发降级,时间窗口结束后,关闭降级

具体使用

RT
如果200毫秒还没处理完,在未来1s钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,
在这里插入图片描述

异常比例
设置异常数为百分之二十的时候,在未来的时间窗口1s内开启熔断器

在这里插入图片描述

异常数
当资源近一分钟的异常数超过阈值之后会进行熔断,注意偶遇统计时间窗口是分钟级别的,若timeWindow小于60s,则结束熔断状态后仍可能再进入熔断状态;
由于异常数是分钟级的,时间窗口一定要大于等于60s,否则结束熔断之后仍可能再进入熔断;

在这里插入图片描述

热点Key限流

概述

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

@HystrixCommand=@SentinelResource

编码实现:

在controller添加热点key异常处理:


 @GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
    public String testHotKey(@RequestParam(value ="p1",required = false ) String p1,
                             @RequestParam(value ="p2",required = false )String p2){

        return "#####testHostKey###############";
    }

    //处理如果违背了sentinel配置规则的,则采用以下方法进行兜底
    public String deal_testHotKey(String p1, String p2, BlockException exception){
            return "$$$$$$deal_testHotKey$$$$$$$$   o(╥﹏╥)o";

    }

在sentinel中进行配置:
在这里插入图片描述


当访问资源(资源名称为@SentinelResource里面的value值)的下标为0(即第一个元素)的QPS阈值达到1s一次以上的时候,就会进行限流

如果使用热点限流规则,就必须配置兜底方法,否则将会返回给用户不友好的错误页面,即在@SentinelResource注解中一定要配blockHandler方法;

参数例外项

我们期望p1参数当它是某个特殊值的时候,它的限流值和平时的不一样,例如当p1 的值为5时,它的QPS的值可以达到200,比较灵活
参数必须要是基本数据类型或者String

在这里插入图片描述

@SentinelResource
处理的是Sentinel控制台配置的违规情况,有blockHandle配置的兜底处理

RuntimeException
int age=1/0,这个是java运行时报出的运行时异常RuntimeException。@SentinelResource不管

系统规则


Snetinel系统自适应限流从整体维度对应用入口流量进行控制,结合应用的Load、CPU使用率、总体平均RT、入口QTS和并发线程数等几个维度的监控指标,通过子适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性

也就是之前的基础上,在整个系统外部进行一个配置

  • Load自适应(仅对LInux/Unix机器生效):系统的load1最为启发指标,进行自适应系统保护。当系统load超过设定的启发值,且系统当前的并发线程数超过估算的系统容量的时候才会触发系统保护(BBR阶段)。系统容量由系统的maxQps * minRt估算得出。设定参考值一般是CPU cores * 2.5
  • CPU usage(1.5.0+版本):当系统的CPU使用率超过阈值即触发系统保护(取值范围0.0-1),比较灵敏
  • 平均RT:当单台机器上所有 入口流量的平均RT达到阈值即触发系统保护,单位是毫秒
  • 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护
  • 入口QPS:当单台机器上的所有入口流量的QPS达到阈值即触发系统保护

SentinelResource配置

按资源名称限流+后续处理
  1. 启动nacos成功
  2. 启动Sentinel成功
  3. 修改cloudalibaba-Sentinel-service8401
    1. 新增一个业务类RateLimitController
@RestController
public class RateLimitController {
    @GetMapping("/byResource")
    @SentinelResource(value = "/byResource",blockHandler = "handleException")
    public CommonResult byResource(){
        return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001"));

    }
    
    public CommonResult handleException(BlockException exception){
        return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务不可用")
    }
}
  1. 当访问该接口违背控制台限流规则之后,将调用该兜底方法
按Url地址限流+后续处理

在RateLimitController中新增内容:

    @GetMapping("/byUrl")
    @SentinelResource(value = "byUrl")
    public CommonResult byUrl() {
        return new CommonResult(200, "按URL地址限流测试OK", new Payment(2020L, "serial002"));

    }

限流规则资源名可以直接只用/byUrl ,唯一即可

减少代码耦合膨胀,兜底方案完善
  1. 创建CustomerBlockHandler类终于自定义限流处理逻辑(类中定义l两个异常处理方法)
public class customerBlockhandler {

    public static CommonResult handlerExceptoon(BlockException blockException){
        return new CommonResult(444, "按客户自定义  Global handlerExceptoon----1");
    }

    public static CommonResult handlerExceptoon2(BlockException blockException){
        return new CommonResult(444, "按客户自定义  Global handlerExceptoon----2");
    }
}
  1. 在controller中新建方法对全局异常处理中的方法进行引用(这里引用了类中第二个兜底的方法)
    @GetMapping("/customerBlockhandler")
    @SentinelResource(value = "customerBlockhandler",
            blockHandlerClass = customerBlockhandler.class,
            blockHandler ="handlerExceptoon2")
    public CommonResult customerBlockhandler() {
        return new CommonResult(200, "按客户自定义测试OK", new Payment(2020L, "serial002"));

    }

更多注解属性的说明

Sentine主要用三个核心API:

  • SphU定义资源
  • Trace定义统计
  • ContextUtil定义了上下文

Sentinel服务熔断

熔断是发生在客户端,降级发生在配置端

环境搭建

新建cloudalibaba-provider-payment9003和cloudalibaba-provider-payment9004

  1. pom文件(主要依赖):
<!--SpringCloud ailibaba nacos -->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
  1. 配置文件application.yml
server:
port: 9003

spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址

management:
endpoints:
web:
exposure:
        include: '*'
  1. 主启动类
  2. controller业务逻辑类
@RestController
public class PaymentController
{
    @Value("${server.port}")
    private String serverPort;
    
    public static HashMap<Long, Payment> hashMap = new HashMap<>();
    static
    {
        hashMap.put(1L,new Payment(1L,"28a8c1e3bc2742d8848569891fb42181"));
        hashMap.put(2L,new Payment(2L,"bba8c1e3bc2742d8848569891ac32182"));
        hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183"));
    }
    
    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id)
    {
        Payment payment = hashMap.get(id);
        CommonResult<Payment> result = new CommonResult(200,"from mysql,serverPort:  "+serverPort,payment);
        return result;
    }
    
    
    
}

新建服务提供者cloudalibaba-condumer-nacos-order84

  1. pom文件(主要依赖)
<!--SpringCloud openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--SpringCloud ailibaba sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
  1. 配置文件application.yml
server:
  port: 84


spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        #配置Sentinel dashboard地址
        dashboard: localhost:8080
        #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
        port: 8719

#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
  nacos-user-service: http://nacos-payment-provider

# 激活Sentinel对Feign的支持
feign:
  sentinel:
    enabled: true
  1. 主启动类
  2. controller业务逻辑类
@RestController
@Slf4j
public class CircleBreakerController
{
    public static final String SERVICE_URL = "http://nacos-payment-provider";
    
    @Resource
    private RestTemplate restTemplate;
    
    @RequestMapping("/consumer/fallback/{id}")
    //    @SentinelResource(value = "fallback") //没有配置
    //    @SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常
    //@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规
    @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",
                      exceptionsToIgnore = {IllegalArgumentException.class})
    public CommonResult<Payment> fallback(@PathVariable Long id)
    {
        CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id, CommonResult.class,id);
        
        if (id == 4) {
            throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
        }else if (result.getData() == null) {
            throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
        }
        
        return result;
    }
    //本例是fallback
    public CommonResult handlerFallback(@PathVariable Long id, Throwable e) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);
    }
    //本例是blockHandler
    public CommonResult blockHandler(@PathVariable  Long id, BlockException blockException) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);
    }
    
    //==================OpenFeign
    @Resource
    private PaymentService paymentService;
    
    @GetMapping(value = "/consumer/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id)
    {
        return paymentService.paymentSQL(id);
    }
}

Sentinel服务熔断各种情况
  1. 无配置:即使用@SentinelResource注解中只有一个value属性时,未配置fallback属性和blockHandler属性:java异常以error页面非常不友好的形式直接显示给前端用户
  2. 只配置fallback:后端java异常将会以fallback配置好的方法进行数据返回
  3. 只配置blockHandler:这时候需要使用sentinel增加流控,当未达到流控要求的时候,java异常不会使用兜底的方法,而是会以error页面 形式反馈给用户,当达到流控要求的时候,将会使用定义的兜底方法;
  4. fallback和blockHandler都配置:如果被降级限流而抛出BlockException时只会 进入blockHandler处理逻辑;

服务熔断OpenFeign

一般ribbon和openfigen二者取其一,ribbon需要使用restTemplate,openfigen直接调用
Sentinel整合openfeign,ribbon,fallback

  1. 在8401的基础上新建paymentService接口
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService
{
    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}

2.新建paymentService实现类:

@Component
public class PaymentFallbackService implements PaymentService
{
    @Override
    public CommonResult<Payment> paymentSQL(Long id)
    {
        return new CommonResult<>(44444,"服务降级返回,---PaymentFallbackService",new Payment(id,"errorSerial"));
    }
}

  1. 在controller新建接口:
@Resource
    private PaymentService paymentService;

    @GetMapping(value = "/consumer/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id)
    {
        return paymentService.paymentSQL(id);
    }
  1. 测试

Sentinel持久化规则

在服务关闭后,配置进Sentinel的规则都会消失,所以生产规则需要将配置规则持久化

持久化方式

将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效;

步骤
  1. 修改cloudalibaba-sentinel-service8401
  2. pom文件添加依赖
<!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
  1. 配置文件中添加nacos数据源配置
  sentinel:
      transport:
        dashboard: localhost:8080 #配置Sentinel dashboard地址
        port: 8719
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: cloudalibaba-sentinel-service
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow
  1. 在nacos中新建配置文件,可以按照下图进行配置:

在这里插入图片描述

相当于将流控写入nacos的库里面,服务启动会根据dataid找对应的流控规则,找到就给加到sentinel中

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PoJo123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值