Spring Cloud Sentinel配置

时间:2024-10-19 08:26:41

Spring Cloud Sentinel

文章目录

      • Spring Cloud Sentinel
        • 1. Sentinel Dashboard 启动
        • 2. Spring Cloud 客户端配置
        • 3. Sentinel Dashboard 限流配置
          • 流控模式
            • 直连
            • 关联
            • 链路
          • 流控规则
            • 快速失败
            • Warm Up
            • 排队等待
        • 4. Sentinel Dashboard 熔断配置
        • 5. Sentinel Dashboard 热点配置

1. Sentinel Dashboard 启动
  1. 下载sentinel dashboard jar
  2. sentinel dashboard启动在8889端口上
    java -jar -Dserver.port=8889 sentinel-dashboard-1.8.8.jar
    在这里插入图片描述
  3. 打开dashboard页面
    http://localhost:8889
    默认用户名密码都是sentinel
2. Spring Cloud 客户端配置
  1. 引入sentinel客户端
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
  1. application.yaml文件添加sentinel客户端配置
spring:
  application:
    name: stock-service
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8889 # dashboard服务所在的地址跟ip
        client-ip: 192.168.0.5 # 这里是程序所在的客户端ip,使用ipconfig命令查看,不配置无法再dashboard上显示当前服务
  1. 打开dashboard页面,由于Sentinel Dashboard是惰性加载,此时簇点链路不会有服务的信息跟服务的接口信息,需要我们调用接口,才会在上面显示
    在这里插入图片描述
  2. 调用order-service的/order/add接口后可以看到比上图增加了一个/order/add接口. 在这里插入图片描述
3. Sentinel Dashboard 限流配置
流控模式

流控模式有三种,直连,关联跟链路
在这里插入图片描述

直连

限制并发过高问题,防止当前程序崩溃,限制当前接口的qps,设置为5,可以在流控规则中看到
在这里插入图片描述
发起压测,并发10个,只能通过5个,不限流的话10个请求时都可以通过的
在这里插入图片描述


关联

关联用于两个接口间的资源竞争
这里有两个接口/order/{productId}跟/order/add接口,一个接口是订单查询,一个接口是订单新增后,因为两者都要去操作数据库的订单表,所以可能存在数据库资源抢占问题,一般认为订单的新增接口优先级比订单查询接口高,所以当/order/add的QPS达到某个阈值时,对优先级低的/order/{productId}接口进行限流,这里设置当/order/add的QPS达到5时(只是一个监控,不会限制add接口的QPS),/order/{productId}接口进行限流
在这里插入图片描述


同时发起/order/add接口跟/order/{productId}接口的压测,add接口都成功,QPS是8,不会被上图设置的QPS=5限制住,/order/{productId}则是因为add接口的QPS达到8,直接限制了接口,拒绝请求

/order/add并发:
/order/add关联并发情况


/order/{productId}并发:
/order/{productId}关联并发情况


链路

链路主要是资源分配,对同一个资源有多个入口的情况下,限制该资源的访问

  1. Service类通过@SentinelResource定义一个方法为资源,/order/add接口跟/order/{productId}接口都会调用到这个服务,add是新增后查询,/order/{productId}是直接查询
    @Override
    @SentinelResource(value = "order")
    public TProduct getProduct(String productId) {
        return productMapper.selectById(productId);
    }
  1. 客户端的spring.cloud.sentinel.web-context-unify的默认值是true,资源是聚合的,这里的入口资源是sentinel_web_servlet_context,顶层的入口,统一对资源进行限流,两个接口共享资源的QPS,同一时间两个接口并发的总QPS为5
    资源共享链路配置

同时发起/order/add接口跟/order/{productId}并发请求10s,可以看到 两个接口的成功数(5+46)/并发时间10s = 5QPS
/order/add并发:
/order/add接口链路并发情况


/order/{productId}并发:
/order/{productId}接口链路并发情况


  1. spring.cloud.sentinel.web-context-unify设置为false,则入口资源可以分别单独设置为/order/add接口跟/order/{productId},将对不同接口进行限流
