Sentinel——网关限流

网关限流

Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流。[1]

从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:

  • route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
  • 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组

route维度

使用时需引入以下模块(以 Maven 为例):

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

官网写的是下面的配置,可能是版本问题,我看的教程是需要配置上面的模块

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
    <version>x.y.z</version>
</dependency>

完整依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.zjw</groupId>
    <artifactId>sc-06-sentinel-gateway-router-9000</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sc-06-sentinel-gateway-router-9000</name>
    <description>sc-06-sentinel-gateway-router-9000</description>
    <properties>
        <java.version>17</java.version>
        <spring-cloud.version>2022.0.0</spring-cloud.version>
        <spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
    </properties>
    <dependencies>
        <!--sentinel与spring cloud gateway整合依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <image>
                        <builder>paketobuildpacks/builder-jammy-base:latest</builder>
                    </image>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

server:
  port: 9000

spring:
  application:
    name: sentinel-gateway # 微服务名称
  cloud:
    nacos:
      discovery:
        server-addr: nacos-local:8848 # nacos注册中心地址
        username: nacos # 用户名密码
        password: nacos
    gateway:
      routes:
        - id: get_route
          uri: lb://depart-consumer
          predicates:
            - Path=/depart/get/**
        - id: list
          uri: lb://depart-consumer
          predicates:
            - Path=/depart/list
    sentinel: # sentinel配置
      eager: true # 饥饿加载,默认false
      filter:
        enabled: false  # 若在网关流控控制台上看到了 URL 资源
      transport:
        port: 8719  # sentinel内部启动的http服务
        dashboard: localhost:8888 # sentinel服务

启动类不需要额外配置。
sentinel控制台配置网关限流规则。

当请求过快,会出现以下界面。

控制台会报错,说方法不存在

点击查看代码
2023-12-07T02:15:29.713+08:00 ERROR 20520 --- [ctor-http-nio-3] reactor.netty.http.server.HttpServer     : [c632f6fc-1, L:/[0:0:0:0:0:0:0:1]:9000 - R:/[0:0:0:0:0:0:0:1]:13569] 

java.lang.NoSuchMethodError: 'org.springframework.web.reactive.function.server.ServerResponse$BodyBuilder org.springframework.web.reactive.function.server.ServerResponse.status(org.springframework.http.HttpStatus)'
	at com.alibaba.csp.sentinel.adapter.gateway.sc.callback.DefaultBlockRequestHandler.htmlErrorResponse(DefaultBlockRequestHandler.java:51) ~[sentinel-spring-cloud-gateway-adapter-1.8.6.jar:na]
	at com.alibaba.csp.sentinel.adapter.gateway.sc.callback.DefaultBlockRequestHandler.handleRequest(DefaultBlockRequestHandler.java:42) ~[sentinel-spring-cloud-gateway-adapter-1.8.6.jar:na]

自定义异常

重定向

package com.zjw;

import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;

@SpringBootApplication
public class SentinelGateWay9001Application {

    public static void main(String[] args) {
        SpringApplication.run(SentinelGateWay9001Application.class, args);
        // 初始化阻塞处理器
        initBlockHandler();
    }

    /**
     * 初始化阻塞处理器
     */
    private static void initBlockHandler() {
        //重定向
        GatewayCallbackManager.setBlockHandler(new RedirectBlockRequestHandler("https://baidu.com"));
    }

}

也可以在配置文件中添加重定向

    sentinel: # sentinel配置
      eager: true # 饥饿加载,默认false
      filter:
        enabled: false  # 若在网关流控控制台上看到了 URL 资源
      transport:
        port: 8719  # sentinel内部启动的http服务
        dashboard: localhost:8888 # sentinel服务
      scg:
        mode: redirect
        redirect: https://baidu.com

自定义结果

package com.zjw;

import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;

@SpringBootApplication
public class SentinelGateWay9001Application {

    public static void main(String[] args) {
        SpringApplication.run(SentinelGateWay9001Application.class, args);
        // 初始化阻塞处理器
        initBlockHandler();
    }

    /**
     * 初始化阻塞处理器
     */
    private static void initBlockHandler() {
        // 自定义异常结果
        GatewayCallbackManager.setBlockHandler((serverWebExchange, throwable) -> {
            Map<String, Object> map = new HashMap<>();
            map.put("uri", serverWebExchange.getRequest().getURI());
            map.put("msg","访问量过大,稍后请重试");
            map.put("code", HttpStatus.TOO_MANY_REQUESTS.value());

            return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(Mono.just(map), Map.class);
        });

    }
}

也可以在配置中配置,但是显得很呆

    sentinel: # sentinel配置
      scg:
        fallback:
          mode: response
          content-type: 'application/json'
          response-status: 429
          response-body: '{"code":"429","msg":"访问量过大,稍后请重试"}'

