https://sentinelguard.io/zh-cn/

简述

面向分布式、多语言异构化服务架构的流量治理组件,实现熔断与限流


安装

控制台面板使用docker进行安装,默认用户名和密码都是sentinel

docker run \
--name sentinel \
-p 8858:8858 \
-p 8719:8719 \
-d \
-e JVM_XMX=256m \
bladex/sentinel-dashboard

名称

端口

8858

控制台界面

8719

与控制台api命令交互


整合使用

将微服务纳入Sentinel监控

这里需要同时使用Nacos才能正常工作

pom依赖

<properties>
    <alibaba.cloud.version>2023.0.1.0</alibaba.cloud.version>
</properties>
​
<dependencies>
    <!-- Sentinel -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        <version>${alibaba.cloud.version}</version>
    </dependency>
    <!-- Nacos -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        <version>${alibaba.cloud.version}</version>
    </dependency>
</dependencies>

配置文件

spring:
  application:
    name: sentinel-learn-serivce
​
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.102.131:8848   # nacos的地址
        username: nacos
        password: nacos
​
    sentinel:
      transport:
        dashboard: 192.168.102.131:8858   # Sentinel控制台地址
        port: 8719                        # Sentinel控制台API命令交互地址
​
​
server:
  port: 8401

接口控制器

package com.xlyo.sentinellearnserivce.controller;
​
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
​
/**
 * 流量控制测试
 */
@RestController
public class FlowLimitController {
​
    @GetMapping("/testA")
    public String testA() {
        return "test => A";
    }
​
    @GetMapping("/testB")
    public String testB() {
        return "test => B";
    }
}

测试

现在访问接口

GET http://localhost:8401/testA
GET http://localhost:8401/testB

然后查看Sentinel控制台页面

GET http://192.168.102.131:8858/#/dashboard/home


流控模式

流控模式有三种模式

  • 直接

  • 关联

  • 链路


直接模式

设置单机阈值2,快速访问多次接口,就会看到直接返回的限流提示


关联模式

当关联的资源达到阈值时,就限流自己(A关联的资源B达到阈值后,限流A自己)

使用JMeter频繁请求接口/testB

JMeter请求的时候请求接口/testA,会发现被限流


链路模式

来自不同链路的请求对同一个目标访问时,实施针对性的不同限流措施(C请求来访问就限流,D请求来访问就通过)

新增服务层

package com.xlyo.sentinellearnserivce.service;
​
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.stereotype.Service;
​
@Service
public class FlowLimitService {
​
    @SentinelResource("common")
    public void common() {
        System.out.println("FlowLimitService => Come in");
    }
}

配置文件

spring:
  ...
    sentinel:
      ...
      web-context-unify: false            # controller层的方法对service层调用不认为是同一个根链路

上述配置开启后,其每个接口根路径会分开

接口新增

package com.xlyo.sentinellearnserivce.controller;
​
import com.xlyo.sentinellearnserivce.service.FlowLimitService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
​
/**
 * 流量控制测试
 */
@RestController
public class FlowLimitController {
    ...
​
    /**
     * C和D都访问flowLimitService.common()方法,阈值达到后对C限流,对D不管
     */
    @Autowired
    private FlowLimitService flowLimitService;
​
    @GetMapping("/testC")
    public String testC() {
        flowLimitService.common();
        return "test => C";
    }
​
    @GetMapping("/testD")
    public String testD() {
        flowLimitService.common();
        return "test => D";
    }
}

配置规则

访问测试

现在快速多次访问接口/testC会被限流,而/testD则不会


流控效果

其有三种流控效果

  • 快速失败(默认效果)

  • 预热

  • 排队等待

快速失败效果

直接失败,返回限流信息

预热效果

当流量突然增大的时候,我们常会希望系统从空闲状态繁忙状态的切换时间长一点

其公式为

  • threshold:单机阈值

  • coldFactor:冷却因子,默认为3

示例如下配置

则说明

  • 5秒以内,单机阈值为10/3≈3

  • 5秒过后,单机阈值慢慢到10