spring:
  application:
    name: order-service
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8889
        client-ip: 192.168.0.5
      web-context-unify: false

/order/{productId}链路并发设置
QPS大概是5
/order/{productId}链路并发情况


流控规则

流控规则也有三种快速失败Warm Up排队等待

快速失败

快速失败就是之前例子展示的,达到阈值时,拒绝请求:
在这里插入图片描述


Warm Up

Warm Up代表一个预热过程,使用场景是刚启动时需要一些耗时的准备工作的接口,例如刚开始需要跟其他系统建立长连接的接口,待接口逐渐建立长连接时,接口能处理的请求量也逐渐上升

客户端流控配置有个coldFactor,默认值是3,例如我们设置的PQS是12,预热时间是4s,一开始访问请求时,能达到的 起始QPS=设置的QPS/coldFactor=12/3=4,之后在4s内QPS逐渐上升到12

spring.cloud.sentinel.flow.coldFactor:
在这里插入图片描述
Warm Up流控配置:
在这里插入图片描述


接口进行4s的压测,预测下请求成功数,这里可以看成是求梯形的面积,(上底是4QPS+下底12QPS)*4秒 / 2 = 32,压测结果成功的请求数是34,差不多:
在这里插入图片描述


查看dashboard的接口监控,可以看到接口在4s内,QPS由起始的4达到最终的12:
在这里插入图片描述


排队等待

排队等待就是让当前无法处理的请求进入等待队列,等待超时时间,等待期间如果可以处理则处理,等待超时则拒绝,可以应对某个时间段峰值请求,当某个时间段有大量请求进入,又不想抛弃这些请求,就让这些请求进入队列等待处理或超时。

等待队列设置,QPS为5,等待时间为2s:
在这里插入图片描述


接口并发测试如下
10个并发请求:

  1. 第1s进来10个请求,按照5个5个划分为A、B两组请求,每组5个请求
  2. 第1s,A组的5个请求被处理,B组等待1s
  3. 第2s,A组5个已处理完成,B组5个还未超时,则再处理B组的5个请求
  4. 预测10个请求都被处理完成

队列等待10个并发请求


接下来是并发15个请求

  1. 第1s进来15个请求,按照5个5个划分为A、B、C三组请求,每组5个请求
  2. 第1s,A组的5个请求被处理,B组跟C组共10个等待1s
  3. 第2s,A组5个已处理完成,B组5个还未超时,则再处理B组的5个请求,C组的5个请求等待1s
  4. 第3s,A组跟B组共10个请求已处理完成,但是此时C组已经等待了2s,达到了超时时间,C组不会被处理
  5. 预测大约10个请求被处理,5个请求被拒绝
    队列等待15个并发请求

4. Sentinel Dashboard 熔断配置

熔断是上级微服务调用下级微服务时,下级微服务如果出现超时或者异常达到一定比例时,则下次调用下级接口时,需要在上级微服务层面上不去调用下级微服务,所以上级微服务的调用框架需要跟Sentinel有所关联,这里微服务调用框架使用的Feign,需要开启Feign跟Sentinel之间的联系,在Feign调用其他微服务接口前,使用Sentinel进行判断是否熔断。

  1. application.yaml中配置feign开启sentinel
feign:
  sentinel:
    enabled: true

这样Sentinel就会使用SpringBoot的自动装配修改Feign.Builder,往Feign相关代码中加入Sentinel相关代码

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ SphU.class, Feign.class })
public class SentinelFeignAutoConfiguration {

	@Bean
	@Scope("prototype")
	@ConditionalOnMissingBean
	@ConditionalOnProperty(name = "feign.sentinel.enabled")
	public Feign.Builder feignSentinelBuilder() {
		return SentinelFeign.builder();
	}

}
  1. Sentinel 的熔断配置
    客户端添加了Feign跟Sentinel相关联的配置后,重启再调用接口,可以看到下次服务的资源
    在这里插入图片描述

