学习笔记【SpringCloud-第十三节: SpringCloud Alibaba Sentinel实现熔断与限流】

本文详细介绍了如何使用SpringCloud Alibaba Sentinel实现服务的熔断和限流。Sentinel作为一个独立组件,提供界面化的配置,包括流控规则、降级规则、热点规则和系统自适应限流。通过案例展示了Sentinel的配置和使用,包括@SentinelResource注解的应用,全局统一的限流兜底方法,以及Sentinel与OpenFeign的整合。此外,还讨论了规则的持久化,确保服务重启后规则依然有效。
摘要由CSDN通过智能技术生成

SpringCloud Alibaba Sentinel实现熔断与限流

Hystrix的不足

  • 需要我们程序员自己手工搭建监控平台
  • 没有一套web界面可一个我们进行更加细粒度化的配置流控、速率控制、服务熔断、服务降级

Sentinel的优势

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

在这里插入图片描述
https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D

下载安装后运行即可

启动成功后则是
在这里插入图片描述

案例

创建一个cloudalibaba-sentinel-service8401
写pom

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

<!--自定义的api包-->
<dependency>
    <groupId>com.huangyy.cloud2021</groupId>
    <artifactId>cloud-api-commons</artifactId>
    <version>${project.version}</version>
</dependency>

写yml

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        #默认是8719,加入被占用会自动从8719开始扫描,直至找到未被占用的端口
        port: 8719

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

主启动:

@SpringBootApplication
@EnableDiscoveryClient
public class MainApp8401 {
    public static void main(String[] args) {
        SpringApplication.run(MainApp8401.class,args);
    }
}

controller

@RestController
public class FlowLimitController {
    @GetMapping("/testA")
    public String testA(){
        return "----------A";
    }

    @GetMapping("/testB")
    public String testB(){
        return "----------B";
    }
}

Sentinel采用的是懒加载,需要执行一次才可
在这里插入图片描述

流控规则

在这里插入图片描述
流控:

  • QPS(每秒钟的请求数量):当调用该api的QPS达到阈值的时候,进行限流
  • 线程数:当调用该api的线程数达到阈值的时候,进行限流

直接:api达到限流条件时,直接限流
在这里插入图片描述
阈值1,一秒点一下没事,但一旦点多
在这里插入图片描述

关联:当关联的资源达到阈值时,就限流自己,B惹事,A挂了
在这里插入图片描述
链路:只记录指定连路上的流量(制定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】

快速失败:直接失败,抛异常

Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热市场,才达到设置的QPS阈值

排队等待:匀速排队

降级规则:

在这里插入图片描述

  • 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
  • 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
  • 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

热点规则:

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

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制

  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

用@SentinelResource注解来设置兜底方法:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-parameter-flow-control</artifactId>
    <version>x.y.z</version>
</dependency>
@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 "--------------testHotKey";
}
public String deal_testHotKey(String p1, String p2, BlockException exception){
    return "---------------deal_testHotKey,o(╥﹏╥)o";
}

在这里插入图片描述
参数索引

及参数组的下标,从0开始

假设若期望p1参数当它是某个特殊值时,它的限流值和平时不一样,如p1的值为5时,他的阈值可以达到200

在这里插入图片描述
注意:
@SentinelResource处理的是Sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理,但如果是运行时异常,@SentinelResource则不管,后面会讲

系统自适应限流

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

目的:

  • 保证系统不被拖垮
  • 在系统稳定的前提下,保持系统的吞吐量

系统规则支持以下的模式:

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

在这里插入图片描述


@SentinelResource

在8401多写一个controller类:
按资源名称限流

@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服务不可用");
    }    
}

运行方法一次后,会出现:
在这里插入图片描述
添加流控规则
在这里插入图片描述
当快速点击时,会返回自定义限流信息

额外问题:当关闭8401时,Sentinel控制台上的信息消失 ,后面会说

按url地址限流
在controller上加:

@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl(){
    return new CommonResult(200,"按url限流测试ok",new Payment(2020L,"serial002"));
    }

在这里插入图片描述
在这里插入图片描述

全局统一的限流兜底方法

创建一个CustomerBlockHandler类
在这里插入图片描述

public class CustomerBlockHandler {
    
    public static CommonResult handlerException(BlockException exception){
        return new CommonResult(4444,"按客户自定义1,global");
    }


    public static CommonResult handlerException2(BlockException exception){
        return new CommonResult(4444,"按客户自定义2,global");
    }
}