现在快速多次访问接口/testB,5秒内有几次会出现限流提示,5秒后就很难出现限流提示了


排队等待

主要用于处理间隔性突发流量,在某一秒有大量请求,之后几秒处于空闲状态,希望在接下来的空闲时间进行匀速处理


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

接口

/**
 * 排队等待测试
 */
@GetMapping("/testE")
public String testE() {
    System.out.println(Instant.now().toEpochMilli() + " => 排队等待...");
    return "test => E";
}

配置规则

JMeter

1秒打20个请求

测试

启动JMeter,观察控制台输出

可以看到以1秒/个的速率进行处理,只有前11个请求到达了,其余的全部超时阻止


熔断规则

其有3种熔断策略

  • 慢调用比例

  • 异常比例

  • 异常数

异常数慢调用比例

  • 1000ms持续进入5个请求,其响应时间(RT)大于200ms的请求数量大于比例阈值

  • 触发降级,断路器打开

  • 5s后尝试继续统计响应时间,小于则恢复,大于则继续熔断,等待5s

接口

/**
 * 慢调用比例测试
 */
@GetMapping("/testF")
public String testF() {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {}
​
    System.out.println(" => 慢调用比例");
    return "test => F";
}

JMeter

每秒10个请求,无限请求

测试

启动JMeter,之后手动访问接口/testF


异常比例

类似于慢调用比例,在单位统计时长,接口出现异常超过比例阈值就触发降级


异常数

类似于异常数,在单位统计时长,接口出现异常数量超过设定数量就触发降级


@SentinelResource

其是一个流量防卫组件注解,用于指定防卫资源,对配置的资源进行流量控制、熔断降级等功能

blockHandler

主要针对sentinel配置后出现的违规情况处理

接口

/**
 * SentinelResource测试1
 */
@SentinelResource(value = "bySentinelResource", blockHandler = "handleBlockHandler")
@GetMapping("/test/resource")
public String testResource() {
    return "test => resource";
}
public String handleBlockHandler(BlockException ex) {
    return "服务不可用(testResource)";
}

配置流控

此时簇点链路就有上述注解里指定的名称了

之后配置流控规则

测试

快速访问可以看见出现了自定义限流提示


fallback

主要针对程序异常JVM抛出的异常服务降级

接口

/**
 * SentinelResource测试2
 */