API维度

sentinel控制台配置

网关限流代码配置

实现api和route模式

package com.zjw;

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@SpringBootApplication
public class SentinelGateWay9001Application {

    private static final String API_GROUP1 = "API_group1";

    public static void main(String[] args) {
        SpringApplication.run(SentinelGateWay9001Application.class, args);
        // 初始化阻塞处理器
        initBlockHandler();
        // 初始化网关规则
//        initGatewayRuleRoute();
        initGatewayRuleAPI();
    }

    /**
     * 初始化网关规则 ROUTE
     */
    private static void initGatewayRuleRoute() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        /*
         * 结合文档和sentinel配置页面来写
         * 文档:https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81
         */
        GatewayFlowRule rule = new GatewayFlowRule();
        // API资源类型 route: RESOURCE_MODE_ROUTE_ID 0, api分组:RESOURCE_MODE_CUSTOM_API_NAME 1
        rule.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID);
        // 资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称
        rule.setResource("get_route");
        // 阈值类型 QPS:FLOW_GRADE_QPS 1 线程数:FLOW_GRADE_THREAD 0
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // QPS阈值
        rule.setCount(3);
        // 统计时间窗口,单位是秒,默认是 1 秒
        rule.setIntervalSec(1);
        // 流量整形的控制效果,同限流规则的 controlBehavior 字段,目前支持快速失败CONTROL_BEHAVIOR_DEFAULT和匀速排队CONTROL_BEHAVIOR_RATE_LIMITER两种模式,默认是快速失败0。
        rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
        // 应对突发请求时额外允许的请求数目,默认为int类型 0
        rule.setBurst(0);
        rules.add(rule);

        GatewayRuleManager.loadRules(rules);
    }

    /**
     * 初始化网关规则 API
     */
    private static void initGatewayRuleAPI() {

        //初始化API分组
        initCustomizedApis();

        Set<GatewayFlowRule> rules = new HashSet<>();
        /*
         * 结合文档和sentinel配置页面来写
         * 文档:https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81
         */
        GatewayFlowRule rule = new GatewayFlowRule();
        // API资源类型 route: RESOURCE_MODE_ROUTE_ID 0, api分组:RESOURCE_MODE_CUSTOM_API_NAME 1
        rule.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME);
        // 资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称
        rule.setResource(API_GROUP1);
        // 阈值类型 QPS:FLOW_GRADE_QPS 1 线程数:FLOW_GRADE_THREAD 0
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // QPS阈值
        rule.setCount(3);
        // 统计时间窗口,单位是秒,默认是 1 秒
        rule.setIntervalSec(1);
        // 流量整形的控制效果,同限流规则的 controlBehavior 字段,目前支持快速失败CONTROL_BEHAVIOR_DEFAULT和匀速排队CONTROL_BEHAVIOR_RATE_LIMITER两种模式,默认是快速失败0。
        rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
        // 应对突发请求时额外允许的请求数目,默认为int类型 0
        rule.setBurst(0);
        rules.add(rule);

        GatewayRuleManager.loadRules(rules);
    }

    /**
     * 初始化API分组
     */
    private static void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api1 = new ApiDefinition(API_GROUP1)
                //匹配模式 支持精确匹配(PARAM_MATCH_STRATEGY_EXACT)、子串匹配(PARAM_MATCH_STRATEGY_CONTAINS)和正则匹配(PARAM_MATCH_STRATEGY_REGEX),默认是精确匹配
                .setPredicateItems(new HashSet<>() {{
                    add(new ApiPathPredicateItem().setPattern("/depart/update"));
                    add(new ApiPathPredicateItem().setPattern("/depart/get/.*")
                            .setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_REGEX));
                }});
        definitions.add(api1);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

    /**
     * 初始化阻塞处理器
     */
    private static void initBlockHandler() {
        //重定向
//        GatewayCallbackManager.setBlockHandler(new RedirectBlockRequestHandler("https://baidu.com"));
        // 自定义异常结果
        GatewayCallbackManager.setBlockHandler((serverWebExchange, throwable) -> {
            Map<String, Object> map = new HashMap<>();
            map.put("uri", serverWebExchange.getRequest().getURI());
            map.put("msg","访问量过大,稍后请重试");
            map.put("code", HttpStatus.TOO_MANY_REQUESTS.value());

            return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(Mono.just(map), Map.class);
        });
    }
}

  1. https://github.com/alibaba/Sentinel/wiki/网关限流 ↩︎

posted @ 2023-12-07 02:21  雨中遐想  阅读(321)  评论(0编辑  收藏  举报