对该资源添加熔断配置:

  • RT配置200ms,代表一个请求的响应时长超出200ms就代表着慢调用
  • 比例阈值0.1代表统计时间内10%的请求超过200ms就进行熔断
  • 最小请求数5代表统计时长内如果有超过5个请求,才会触发该熔断器,进行阈值判断
  • 熔断时长代表熔断器生效时,10s内无法调用下级接口
  • 统计时长就是统计这段时间内的请求,这里是统计1s内的请求
    在这里插入图片描述
  1. 程序模拟上级服务是OrderService,下级服务是StockService集群,其中StockService2修改接口内容,调用该接口时,当前线程会sleep 300ms,这样就超过了上面配置的RT时间
X
OrderService
StockService1
StockService2
	@ResponseBody
    @GetMapping("reduceProduct")
    public String reduce(@RequestParam("productId") String productId, @RequestParam("num") int num) {
        System.out.println("Current Time:" + LocalDateTime.now() + " Reduce Product:" + productId + " for:" + num);
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "SUCCESS";
    }
  1. 并发请求
    并发10次,由于Feign使用的轮询策略,所以OrderServce会两个下级服务各调用5次,这样慢请求的比例会达到0.5,这次统计窗口就会触发熔断:
    第一次调用触发熔断

下一次统计窗口再发起请求时就不会调用下级服务,10个请求都失败:
在这里插入图片描述
这里原本以为熔断是熔断集群内的一个微服务节点(StockService2),但是发现其实是整个下级微服务集群的某个接口的熔断,查看代码确实是熔断的一个集群:
SentinelFeign.Builder返回了一个SentinelInvocationHandler
在这里插入图片描述


代理方法中methodHandler.invoke是Feign的原逻辑,根据服务名选择具体的微服务进行调用,上面的SphU.entry则是Sentinel处理一些流控或者熔断逻辑,如果被熔断就会走catch异常语句:
在这里插入图片描述


往里面走会获取到一个chain对象,是一个ProcessorSlot链对象,chain.entry()方法来对请求进行判断,是否需要熔断降级:
在这里插入图片描述


其中的一个实现类是DegradeSlot,在判断的时候,拿的是ResourceName是GET:http://stock-service/stock/reduceProduct,没有区分出是哪一个下级微服务(StockService1 or StockService2),只要tryPass匹配不通过,直接就往外抛DegradeException,后面的Feign原逻辑就不会往下走去调用下级微服务了。
在这里插入图片描述

5. Sentinel Dashboard 热点配置

热点配置是对接口的热点数据进行限制,比如/order/{productId},productId代表产品id,产品有热销跟不热销产品,在秒杀系统时,某些产品的访问量会比其他产品多。

  1. 热点配置只对@SentinelResource有限制反应,无法直接对某个接口进行热点限制,在查询接口上添加@SentinelResource
    @ResponseBody
    @GetMapping("/{productId}")
    @SentinelResource("query")
    public TProduct getProduct(@PathVariable("productId") String productId) {
        TProduct product = tProductService.getProduct(productId);
        return product;
    }
  1. 全局热点配置
  • 参数索引0,代表一个参数,即productId,不管productId是什么,查询阈值都是10
  • 单机阈值10QPS
    在这里插入图片描述
  1. 全局并发测试
    同时发起100045跟1000455产品的请求,并发5s,两个请求成功数都是50,可以看到两个不同商品的QPS互不影响,都是10

100045:
在这里插入图片描述


1000455:
在这里插入图片描述

  1. 例外热点配置
    热点规则的高级选项可以配置例外项,这里配置100046的QPS为15,注意参数类型一定要跟后端接口的参数类型对得上,否则例外配置不会生效:
    在这里插入图片描述
  2. 例外项并发测试
    100046产品并发5s,成功数是75,QPS是15
    在这里插入图片描述

100045的QPS没有变,还是10
在这里插入图片描述