@SentinelResource(
        value = "doActionSentinelResource",
        blockHandler = "doActionBlockHandler",
        fallback = "doActionFallback"
)
@GetMapping("/test/do/{p1}")
public String doAction(@PathVariable("p1") Integer p1) {
    if (p1 == 0) throw new RuntimeException("p1不能为0");
    return "doAction => " + p1;
}
// 限流走这
public String doActionBlockHandler(@PathVariable("p1") Integer p1, BlockException ex) {
    System.err.println("自定义限流 => " + ex); // FlowException
    return "服务不可用(doAction)";
}
// 程序异常走这
public String doActionFallback(@PathVariable("p1") Integer p1, Throwable ex) {
    System.err.println("程序发生异常 => " + ex); // p1不能为0
    return 

配置

测试

blockHandler

GET http://localhost:8401/test/do/114514

fallback

GET http://localhost:8401/test/do/0


热点规则

热点及经常访问的数据,我们希望统计或限制某个热点数据中的访问频次最高的TopN数据,并对其访问进行限流或其它操作

接口

/**
 * 热点参数限流测试
 *      只要参数p1的QPS超过1秒,马上限流,参数p2不进行处理
 */
@SentinelResource(value = "testHotKey", blockHandler = "dealHandler_testHotKey")
@GetMapping("/testHotKey")
public String testHotKey(
        @RequestParam(value = "p1", required = false) String p1,
        @RequestParam(value = "p2", required = false) String p2
) {
    return "test => hotKey";
}
public String dealHandler_testHotKey(String p1, String p2, BlockException ex) {
    return "dealHandler_testHotKey => p1 = " + p1 + ", p2 = " + p2;
}

配置

  • 参数索引:从0开始,这里为参数p1

只要参数带p1,且超过阈值就会限流

测试

GET http://localhost:8401/testHotKey?p1=abc

GET HTTP://localhost:8401/testHotKey?p1=abc&p2=xyz

GET http://localhost:8401/testHotKey?p2=xyz


参数例外项

当参数为某个特定值时,采用另一种限流阈值

这里是当参数p1no时,QPS达到200才会限流


授权规则

根据调用接口的来源判断是否运行执行本次请求

这里提供了2种授权规则

  • 白名单:放行

  • 黑名单:禁止

接口

/**
* 授权规则测试
*/
@GetMapping("/testEmpower")
    public String testEmpower() {
    return "test => empower";
}

RequestOriginParser

package com.xlyo.sentinellearnserivce.handler;
​
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
​
@Component
public class MyRequestOriginParser implements RequestOriginParser {
​
    /**
     * 获取用来进行授权规则比较的值
     */
    @Override
    public String parseOrigin(HttpServletRequest request) {
        return request.getParameter("server-name");
    }
}

配置


测试

GET http://localhost:8401/testEmpower?server-name=test21

GET http://localhost:8401/testEmpower?server-name=test
GET http://localhost:8401/testEmpower?server-name=test2


规则持久化

目前配置的规则在重启或微服务重启后会丢失配置,生成环境中需要将配置规则持久化

我们需要将配置存入Nacos中,只要Nacos中的配置不删除,规则就持续有效

引入新依赖

<!-- Sentinel 规则持久化 -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <version>1.8.7</version>
</dependency>

配置文件

spring:
  application:
    name: sentinel-learn-serivce
​
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.102.131:8848   # nacos的地址
        username: nacos
        password: nacos
​
    sentinel:
      transport:
        dashboard: 192.168.102.131:8858   # Sentinel控制台地址
        port: 8719                        # Sentinel控制台API命令交互地址
      web-context-unify: false            # controller层的方法对service层调用不认为是同一个根链路
      datasource:
        ds1:    # 限流类型
          nacos:
            server-addr: 192.168.102.131:8848   # nacos的地址
            username: nacos
            password: nacos
            data-id: ${spring.application.name}-flow
            group-id: DEFAULT_GROUP
            data-type: json
            rule-type: flow                     # 此处为流控规则
#        ds2: # 限流类型
#          nacos:
#            server-addr: 192.168.102.131:8848   # nacos的地址
#            username: nacos
#            password: nacos
#            data-id: ${spring.application.name}-degrade
#            group-id: DEFAULT_GROUP
#            data-type: json
#            rule-type: degrade                  # 此处为熔断规则
​
server:
  port: 8401

这里的rule-type为限流类型,具体定义在com.alibaba.cloud.sentinel.datasource.RuleType

FLOW("flow", FlowRule.class),                   // 流控规则
DEGRADE("degrade", DegradeRule.class),          // 熔断规则
PARAM_FLOW("param-flow", ParamFlowRule.class),  // 热点规则
SYSTEM("system", SystemRule.class),             // 系统保护规则
AUTHORITY("authority", AuthorityRule.class),    // 访问控制规则
GW_FLOW("gw-flow", "com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule"),
GW_API_GROUP("gw-api-group", "com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition");

Nacos配置

[
    {
        "resource":"/testA", 	// 资源名称
        "limitApp":"default",	// 来源应用
        "grade":1,				// 阈值类型,0为线程数,1为QPS
        "count":1,				// 单机阈值
        "strategy":0,			// 流控模式,0为直接,1为关联,2为链路
        "controlBehavior":0,	// 流控效果,0为快速失败,1为WarmUp,2为排队等待
        "clusterMode":false		// 是否集群
    }
]

整合Open Feign统一fallback

整合Open Feign统一实现fallback服务降级

https://www.bilibili.com/video/BV1gW421P7RD?p=122

整合Gateway服务限流

整合Spring Gateway实现服务限流

https://www.bilibili.com/video/BV1gW421P7RD?p=126

规则,就是用来打破的( ̄へ ̄)!