然后改controller,blockHandlerClass可指定类,blockHandler指定方法

@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
        blockHandlerClass = CustomerBlockHandler.class,blockHandler = "handlerException2")
public CommonResult customerBlockHandler(){
    return new CommonResult(200,"按客户自定义ok",new Payment(2020L,"serial002"));
}

在这里插入图片描述
最终会走2的方法

Sentinel主要有三个核心API

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

服务熔断功能

新建一个9003、9004
写pom

<dependency>
   <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.huangyy.cloud2021</groupId>
    <artifactId>cloud-api-commons</artifactId>
    <version>${project.version}</version>
</dependency>

写yml

server:
  port: 9003

spring:
  application:
    name: nacos-payment-provider
    
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

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

主启动

@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain9003.class,args);
    }
}

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,"34342154126265126446521"));
        hashMap.put(2L,new Payment(2L,"sdadasfafea33343aa34521"));
        hashMap.put(3L,new Payment(3L,"34adffaetret454q6446521"));
    }
    
    @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,servletPort:"+serverPort,payment);
        return result;
    }
}

9004和9003一样。
下面是消费者84,pom和上面一样

yml

server:
  port: 84

spring:
  application:
    name: nacos-order-consumer

  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719

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

主启动

@SpringBootApplication
@EnableDiscoveryClient
public class OrderNacosMain84 {
    public static void main(String[] args) {
        SpringApplication.run(OrderNacosMain84.class,args);
    }
}

config

@Configuration
public class ApplicationContextConfig {
    
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

controller

@RestController
@Slf4j
public class CircleBreakerController {
    
    @Value("service-url.nacos-user-service")
    private String service_URL;
    
    @Resource
    private RestTemplate restTemplate;
    
    @RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback")
    public CommonResult<Payment> fallback(@PathVariable("id") 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;
    }
}

到此,我们实现了轮询的效果
我们这里什么都没配,如果是4,这里是Error Page,非法参数异常
在这里插入图片描述
将注释改为:@SentinelResource(value = "fallback",fallback = "handlerFallback")
并写上兜底方法:

public CommonResult<Payment> handlerFallback(@PathVariable("id") Long id,Throwable e){
    Payment payment=new Payment(id,"null");
    return new CommonResult<>(444,"兜底异常handlerFallback,exception内容"+e.getMessage(),payment);
}

如果id>=4,则会去到友好提示

若将注解改为blockhandler:@SentinelResource(value = "fallback",blockHandler = "blockHandler")
兜底方法改为:

public CommonResult<Payment> blockHandler(@PathVariable("id") Long id, BlockException blockException){
    Payment payment=new Payment(id,"null");
    return new CommonResult<>(445,"blockHandler限流,exception内容"+blockException.getMessage(),payment);
}

设置降级规则
在这里插入图片描述
当id等于4时报错误页面,非法参数。当达到规则,会有友好界面

综上 :fallback管java业务异常,blockHandler管控制台配置违规

fallback和blockHandler都配置:@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler")

若配置流控:
在这里插入图片描述
最终结果,都会生效,若进入了限流,则走blockhandler

其他属性:

  • exceptionsToIgnore,排除某个异常类

Sentinel整合openfeign

84改pom,加上(之前 我加过了):

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

改yml,加:

feign:
  sentinel:
    enabled: true

主启动类上加注解:@EnableFeignClients

接口加注解:

@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService {

    @GetMapping(value = "/paymentSQL/{id}")
    CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);

}

实现类:

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

    }
}

controller:

@Resource
private PaymentService paymentService;

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

持久化

最后的问题,Sentinel的规则在服务器挂了后则会消失

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

这里用8401:
改pom(之前我加过 ):

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

改yml:加:

datasource:
        ds1:
          nacos:
            server-addr: localhost:8448
              dataId: cloudalibaba-sentinel-service
              groupID: DEFAULT_GROUP
              data-type: json
              rule-type: flow

在这里插入图片描述
在nacos中:
在这里插入图片描述

  • resource:资源名称
  • limitApp:来源应用
  • grade:阈值类型,0代表线程数,1表示QPS
  • count:单机阈值
  • strategy:流控模式,0表示直接,1表示关联,2表示链路;
  • controlBehavior:流控效果,0表示快速失败,1表示warm Up,2表示排队等待
  • clusterMode:是否集群
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值